cregit-Linux how code gets into the kernel

Release 4.14 tools/perf/builtin-diff.c

Directory: tools/perf
// SPDX-License-Identifier: GPL-2.0
/*
 * 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 "util/data.h"
#include "util/config.h"

#include <errno.h>
#include <inttypes.h>
#include <stdlib.h>
#include <math.h>

/* Diff command specific HPP columns. */
enum {
	
PERF_HPP_DIFF__BASELINE,
	
PERF_HPP_DIFF__PERIOD,
	
PERF_HPP_DIFF__PERIOD_BASELINE,
	
PERF_HPP_DIFF__DELTA,
	
PERF_HPP_DIFF__RATIO,
	
PERF_HPP_DIFF__WEIGHTED_DIFF,
	
PERF_HPP_DIFF__FORMULA,
	
PERF_HPP_DIFF__DELTA_ABS,

	
PERF_HPP_DIFF__MAX_INDEX
};


struct diff_hpp_fmt {
	
struct perf_hpp_fmt	 fmt;
	
int			 idx;
	
char			*header;
	
int			 header_width;
};


struct data__file {
	
struct perf_session	*session;
	
struct perf_data_file	file;
	
int			 idx;
	
struct hists		*hists;
	
struct diff_hpp_fmt	 fmt[PERF_HPP_DIFF__MAX_INDEX];
};


static struct data__file *data__files;

static int data__files_cnt;


#define data__for_each_file_start(i, d, s)	\
	for (i = s, d = &data__files[s];        \
             i < data__files_cnt;               \
             i++, d = &data__files[i])


#define data__for_each_file(i, d) data__for_each_file_start(i, d, 0)

#define data__for_each_file_new(i, d) data__for_each_file_start(i, d, 1)


static bool force;

static bool show_period;

static bool show_formula;

static bool show_baseline_only;

static unsigned int sort_compute = 1;


static s64 compute_wdiff_w1;

static s64 compute_wdiff_w2;

enum {
	
COMPUTE_DELTA,
	
COMPUTE_RATIO,
	
COMPUTE_WEIGHTED_DIFF,
	
COMPUTE_DELTA_ABS,
	
COMPUTE_MAX,
};


const char *compute_names[COMPUTE_MAX] = {
	[COMPUTE_DELTA] = "delta",
	[COMPUTE_DELTA_ABS] = "delta-abs",
	[COMPUTE_RATIO] = "ratio",
	[COMPUTE_WEIGHTED_DIFF] = "wdiff",
};


static int compute = COMPUTE_DELTA_ABS;


static int compute_2_hpp[COMPUTE_MAX] = {
	[COMPUTE_DELTA]		= PERF_HPP_DIFF__DELTA,
	[COMPUTE_DELTA_ABS]	= PERF_HPP_DIFF__DELTA_ABS,
	[COMPUTE_RATIO]		= PERF_HPP_DIFF__RATIO,
	[COMPUTE_WEIGHTED_DIFF]	= PERF_HPP_DIFF__WEIGHTED_DIFF,
};


#define MAX_COL_WIDTH 70


static struct header_column {
	
const char *name;
	
int width;

} columns[PERF_HPP_DIFF__MAX_INDEX] = {
	[PERF_HPP_DIFF__BASELINE] = {
		.name  = "Baseline",
        },
	[PERF_HPP_DIFF__PERIOD] = {
		.name  = "Period",
		.width = 14,
        },
	[PERF_HPP_DIFF__PERIOD_BASELINE] = {
		.name  = "Base period",
		.width = 14,
        },
	[PERF_HPP_DIFF__DELTA] = {
		.name  = "Delta",
		.width = 7,
        },
	[PERF_HPP_DIFF__DELTA_ABS] = {
		.name  = "Delta Abs",
		.width = 7,
        },
	[PERF_HPP_DIFF__RATIO] = {
		.name  = "Ratio",
		.width = 14,
        },
	[PERF_HPP_DIFF__WEIGHTED_DIFF] = {
		.name  = "Weighted diff",
		.width = 14,
        },
	[PERF_HPP_DIFF__FORMULA] = {
		.name  = "Formula",
		.width = MAX_COL_WIDTH,
        }
};


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; }

