perf_counter: track task-comm data
authorPeter Zijlstra <a.p.zijlstra@chello.nl>
Wed, 8 Apr 2009 13:01:30 +0000 (15:01 +0200)
committerIngo Molnar <mingo@elte.hu>
Wed, 8 Apr 2009 17:05:47 +0000 (19:05 +0200)
Similar to the mmap data stream, add one that tracks the task COMM field,
so that the userspace reporting knows what to call a task.

Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com>
LKML-Reference: <20090408130409.127422406@chello.nl>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
fs/exec.c
include/linux/perf_counter.h
kernel/perf_counter.c

index e015c0b..bf47ed0 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -951,6 +951,7 @@ void set_task_comm(struct task_struct *tsk, char *buf)
        task_lock(tsk);
        strlcpy(tsk->comm, buf, sizeof(tsk->comm));
        task_unlock(tsk);
+       perf_counter_comm(tsk);
 }
 
 int flush_old_exec(struct linux_binprm * bprm)
index 8bf764f..a70a55f 100644 (file)
@@ -142,8 +142,9 @@ struct perf_counter_hw_event {
                                exclude_idle   :  1, /* don't count when idle */
                                mmap           :  1, /* include mmap data     */
                                munmap         :  1, /* include munmap data   */
+                               comm           :  1, /* include comm data     */
 
-                               __reserved_1   : 53;
+                               __reserved_1   : 52;
 
        __u32                   extra_config_len;
        __u32                   wakeup_events;  /* wakeup every n events */
@@ -231,6 +232,16 @@ enum perf_event_type {
        PERF_EVENT_MUNMAP               = 2,
 
        /*
+        * struct {
+        *      struct perf_event_header        header;
+        *
+        *      u32                             pid, tid;
+        *      char                            comm[];
+        * };
+        */
+       PERF_EVENT_COMM                 = 3,
+
+       /*
         * When header.misc & PERF_EVENT_MISC_OVERFLOW the event_type field
         * will be PERF_RECORD_*
         *
@@ -545,6 +556,8 @@ extern void perf_counter_mmap(unsigned long addr, unsigned long len,
 extern void perf_counter_munmap(unsigned long addr, unsigned long len,
                                unsigned long pgoff, struct file *file);
 
+extern void perf_counter_comm(struct task_struct *tsk);
+
 #define MAX_STACK_DEPTH                255
 
 struct perf_callchain_entry {
@@ -583,6 +596,7 @@ static inline void
 perf_counter_munmap(unsigned long addr, unsigned long len,
                    unsigned long pgoff, struct file *file)             { }
 
+static inline void perf_counter_comm(struct task_struct *tsk)          { }
 #endif
 
 #endif /* __KERNEL__ */
index bf12df6..2d4aebb 100644 (file)
@@ -1917,6 +1917,99 @@ static void perf_counter_output(struct perf_counter *counter,
 }
 
 /*
+ * comm tracking
+ */
+
+struct perf_comm_event {
+       struct task_struct      *task;
+       char                    *comm;
+       int                     comm_size;
+
+       struct {
+               struct perf_event_header        header;
+
+               u32                             pid;
+               u32                             tid;
+       } event;
+};
+
+static void perf_counter_comm_output(struct perf_counter *counter,
+                                    struct perf_comm_event *comm_event)
+{
+       struct perf_output_handle handle;
+       int size = comm_event->event.header.size;
+       int ret = perf_output_begin(&handle, counter, size, 0, 0);
+
+       if (ret)
+               return;
+
+       perf_output_put(&handle, comm_event->event);
+       perf_output_copy(&handle, comm_event->comm,
+                                  comm_event->comm_size);
+       perf_output_end(&handle);
+}
+
+static int perf_counter_comm_match(struct perf_counter *counter,
+                                  struct perf_comm_event *comm_event)
+{
+       if (counter->hw_event.comm &&
+           comm_event->event.header.type == PERF_EVENT_COMM)
+               return 1;
+
+       return 0;
+}
+
+static void perf_counter_comm_ctx(struct perf_counter_context *ctx,
+                                 struct perf_comm_event *comm_event)
+{
+       struct perf_counter *counter;
+
+       if (system_state != SYSTEM_RUNNING || list_empty(&ctx->event_list))
+               return;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(counter, &ctx->event_list, event_entry) {
+               if (perf_counter_comm_match(counter, comm_event))
+                       perf_counter_comm_output(counter, comm_event);
+       }
+       rcu_read_unlock();
+}
+
+static void perf_counter_comm_event(struct perf_comm_event *comm_event)
+{
+       struct perf_cpu_context *cpuctx;
+       unsigned int size;
+       char *comm = comm_event->task->comm;
+
+       size = ALIGN(strlen(comm), sizeof(u64));
+
+       comm_event->comm = comm;
+       comm_event->comm_size = size;
+
+       comm_event->event.header.size = sizeof(comm_event->event) + size;
+
+       cpuctx = &get_cpu_var(perf_cpu_context);
+       perf_counter_comm_ctx(&cpuctx->ctx, comm_event);
+       put_cpu_var(perf_cpu_context);
+
+       perf_counter_comm_ctx(&current->perf_counter_ctx, comm_event);
+}
+
+void perf_counter_comm(struct task_struct *task)
+{
+       struct perf_comm_event comm_event = {
+               .task   = task,
+               .event  = {
+                       .header = { .type = PERF_EVENT_COMM, },
+                       .pid    = task->group_leader->pid,
+                       .tid    = task->pid,
+               },
+       };
+
+       perf_counter_comm_event(&comm_event);
+}
+
+/*
  * mmap tracking
  */