perf: Do not send exit event twice
authorJiri Olsa <jolsa@kernel.org>
Wed, 4 Nov 2015 15:00:05 +0000 (16:00 +0100)
committerIngo Molnar <mingo@kernel.org>
Sun, 6 Dec 2015 11:54:49 +0000 (12:54 +0100)
In case we monitor events system wide, we get EXIT event
(when configured) twice for each task that exited.

Note doubled lines with same pid/tid in following example:

  $ sudo ./perf record -a
  ^C[ perf record: Woken up 1 times to write data ]
  [ perf record: Captured and wrote 0.480 MB perf.data (2518 samples) ]
  $ sudo ./perf report -D | grep EXIT

  0 60290687567581 0x59910 [0x38]: PERF_RECORD_EXIT(1250:1250):(1250:1250)
  0 60290687568354 0x59948 [0x38]: PERF_RECORD_EXIT(1250:1250):(1250:1250)
  0 60290687988744 0x59ad8 [0x38]: PERF_RECORD_EXIT(1250:1250):(1250:1250)
  0 60290687989198 0x59b10 [0x38]: PERF_RECORD_EXIT(1250:1250):(1250:1250)
  1 60290692567895 0x62af0 [0x38]: PERF_RECORD_EXIT(1253:1253):(1253:1253)
  1 60290692568322 0x62b28 [0x38]: PERF_RECORD_EXIT(1253:1253):(1253:1253)
  2 60290692739276 0x69a18 [0x38]: PERF_RECORD_EXIT(1252:1252):(1252:1252)
  2 60290692739910 0x69a50 [0x38]: PERF_RECORD_EXIT(1252:1252):(1252:1252)

The reason is that the cpu contexts are processes each time
we call perf_event_task. I'm changing the perf_event_aux logic
to serve task_ctx and cpu contexts separately, which ensure we
don't get EXIT event generated twice on same cpu context.

This does not affect other auxiliary events, as they don't
use task_ctx at all.

Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Vince Weaver <vincent.weaver@maine.edu>
Link: http://lkml.kernel.org/r/1446649205-5822-1-git-send-email-jolsa@kernel.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>
kernel/events/core.c

index 49a5118..39cf4a4 100644 (file)
@@ -5683,6 +5683,17 @@ perf_event_aux_ctx(struct perf_event_context *ctx,
 }
 
 static void
+perf_event_aux_task_ctx(perf_event_aux_output_cb output, void *data,
+                       struct perf_event_context *task_ctx)
+{
+       rcu_read_lock();
+       preempt_disable();
+       perf_event_aux_ctx(task_ctx, output, data);
+       preempt_enable();
+       rcu_read_unlock();
+}
+
+static void
 perf_event_aux(perf_event_aux_output_cb output, void *data,
               struct perf_event_context *task_ctx)
 {
@@ -5691,14 +5702,23 @@ perf_event_aux(perf_event_aux_output_cb output, void *data,
        struct pmu *pmu;
        int ctxn;
 
+       /*
+        * If we have task_ctx != NULL we only notify
+        * the task context itself. The task_ctx is set
+        * only for EXIT events before releasing task
+        * context.
+        */
+       if (task_ctx) {
+               perf_event_aux_task_ctx(output, data, task_ctx);
+               return;
+       }
+
        rcu_read_lock();
        list_for_each_entry_rcu(pmu, &pmus, entry) {
                cpuctx = get_cpu_ptr(pmu->pmu_cpu_context);
                if (cpuctx->unique_pmu != pmu)
                        goto next;
                perf_event_aux_ctx(&cpuctx->ctx, output, data);
-               if (task_ctx)
-                       goto next;
                ctxn = pmu->task_ctx_nr;
                if (ctxn < 0)
                        goto next;
@@ -5708,12 +5728,6 @@ perf_event_aux(perf_event_aux_output_cb output, void *data,
 next:
                put_cpu_ptr(pmu->pmu_cpu_context);
        }
-
-       if (task_ctx) {
-               preempt_disable();
-               perf_event_aux_ctx(task_ctx, output, data);
-               preempt_enable();
-       }
        rcu_read_unlock();
 }
 
@@ -8803,10 +8817,8 @@ static void perf_event_exit_task_context(struct task_struct *child, int ctxn)
        struct perf_event_context *child_ctx, *clone_ctx = NULL;
        unsigned long flags;
 
-       if (likely(!child->perf_event_ctxp[ctxn])) {
-               perf_event_task(child, NULL, 0);
+       if (likely(!child->perf_event_ctxp[ctxn]))
                return;
-       }
 
        local_irq_save(flags);
        /*
@@ -8890,6 +8902,14 @@ void perf_event_exit_task(struct task_struct *child)
 
        for_each_task_context_nr(ctxn)
                perf_event_exit_task_context(child, ctxn);
+
+       /*
+        * The perf_event_exit_task_context calls perf_event_task
+        * with child's task_ctx, which generates EXIT events for
+        * child contexts and sets child->perf_event_ctxp[] to NULL.
+        * At this point we need to send EXIT events to cpu contexts.
+        */
+       perf_event_task(child, NULL, 0);
 }
 
 static void perf_free_event(struct perf_event *event,