Contributors

PersonTokensPropCommitsCommitProp
Jiri Olsa130100.00%1100.00%
Total130100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Jiri Olsa42100.00%1100.00%
Total42100.00%1100.00%


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; } 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; }

Contributors

PersonTokensPropCommitsCommitProp
Jiri Olsa180100.00%2100.00%
Total180100.00%2100.00%


static double period_percent(struct hist_entry *he, u64 period) { u64 total = hists__total_period(he->hists); return (period * 100.0) / total; }

Contributors

PersonTokensPropCommitsCommitProp
Jiri Olsa3090.91%266.67%
Namhyung Kim39.09%133.33%
Total33100.00%3100.00%


static double compute_delta(struct hist_entry *he, struct hist_entry *pair) { double old_percent = period_percent(he, he->stat.period); double new_percent = period_percent(pair, pair->stat.period); pair->diff.period_ratio_delta = new_percent - old_percent; pair->diff.computed = true; return pair->diff.period_ratio_delta; }

Contributors

PersonTokensPropCommitsCommitProp
Jiri Olsa69100.00%4100.00%
Total69100.00%4100.00%


static double compute_ratio(struct hist_entry *he, struct hist_entry *pair) { double old_period = he->stat.period ?: 1; double new_period = pair->stat.period; pair->diff.computed = true; pair->diff.period_ratio = new_period / old_period; return pair->diff.period_ratio; }

Contributors

PersonTokensPropCommitsCommitProp
Jiri Olsa62100.00%4100.00%
Total62100.00%4100.00%


static s64 compute_wdiff(struct hist_entry *he, struct hist_entry *pair) { u64 old_period = he->stat.period; u64 new_period = pair->stat.period; pair->diff.computed = true; pair->diff.wdiff = new_period * compute_wdiff_w2 - old_period * compute_wdiff_w1; return pair->diff.wdiff; }

Contributors

PersonTokensPropCommitsCommitProp
Jiri Olsa63100.00%4100.00%
Total63100.00%4100.00%


static int formula_delta(struct hist_entry *he, struct hist_entry *pair, char *buf, size_t size) { u64 he_total = he->hists->stats.total_period; u64 pair_total = pair->hists->stats.total_period; if (symbol_conf.filter_relative) { he_total = he->hists->stats.total_non_filtered_period; pair_total = pair->hists->stats.total_non_filtered_period; } return scnprintf(buf, size, "(%" PRIu64 " * 100 / %" PRIu64 ") - " "(%" PRIu64 " * 100 / %" PRIu64 ")", pair->stat.period, pair_total, he->stat.period, he_total); }

Contributors

PersonTokensPropCommitsCommitProp
Jiri Olsa5651.85%375.00%
Namhyung Kim5248.15%125.00%
Total108100.00%4100.00%


static int formula_ratio(struct hist_entry *he, struct hist_entry *pair, char *buf, size_t size) { double old_period = he->stat.period; double new_period = pair->stat.period; return scnprintf(buf, size, "%.0F / %.0F", new_period, old_period); }

Contributors

PersonTokensPropCommitsCommitProp
Jiri Olsa55100.00%3100.00%
Total55100.00%3100.00%


static int formula_wdiff(struct hist_entry *he, struct hist_entry *pair, char *buf, size_t size) { u64 old_period = he->stat.period; u64 new_period = pair->stat.period; return scnprintf(buf, size, "(%" PRIu64 " * " "%" PRId64 ") - (%" PRIu64 " * " "%" PRId64 ")", new_period, compute_wdiff_w2, old_period, compute_wdiff_w1); }

Contributors

PersonTokensPropCommitsCommitProp
Jiri Olsa69100.00%3100.00%
Total69100.00%3100.00%


