Commit a328a259 authored by Ingo Molnar's avatar Ingo Molnar
Browse files

Merge tag 'perf-core-for-mingo-5.3-20190703' of...

Merge tag 'perf-core-for-mingo-5.3-20190703' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux

 into perf/core

Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:

perf metrics:

  Andi Kleen:

  - Fixes for SkylakeX and CascadeLakeX Intel vendor events.

  - Avoid extra ':' for --raw metrics.

  - Don't include duration_time in group.

perf script:

  Arnaldo Carvalho de Melo/Jiri Olsa:

  - Fix processing guest samples.

perf diff:

  Jin Yao:

  - Do diffs by basic blocks.

objtool:

  Jiri Olsa:

  - Fix build by linking against tools/lib/ctype.o sources.

perf pmu:

  John Garry:

  - Support more complex PMU event aliasing.

  - Add support for Hisi hip08 DDRC, HHA and L3C PMU aliasing.

Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents a041ede0 15a108af
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@ objtool-y += special.o
objtool-y += objtool.o

objtool-y += libstring.o
objtool-y += libctype.o
objtool-y += str_error_r.o

CFLAGS += -I$(srctree)/tools/lib
@@ -17,6 +18,10 @@ $(OUTPUT)libstring.o: ../lib/string.c FORCE
	$(call rule_mkdir)
	$(call if_changed_dep,cc_o_c)

$(OUTPUT)libctype.o: ../lib/ctype.c FORCE
	$(call rule_mkdir)
	$(call if_changed_dep,cc_o_c)

$(OUTPUT)str_error_r.o: ../lib/str_error_r.c FORCE
	$(call rule_mkdir)
	$(call if_changed_dep,cc_o_c)
+14 −3
Original line number Diff line number Diff line
@@ -90,9 +90,10 @@ OPTIONS

-c::
--compute::
        Differential computation selection - delta, ratio, wdiff, delta-abs
        (default is delta-abs).  Default can be changed using diff.compute
        config option.  See COMPARISON METHODS section for more info.
        Differential computation selection - delta, ratio, wdiff, cycles,
        delta-abs (default is delta-abs).  Default can be changed using
        diff.compute config option.  See COMPARISON METHODS section for
        more info.

-p::
--period::
@@ -280,6 +281,16 @@ If specified the 'Weighted diff' column is displayed with value 'd' computed as:
    - WEIGHT-A being the weight of the data file
    - WEIGHT-B being the weight of the baseline data file

cycles
~~~~~~
If specified the '[Program Block Range] Cycles Diff' column is displayed.
It displays the cycles difference of same program basic block amongst
two perf.data. The program basic block is the code between two branches.

'[Program Block Range]' indicates the range of a program basic block.
Source line is reported if it can be found otherwise uses symbol+offset
instead.

SEE ALSO
--------
linkperf:perf-record[1], linkperf:perf-report[1]
+1 −1
Original line number Diff line number Diff line
@@ -89,7 +89,7 @@ OPTIONS
	- socket: processor socket number the task ran at the time of sample
	- srcline: filename and line number executed at the time of sample.  The
	DWARF debugging info must be provided.
	- srcfile: file name of the source file of the same. Requires dwarf
	- srcfile: file name of the source file of the samples. Requires dwarf
	information.
	- weight: Event specific weight, e.g. memory latency or transaction
	abort cost. This is the global weight.
+1 −1
Original line number Diff line number Diff line
@@ -38,6 +38,6 @@ To report cacheline events from previous recording: perf c2c report
To browse sample contexts use perf report --sample 10 and select in context menu
To separate samples by time use perf report --sort time,overhead,sym
To set sample time separation other than 100ms with --sort time use --time-quantum
Add -I to perf report to sample register values visible in perf report context.
Add -I to perf record to sample register values, which will be visible in perf report sample context.
To show IPC for sampling periods use perf record -e '{cycles,instructions}:S' and then browse context
To show context switches in perf report sample context add --switch-events to perf record.
+377 −5
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@
#include "util/data.h"
#include "util/config.h"
#include "util/time-utils.h"
#include "util/annotate.h"
#include "util/map.h"

#include <errno.h>
#include <inttypes.h>
@@ -32,6 +34,7 @@ struct perf_diff {
	struct perf_time_interval	*ptime_range;
	int				 range_size;
	int				 range_num;
	bool				 has_br_stack;
};

