perf diff: Make diff command work with evsel hists
authorJiri Olsa <jolsa@redhat.com>
Thu, 6 Sep 2012 15:46:55 +0000 (17:46 +0200)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Sat, 8 Sep 2012 00:44:02 +0000 (21:44 -0300)
Putting 'perf diff' command back on track with the 'latest'
evsel hists changes. Each evsel has its own 'hists' object
gathering stats for the particular event.

While currently counts are accumulated for the whole session
regardless of the events diversification within compared
sessions.

The 'perf diff' command now outputs all matching events within
compared sessions (with event name specified). The per event
diff output stays the same.

  $ ./perf diff
  # Event 'cycles'
  #
  # Baseline  Delta          Shared Object                          Symbol
  # ........ ..........  .................  ..............................
  #
       0.00%    +15.14%  [kernel.kallsyms]  [k] __wake_up
       0.00%    +13.38%  [kernel.kallsyms]  [k] ext4fs_dirhash

... SNIP

       0.00%     +0.42%  [kernel.kallsyms]  [k] local_clock
       0.17%     -0.05%  [kernel.kallsyms]  [k] native_write_msr_safe

  # Event 'faults'
  #
  # Baseline  Delta          Shared Object                          Symbol
  # ........ ..........  .................  ..............................
  #
       0.00%    +79.12%  ld-2.15.so         [.] _dl_relocate_object
       0.00%    +11.62%  ld-2.15.so         [.] openaux

Signed-off-by: Jiri Olsa <jolsa@redhat.com>
Cc: Andi Kleen <andi@firstfloor.org>
Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1346946426-13496-2-git-send-email-jolsa@redhat.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/Documentation/perf-diff.txt
tools/perf/builtin-diff.c
tools/perf/util/evsel.h
tools/perf/util/session.h

index 74d7481ed7a6916f8797ed67b91b5c5d80382edc..ab7f667de1b158d290443397696166a573bbb7f4 100644 (file)
@@ -17,6 +17,9 @@ captured via perf record.
 
 If no parameters are passed it will assume perf.data.old and perf.data.
 
+The differential profile is displayed only for events matching both
+specified perf.data files.
+
 OPTIONS
 -------
 -M::
index d29d350fb2b731187d92233d6c0ddb5ef2a368a6..e9933fdd256e1918aa0d73af8f9dd5e614689087 100644 (file)
@@ -10,6 +10,7 @@
 #include "util/event.h"
 #include "util/hist.h"
 #include "util/evsel.h"
+#include "util/evlist.h"
 #include "util/session.h"
 #include "util/tool.h"
 #include "util/sort.h"
@@ -24,11 +25,6 @@ static char    diff__default_sort_order[] = "dso,symbol";
 static bool  force;
 static bool show_displacement;
 
