gator-driver: ARM DS-5.8 Streamline gator driver (RC1)
authorPawel Moll <pawel.moll@arm.com>
Thu, 17 Nov 2011 18:14:03 +0000 (18:14 +0000)
committerPawel Moll <pawel.moll@arm.com>
Thu, 17 Nov 2011 18:14:03 +0000 (18:14 +0000)
driver/Makefile
driver/gator_annotate.c
driver/gator_annotate_kernel.c [new file with mode: 0644]
driver/gator_backtrace.c
driver/gator_cookies.c
driver/gator_ebs.c
driver/gator_events_armv7.c
driver/gator_main.c
driver/gator_trace_sched.c

index de599579f3ee7874178be0037547bff65dd8f24e..b3981ff0aee0b7612008d1028272ded8d45426ae 100644 (file)
@@ -1,5 +1,8 @@
 ifneq ($(KERNELRELEASE),)
 
+# Uncomment the following line to enable kernel stack unwinding within gator, or update gator_backtrace.c
+# EXTRA_CFLAGS +=      -DGATOR_KERNEL_STACK_UNWINDING
+
 obj-m := gator.o
 
 gator-y :=     gator_main.o \
@@ -12,8 +15,13 @@ gator-y :=   gator_main.o \
 gator-y +=     gator_events_mmaped.o
 
 ifneq ($(GATOR_WITH_MALI_SUPPORT),)
+ifeq ($(GATOR_WITH_MALI_SUPPORT),MALI_T6xx)
+gator-y +=     gator_events_mali_t6xx.o
+else
 gator-y +=     gator_events_mali.o
 endif
+EXTRA_CFLAGS +=        -DMALI_SUPPORT=$(GATOR_WITH_MALI_SUPPORT)
+endif
 
 gator-$(CONFIG_ARM) += gator_events_armv6.o \
                        gator_events_armv7.o \