/* Diff command specific HPP columns. */
@@ -44,6 +47,7 @@ enum {
	PERF_HPP_DIFF__WEIGHTED_DIFF,
	PERF_HPP_DIFF__FORMULA,
	PERF_HPP_DIFF__DELTA_ABS,
	PERF_HPP_DIFF__CYCLES,

	PERF_HPP_DIFF__MAX_INDEX
};
@@ -86,11 +90,14 @@ static s64 compute_wdiff_w2;
static const char		*cpu_list;
static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);

static struct addr_location dummy_al;

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

@@ -99,6 +106,7 @@ const char *compute_names[COMPUTE_MAX] = {
	[COMPUTE_DELTA_ABS] = "delta-abs",
	[COMPUTE_RATIO] = "ratio",
	[COMPUTE_WEIGHTED_DIFF] = "wdiff",
	[COMPUTE_CYCLES] = "cycles",
};

static int compute = COMPUTE_DELTA_ABS;
@@ -108,6 +116,7 @@ static int compute_2_hpp[COMPUTE_MAX] = {
	[COMPUTE_DELTA_ABS]	= PERF_HPP_DIFF__DELTA_ABS,
	[COMPUTE_RATIO]		= PERF_HPP_DIFF__RATIO,
	[COMPUTE_WEIGHTED_DIFF]	= PERF_HPP_DIFF__WEIGHTED_DIFF,
	[COMPUTE_CYCLES]	= PERF_HPP_DIFF__CYCLES,
};

#define MAX_COL_WIDTH 70
@@ -146,6 +155,10 @@ static struct header_column {
	[PERF_HPP_DIFF__FORMULA] = {
		.name  = "Formula",
		.width = MAX_COL_WIDTH,
	},
	[PERF_HPP_DIFF__CYCLES] = {
		.name  = "[Program Block Range] Cycles Diff",
		.width = 70,
	}
};

@@ -335,6 +348,31 @@ static int formula_fprintf(struct hist_entry *he, struct hist_entry *pair,
	return -1;
}

static void *block_hist_zalloc(size_t size)
{
	struct block_hist *bh;

	bh = zalloc(size + sizeof(*bh));
	if (!bh)
		return NULL;

	return &bh->he;
}

static void block_hist_free(void *he)
{
	struct block_hist *bh;

	bh = container_of(he, struct block_hist, he);
	hists__delete_entries(&bh->block_hists);
	free(bh);
}

struct hist_entry_ops block_hist_ops = {
	.new    = block_hist_zalloc,
	.free   = block_hist_free,
};