static int formula_fprintf(struct hist_entry *he, struct hist_entry *pair, char *buf, size_t size) { switch (compute) { case COMPUTE_DELTA: case COMPUTE_DELTA_ABS: return formula_delta(he, pair, buf, size); case COMPUTE_RATIO: return formula_ratio(he, pair, buf, size); case COMPUTE_WEIGHTED_DIFF: return formula_wdiff(he, pair, buf, size); default: BUG_ON(1); } return -1; }

Contributors

PersonTokensPropCommitsCommitProp
Jiri Olsa8496.55%375.00%
Namhyung Kim33.45%125.00%
Total87100.00%4100.00%


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; struct hists *hists = evsel__hists(evsel); int ret = -1; if (machine__resolve(machine, &al, sample) < 0) { pr_warning("problem processing %d event, skipping it.\n", event->header.type); return -1; } if (!hists__add_entry(hists, &al, NULL, NULL, NULL, sample, true)) { pr_warning("problem incrementing symbol period, skipping event\n"); goto out_put; } /* * The total_period is updated here before going to the output * tree since normally only the baseline hists will call * hists__output_resort() and precompute needs the total * period in order to sort entries by percentage delta. */ hists->stats.total_period += sample->period; if (!al.filtered) hists->stats.total_non_filtered_period += sample->period; ret = 0; out_put: addr_location__put(&al); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Arnaldo Carvalho de Melo12982.17%1477.78%
Namhyung Kim1811.46%211.11%
Jiri Olsa95.73%15.56%
Irina Tirdea10.64%15.56%
Total157100.00%18100.00%