index e096d1552e3140d5c0401e6a6d2e1e40b66149bb..ee5a1601f4b3618d26711f624a464bd484e7729d 100644 (file)
@@ -27,27 +27,35 @@ static bool collect_annotations = false;
 static ssize_t annotate_write(struct file *file, char const __user *buf, size_t count, loff_t *offset)
 {
        char tempBuffer[512];
-       int retval, remaining, size;
+       int remaining, size;
+       uint32_t tid;
 
        if (*offset)
                return -EINVAL;
 
        // determine size to capture
-       remaining = ANNOTATE_SIZE - annotatePos - 256; // pad for headers and release
        size = count < sizeof(tempBuffer) ? count : sizeof(tempBuffer);
-       size = size < remaining ? size : remaining;
-       if (size <= 0) {
-               wake_up(&gator_buffer_wait);
-               return 0;
+
+       // note: copy may be for naught if remaining is zero, but better to do the copy outside of the spinlock
+       if (file == NULL) {
+               // copy from kernel
+               memcpy(tempBuffer, buf, size);
+
+               // set the thread id to the kernel thread, not the current thread
+               tid = -1;
+       } else {
+               // copy from user space
+               if (copy_from_user(tempBuffer, buf, size) != 0)
+                       return -EINVAL;
+               tid = current->pid;
        }
 
-       // copy from user space
-       retval = copy_from_user(tempBuffer, buf, size);
-       if (retval == 0) {
-               // synchronize shared variables annotateBuf and annotatePos
-               spin_lock(&annotate_lock);
-               if (collect_annotations && annotateBuf) {
-                       uint32_t tid = current->pid;
+       // synchronize shared variables annotateBuf and annotatePos
+       spin_lock(&annotate_lock);
+       if (collect_annotations && annotateBuf) {
+               remaining = ANNOTATE_SIZE - annotatePos - 256; // pad for headers and release
+               size = size < remaining ? size : remaining;
+               if (size > 0) {
                        uint64_t time = gator_get_time();
                        uint32_t cpuid = smp_processor_id();
                        int pos = annotatePos;
@@ -58,17 +66,20 @@ static ssize_t annotate_write(struct file *file, char const __user *buf, size_t
                        memcpy(&annotateBuf[pos], tempBuffer, size);
                        annotatePos = pos + size;
                }
-               spin_unlock(&annotate_lock);
+       }
+       spin_unlock(&annotate_lock);
 
-               // return the number of bytes written
-               retval = size;
-       } else {
-               retval = -EINVAL;
+       if (size <= 0) {
+               wake_up(&gator_buffer_wait);
+               return 0;
        }
 
-       return retval;
+       // return the number of bytes written
+       return size;
 }
 
+#include "gator_annotate_kernel.c"
+
 static int annotate_release(struct inode *inode, struct file *file)
 {
        int remaining = ANNOTATE_SIZE - annotatePos;
diff --git a/driver/gator_annotate_kernel.c b/driver/gator_annotate_kernel.c
new file mode 100644 (file)
index 0000000..4f690e8
--- /dev/null
@@ -0,0 +1,91 @@
+/**
+ * Copyright (C) ARM Limited 2011. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+static void kannotate_write(char* ptr, unsigned int size)
+{
+       int retval;
+       int pos = 0;
+       loff_t offset = 0;
+       while (pos < size) {
+                retval = annotate_write(NULL, &ptr[pos], size - pos, &offset);
+                if (retval < 0) {
+                        printk(KERN_WARNING "gator: kannotate_write failed with return value %d\n", retval);
+                        return;
+                }
+                pos += retval;
+       }
+}
+
+// String annotation
+void gator_annotate(char* string)
+{
+       printk(KERN_ERR "module: %s\n", string);
+       kannotate_write(string, strlen(string) + 1);
+}
+EXPORT_SYMBOL(gator_annotate);
+
+// String annotation with color
+void gator_annotate_color(int color, char* string)
+{
+       kannotate_write((char*)&color, sizeof(color));
+       kannotate_write(string, strlen(string) + 1);
+}
+EXPORT_SYMBOL(gator_annotate_color);
+
+// Terminate an annotation
+void gator_annotate_end(void)
+{
+       char nul = 0;
+       kannotate_write(&nul, sizeof(nul));
+}
+EXPORT_SYMBOL(gator_annotate_end);
+
+// Image annotation with optional string
+void gator_annotate_visual(char* data, unsigned int length, char* string)
+{
+       long long visual_annotation = 0x011c | (strlen(string) << 16) | ((long long)length << 32);
+       kannotate_write((char*)&visual_annotation, 8);
+       kannotate_write(string, strlen(string));
+       kannotate_write(data, length);
+}
+EXPORT_SYMBOL(gator_annotate_visual);
+
+// Marker annotation
+void gator_annotate_marker(void)
+{
+       int marker_annotation = 0x00021c;
+       kannotate_write((char*)&marker_annotation, 3);
+}
+EXPORT_SYMBOL(gator_annotate_marker);
+
+// Marker annotation with a string
+void gator_annotate_marker_str(char* string)
+{
+       int marker_annotation = 0x021c;
+       kannotate_write((char*)&marker_annotation, 2);
+       kannotate_write(string, strlen(string) + 1);
+}
+EXPORT_SYMBOL(gator_annotate_marker_str);
+
+// Marker annotation with a color
+void gator_annotate_marker_color(int color)
+{
+       long long marker_annotation = (0x021c | ((long long)color << 16)) & 0x0000ffffffffffffLL;
+       kannotate_write((char*)&marker_annotation, 7);
+}
+EXPORT_SYMBOL(gator_annotate_marker_color);
+
+// Marker annotationw ith a string and color
+void gator_annotate_marker_color_str(int color, char* string)
+{
+       long long marker_annotation = 0x021c | ((long long)color << 16);
+       kannotate_write((char*)&marker_annotation, 6);
+       kannotate_write(string, strlen(string) + 1);
+}
+EXPORT_SYMBOL(gator_annotate_marker_color_str);
index b5b0e6363bbb87bb8a5d0b76c46be87fdf1bd4ed..fc81233c99eeec7dc9387ccc1339b15c3fd8b5f1 100644 (file)
@@ -23,6 +23,7 @@ static void arm_backtrace_eabi(int cpu, int buftype, struct pt_regs * const regs
        struct frame_tail_eabi *ptrtail;
        struct frame_tail_eabi buftail;
        unsigned long fp = regs->ARM_fp;
+       unsigned long sp = regs->ARM_sp;
        unsigned long lr = regs->ARM_lr;
        int is_user_mode = user_mode(regs);
 
@@ -34,7 +35,7 @@ static void arm_backtrace_eabi(int cpu, int buftype, struct pt_regs * const regs
        gator_add_trace(cpu, buftype, lr);
 
        /* check tail is valid */
-       if (fp == 0) {
+       if (fp == 0 || fp < sp) {
                return;
        }
 
@@ -66,3 +67,50 @@ static void arm_backtrace_eabi(int cpu, int buftype, struct pt_regs * const regs
        }
 #endif
 }
+
+#if defined(__arm__)
+static DEFINE_PER_CPU(int, backtrace_buffer);
+static int report_trace(struct stackframe *frame, void *d)
+{
+       struct module *mod;
+       unsigned int *depth = d, addr = frame->pc, cookie = NO_COOKIE, cpu = smp_processor_id();
+
+       if (*depth) {
+               mod = __module_address(addr);
+               if (mod) {
+                       cookie = get_cookie(cpu, per_cpu(backtrace_buffer, cpu), current, NULL, mod, true);
+                       addr = addr - (unsigned long)mod->module_core;
+               }
+               gator_buffer_write_packed_int(cpu, per_cpu(backtrace_buffer, cpu), addr & ~1);
+               gator_buffer_write_packed_int(cpu, per_cpu(backtrace_buffer, cpu), cookie);
+               (*depth)--;
+       }
+
+       return *depth == 0;
+}
+#endif
+
+// Uncomment the following line to enable kernel stack unwinding within gator, note it can also be defined from the Makefile
+// #define GATOR_KERNEL_STACK_UNWINDING
+static void kernel_backtrace(int cpu, int buftype, struct pt_regs * const regs)
+{
+#if defined(__arm__)
+#ifdef GATOR_KERNEL_STACK_UNWINDING
+       int depth = gator_backtrace_depth;
+#else
+       int depth = 1;
+#endif
+       struct stackframe frame;
+       if (depth == 0)
+               depth = 1;
+       frame.fp = regs->ARM_fp;
+       frame.sp = regs->ARM_sp;
+       frame.lr = regs->ARM_lr;
+       frame.pc = regs->ARM_pc;
+       per_cpu(backtrace_buffer, cpu) = buftype;
+       walk_stackframe(&frame, report_trace, &depth);
+#else
+       gator_buffer_write_packed_int(cpu, buftype, PC_REG & ~1);
+       gator_buffer_write_packed_int(cpu, buftype, NO_COOKIE);
+#endif
+}
index a646fb23f04e6c28e4e6fd60115fa44b6e0daae5..c0c4b9c6ac7c1b338ecb6e51debb2f96ea94f35f 100644 (file)
@@ -122,12 +122,18 @@ static void wq_cookie_handler(struct work_struct *unused)
        int cpu = smp_processor_id();
        unsigned int cookie, commit;
 
-       commit = per_cpu(translate_buffer_write, cpu);
-       while (per_cpu(translate_buffer_read, cpu) != commit) {
-               task = (struct task_struct *)translate_buffer_read_int(cpu);
-               vma = (struct vm_area_struct *)translate_buffer_read_int(cpu);
-               cookie = get_cookie(cpu, TIMER_BUF, task, vma, NULL, false);
+       mutex_lock(&start_mutex);
+
+       if (gator_started != 0) {
+               commit = per_cpu(translate_buffer_write, cpu);
+               while (per_cpu(translate_buffer_read, cpu) != commit) {
+                       task = (struct task_struct *)translate_buffer_read_int(cpu);
+                       vma = (struct vm_area_struct *)translate_buffer_read_int(cpu);
+                       cookie = get_cookie(cpu, TIMER_BUF, task, vma, NULL, false);
+               }
        }
+
+       mutex_unlock(&start_mutex);
 }
 
 // Retrieve full name from proc/pid/cmdline for java processes on Android
index 9b5534700153bd85c6f36e0c44ece749c50b9744..8b4b5ff7122fd71669f31555a6de9600a0989de2 100644 (file)
@@ -95,16 +95,19 @@ static int gator_event_sampling_start(void)
                int irq, i;
 
                if (IS_ERR(pmu_device)) {
-                       pr_err("gator: event based sampling is not supported as the kernel function reserve_pmu() failed");
+                       pr_err("gator: event based sampling is not supported as the kernel function reserve_pmu() failed\n");
                        return -1;
                }
 
-               init_pmu(ARM_PMU_DEVICE_CPU);
+               // init_pmu sets the irq affinity, therefore we do not care if it fails for single core
+               if (init_pmu(ARM_PMU_DEVICE_CPU) != 0 && gator_cpu_cores > 1) {
+                       pr_err("gator: unable to initialize the pmu\n");
+                       goto out_ebs_start;
+               }
+
                if (pmu_device->num_resources == 0) {
                        pr_err("gator: no irqs for PMUs defined\n");
-                       release_pmu(pmu_device);
-                       pmu_device = NULL;
-                       return -1;
+                       goto out_ebs_start;
                }
 
                for (i = 0; i < pmu_device->num_resources; ++i) {
@@ -121,9 +124,7 @@ static int gator_event_sampling_start(void)
                                        if (irq >= 0)
                                                free_irq(irq, NULL);
                                }
-                               release_pmu(pmu_device);
-                               pmu_device = NULL;
-                               return -1;
+                               goto out_ebs_start;
                        }
                }
        }
@@ -135,6 +136,17 @@ static int gator_event_sampling_start(void)
 #endif
 
        return 0;
+
+#if LINUX_PMU_SUPPORT
+out_ebs_start:
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0)
+       release_pmu(pmu_device);
+#else
+       release_pmu(ARM_PMU_DEVICE_CPU);
+#endif
+       pmu_device = NULL;
+       return -1;
+#endif
 }
 
 static void gator_event_sampling_stop(void)