static int diff__process_sample_event(struct perf_tool *tool,
				      union perf_event *event,
				      struct perf_sample *sample,
@@ -362,11 +400,24 @@ static int diff__process_sample_event(struct perf_tool *tool,
		goto out_put;
	}

	if (!hists__add_entry(hists, &al, NULL, NULL, NULL, sample, true)) {
		pr_warning("problem incrementing symbol period, skipping event\n");
	if (compute != COMPUTE_CYCLES) {
		if (!hists__add_entry(hists, &al, NULL, NULL, NULL, sample,
				      true)) {
			pr_warning("problem incrementing symbol period, "
				   "skipping event\n");
			goto out_put;
		}
	} else {
		if (!hists__add_entry_ops(hists, &block_hist_ops, &al, NULL,
					  NULL, NULL, sample, true)) {
			pr_warning("problem incrementing symbol period, "
				   "skipping event\n");
			goto out_put;
		}

		hist__account_cycles(sample->branch_stack, &al, sample, false);
	}

	/*
	 * The total_period is updated here before going to the output
	 * tree since normally only the baseline hists will call
@@ -474,6 +525,203 @@ static void hists__baseline_only(struct hists *hists)
	}
}

static int64_t block_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
			 struct hist_entry *left, struct hist_entry *right)
{
	struct block_info *bi_l = left->block_info;
	struct block_info *bi_r = right->block_info;
	int cmp;

	if (!bi_l->sym || !bi_r->sym) {
		if (!bi_l->sym && !bi_r->sym)
			return 0;
		else if (!bi_l->sym)
			return -1;
		else
			return 1;
	}

	if (bi_l->sym == bi_r->sym) {
		if (bi_l->start == bi_r->start) {
			if (bi_l->end == bi_r->end)
				return 0;
			else
				return (int64_t)(bi_r->end - bi_l->end);
		} else
			return (int64_t)(bi_r->start - bi_l->start);
	} else {
		cmp = strcmp(bi_l->sym->name, bi_r->sym->name);
		return cmp;
	}

	if (bi_l->sym->start != bi_r->sym->start)
		return (int64_t)(bi_r->sym->start - bi_l->sym->start);

	return (int64_t)(bi_r->sym->end - bi_l->sym->end);
}

static int64_t block_cycles_diff_cmp(struct hist_entry *left,
				     struct hist_entry *right)
{
	bool pairs_left  = hist_entry__has_pairs(left);
	bool pairs_right = hist_entry__has_pairs(right);
	s64 l, r;

	if (!pairs_left && !pairs_right)
		return 0;

	l = labs(left->diff.cycles);
	r = labs(right->diff.cycles);
	return r - l;
}

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

static void init_block_hist(struct block_hist *bh)
{
	__hists__init(&bh->block_hists, &bh->block_list);
	perf_hpp_list__init(&bh->block_list);

	INIT_LIST_HEAD(&bh->block_fmt.list);
	INIT_LIST_HEAD(&bh->block_fmt.sort_list);
	bh->block_fmt.cmp = block_cmp;
	bh->block_fmt.sort = block_sort;
	perf_hpp_list__register_sort_field(&bh->block_list,
					   &bh->block_fmt);
	bh->valid = true;
}

static void init_block_info(struct block_info *bi, struct symbol *sym,
			    struct cyc_hist *ch, int offset)
{
	bi->sym = sym;
	bi->start = ch->start;
	bi->end = offset;
	bi->cycles = ch->cycles;
	bi->cycles_aggr = ch->cycles_aggr;
	bi->num = ch->num;
	bi->num_aggr = ch->num_aggr;
}

static int process_block_per_sym(struct hist_entry *he)
{
	struct annotation *notes;
	struct cyc_hist *ch;
	struct block_hist *bh;

	if (!he->ms.map || !he->ms.sym)
		return 0;

	notes = symbol__annotation(he->ms.sym);
	if (!notes || !notes->src || !notes->src->cycles_hist)
		return 0;

	bh = container_of(he, struct block_hist, he);
	init_block_hist(bh);

	ch = notes->src->cycles_hist;
	for (unsigned int i = 0; i < symbol__size(he->ms.sym); i++) {
		if (ch[i].num_aggr) {
			struct block_info *bi;
			struct hist_entry *he_block;

			bi = block_info__new();
			if (!bi)
				return -1;

			init_block_info(bi, he->ms.sym, &ch[i], i);
			he_block = hists__add_entry_block(&bh->block_hists,
							  &dummy_al, bi);
			if (!he_block) {
				block_info__put(bi);
				return -1;
			}
		}
	}

	return 0;
}

static int block_pair_cmp(struct hist_entry *a, struct hist_entry *b)
{
	struct block_info *bi_a = a->block_info;
	struct block_info *bi_b = b->block_info;
	int cmp;

	if (!bi_a->sym || !bi_b->sym)
		return -1;

	cmp = strcmp(bi_a->sym->name, bi_b->sym->name);

	if ((!cmp) && (bi_a->start == bi_b->start) && (bi_a->end == bi_b->end))
		return 0;

	return -1;
}

static struct hist_entry *get_block_pair(struct hist_entry *he,
					 struct hists *hists_pair)
{
	struct rb_root_cached *root = hists_pair->entries_in;
	struct rb_node *next = rb_first_cached(root);
	int cmp;

	while (next != NULL) {
		struct hist_entry *he_pair = rb_entry(next, struct hist_entry,
						      rb_node_in);

		next = rb_next(&he_pair->rb_node_in);

		cmp = block_pair_cmp(he_pair, he);
		if (!cmp)
			return he_pair;
	}

	return NULL;
}

static void compute_cycles_diff(struct hist_entry *he,
				struct hist_entry *pair)
{
	pair->diff.computed = true;
	if (pair->block_info->num && he->block_info->num) {
		pair->diff.cycles =
			pair->block_info->cycles_aggr / pair->block_info->num_aggr -
			he->block_info->cycles_aggr / he->block_info->num_aggr;
	}
}

static void block_hists_match(struct hists *hists_base,
			      struct hists *hists_pair)
{
	struct rb_root_cached *root = hists_base->entries_in;
	struct rb_node *next = rb_first_cached(root);

	while (next != NULL) {
		struct hist_entry *he = rb_entry(next, struct hist_entry,
						 rb_node_in);
		struct hist_entry *pair = get_block_pair(he, hists_pair);

		next = rb_next(&he->rb_node_in);

		if (pair) {
			hist_entry__add_pair(pair, he);
			compute_cycles_diff(he, pair);
		}
	}
}

static int filter_cb(struct hist_entry *he, void *arg __maybe_unused)
{
	/* Skip the calculation of column length in output_resort */
	he->filtered = true;
	return 0;
}

static void hists__precompute(struct hists *hists)
{
	struct rb_root_cached *root;
@@ -486,6 +734,7 @@ static void hists__precompute(struct hists *hists)

	next = rb_first_cached(root);
	while (next != NULL) {
		struct block_hist *bh, *pair_bh;
		struct hist_entry *he, *pair;
		struct data__file *d;
		int i;
@@ -493,6 +742,9 @@ static void hists__precompute(struct hists *hists)
		he   = rb_entry(next, struct hist_entry, rb_node_in);
		next = rb_next(&he->rb_node_in);

		if (compute == COMPUTE_CYCLES)
			process_block_per_sym(he);

		data__for_each_file_new(i, d) {
			pair = get_pair_data(he, d);
			if (!pair)
@@ -509,6 +761,19 @@ static void hists__precompute(struct hists *hists)
			case COMPUTE_WEIGHTED_DIFF:
				compute_wdiff(he, pair);
				break;
			case COMPUTE_CYCLES:
				process_block_per_sym(pair);
				bh = container_of(he, struct block_hist, he);
				pair_bh = container_of(pair, struct block_hist,
						       he);

				if (bh->valid && pair_bh->valid) {
					block_hists_match(&bh->block_hists,
							  &pair_bh->block_hists);
					hists__output_resort_cb(&pair_bh->block_hists,
								NULL, filter_cb);
				}
				break;
			default:
				BUG_ON(1);
			}
@@ -720,6 +985,9 @@ static void hists__process(struct hists *hists)
	hists__precompute(hists);
	hists__output_resort(hists, NULL);

	if (compute == COMPUTE_CYCLES)
		symbol_conf.report_block = true;

	hists__fprintf(hists, !quiet, 0, 0, 0, stdout,
		       !symbol_conf.use_callchain);
}
@@ -873,6 +1141,31 @@ static int parse_time_str(struct data__file *d, char *abstime_ostr,
	return ret;
}

static int check_file_brstack(void)
{
	struct data__file *d;
	bool has_br_stack;
	int i;

	data__for_each_file(i, d) {
		d->session = perf_session__new(&d->data, false, &pdiff.tool);
		if (!d->session) {
			pr_err("Failed to open %s\n", d->data.path);
			return -1;
		}

		has_br_stack = perf_header__has_feat(&d->session->header,
						     HEADER_BRANCH_STACK);
		perf_session__delete(d->session);
		if (!has_br_stack)
			return 0;
	}

	/* Set only all files having branch stacks */
	pdiff.has_br_stack = true;
	return 0;
}

static int __cmd_diff(void)
{
	struct data__file *d;
@@ -950,7 +1243,7 @@ static const struct option options[] = {
	OPT_BOOLEAN('b', "baseline-only", &show_baseline_only,
		    "Show only items with match in baseline"),
	OPT_CALLBACK('c', "compute", &compute,
		     "delta,delta-abs,ratio,wdiff:w1,w2 (default delta-abs)",
		     "delta,delta-abs,ratio,wdiff:w1,w2 (default delta-abs),cycles",
		     "Entries differential computation selection",
		     setup_compute),
	OPT_BOOLEAN('p', "period", &show_period,
@@ -1028,6 +1321,49 @@ static int hpp__entry_baseline(struct hist_entry *he, char *buf, size_t size)
	return ret;
}

static int cycles_printf(struct hist_entry *he, struct hist_entry *pair,
			 struct perf_hpp *hpp, int width)
{
	struct block_hist *bh = container_of(he, struct block_hist, he);
	struct block_hist *bh_pair = container_of(pair, struct block_hist, he);
	struct hist_entry *block_he;
	struct block_info *bi;
	char buf[128];
	char *start_line, *end_line;

	block_he = hists__get_entry(&bh_pair->block_hists, bh->block_idx);
	if (!block_he) {
		hpp->skip = true;
		return 0;
	}

	/*
	 * Avoid printing the warning "addr2line_init failed for ..."
	 */
	symbol_conf.disable_add2line_warn = true;

	bi = block_he->block_info;

	start_line = map__srcline(he->ms.map, bi->sym->start + bi->start,
				  he->ms.sym);

	end_line = map__srcline(he->ms.map, bi->sym->start + bi->end,
				he->ms.sym);

	if ((start_line != SRCLINE_UNKNOWN) && (end_line != SRCLINE_UNKNOWN)) {
		scnprintf(buf, sizeof(buf), "[%s -> %s] %4ld",
			  start_line, end_line, block_he->diff.cycles);
	} else {
		scnprintf(buf, sizeof(buf), "[%7lx -> %7lx] %4ld",
			  bi->start, bi->end, block_he->diff.cycles);
	}

	free_srcline(start_line);
	free_srcline(end_line);

	return scnprintf(hpp->buf, hpp->size, "%*s", width, buf);
}

static int __hpp__color_compare(struct perf_hpp_fmt *fmt,
				struct perf_hpp *hpp, struct hist_entry *he,
				int comparison_method)
@@ -1039,8 +1375,17 @@ static int __hpp__color_compare(struct perf_hpp_fmt *fmt,
	s64 wdiff;
	char pfmt[20] = " ";

	if (!pair)
	if (!pair) {
		if (comparison_method == COMPUTE_CYCLES) {
			struct block_hist *bh;

			bh = container_of(he, struct block_hist, he);
			if (bh->block_idx)
				hpp->skip = true;
		}

		goto no_print;
	}

	switch (comparison_method) {
	case COMPUTE_DELTA:
@@ -1075,6 +1420,8 @@ static int __hpp__color_compare(struct perf_hpp_fmt *fmt,
		return color_snprintf(hpp->buf, hpp->size,
				get_percent_color(wdiff),
				pfmt, wdiff);
	case COMPUTE_CYCLES:
		return cycles_printf(he, pair, hpp, dfmt->header_width);
	default:
		BUG_ON(1);
	}
@@ -1104,6 +1451,12 @@ static int hpp__color_wdiff(struct perf_hpp_fmt *fmt,
	return __hpp__color_compare(fmt, hpp, he, COMPUTE_WEIGHTED_DIFF);
}

static int hpp__color_cycles(struct perf_hpp_fmt *fmt,
			     struct perf_hpp *hpp, struct hist_entry *he)
{
	return __hpp__color_compare(fmt, hpp, he, COMPUTE_CYCLES);
}

static void
hpp__entry_unpair(struct hist_entry *he, int idx, char *buf, size_t size)
{
@@ -1305,6 +1658,10 @@ static void data__hpp_register(struct data__file *d, int idx)
		fmt->color = hpp__color_delta;
		fmt->sort  = hist_entry__cmp_delta_abs;
		break;
	case PERF_HPP_DIFF__CYCLES:
		fmt->color = hpp__color_cycles;
		fmt->sort  = hist_entry__cmp_nop;
		break;
	default:
		fmt->sort  = hist_entry__cmp_nop;
		break;
@@ -1385,6 +1742,13 @@ static int ui_init(void)
	case COMPUTE_DELTA_ABS:
		fmt->sort = hist_entry__cmp_delta_abs_idx;
		break;
	case COMPUTE_CYCLES:
		/*
		 * Should set since 'fmt->sort' is called without
		 * checking valid during sorting
		 */
		fmt->sort = hist_entry__cmp_nop;
		break;
	default:
		BUG_ON(1);
	}
@@ -1481,12 +1845,20 @@ int cmd_diff(int argc, const char **argv)
	if (quiet)
		perf_quiet_option();

	symbol__annotation_init();

	if (symbol__init(NULL) < 0)
		return -1;

	if (data_init(argc, argv) < 0)
		return -1;

	if (check_file_brstack() < 0)
		return -1;

	if (compute == COMPUTE_CYCLES && !pdiff.has_br_stack)
		return -1;

	if (ui_init() < 0)
		return -1;

Loading