diff --git a/tools/perf/Documentation/perf-diff.txt b/tools/perf/Documentation/perf-diff.txt index 74d7481ed7a..ab7f667de1b 100644 --- a/tools/perf/Documentation/perf-diff.txt +++ b/tools/perf/Documentation/perf-diff.txt @@ -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:: diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index d29d350fb2b..e9933fdd256 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -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]); diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index a3f562cec43..390690eb878 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -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); diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 176a60902f5..aab414fbb64 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -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;