@@ -148,8 +160,13 @@ static void gator_event_sampling_stop(void)
                                free_irq(irq, NULL);
                }
        }
-       if (!IS_ERR(pmu_device))
+       if (!IS_ERR(pmu_device)) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0)
                release_pmu(pmu_device);
+#else
+               release_pmu(ARM_PMU_DEVICE_CPU);
+#endif
+       }
        pmu_device = NULL;
 #endif
 }
index 58855f8ed3b7fee5bf0e45c8327db5518257666d..e1434e2147f0e64910aa58ad23235887fd8521b9 100644 (file)
@@ -204,6 +204,7 @@ static void gator_events_armv7_online(void)
 
 static void gator_events_armv7_offline(void)
 {
+       // disbale all counters, including PMCCNTR; overflow IRQs will not be signaled
        armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E);
 }
 
index 91744adc9b2ce17dc81b3ba7380ebd42c4f2ad47..36e951ba915817309bd8b7599f59a27d451def8c 100644 (file)
@@ -7,7 +7,7 @@
  *
  */
 
-static unsigned long gator_protocol_version = 6;
+static unsigned long gator_protocol_version = 7;
 
 #include <linux/slab.h>
 #include <linux/cpu.h>
@@ -17,6 +17,8 @@ static unsigned long gator_protocol_version = 6;
 #include <linux/hardirq.h>
 #include <linux/highmem.h>
 #include <linux/pagemap.h>
