/* * builtin-diff.c * * Builtin diff command: Analyze two perf.data input files, look up and read * DSOs and symbol information, sort them and produce a diff. */#include "builtin.h" #include "util/debug.h"#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"#include "util/symbol.h"#include "util/util.h" #include <stdlib.h> static char const *input_old = "perf.data.old", *input_new = "perf.data";static char diff__default_sort_order[] = "dso,symbol";static bool force;static bool show_displacement;static bool show_period;static bool show_formula;static bool show_baseline_only;static bool sort_compute; static s64 compute_wdiff_w1;static s64 compute_wdiff_w2; enum { COMPUTE_DELTA, COMPUTE_RATIO, COMPUTE_WEIGHTED_DIFF, COMPUTE_MAX,}; const char *compute_names[COMPUTE_MAX] = { [COMPUTE_DELTA] = "delta", [COMPUTE_RATIO] = "ratio", [COMPUTE_WEIGHTED_DIFF] = "wdiff",}; static int compute; static int setup_compute_opt_wdiff(char *opt){ char *w1_str = opt; char *w2_str; int ret = -EINVAL; if (!opt) goto out; w2_str = strchr(opt, ','); if (!w2_str) goto out; *w2_str++ = 0x0; if (!*w2_str) goto out; compute_wdiff_w1 = strtol(w1_str, NULL, 10); compute_wdiff_w2 = strtol(w2_str, NULL, 10); if (!compute_wdiff_w1 || !compute_wdiff_w2) goto out; pr_debug("compute wdiff w1(%" PRId64 ") w2(%" PRId64 ")\n", compute_wdiff_w1, compute_wdiff_w2); ret = 0; out: if (ret) pr_err("Failed: wrong weight data, use 'wdiff:w1,w2'\n"); return ret;} static int setup_compute_opt(char *opt){ if (compute == COMPUTE_WEIGHTED_DIFF) return setup_compute_opt_wdiff(opt); if (opt) { pr_err("Failed: extra option specified '%s'", opt); return -EINVAL; } return 0;} static int setup_compute(const struct option *opt, const char *str, int unset __maybe_unused){ int *cp = (int *) opt->value; char *cstr = (char *) str; char buf[50]; unsigned i; char *option; if (!str) { *cp = COMPUTE_DELTA; return 0; } if (*str == '+') { sort_compute = true; cstr = (char *) ++str; if (!*str) return 0; } option = strchr(str, ':'); if (option) { unsigned len = option++ - str; /* * The str data are not writeable, so we need * to use another buffer. */ /* No option value is longer. */ if (len >= sizeof(buf)) return -EINVAL; strncpy(buf, str, len); buf[len] = 0x0; cstr = buf; } for (i = 0; i < COMPUTE_MAX; i++) if (!strcmp(cstr, compute_names[i])) { *cp = i; return setup_compute_opt(option); } pr_err("Failed: '%s' is not computation method " "(use 'delta','ratio' or 'wdiff')\n", str); return -EINVAL;} static double get_period_percent(struct hist_entry *he, u64 period){ u64 total = he->hists->stats.total_period; return (period * 100.0) / total;} double perf_diff__compute_delta(struct hist_entry *he){ struct hist_entry *pair = hist_entry__next_pair(he); double new_percent = get_period_percent(he, he->stat.period); double old_percent = pair ? get_period_percent(pair, pair->stat.period) : 0.0; he->diff.period_ratio_delta = new_percent - old_percent; he->diff.computed = true; return he->diff.period_ratio_delta;} double perf_diff__compute_ratio(struct hist_entry *he){ struct hist_entry *pair = hist_entry__next_pair(he); double new_period = he->stat.period; double old_period = pair ? pair->stat.period : 0; he->diff.computed = true; he->diff.period_ratio = pair ? (new_period / old_period) : 0; return he->diff.period_ratio;} s64 perf_diff__compute_wdiff(struct hist_entry *he){ struct hist_entry *pair = hist_entry__next_pair(he); u64 new_period = he->stat.period; u64 old_period = pair ? pair->stat.period : 0; he->diff.computed = true; if (!pair) he->diff.wdiff = 0; else he->diff.wdiff = new_period * compute_wdiff_w2 - old_period * compute_wdiff_w1; return he->diff.wdiff;} static int formula_delta(struct hist_entry *he, char *buf, size_t size){ struct hist_entry *pair = hist_entry__next_pair(he); if (!pair) return -1; return scnprintf(buf, size, "(%" PRIu64 " * 100 / %" PRIu64 ") - " "(%" PRIu64 " * 100 / %" PRIu64 ")", he->stat.period, he->hists->stats.total_period, pair->stat.period, pair->hists->stats.total_period);} static int formula_ratio(struct hist_entry *he, char *buf, size_t size){ struct hist_entry *pair = hist_entry__next_pair(he); double new_period = he->stat.period; double old_period = pair ? pair->stat.period : 0; if (!pair) return -1; return scnprintf(buf, size, "%.0F / %.0F", new_period, old_period);} static int formula_wdiff(struct hist_entry *he, char *buf, size_t size){ struct hist_entry *pair = hist_entry__next_pair(he); u64 new_period = he->stat.period; u64 old_period = pair ? pair->stat.period : 0; if (!pair) return -1; return scnprintf(buf, size, "(%" PRIu64 " * " "%" PRId64 ") - (%" PRIu64 " * " "%" PRId64 ")", new_period, compute_wdiff_w2, old_period, compute_wdiff_w1);} int perf_diff__formula(char *buf, size_t size, struct hist_entry *he){ switch (compute) { case COMPUTE_DELTA: return formula_delta(he, buf, size); case COMPUTE_RATIO: return formula_ratio(he, buf, size); case COMPUTE_WEIGHTED_DIFF: return formula_wdiff(he, buf, size); default: BUG_ON(1); } return -1;} static int hists__add_entry(struct hists *self, struct addr_location *al, u64 period){ if (__hists__add_entry(self, al, NULL, period) != NULL) return 0; return -ENOMEM;} static int diff__process_sample_event(struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, struct perf_evsel *evsel, struct machine *machine){ struct addr_location al; if (perf_event__preprocess_sample(event, machine, &al, sample, NULL) < 0) { pr_warning("problem processing %d event, skipping it.\n", event->header.type); return -1; } if (al.filtered) return 0; if (hists__add_entry(&evsel->hists, &al, sample->period)) { pr_warning("problem incrementing symbol period, skipping event\n"); return -1; } evsel->hists.stats.total_period += sample->period; return 0;} static struct perf_tool tool = { .sample = diff__process_sample_event, .mmap = perf_event__process_mmap, .comm = perf_event__process_comm, .exit = perf_event__process_exit, .fork = perf_event__process_fork, .lost = perf_event__process_lost, .ordered_samples = true, .ordering_requires_timestamps = true,}; static void insert_hist_entry_by_name(struct rb_root *root, struct hist_entry *he){ struct rb_node **p = &root->rb_node; struct rb_node *parent = NULL; struct hist_entry *iter; while (*p != NULL) { parent = *p; iter = rb_entry(parent, struct hist_entry, rb_node); if (hist_entry__cmp(he, iter) < 0) p = &(*p)->rb_left; else p = &(*p)->rb_right; } rb_link_node(&he->rb_node, parent, p); rb_insert_color(&he->rb_node, root);} static void hists__name_resort(struct hists *self, bool sort){ unsigned long position = 1; struct rb_root tmp = RB_ROOT; struct rb_node *next = rb_first(&self->entries); while (next != NULL) { struct hist_entry *n = rb_entry(next, struct hist_entry, rb_node); next = rb_next(&n->rb_node); n->position = position++; if (sort) { rb_erase(&n->rb_node, &self->entries); insert_hist_entry_by_name(&tmp, n); } } if (sort) self->entries = tmp;} 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 void perf_evlist__resort_hists(struct perf_evlist *evlist, bool name){ struct perf_evsel *evsel; list_for_each_entry(evsel, &evlist->entries, node) { struct hists *hists = &evsel->hists; hists__output_resort(hists); /* * The hists__name_resort only sets possition * if name is false. */ if (name || ((!name) && show_displacement)) hists__name_resort(hists, name); }} static void hists__baseline_only(struct hists *hists){ struct rb_node *next = rb_first(&hists->entries); while (next != NULL) { struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node); next = rb_next(&he->rb_node); if (!hist_entry__next_pair(he)) { rb_erase(&he->rb_node, &hists->entries); hist_entry__free(he); } }} static void hists__precompute(struct hists *hists){ struct rb_node *next = rb_first(&hists->entries); while (next != NULL) { struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node); next = rb_next(&he->rb_node); switch (compute) { case COMPUTE_DELTA: perf_diff__compute_delta(he); break; case COMPUTE_RATIO: perf_diff__compute_ratio(he); break; case COMPUTE_WEIGHTED_DIFF: perf_diff__compute_wdiff(he); break; default: BUG_ON(1); } }} static int64_t cmp_doubles(double l, double r){ if (l > r) return -1; else if (l < r) return 1; else return 0;} static int64_thist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right, int c){ switch (c) { case COMPUTE_DELTA: { double l = left->diff.period_ratio_delta; double r = right->diff.period_ratio_delta; return cmp_doubles(l, r); } case COMPUTE_RATIO: { double l = left->diff.period_ratio; double r = right->diff.period_ratio; return cmp_doubles(l, r); } case COMPUTE_WEIGHTED_DIFF: { s64 l = left->diff.wdiff; s64 r = right->diff.wdiff; return r - l; } default: BUG_ON(1); } return 0;} static void insert_hist_entry_by_compute(struct rb_root *root, struct hist_entry *he, int c){ struct rb_node **p = &root->rb_node; struct rb_node *parent = NULL; struct hist_entry *iter; while (*p != NULL) { parent = *p; iter = rb_entry(parent, struct hist_entry, rb_node); if (hist_entry__cmp_compute(he, iter, c) < 0) p = &(*p)->rb_left; else p = &(*p)->rb_right; } rb_link_node(&he->rb_node, parent, p); rb_insert_color(&he->rb_node, root);} static void hists__compute_resort(struct hists *hists){ struct rb_root tmp = RB_ROOT; struct rb_node *next = rb_first(&hists->entries); while (next != NULL) { struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node); next = rb_next(&he->rb_node); rb_erase(&he->rb_node, &hists->entries); insert_hist_entry_by_compute(&tmp, he, compute); } hists->entries = tmp;} static void hists__process(struct hists *old, struct hists *new){ hists__match(new, old); if (show_baseline_only) hists__baseline_only(new); else hists__link(new, old); if (sort_compute) { hists__precompute(new); hists__compute_resort(new); }Lines 1–500 of 669. Load more
/* * builtin-diff.c * * Builtin diff command: Analyze two perf.data input files, look up and read * DSOs and symbol information, sort them and produce a diff. */ #include "builtin.h" #include "util/debug.h" #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" #include "util/symbol.h" #include "util/util.h" #include <stdlib.h> static char const *input_old = "perf.data.old", *input_new = "perf.data"; static char diff__default_sort_order[] = "dso,symbol"; static bool force; static bool show_displacement; static bool show_period; static bool show_formula; static bool show_baseline_only; static bool sort_compute; static s64 compute_wdiff_w1; static s64 compute_wdiff_w2; enum { COMPUTE_DELTA, COMPUTE_RATIO, COMPUTE_WEIGHTED_DIFF, COMPUTE_MAX, }; const char *compute_names[COMPUTE_MAX] = { [COMPUTE_DELTA] = "delta", [COMPUTE_RATIO] = "ratio", [COMPUTE_WEIGHTED_DIFF] = "wdiff", }; static int compute; static int setup_compute_opt_wdiff(char *opt) { char *w1_str = opt; char *w2_str; int ret = -EINVAL; if (!opt) goto out; w2_str = strchr(opt, ','); if (!w2_str) goto out; *w2_str++ = 0x0; if (!*w2_str) goto out; compute_wdiff_w1 = strtol(w1_str, NULL, 10); compute_wdiff_w2 = strtol(w2_str, NULL, 10); if (!compute_wdiff_w1 || !compute_wdiff_w2) goto out; pr_debug("compute wdiff w1(%" PRId64 ") w2(%" PRId64 ")\n", compute_wdiff_w1, compute_wdiff_w2); ret = 0; out: if (ret) pr_err("Failed: wrong weight data, use 'wdiff:w1,w2'\n"); return ret; } static int setup_compute_opt(char *opt) { if (compute == COMPUTE_WEIGHTED_DIFF) return setup_compute_opt_wdiff(opt); if (opt) { pr_err("Failed: extra option specified '%s'", opt); return -EINVAL; } return 0; } static int setup_compute(const struct option *opt, const char *str, int unset __maybe_unused) { int *cp = (int *) opt->value; char *cstr = (char *) str; char buf[50]; unsigned i; char *option; if (!str) { *cp = COMPUTE_DELTA; return 0; } if (*str == '+') { sort_compute = true; cstr = (char *) ++str; if (!*str) return 0; } option = strchr(str, ':'); if (option) { unsigned len = option++ - str; /* * The str data are not writeable, so we need * to use another buffer. */ /* No option value is longer. */ if (len >= sizeof(buf)) return -EINVAL; strncpy(buf, str, len); buf[len] = 0x0; cstr = buf; } for (i = 0; i < COMPUTE_MAX; i++) if (!strcmp(cstr, compute_names[i])) { *cp = i; return setup_compute_opt(option); } pr_err("Failed: '%s' is not computation method " "(use 'delta','ratio' or 'wdiff')\n", str); return -EINVAL; } static double get_period_percent(struct hist_entry *he, u64 period) { u64 total = he->hists->stats.total_period; return (period * 100.0) / total; } double perf_diff__compute_delta(struct hist_entry *he) { struct hist_entry *pair = hist_entry__next_pair(he); double new_percent = get_period_percent(he, he->stat.period); double old_percent = pair ? get_period_percent(pair, pair->stat.period) : 0.0; he->diff.period_ratio_delta = new_percent - old_percent; he->diff.computed = true; return he->diff.period_ratio_delta; } double perf_diff__compute_ratio(struct hist_entry *he) { struct hist_entry *pair = hist_entry__next_pair(he); double new_period = he->stat.period; double old_period = pair ? pair->stat.period : 0; he->diff.computed = true; he->diff.period_ratio = pair ? (new_period / old_period) : 0; return he->diff.period_ratio; } s64 perf_diff__compute_wdiff(struct hist_entry *he) { struct hist_entry *pair = hist_entry__next_pair(he); u64 new_period = he->stat.period; u64 old_period = pair ? pair->stat.period : 0; he->diff.computed = true; if (!pair) he->diff.wdiff = 0; else he->diff.wdiff = new_period * compute_wdiff_w2 - old_period * compute_wdiff_w1; return he->diff.wdiff; } static int formula_delta(struct hist_entry *he, char *buf, size_t size) { struct hist_entry *pair = hist_entry__next_pair(he); if (!pair) return -1; return scnprintf(buf, size, "(%" PRIu64 " * 100 / %" PRIu64 ") - " "(%" PRIu64 " * 100 / %" PRIu64 ")", he->stat.period, he->hists->stats.total_period, pair->stat.period, pair->hists->stats.total_period); } static int formula_ratio(struct hist_entry *he, char *buf, size_t size) { struct hist_entry *pair = hist_entry__next_pair(he); double new_period = he->stat.period; double old_period = pair ? pair->stat.period : 0; if (!pair) return -1; return scnprintf(buf, size, "%.0F / %.0F", new_period, old_period); } static int formula_wdiff(struct hist_entry *he, char *buf, size_t size) { struct hist_entry *pair = hist_entry__next_pair(he); u64 new_period = he->stat.period; u64 old_period = pair ? pair->stat.period : 0; if (!pair) return -1; return scnprintf(buf, size, "(%" PRIu64 " * " "%" PRId64 ") - (%" PRIu64 " * " "%" PRId64 ")", new_period, compute_wdiff_w2, old_period, compute_wdiff_w1); } int perf_diff__formula(char *buf, size_t size, struct hist_entry *he) { switch (compute) { case COMPUTE_DELTA: return formula_delta(he, buf, size); case COMPUTE_RATIO: return formula_ratio(he, buf, size); case COMPUTE_WEIGHTED_DIFF: return formula_wdiff(he, buf, size); default: BUG_ON(1); } return -1; } static int hists__add_entry(struct hists *self, struct addr_location *al, u64 period) { if (__hists__add_entry(self, al, NULL, period) != NULL) return 0; return -ENOMEM; } static int diff__process_sample_event(struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, struct perf_evsel *evsel, struct machine *machine) { struct addr_location al; if (perf_event__preprocess_sample(event, machine, &al, sample, NULL) < 0) { pr_warning("problem processing %d event, skipping it.\n", event->header.type); return -1; } if (al.filtered) return 0; if (hists__add_entry(&evsel->hists, &al, sample->period)) { pr_warning("problem incrementing symbol period, skipping event\n"); return -1; } evsel->hists.stats.total_period += sample->period; return 0; } static struct perf_tool tool = { .sample = diff__process_sample_event, .mmap = perf_event__process_mmap, .comm = perf_event__process_comm, .exit = perf_event__process_exit, .fork = perf_event__process_fork, .lost = perf_event__process_lost, .ordered_samples = true, .ordering_requires_timestamps = true, }; static void insert_hist_entry_by_name(struct rb_root *root, struct hist_entry *he) { struct rb_node **p = &root->rb_node; struct rb_node *parent = NULL; struct hist_entry *iter; while (*p != NULL) { parent = *p; iter = rb_entry(parent, struct hist_entry, rb_node); if (hist_entry__cmp(he, iter) < 0) p = &(*p)->rb_left; else p = &(*p)->rb_right; } rb_link_node(&he->rb_node, parent, p); rb_insert_color(&he->rb_node, root); } static void hists__name_resort(struct hists *self, bool sort) { unsigned long position = 1; struct rb_root tmp = RB_ROOT; struct rb_node *next = rb_first(&self->entries); while (next != NULL) { struct hist_entry *n = rb_entry(next, struct hist_entry, rb_node); next = rb_next(&n->rb_node); n->position = position++; if (sort) { rb_erase(&n->rb_node, &self->entries); insert_hist_entry_by_name(&tmp, n); } } if (sort) self->entries = tmp; } 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 void perf_evlist__resort_hists(struct perf_evlist *evlist, bool name) { struct perf_evsel *evsel; list_for_each_entry(evsel, &evlist->entries, node) { struct hists *hists = &evsel->hists; hists__output_resort(hists); /* * The hists__name_resort only sets possition * if name is false. */ if (name || ((!name) && show_displacement)) hists__name_resort(hists, name); } } static void hists__baseline_only(struct hists *hists) { struct rb_node *next = rb_first(&hists->entries); while (next != NULL) { struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node); next = rb_next(&he->rb_node); if (!hist_entry__next_pair(he)) { rb_erase(&he->rb_node, &hists->entries); hist_entry__free(he); } } } static void hists__precompute(struct hists *hists) { struct rb_node *next = rb_first(&hists->entries); while (next != NULL) { struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node); next = rb_next(&he->rb_node); switch (compute) { case COMPUTE_DELTA: perf_diff__compute_delta(he); break; case COMPUTE_RATIO: perf_diff__compute_ratio(he); break; case COMPUTE_WEIGHTED_DIFF: perf_diff__compute_wdiff(he); break; default: BUG_ON(1); } } } static int64_t cmp_doubles(double l, double r) { if (l > r) return -1; else if (l < r) return 1; else return 0; } static int64_t hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right, int c) { switch (c) { case COMPUTE_DELTA: { double l = left->diff.period_ratio_delta; double r = right->diff.period_ratio_delta; return cmp_doubles(l, r); } case COMPUTE_RATIO: { double l = left->diff.period_ratio; double r = right->diff.period_ratio; return cmp_doubles(l, r); } case COMPUTE_WEIGHTED_DIFF: { s64 l = left->diff.wdiff; s64 r = right->diff.wdiff; return r - l; } default: BUG_ON(1); } return 0; } static void insert_hist_entry_by_compute(struct rb_root *root, struct hist_entry *he, int c) { struct rb_node **p = &root->rb_node; struct rb_node *parent = NULL; struct hist_entry *iter; while (*p != NULL) { parent = *p; iter = rb_entry(parent, struct hist_entry, rb_node); if (hist_entry__cmp_compute(he, iter, c) < 0) p = &(*p)->rb_left; else p = &(*p)->rb_right; } rb_link_node(&he->rb_node, parent, p); rb_insert_color(&he->rb_node, root); } static void hists__compute_resort(struct hists *hists) { struct rb_root tmp = RB_ROOT; struct rb_node *next = rb_first(&hists->entries); while (next != NULL) { struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node); next = rb_next(&he->rb_node); rb_erase(&he->rb_node, &hists->entries); insert_hist_entry_by_compute(&tmp, he, compute); } hists->entries = tmp; } static void hists__process(struct hists *old, struct hists *new) { hists__match(new, old); if (show_baseline_only) hists__baseline_only(new); else hists__link(new, old); if (sort_compute) { hists__precompute(new); hists__compute_resort(new); }