Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 57646b6f authored by Ingo Molnar's avatar Ingo Molnar
Browse files

Merge tag 'perf-core-for-mingo-4.15-20171025' of...

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

 into perf/core

Pull perf/core inline improvements from Arnaldo Carvalho de Melo:

From Milian's cover letter: (Milian Wolff)

"This series of patches completely reworks the way inline frames are
 handled.  Instead of querying for the inline nodes on-demand in the
 individual tools, we now create proper callchain nodes for inlined
 frames. The advantages this approach brings are numerous:

 - Less duplicated code in the individual browser

 - Aggregated cost for inlined frames for the --children top-down list

 - Various bug fixes that arose from querying for a srcline/symbol based on
   the IP of a sample, which will always point to the last inlined frame
   instead of the corresponding non-inlined frame

 - Overall much better support for visualizing cost for heavily-inlined C++
   code, which simply was confusing and unreliably before

 - srcline honors the global setting as to whether full paths or basenames
   should be shown

 - Caches for inlined frames and srcline information, which allow us to
   enable inline frame handling by default"

Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 9b7c8547 d8a88dd2
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -434,7 +434,8 @@ include::itrace.txt[]

--inline::
	If a callgraph address belongs to an inlined function, the inline stack
	will be printed. Each entry is function name or file/line.
	will be printed. Each entry is function name or file/line. Enabled by
	default, disable with --no-inline.

include::callchain-overhead-calculation.txt[]

+2 −1
Original line number Diff line number Diff line
@@ -327,7 +327,8 @@ include::itrace.txt[]

--inline::
	If a callgraph address belongs to an inlined function, the inline stack
	will be printed. Each entry has function name and file/line.
	will be printed. Each entry has function name and file/line. Enabled by
	default, disable with --no-inline.

SEE ALSO
--------
+12 −168
Original line number Diff line number Diff line
@@ -154,57 +154,9 @@ static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
	cl->unfolded = unfold ? cl->has_children : false;
}

static struct inline_node *inline_node__create(struct map *map, u64 ip)
{
	struct dso *dso;
	struct inline_node *node;

	if (map == NULL)
		return NULL;

	dso = map->dso;
	if (dso == NULL)
		return NULL;

	node = dso__parse_addr_inlines(dso,
				       map__rip_2objdump(map, ip));

	return node;
}

static int inline__count_rows(struct inline_node *node)
{
	struct inline_list *ilist;
	int i = 0;

	if (node == NULL)
		return 0;

	list_for_each_entry(ilist, &node->val, list) {
		if ((ilist->filename != NULL) || (ilist->funcname != NULL))
			i++;
	}

	return i;
}

static int callchain_list__inline_rows(struct callchain_list *chain)
{
	struct inline_node *node;
	int rows;

	node = inline_node__create(chain->ms.map, chain->ip);
	if (node == NULL)
		return 0;

	rows = inline__count_rows(node);
	inline_node__delete(node);
	return rows;
}