+#include <linux/suspend.h>
+#include <asm/stacktrace.h>
 #include <asm/uaccess.h>
 
 #include "gator.h"
@@ -51,7 +53,7 @@ static unsigned long gator_protocol_version = 6;
 /******************************************************************************
  * DEFINES
  ******************************************************************************/
-#define TIMER_BUFFER_SIZE_DEFAULT      (256*1024)
+#define TIMER_BUFFER_SIZE_DEFAULT      (512*1024)
 #define EVENT_BUFFER_SIZE_DEFAULT      (128*1024)
 
 #define NO_COOKIE                              0UL
@@ -267,11 +269,12 @@ static void gator_add_trace(int cpu, int buftype, unsigned int address)
 
 static void gator_add_sample(int cpu, int buftype, struct pt_regs * const regs)
 {
-       struct module *mod;
-       unsigned int addr, cookie = 0;
        int inKernel = regs ? !user_mode(regs) : 1;
        unsigned long exec_cookie = inKernel ? NO_COOKIE : get_exec_cookie(cpu, buftype, current);
 
+       if (!regs)
+               return;
+
        gator_buffer_write_packed_int(cpu, buftype, MESSAGE_START_BACKTRACE);
        gator_buffer_write_packed_int64(cpu, buftype, gator_get_time());
        gator_buffer_write_packed_int(cpu, buftype, exec_cookie);
@@ -279,25 +282,15 @@ static void gator_add_sample(int cpu, int buftype, struct pt_regs * const regs)
        gator_buffer_write_packed_int(cpu, buftype, (unsigned int)current->pid);
        gator_buffer_write_packed_int(cpu, buftype, inKernel);
 
-       // get_irq_regs() will return NULL outside of IRQ context (e.g. nested IRQ)
-       if (regs) {
-               if (inKernel) {
-                       addr = PC_REG;
-                       mod = __module_address(addr);
-                       if (mod) {
-                               cookie = get_cookie(cpu, buftype, current, NULL, mod, true);
-                               addr = addr - (unsigned long)mod->module_core;
-                       }
-                       gator_buffer_write_packed_int(cpu, buftype, addr & ~1);
-                       gator_buffer_write_packed_int(cpu, buftype, cookie);
-               } else {
-                       // Cookie+PC
-                       gator_add_trace(cpu, buftype, PC_REG);
-
-                       // Backtrace
-                       if (gator_backtrace_depth)
-                               arm_backtrace_eabi(cpu, buftype, regs, gator_backtrace_depth);
-               }
+       if (inKernel) {
+               kernel_backtrace(cpu, buftype, regs);
+       } else {
+               // Cookie+PC
+               gator_add_trace(cpu, buftype, PC_REG);
+
+               // Backtrace
+               if (gator_backtrace_depth)
+                       arm_backtrace_eabi(cpu, buftype, regs, gator_backtrace_depth);
        }
 
        gator_buffer_write_packed_int(cpu, buftype, MESSAGE_END_BACKTRACE);
@@ -352,11 +345,12 @@ static void gator_timer_interrupt(void)
                        }
                } else if (gi->read64) {
                        len = gi->read64(&buffer64);
-                       if (len > 0)
+                       if (len > 0) {
                                gator_buffer_write_packed_int(cpu, buftype, len);
                                for (i = 0; i < len; i++) {
                                        gator_buffer_write_packed_int64(cpu, buftype, buffer64[i]);
                                }
+                       }
                }
        }
        gator_buffer_write_packed_int(cpu, buftype, 0);
