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

Commit 0d3eb0b7 authored by Jin Yao's avatar Jin Yao Committed by Arnaldo Carvalho de Melo
Browse files

perf report: Show inline stack for browser mode



If the address belongs to an inlined function, the source information
back to the first non-inlined function will be printed.

For example:

1. Show inlined function name
   perf report -g function --inline

-    0.69%     0.00%  inline   ld-2.23.so           [.] dl_main
   - dl_main
        0.56% _dl_relocate_object
         _dl_relocate_object (inline)
         elf_dynamic_do_Rela (inline)

2. Show the file/line information
   perf report -g address --inline

-    0.69%     0.00%  inline   ld-2.23.so           [.] _dl_start
     _dl_start rtld.c:307
      /build/glibc-GKVZIf/glibc-2.23/elf/rtld.c:413 (inline)
   + _dl_sysdep_start dl-sysdep.c:250

Signed-off-by: default avatarYao Jin <yao.jin@linux.intel.com>
Tested-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Tested-by: default avatarMilian Wolff <milian.wolff@kdab.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kan Liang <kan.liang@intel.com>
Link: http://lkml.kernel.org/r/1490474069-15823-6-git-send-email-yao.jin@linux.intel.com


Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 0db64dd0
Loading
Loading
Loading
Loading
+172 −8
Original line number Diff line number Diff line
@@ -144,9 +144,60 @@ 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;

	if (dso->kernel != DSO_TYPE_USER)
		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;
	int n = 0, inline_rows;
	struct rb_node *nd;

	for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
@@ -156,6 +207,13 @@ 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 == '+')
@@ -207,7 +265,7 @@ static int callchain_node__count_rows(struct callchain_node *node)
{
	struct callchain_list *chain;
	bool unfolded = false;
	int n = 0;
	int n = 0, inline_rows;

	if (callchain_param.mode == CHAIN_FLAT)
		return callchain_node__count_flat_rows(node);
@@ -216,6 +274,11 @@ 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;
	}

@@ -362,6 +425,19 @@ 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;
@@ -393,7 +469,12 @@ static bool hist_browser__toggle_fold(struct hist_browser *browser)

		if (he->unfolded) {
			if (he->leaf)
				he->nr_rows = callchain__count_rows(&he->sorted_chain);
				if (he->inline_node)
					he->nr_rows = inline__count_rows(
							he->inline_node);
				else
					he->nr_rows = callchain__count_rows(
							&he->sorted_chain);
			else
				he->nr_rows = hierarchy_count_rows(browser, he, false);

@@ -753,6 +834,70 @@ 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) {
				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,
@@ -764,6 +909,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;

	if (arg->row_offset != 0) {
		arg->row_offset--;
@@ -801,10 +947,15 @@ static int hist_browser__show_callchain_list(struct hist_browser *browser,
	}

	print(browser, chain, str, offset, row, arg);

	free(alloc_str);
	free(alloc_str2);
	return 1;

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

	return ret + inline_rows;
}

static bool check_percent_display(struct rb_node *node, u64 parent_total)
@@ -1228,6 +1379,12 @@ 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,
@@ -1259,7 +1416,8 @@ static int hist_browser__show_entry(struct hist_browser *browser,
			}

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

		printed += hist_browser__show_callchain(browser, entry, 1, row,
					hist_browser__show_callchain_entry, &arg,
		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,
					&arg,
					hist_browser__check_output_full);
	}

+5 −0
Original line number Diff line number Diff line
@@ -1136,6 +1136,11 @@ void hist_entry__delete(struct hist_entry *he)
		zfree(&he->mem_info);
	}

	if (he->inline_node) {
		inline_node__delete(he->inline_node);
		he->inline_node = NULL;
	}

	zfree(&he->stat_acc);
	free_srcline(he->srcline);
	if (he->srcfile && he->srcfile[0])
+1 −0
Original line number Diff line number Diff line
@@ -128,6 +128,7 @@ struct hist_entry {
	};
	char			*srcline;
	char			*srcfile;
	struct inline_node	*inline_node;
	struct symbol		*parent;
	struct branch_info	*branch_info;
	struct hists		*hists;