static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
{
	int n = 0, inline_rows;
	int n = 0;
	struct rb_node *nd;

	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
@@ -215,12 +167,6 @@ static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
		list_for_each_entry(chain, &child->val, list) {
			++n;

			if (symbol_conf.inline_name) {
				inline_rows =
					callchain_list__inline_rows(chain);
				n += inline_rows;
			}

			/* We need this because we may not have children */
			folded_sign = callchain_list__folded(chain);
			if (folded_sign == '+')
@@ -272,7 +218,7 @@ static int callchain_node__count_rows(struct callchain_node *node)
{
	struct callchain_list *chain;
	bool unfolded = false;
	int n = 0, inline_rows;
	int n = 0;

	if (callchain_param.mode == CHAIN_FLAT)
		return callchain_node__count_flat_rows(node);
@@ -281,10 +227,6 @@ static int callchain_node__count_rows(struct callchain_node *node)

	list_for_each_entry(chain, &node->val, list) {
		++n;
		if (symbol_conf.inline_name) {
			inline_rows = callchain_list__inline_rows(chain);
			n += inline_rows;
		}

		unfolded = chain->unfolded;
	}
@@ -432,19 +374,6 @@ static void hist_entry__init_have_children(struct hist_entry *he)
	he->init_have_children = true;
}

static void hist_entry_init_inline_node(struct hist_entry *he)
{
	if (he->inline_node)
		return;

	he->inline_node = inline_node__create(he->ms.map, he->ip);

	if (he->inline_node == NULL)
		return;

	he->has_children = true;
}

static bool hist_browser__toggle_fold(struct hist_browser *browser)
{
	struct hist_entry *he = browser->he_selection;
@@ -476,10 +405,6 @@ static bool hist_browser__toggle_fold(struct hist_browser *browser)

		if (he->unfolded) {
			if (he->leaf)
				if (he->inline_node)
					he->nr_rows = inline__count_rows(
							he->inline_node);
				else
				he->nr_rows = callchain__count_rows(
						&he->sorted_chain);
			else
@@ -841,71 +766,6 @@ static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_u

#define LEVEL_OFFSET_STEP 3

static int hist_browser__show_inline(struct hist_browser *browser,
				     struct inline_node *node,
				     unsigned short row,
				     int offset)
{
	struct inline_list *ilist;
	char buf[1024];
	int color, width, first_row;

	first_row = row;
	width = browser->b.width - (LEVEL_OFFSET_STEP + 2);
	list_for_each_entry(ilist, &node->val, list) {
		if ((ilist->filename != NULL) || (ilist->funcname != NULL)) {
			color = HE_COLORSET_NORMAL;
			if (ui_browser__is_current_entry(&browser->b, row))
				color = HE_COLORSET_SELECTED;

			if (callchain_param.key == CCKEY_ADDRESS ||
			    callchain_param.key == CCKEY_SRCLINE) {
				if (ilist->filename != NULL)
					scnprintf(buf, sizeof(buf),
						  "%s:%d (inline)",
						  ilist->filename,
						  ilist->line_nr);
				else
					scnprintf(buf, sizeof(buf), "??");
			} else if (ilist->funcname != NULL)
				scnprintf(buf, sizeof(buf), "%s (inline)",
					  ilist->funcname);
			else if (ilist->filename != NULL)
				scnprintf(buf, sizeof(buf),
					  "%s:%d (inline)",
					  ilist->filename,
					  ilist->line_nr);
			else
				scnprintf(buf, sizeof(buf), "??");

			ui_browser__set_color(&browser->b, color);
			hist_browser__gotorc(browser, row, 0);
			ui_browser__write_nstring(&browser->b, " ",
				LEVEL_OFFSET_STEP + offset);
			ui_browser__write_nstring(&browser->b, buf, width);
			row++;
		}
	}

	return row - first_row;
}

static size_t show_inline_list(struct hist_browser *browser, struct map *map,
			       u64 ip, int row, int offset)
{
	struct inline_node *node;
	int ret;

	node = inline_node__create(map, ip);
	if (node == NULL)
		return 0;

	ret = hist_browser__show_inline(browser, node, row, offset);

	inline_node__delete(node);
	return ret;
}

static int hist_browser__show_callchain_list(struct hist_browser *browser,
					     struct callchain_node *node,
					     struct callchain_list *chain,
@@ -917,7 +777,7 @@ static int hist_browser__show_callchain_list(struct hist_browser *browser,
	char bf[1024], *alloc_str;
	char buf[64], *alloc_str2;
	const char *str;
	int inline_rows = 0, ret = 1;
	int ret = 1;

	if (arg->row_offset != 0) {
		arg->row_offset--;
@@ -954,12 +814,7 @@ static int hist_browser__show_callchain_list(struct hist_browser *browser,
	free(alloc_str);
	free(alloc_str2);

	if (symbol_conf.inline_name) {
		inline_rows = show_inline_list(browser, chain->ms.map,
					       chain->ip, row + 1, offset);
	}

	return ret + inline_rows;
	return ret;
}

static bool check_percent_display(struct rb_node *node, u64 parent_total)
@@ -1383,12 +1238,6 @@ static int hist_browser__show_entry(struct hist_browser *browser,
		folded_sign = hist_entry__folded(entry);
	}

	if (symbol_conf.inline_name &&
	    (!entry->has_children)) {
		hist_entry_init_inline_node(entry);
		folded_sign = hist_entry__folded(entry);
	}

	if (row_offset == 0) {
		struct hpp_arg arg = {
			.b		= &browser->b,
@@ -1420,8 +1269,7 @@ static int hist_browser__show_entry(struct hist_browser *browser,
			}

			if (first) {
				if (symbol_conf.use_callchain ||
					symbol_conf.inline_name) {
				if (symbol_conf.use_callchain) {
					ui_browser__printf(&browser->b, "%c ", folded_sign);
					width -= 2;
				}
@@ -1463,10 +1311,6 @@ static int hist_browser__show_entry(struct hist_browser *browser,
			.is_current_entry = current_entry,
		};

		if (entry->inline_node)
			printed += hist_browser__show_inline(browser,
					entry->inline_node, row, 0);
		else
		printed += hist_browser__show_callchain(browser,
				entry, 1, row,
				hist_browser__show_callchain_entry,
+1 −76
Original line number Diff line number Diff line
@@ -21,64 +21,6 @@ static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
	return ret;
}

static size_t inline__fprintf(struct map *map, u64 ip, int left_margin,
			      int depth, int depth_mask, FILE *fp)
{
	struct dso *dso;
	struct inline_node *node;
	struct inline_list *ilist;
	int ret = 0, i;

	if (map == NULL)
		return 0;

	dso = map->dso;
	if (dso == NULL)
		return 0;

	node = dso__parse_addr_inlines(dso,
				       map__rip_2objdump(map, ip));
	if (node == NULL)
		return 0;

	list_for_each_entry(ilist, &node->val, list) {
		if ((ilist->filename != NULL) || (ilist->funcname != NULL)) {
			ret += callchain__fprintf_left_margin(fp, left_margin);

			for (i = 0; i < depth; i++) {
				if (depth_mask & (1 << i))
					ret += fprintf(fp, "|");
				else
					ret += fprintf(fp, " ");
				ret += fprintf(fp, "          ");
			}

			if (callchain_param.key == CCKEY_ADDRESS ||
			    callchain_param.key == CCKEY_SRCLINE) {
				if (ilist->filename != NULL)
					ret += fprintf(fp, "%s:%d (inline)",
						       ilist->filename,
						       ilist->line_nr);
				else
					ret += fprintf(fp, "??");
			} else if (ilist->funcname != NULL)
				ret += fprintf(fp, "%s (inline)",
					       ilist->funcname);
			else if (ilist->filename != NULL)
				ret += fprintf(fp, "%s:%d (inline)",
					       ilist->filename,
					       ilist->line_nr);
			else
				ret += fprintf(fp, "??");

			ret += fprintf(fp, "\n");
		}
	}

	inline_node__delete(node);
	return ret;
}

static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask,
					  int left_margin)
{
@@ -137,9 +79,6 @@ static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_node *node,
	fputc('\n', fp);
	free(alloc_str);

	if (symbol_conf.inline_name)
		ret += inline__fprintf(chain->ms.map, chain->ip,
				       left_margin, depth, depth_mask, fp);
	return ret;
}

@@ -314,13 +253,6 @@ static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,

			if (++entries_printed == callchain_param.print_limit)
				break;

			if (symbol_conf.inline_name)
				ret += inline__fprintf(chain->ms.map,
						       chain->ip,
						       left_margin,
						       0, 0,
						       fp);
		}
		root = &cnode->rb_root;
	}
@@ -600,7 +532,6 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size,
{
	int ret;
	int callchain_ret = 0;
	int inline_ret = 0;
	struct perf_hpp hpp = {
		.buf		= bf,
		.size		= size,
@@ -622,12 +553,6 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size,
		callchain_ret = hist_entry_callchain__fprintf(he, total_period,
							      0, fp);

	if (callchain_ret == 0 && symbol_conf.inline_name) {
		inline_ret = inline__fprintf(he->ms.map, he->ip, 0, 0, 0, fp);
		ret += inline_ret;
		if (inline_ret > 0)
			ret += fprintf(fp, "\n");
	} else
	ret += callchain_ret;

	return ret;
+97 −77
Original line number Diff line number Diff line
@@ -566,6 +566,7 @@ fill_node(struct callchain_node *node, struct callchain_cursor *cursor)
		call->ip = cursor_node->ip;
		call->ms.sym = cursor_node->sym;
		call->ms.map = map__get(cursor_node->map);
		call->srcline = cursor_node->srcline;

		if (cursor_node->branch) {
			call->branch_count = 1;
@@ -644,69 +645,90 @@ enum match_result {
	MATCH_GT,
};

static enum match_result match_chain_srcline(struct callchain_cursor_node *node,
					     struct callchain_list *cnode)
static enum match_result match_chain_strings(const char *left,
					     const char *right)
{
	char *left = NULL;
	char *right = NULL;
	enum match_result ret = MATCH_EQ;
	int cmp;

	if (cnode->ms.map)
		left = get_srcline(cnode->ms.map->dso,
				 map__rip_2objdump(cnode->ms.map, cnode->ip),
				 cnode->ms.sym, true, false);
	if (node->map)
		right = get_srcline(node->map->dso,
				  map__rip_2objdump(node->map, node->ip),
				  node->sym, true, false);

	if (left && right)
		cmp = strcmp(left, right);
	else if (!left && right)
		cmp = 1;
	else if (left && !right)
		cmp = -1;
	else if (cnode->ip == node->ip)
		cmp = 0;
	else
		cmp = (cnode->ip < node->ip) ? -1 : 1;
		return MATCH_ERROR;

	if (cmp != 0)
		ret = cmp < 0 ? MATCH_LT : MATCH_GT;

	free_srcline(left);
	free_srcline(right);
	return ret;
}

static enum match_result match_chain(struct callchain_cursor_node *node,
				     struct callchain_list *cnode)
/*
 * We need to always use relative addresses because we're aggregating
 * callchains from multiple threads, i.e. different address spaces, so
 * comparing absolute addresses make no sense as a symbol in a DSO may end up
 * in a different address when used in a different binary or even the same
 * binary but with some sort of address randomization technique, thus we need
 * to compare just relative addresses. -acme
 */
static enum match_result match_chain_dso_addresses(struct map *left_map, u64 left_ip,
						   struct map *right_map, u64 right_ip)
{
	struct symbol *sym = node->sym;
	u64 left, right;
	struct dso *left_dso = NULL;
	struct dso *right_dso = NULL;
	struct dso *left_dso = left_map ? left_map->dso : NULL;
	struct dso *right_dso = right_map ? right_map->dso : NULL;

	if (callchain_param.key == CCKEY_SRCLINE) {
		enum match_result match = match_chain_srcline(node, cnode);
	if (left_dso != right_dso)
		return left_dso < right_dso ? MATCH_LT : MATCH_GT;

		if (match != MATCH_ERROR)
			return match;
	if (left_ip != right_ip)
 		return left_ip < right_ip ? MATCH_LT : MATCH_GT;

	return MATCH_EQ;
}

	if (cnode->ms.sym && sym && callchain_param.key == CCKEY_FUNCTION) {
		left = cnode->ms.sym->start;
		right = sym->start;
		left_dso = cnode->ms.map->dso;
		right_dso = node->map->dso;
static enum match_result match_chain(struct callchain_cursor_node *node,
				     struct callchain_list *cnode)
{
	enum match_result match = MATCH_ERROR;

	switch (callchain_param.key) {
	case CCKEY_SRCLINE:
		match = match_chain_strings(cnode->srcline, node->srcline);
		if (match != MATCH_ERROR)
			break;
		/* otherwise fall-back to symbol-based comparison below */
		__fallthrough;
	case CCKEY_FUNCTION:
		if (node->sym && cnode->ms.sym) {
			/*
			 * Compare inlined frames based on their symbol name
			 * because different inlined frames will have the same
			 * symbol start. Otherwise do a faster comparison based
			 * on the symbol start address.
			 */
			if (cnode->ms.sym->inlined || node->sym->inlined) {
				match = match_chain_strings(cnode->ms.sym->name,
							    node->sym->name);
				if (match != MATCH_ERROR)
					break;
			} else {
		left = cnode->ip;
		right = node->ip;
				match = match_chain_dso_addresses(cnode->ms.map, cnode->ms.sym->start,
								  node->map, node->sym->start);
				break;
			}
		}
		/* otherwise fall-back to IP-based comparison below */
		__fallthrough;
	case CCKEY_ADDRESS:
	default:
		match = match_chain_dso_addresses(cnode->ms.map, cnode->ip, node->map, node->ip);
		break;
	}

	if (left == right && left_dso == right_dso) {
		if (node->branch) {
	if (match == MATCH_EQ && node->branch) {
		cnode->branch_count++;

		if (node->branch_from) {
@@ -730,17 +752,13 @@ static enum match_result match_chain(struct callchain_cursor_node *node,
			 * It's "from" of a branch
			 */
			cnode->brtype_stat.branch_to = false;
				cnode->cycles_count +=
					node->branch_flags.cycles;
			cnode->cycles_count += node->branch_flags.cycles;
			cnode->iter_count += node->nr_loop_iter;
			cnode->iter_cycles += node->iter_cycles;
		}
	}

		return MATCH_EQ;
	}

	return left > right ? MATCH_GT : MATCH_LT;
	return match;
}

/*
@@ -969,7 +987,7 @@ merge_chain_branch(struct callchain_cursor *cursor,
	list_for_each_entry_safe(list, next_list, &src->val, list) {
		callchain_cursor_append(cursor, list->ip,
					list->ms.map, list->ms.sym,
					false, NULL, 0, 0, 0);
					false, NULL, 0, 0, 0, list->srcline);
		list_del(&list->list);
		map__zput(list->ms.map);
		free(list);
@@ -1009,7 +1027,8 @@ int callchain_merge(struct callchain_cursor *cursor,
int callchain_cursor_append(struct callchain_cursor *cursor,
			    u64 ip, struct map *map, struct symbol *sym,
			    bool branch, struct branch_flags *flags,
			    int nr_loop_iter, u64 iter_cycles, u64 branch_from)
			    int nr_loop_iter, u64 iter_cycles, u64 branch_from,
			    const char *srcline)
{
	struct callchain_cursor_node *node = *cursor->last;

@@ -1028,6 +1047,7 @@ int callchain_cursor_append(struct callchain_cursor *cursor,
	node->branch = branch;
	node->nr_loop_iter = nr_loop_iter;
	node->iter_cycles = iter_cycles;
	node->srcline = srcline;

	if (flags)
		memcpy(&node->branch_flags, flags,
@@ -1070,6 +1090,7 @@ int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *
{
	al->map = node->map;
	al->sym = node->sym;
	al->srcline = node->srcline;
	if (node->map)
		al->addr = node->map->map_ip(node->map, node->ip);
	else
@@ -1115,16 +1136,15 @@ char *callchain_list__sym_name(struct callchain_list *cl,
	int printed;

	if (cl->ms.sym) {
		if (show_srcline && cl->ms.map && !cl->srcline)
			cl->srcline = get_srcline(cl->ms.map->dso,
						  map__rip_2objdump(cl->ms.map,
								    cl->ip),
						  cl->ms.sym, false, show_addr);
		if (cl->srcline)
			printed = scnprintf(bf, bfsize, "%s %s",
					cl->ms.sym->name, cl->srcline);
		const char *inlined = cl->ms.sym->inlined ? " (inlined)" : "";

		if (show_srcline && cl->srcline)
			printed = scnprintf(bf, bfsize, "%s %s%s",
					    cl->ms.sym->name, cl->srcline,
					    inlined);
		else
			printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
			printed = scnprintf(bf, bfsize, "%s%s",
					    cl->ms.sym->name, inlined);
	} else
		printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);

@@ -1532,7 +1552,7 @@ int callchain_cursor__copy(struct callchain_cursor *dst,
					     node->branch, &node->branch_flags,
					     node->nr_loop_iter,
					     node->iter_cycles,
					     node->branch_from);
					     node->branch_from, node->srcline);
		if (rc)
			break;

Loading