@@ -460,29 +454,28 @@ static uint64_t gator_get_time(void)
        struct timespec ts;
        uint64_t timestamp;
 
-       ktime_get_ts(&ts);
+       getnstimeofday(&ts);
        timestamp = timespec_to_ns(&ts);
 
        return timestamp;
 }
 
 /******************************************************************************
- * cpu online notifier
+ * cpu online and pm notifiers
  ******************************************************************************/
-static int __cpuinit gator_cpu_notify(struct notifier_block *self,
-                                                                                       unsigned long action, void *hcpu)
+static int __cpuinit gator_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu)
 {
        long cpu = (long)hcpu;
 
        switch (action) {
-               case CPU_ONLINE:
-               case CPU_ONLINE_FROZEN:
-                       smp_call_function_single(cpu, __gator_timer_online, NULL, 1);
-                       break;
                case CPU_DOWN_PREPARE:
                case CPU_DOWN_PREPARE_FROZEN:
                        smp_call_function_single(cpu, __gator_timer_offline, NULL, 1);
                        break;
+               case CPU_ONLINE:
+               case CPU_ONLINE_FROZEN:
+                       smp_call_function_single(cpu, __gator_timer_online, NULL, 1);
+                       break;
        }
 
        return NOTIFY_OK;
@@ -492,13 +485,45 @@ static struct notifier_block __refdata gator_cpu_notifier = {
        .notifier_call = gator_cpu_notify,
 };
 
+// n.b. calling "on_each_cpu" only runs on those that are online
+// Registered linux events are not disabled, so their counters will continue to collect
+static int gator_pm_notify(struct notifier_block *nb, unsigned long event, void *dummy)
+{
+       switch (event) {
+               case PM_HIBERNATION_PREPARE:
+               case PM_SUSPEND_PREPARE:
+                       unregister_hotcpu_notifier(&gator_cpu_notifier);
+                       unregister_scheduler_tracepoints();
+                       on_each_cpu(trace_sched_insert_idle, NULL, 1);
+                       on_each_cpu(__gator_timer_offline, NULL, 1);
+                       break;
+               case PM_POST_HIBERNATION:
+               case PM_POST_SUSPEND:
+                       on_each_cpu(__gator_timer_online, NULL, 1);
+                       register_scheduler_tracepoints();
+                       register_hotcpu_notifier(&gator_cpu_notifier);
+                       break;
+       }
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block gator_pm_notifier = {
+       .notifier_call = gator_pm_notify,
+};
+
 static int gator_notifier_start(void)
 {
-       return register_hotcpu_notifier(&gator_cpu_notifier);
+       int retval;
+       retval = register_hotcpu_notifier(&gator_cpu_notifier);
+       if (retval == 0)
+               retval = register_pm_notifier(&gator_pm_notifier);
+       return retval;
 }
 
 static void gator_notifier_stop(void)
 {
+       unregister_pm_notifier(&gator_pm_notifier);
        unregister_hotcpu_notifier(&gator_cpu_notifier);
 }
 
index 7c0bd47ff2cf0ce1ba42aab7ca5623c982342d2e..0225bfb50470264319e5c3ed8d43d4110d9bb28d 100644 (file)
 #define SCHED_SWITCH                   1
 #define SCHED_PROCESS_FREE             2
 
-#define FIELD_TYPE                             0
-#define FIELD_TIME                             1
-#define FIELD_PARAM1                   2
-#define FIELD_PARAM2                   3
-#define FIELD_PARAM3                   4
-#define FIELDS_PER_SCHED               5
-
 #define SCHEDSIZE                              (8*1024)
 #define TASK_MAP_ENTRIES               1024            /* must be power of 2 */
 #define TASK_MAX_COLLISIONS            2
@@ -31,7 +24,13 @@ static DEFINE_PER_CPU(int, theSchedPos);
 static DEFINE_PER_CPU(int, theSchedErr);
 static DEFINE_PER_CPU(uint64_t *, taskname_keys);
 
-void emit_pid_name(uint64_t time, struct task_struct* task)
+enum {
+       STATE_CONTENTION = 0,
+       STATE_WAIT_ON_IO,
+       STATE_WAIT_ON_OTHER
+};
+
+void emit_pid_name(struct task_struct* task)
 {
        bool found = false;
        unsigned long flags;
@@ -68,55 +67,92 @@ void emit_pid_name(uint64_t time, struct task_struct* task)
                // disable interrupts to synchronize with hrtimer populating timer buf
                local_irq_save(flags);
                gator_buffer_write_packed_int(cpu, TIMER_BUF, MESSAGE_PID_NAME);
-               gator_buffer_write_packed_int64(cpu, TIMER_BUF, time);
+               gator_buffer_write_packed_int64(cpu, TIMER_BUF, gator_get_time());
                gator_buffer_write_packed_int(cpu, TIMER_BUF, task->pid);
                gator_buffer_write_string(cpu, TIMER_BUF, taskcomm);
                local_irq_restore(flags);
        }
 }
 
-static void probe_sched_write(int type, int param1, int param2, int param3)
+static void probe_sched_write(int type, struct task_struct* task, struct task_struct* old_task)
 {
+       int schedPos, cookie = 0, state = 0;
        unsigned long flags;
+       uint64_t *schedBuf, time;
        int cpu = smp_processor_id();
-       uint64_t time = gator_get_time();
-       uint64_t *schedBuf;
-       int schedPos, cookie = param3;
+       int pid = task->pid;
+       int tgid = task->tgid;
 
        if (!per_cpu(theSchedBuf, cpu)[per_cpu(theSchedSel, cpu)])
                return;
 
-       if (param3) {
+       if (type == SCHED_SWITCH) {
                // do as much work as possible before disabling interrupts
-               struct task_struct *task = (struct task_struct *)param3;
                cookie = get_exec_cookie(cpu, TIMER_BUF, task);
-               emit_pid_name(time, task);
+               emit_pid_name(task);
+               if (old_task->state == 0)
+                       state = STATE_CONTENTION;
+               else if (old_task->in_iowait)
+                       state = STATE_WAIT_ON_IO;
+               else
+                       state = STATE_WAIT_ON_OTHER;
        }
 
        // disable interrupts to synchronize with gator_trace_sched_read(); spinlocks not needed since percpu buffers are used
        local_irq_save(flags);
 
+       time = gator_get_time();
        schedPos = per_cpu(theSchedPos, cpu);
        schedBuf = per_cpu(theSchedBuf, cpu)[per_cpu(theSchedSel, cpu)];
 
-       if (schedPos < (SCHEDSIZE-100)) {
+       if (schedPos < (SCHEDSIZE - 100)) {
                // capture
-               schedBuf[schedPos+FIELD_TYPE] = type;
-               schedBuf[schedPos+FIELD_TIME] = time;
-               schedBuf[schedPos+FIELD_PARAM1] = param1;
-               schedBuf[schedPos+FIELD_PARAM2] = param2;
-               schedBuf[schedPos+FIELD_PARAM3] = cookie;
-               per_cpu(theSchedPos, cpu) = schedPos + FIELDS_PER_SCHED;
+               schedBuf[schedPos++] = type;
+               schedBuf[schedPos++] = time;
+               schedBuf[schedPos++] = pid;
+               schedBuf[schedPos++] = tgid;
+               schedBuf[schedPos++] = cookie;
+               schedBuf[schedPos++] = state;
        } else if (!per_cpu(theSchedErr, cpu)) {
                per_cpu(theSchedErr, cpu) = 1;
-               schedBuf[schedPos+FIELD_TYPE] = SCHED_OVERFLOW;
-               schedBuf[schedPos+FIELD_TIME] = time;
-               schedBuf[schedPos+FIELD_PARAM1] = 0;
-               schedBuf[schedPos+FIELD_PARAM2] = 0;
-               schedBuf[schedPos+FIELD_PARAM3] = 0;
-               per_cpu(theSchedPos, cpu) = schedPos + FIELDS_PER_SCHED;
+               schedBuf[schedPos++] = SCHED_OVERFLOW;
+               schedBuf[schedPos++] = time;
+               schedBuf[schedPos++] = 0;
+               schedBuf[schedPos++] = 0;
+               schedBuf[schedPos++] = 0;
+               schedBuf[schedPos++] = 0;
                pr_debug("gator: tracepoint overflow\n");
        }
+       per_cpu(theSchedPos, cpu) = schedPos;
+       local_irq_restore(flags);
+}
+
+// special case used during a suspend of the system
+static void trace_sched_insert_idle(void* unused)
+{
+       unsigned long flags;
+       uint64_t *schedBuf;
+       int schedPos, cpu = smp_processor_id();
+
+       if (!per_cpu(theSchedBuf, cpu)[per_cpu(theSchedSel, cpu)])
+               return;
+
+       local_irq_save(flags);
+
+       schedPos = per_cpu(theSchedPos, cpu);
+       schedBuf = per_cpu(theSchedBuf, cpu)[per_cpu(theSchedSel, cpu)];
+
+       if (schedPos < (SCHEDSIZE - (6 * 8))) {
+               // capture
+               schedBuf[schedPos++] = SCHED_SWITCH;
+               schedBuf[schedPos++] = gator_get_time();
+               schedBuf[schedPos++] = 0; // idle pid is zero
+               schedBuf[schedPos++] = 0; // idle tid is zero
+               schedBuf[schedPos++] = 0; // idle cookie is zero
+               schedBuf[schedPos++] = STATE_WAIT_ON_OTHER;
+       }
+
+       per_cpu(theSchedPos, cpu) = schedPos;
        local_irq_restore(flags);
 }
 
@@ -126,12 +162,12 @@ GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct rq *rq, struct task_struct *pre
 GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct task_struct *prev, struct task_struct *next))
 #endif
 {
-       probe_sched_write(SCHED_SWITCH, next->pid, next->tgid, (int)next);
+       probe_sched_write(SCHED_SWITCH, next, prev);
 }
 
 GATOR_DEFINE_PROBE(sched_process_free, TP_PROTO(struct task_struct *p))
 {
-       probe_sched_write(SCHED_PROCESS_FREE, p->pid, 0, 0);
+       probe_sched_write(SCHED_PROCESS_FREE, p, 0);
 }
 
 int gator_trace_sched_init(void)
@@ -139,6 +175,25 @@ int gator_trace_sched_init(void)
        return 0;
 }
 