static struct perf_tool tool = { .sample = diff__process_sample_event, .mmap = perf_event__process_mmap, .mmap2 = perf_event__process_mmap2, .comm = perf_event__process_comm, .exit = perf_event__process_exit, .fork = perf_event__process_fork, .lost = perf_event__process_lost, .namespaces = perf_event__process_namespaces, .ordered_events = true, .ordering_requires_timestamps = true, };
static struct perf_evsel *evsel_match(struct perf_evsel *evsel, struct perf_evlist *evlist) { struct perf_evsel *e; evlist__for_each_entry(evlist, e) { if (perf_evsel__match2(evsel, e)) return e; } return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Jiri Olsa4189.13%133.33%
Arnaldo Carvalho de Melo510.87%266.67%
Total46100.00%3100.00%


static void perf_evlist__collapse_resort(struct perf_evlist *evlist) { struct perf_evsel *evsel; evlist__for_each_entry(evlist, evsel) { struct hists *hists = evsel__hists(evsel); hists__collapse_resort(hists, NULL); } }

Contributors

PersonTokensPropCommitsCommitProp
Jiri Olsa3175.61%116.67%
Arnaldo Carvalho de Melo614.63%350.00%
Namhyung Kim49.76%233.33%
Total41100.00%6100.00%


static struct data__file *fmt_to_data_file(struct perf_hpp_fmt *fmt) { struct diff_hpp_fmt *dfmt = container_of(fmt, struct diff_hpp_fmt, fmt); void *ptr = dfmt - dfmt->idx; struct data__file *d = container_of(ptr, struct data__file, fmt); return d; }

Contributors

PersonTokensPropCommitsCommitProp
Namhyung Kim56100.00%1100.00%
Total56100.00%1100.00%


static struct hist_entry* get_pair_data(struct hist_entry *he, struct data__file *d) { if (hist_entry__has_pairs(he)) { struct hist_entry *pair; list_for_each_entry(pair, &he->pairs.head, pairs.node) if (pair->hists == d->hists) return pair; } return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Jiri Olsa56100.00%1100.00%
Total56100.00%1100.00%


static struct hist_entry* get_pair_fmt(struct hist_entry *he, struct diff_hpp_fmt *dfmt) { struct data__file *d = fmt_to_data_file(&dfmt->fmt); return get_pair_data(he, d); }

Contributors

PersonTokensPropCommitsCommitProp
Jiri Olsa3589.74%150.00%
Namhyung Kim410.26%150.00%
Total39100.00%2100.00%


static void hists__baseline_only(struct hists *hists) { struct rb_root *root; struct rb_node *next; if (hists__has(hists, need_collapse)) root = &hists->entries_collapsed; else root = hists->entries_in; next = rb_first(root); while (next != NULL) { struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node_in); next = rb_next(&he->rb_node_in); if (!hist_entry__next_pair(he)) { rb_erase(&he->rb_node_in, root); hist_entry__delete(he); } } }

Contributors

PersonTokensPropCommitsCommitProp
Jiri Olsa7568.81%240.00%
Namhyung Kim3027.52%120.00%
Arnaldo Carvalho de Melo43.67%240.00%
Total109100.00%5100.00%


static void hists__precompute(struct hists *hists) { struct rb_root *root; struct rb_node *next; if (hists__has(hists, need_collapse)) root = &hists->entries_collapsed; else root = hists->entries_in; next = rb_first(root); while (next != NULL) { struct hist_entry *he, *pair; struct data__file *d; int i; he = rb_entry(next, struct hist_entry, rb_node_in); next = rb_next(&he->rb_node_in); data__for_each_file_new(i, d) { pair = get_pair_data(he, d); if (!pair) continue; switch (compute) { case COMPUTE_DELTA: case COMPUTE_DELTA_ABS: compute_delta(he, pair); break; case COMPUTE_RATIO: compute_ratio(he, pair); break; case COMPUTE_WEIGHTED_DIFF: compute_wdiff(he, pair); break; default: BUG_ON(1); } } } }

Contributors

PersonTokensPropCommitsCommitProp
Jiri Olsa14888.10%880.00%
Namhyung Kim2011.90%220.00%
Total168100.00%10100.00%


static int64_t cmp_doubles(double l, double r) { if (l > r) return -1; else if (l < r) return 1; else return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Jiri Olsa36100.00%1100.00%
Total36100.00%1100.00%


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_DELTA_ABS: { double l = fabs(left->diff.period_ratio_delta); double r = fabs(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; }

Contributors

PersonTokensPropCommitsCommitProp
Jiri Olsa12477.02%375.00%
Namhyung Kim3722.98%125.00%
Total161100.00%4100.00%


static int64_t hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right, int c, int sort_idx) { bool pairs_left = hist_entry__has_pairs(left); bool pairs_right = hist_entry__has_pairs(right); struct hist_entry *p_right, *p_left; if (!pairs_left && !pairs_right) return 0; if (!pairs_left || !pairs_right) return pairs_left ? -1 : 1; p_left = get_pair_data(left, &data__files[sort_idx]); p_right = get_pair_data(right, &data__files[sort_idx]); if (!p_left && !p_right) return 0; if (!p_left || !p_right) return p_left ? -1 : 1; /* * We have 2 entries of same kind, let's * make the data comparison. */ return __hist_entry__cmp_compute(p_left, p_right, c); }

Contributors

PersonTokensPropCommitsCommitProp
Jiri Olsa13296.35%150.00%
Namhyung Kim53.65%150.00%
Total137100.00%2100.00%


static int64_t hist_entry__cmp_compute_idx(struct hist_entry *left, struct hist_entry *right, int c, int sort_idx) { struct hist_entry *p_right, *p_left; p_left = get_pair_data(left, &data__files[sort_idx]); p_right = get_pair_data(right, &data__files[sort_idx]); if (!p_left && !p_right) return 0; if (!p_left || !p_right) return p_left ? -1 : 1; if (c != COMPUTE_DELTA && c != COMPUTE_DELTA_ABS) { /* * The delta can be computed without the baseline, but * others are not. Put those entries which have no * values below. */ if (left->dummy && right->dummy) return 0; if (left->dummy || right->dummy) return left->dummy ? 1 : -1; } return __hist_entry__cmp_compute(p_left, p_right, c); }

Contributors

PersonTokensPropCommitsCommitProp
Namhyung Kim139100.00%3100.00%
Total139100.00%3100.00%


static int64_t hist_entry__cmp_nop(struct perf_hpp_fmt *fmt __maybe_unused, struct hist_entry *left __maybe_unused, struct hist_entry *right __maybe_unused) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Namhyung Kim27100.00%3100.00%
Total27100.00%3100.00%


static int64_t hist_entry__cmp_baseline(struct perf_hpp_fmt *fmt __maybe_unused, struct hist_entry *left, struct hist_entry *right) { if (left->stat.period == right->stat.period) return 0; return left->stat.period > right->stat.period ? 1 : -1; }

Contributors

PersonTokensPropCommitsCommitProp
Namhyung Kim57100.00%2100.00%
Total57100.00%2100.00%


static int64_t hist_entry__cmp_delta(struct perf_hpp_fmt *fmt, struct hist_entry *left, struct hist_entry *right) { struct data__file *d = fmt_to_data_file(fmt); return hist_entry__cmp_compute(right, left, COMPUTE_DELTA, d->idx); }

Contributors

PersonTokensPropCommitsCommitProp
Namhyung Kim45100.00%3100.00%
Total45100.00%3100.00%


static int64_t hist_entry__cmp_delta_abs(struct perf_hpp_fmt *fmt, struct hist_entry *left, struct hist_entry *right) { struct data__file *d = fmt_to_data_file(fmt); return hist_entry__cmp_compute(right, left, COMPUTE_DELTA_ABS, d->idx); }

Contributors

PersonTokensPropCommitsCommitProp
Namhyung Kim45100.00%2100.00%
Total45100.00%2100.00%


static int64_t hist_entry__cmp_ratio(struct perf_hpp_fmt *fmt, struct hist_entry *left, struct hist_entry *right) { struct data__file *d = fmt_to_data_file(fmt); return hist_entry__cmp_compute(right, left, COMPUTE_RATIO, d->idx); }

Contributors

PersonTokensPropCommitsCommitProp
Namhyung Kim45100.00%4100.00%
Total45100.00%4100.00%


static int64_t hist_entry__cmp_wdiff(struct perf_hpp_fmt *fmt, struct hist_entry *left, struct hist_entry *right) { struct data__file *d = fmt_to_data_file(fmt); return hist_entry__cmp_compute(right, left, COMPUTE_WEIGHTED_DIFF, d->idx); }

Contributors

PersonTokensPropCommitsCommitProp
Namhyung Kim45100.00%3100.00%
Total45100.00%3100.00%


static int64_t hist_entry__cmp_delta_idx(struct perf_hpp_fmt *fmt __maybe_unused, struct hist_entry *left, struct hist_entry *right) { return hist_entry__cmp_compute_idx(right, left, COMPUTE_DELTA, sort_compute); }

Contributors

PersonTokensPropCommitsCommitProp
Namhyung Kim34100.00%1100.00%
Total34100.00%1100.00%


static int64_t hist_entry__cmp_delta_abs_idx(struct perf_hpp_fmt *fmt __maybe_unused, struct hist_entry *left, struct hist_entry *right) { return hist_entry__cmp_compute_idx(right, left, COMPUTE_DELTA_ABS, sort_compute); }

Contributors

PersonTokensPropCommitsCommitProp
Namhyung Kim34100.00%2100.00%
Total34100.00%2100.00%


static int64_t hist_entry__cmp_ratio_idx(struct perf_hpp_fmt *fmt __maybe_unused, struct hist_entry *left, struct hist_entry *right) { return hist_entry__cmp_compute_idx(right, left, COMPUTE_RATIO, sort_compute); }

Contributors

PersonTokensPropCommitsCommitProp
Namhyung Kim34100.00%2100.00%
Total34100.00%2100.00%


static int64_t hist_entry__cmp_wdiff_idx(struct perf_hpp_fmt *fmt __maybe_unused, struct hist_entry *left,