-struct perf_diff {
-       struct perf_tool tool;
-       struct perf_session *session;
-};
-
 static int hists__add_entry(struct hists *self,
                            struct addr_location *al, u64 period)
 {
@@ -37,14 +33,12 @@ static int hists__add_entry(struct hists *self,
        return -ENOMEM;
 }
 
-static int diff__process_sample_event(struct perf_tool *tool,
+static int diff__process_sample_event(struct perf_tool *tool __used,
                                      union perf_event *event,
                                      struct perf_sample *sample,
-                                     struct perf_evsel *evsel __used,
+                                     struct perf_evsel *evsel,
                                      struct machine *machine)
 {
-       struct perf_diff *_diff = container_of(tool, struct perf_diff, tool);
-       struct perf_session *session = _diff->session;
        struct addr_location al;
 
        if (perf_event__preprocess_sample(event, machine, &al, sample, NULL) < 0) {
@@ -56,26 +50,24 @@ static int diff__process_sample_event(struct perf_tool *tool,
        if (al.filtered || al.sym == NULL)
                return 0;
 
-       if (hists__add_entry(&session->hists, &al, sample->period)) {
+       if (hists__add_entry(&evsel->hists, &al, sample->period)) {
                pr_warning("problem incrementing symbol period, skipping event\n");
                return -1;
        }
 
-       session->hists.stats.total_period += sample->period;
+       evsel->hists.stats.total_period += sample->period;
        return 0;
 }
 
-static struct perf_diff diff = {
-       .tool = {
-               .sample = diff__process_sample_event,
-               .mmap   = perf_event__process_mmap,
-               .comm   = perf_event__process_comm,
-               .exit   = perf_event__process_task,
-               .fork   = perf_event__process_task,
-               .lost   = perf_event__process_lost,
-               .ordered_samples = true,
-               .ordering_requires_timestamps = true,
-       },
+static struct perf_tool tool = {
+       .sample = diff__process_sample_event,
+       .mmap   = perf_event__process_mmap,
+       .comm   = perf_event__process_comm,
+       .exit   = perf_event__process_task,
+       .fork   = perf_event__process_task,
+       .lost   = perf_event__process_lost,
+       .ordered_samples = true,
+       .ordering_requires_timestamps = true,
 };
 
 static void perf_session__insert_hist_entry_by_name(struct rb_root *root,
@@ -146,34 +138,71 @@ static void hists__match(struct hists *older, struct hists *newer)
        }
 }
 
+static struct perf_evsel *evsel_match(struct perf_evsel *evsel,
+                                     struct perf_evlist *evlist)
+{
+       struct perf_evsel *e;
+
+       list_for_each_entry(e, &evlist->entries, node)
+               if (perf_evsel__match2(evsel, e))
+                       return e;
+
+       return NULL;
+}
+
 static int __cmd_diff(void)
 {
        int ret, i;
 #define older (session[0])
 #define newer (session[1])
        struct perf_session *session[2];
+       struct perf_evlist *evlist_new, *evlist_old;
+       struct perf_evsel *evsel;
+       bool first = true;
 
        older = perf_session__new(input_old, O_RDONLY, force, false,
-                                 &diff.tool);
+                                 &tool);
        newer = perf_session__new(input_new, O_RDONLY, force, false,
-                                 &diff.tool);
+                                 &tool);
        if (session[0] == NULL || session[1] == NULL)
                return -ENOMEM;
 
        for (i = 0; i < 2; ++i) {
-               diff.session = session[i];
-               ret = perf_session__process_events(session[i], &diff.tool);
+               ret = perf_session__process_events(session[i], &tool);
                if (ret)
                        goto out_delete;
-               hists__output_resort(&session[i]->hists);
        }
 
-       if (show_displacement)
-               hists__resort_entries(&older->hists);
+       evlist_old = older->evlist;
+       evlist_new = newer->evlist;
+
+       list_for_each_entry(evsel, &evlist_new->entries, node)
+               hists__output_resort(&evsel->hists);
+
+       list_for_each_entry(evsel, &evlist_old->entries, node) {
+               hists__output_resort(&evsel->hists);
+
+               if (show_displacement)
+                       hists__resort_entries(&evsel->hists);
+       }
+
+       list_for_each_entry(evsel, &evlist_new->entries, node) {
+               struct perf_evsel *evsel_old;
+
+               evsel_old = evsel_match(evsel, evlist_old);
+               if (!evsel_old)
+                       continue;
+
+               fprintf(stdout, "%s# Event '%s'\n#\n", first ? "" : "\n",
+                       perf_evsel__name(evsel));
+
+               first = false;
+
+               hists__match(&evsel_old->hists, &evsel->hists);
+               hists__fprintf(&evsel->hists, &evsel_old->hists,
+                              show_displacement, true, 0, 0, stdout);
+       }
 
-       hists__match(&older->hists, &newer->hists);
-       hists__fprintf(&newer->hists, &older->hists,
-                      show_displacement, true, 0, 0, stdout);
 out_delete:
        for (i = 0; i < 2; ++i)
                perf_session__delete(session[i]);
index a3f562cec433889f1cbc1800006d8f7b0c88c39c..390690eb8781214c78aed54520900fd374e42164 100644 (file)
@@ -124,6 +124,13 @@ void perf_evsel__close(struct perf_evsel *evsel, int ncpus, int nthreads);
        (evsel->attr.type == PERF_TYPE_##t &&   \
         evsel->attr.config == PERF_COUNT_##c)
 
+static inline bool perf_evsel__match2(struct perf_evsel *e1,
+                                     struct perf_evsel *e2)
+{
+       return (e1->attr.type == e2->attr.type) &&
+              (e1->attr.config == e2->attr.config);
+}
+
 int __perf_evsel__read_on_cpu(struct perf_evsel *evsel,
                              int cpu, int thread, bool scale);
 
index 176a60902f569abe5156df13ea7c4a3a9ceff461..aab414fbb64b4f0ba3d455153d68cf9b8860e959 100644 (file)
@@ -36,9 +36,7 @@ struct perf_session {
        struct pevent           *pevent;
        /*
         * FIXME: Need to split this up further, we need global
-        *        stats + per event stats. 'perf diff' also needs
-        *        to properly support multiple events in a single
-        *        perf.data file.
+        *        stats + per event stats.
         */
        struct hists            hists;
        int                     fd;