+static int register_scheduler_tracepoints(void) {
+       // register tracepoints
+       if (GATOR_REGISTER_TRACE(sched_switch))
+               goto fail_sched_switch;
+       if (GATOR_REGISTER_TRACE(sched_process_free))
+               goto fail_sched_process_free;
+       pr_debug("gator: registered tracepoints\n");
+
+       return 0;
+
+       // unregister tracepoints on error
+fail_sched_process_free:
+       GATOR_UNREGISTER_TRACE(sched_switch);
+fail_sched_switch:
+       pr_err("gator: tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n");
+
+       return -1;
+}
+
 int gator_trace_sched_start(void)
 {
        int cpu, size;
@@ -159,30 +214,20 @@ int gator_trace_sched_start(void)
                memset(per_cpu(taskname_keys, cpu), 0, size);
        }
 
-       // register tracepoints
-       if (GATOR_REGISTER_TRACE(sched_switch))
-               goto fail_sched_switch;
-       if (GATOR_REGISTER_TRACE(sched_process_free))
-               goto fail_sched_process_free;
-       pr_debug("gator: registered tracepoints\n");
-
-       return 0;
+       return register_scheduler_tracepoints();
+}
 
-       // unregister tracepoints on error
-fail_sched_process_free:
+static void unregister_scheduler_tracepoints(void)
+{
        GATOR_UNREGISTER_TRACE(sched_switch);
-fail_sched_switch:
-       pr_err("gator: tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n");
-
-       return -1;
+       GATOR_UNREGISTER_TRACE(sched_process_free);
+       pr_debug("gator: unregistered tracepoints\n");
 }
 
 void gator_trace_sched_stop(void)
 {
        int cpu;
-       GATOR_UNREGISTER_TRACE(sched_switch);
-       GATOR_UNREGISTER_TRACE(sched_process_free);
-       pr_debug("gator: unregistered tracepoints\n");
+       unregister_scheduler_tracepoints();
 
        for_each_present_cpu(cpu) {
                kfree(per_cpu(theSchedBuf, cpu)[0]);