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

Commit d0506edb authored by Namhyung Kim's avatar Namhyung Kim Committed by Arnaldo Carvalho de Melo
Browse files

perf hists browser: Implement hierarchy output



Implement hierarchy mode in TUI.  The output is look like stdio but it
also supports to fold/unfold children dynamically.

Signed-off-by: default avatarNamhyung Kim <namhyung@kernel.org>
Acked-by: default avatarPekka Enberg <penberg@kernel.org>
Cc: Andi Kleen <andi@firstfloor.org>
Cc: David Ahern <dsahern@gmail.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/r/1456326830-30456-14-git-send-email-namhyung@kernel.org


Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 492b1010
Loading
Loading
Loading
Loading
+268 −22
Original line number Diff line number Diff line
@@ -1260,6 +1260,158 @@ static int hist_browser__show_entry(struct hist_browser *browser,
	return printed;
}

static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
					      struct hist_entry *entry,
					      unsigned short row,
					      int level, int nr_sort_keys)
{
	int printed = 0;
	int width = browser->b.width;
	char folded_sign = ' ';
	bool current_entry = ui_browser__is_current_entry(&browser->b, row);
	off_t row_offset = entry->row_offset;
	bool first = true;
	struct perf_hpp_fmt *fmt;
	struct hpp_arg arg = {
		.b		= &browser->b,
		.current_entry	= current_entry,
	};
	int column = 0;
	int hierarchy_indent = (nr_sort_keys - 1) * HIERARCHY_INDENT;

	if (current_entry) {
		browser->he_selection = entry;
		browser->selection = &entry->ms;
	}

	hist_entry__init_have_children(entry);
	folded_sign = hist_entry__folded(entry);
	arg.folded_sign = folded_sign;

	if (entry->leaf && row_offset) {
		row_offset--;
		goto show_callchain;
	}

	hist_browser__gotorc(browser, row, 0);

	if (current_entry && browser->b.navkeypressed)
		ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
	else
		ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);

	ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
	width -= level * HIERARCHY_INDENT;

	hists__for_each_format(entry->hists, fmt) {
		char s[2048];
		struct perf_hpp hpp = {
			.buf		= s,
			.size		= sizeof(s),
			.ptr		= &arg,
		};

		if (perf_hpp__should_skip(fmt, entry->hists) ||
		    column++ < browser->b.horiz_scroll)
			continue;

		if (perf_hpp__is_sort_entry(fmt) ||
		    perf_hpp__is_dynamic_entry(fmt))
			break;

		if (current_entry && browser->b.navkeypressed) {
			ui_browser__set_color(&browser->b,
					      HE_COLORSET_SELECTED);
		} else {
			ui_browser__set_color(&browser->b,
					      HE_COLORSET_NORMAL);
		}

		if (first) {
			ui_browser__printf(&browser->b, "%c", folded_sign);
			width--;
			first = false;
		} else {
			ui_browser__printf(&browser->b, "  ");
			width -= 2;
		}

		if (fmt->color) {
			int ret = fmt->color(fmt, &hpp, entry);
			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
			/*
			 * fmt->color() already used ui_browser to
			 * print the non alignment bits, skip it (+ret):
			 */
			ui_browser__printf(&browser->b, "%s", s + ret);
		} else {
			int ret = fmt->entry(fmt, &hpp, entry);
			hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
			ui_browser__printf(&browser->b, "%s", s);
		}
		width -= hpp.buf - s;
	}

	ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
	width -= hierarchy_indent;

	if (column >= browser->b.horiz_scroll) {
		char s[2048];
		struct perf_hpp hpp = {
			.buf		= s,
			.size		= sizeof(s),
			.ptr		= &arg,
		};

		if (current_entry && browser->b.navkeypressed) {
			ui_browser__set_color(&browser->b,
					      HE_COLORSET_SELECTED);
		} else {
			ui_browser__set_color(&browser->b,
					      HE_COLORSET_NORMAL);
		}

		ui_browser__write_nstring(&browser->b, "", 2);
		width -= 2;

		/*
		 * No need to call hist_entry__snprintf_alignment()
		 * since this fmt is always the last column in the
		 * hierarchy mode.
		 */
		fmt = entry->fmt;
		if (fmt->color) {
			width -= fmt->color(fmt, &hpp, entry);
		} else {
			width -= fmt->entry(fmt, &hpp, entry);
			ui_browser__printf(&browser->b, "%s", s);
		}
	}

	/* The scroll bar isn't being used */
	if (!browser->b.navkeypressed)
		width += 1;

	ui_browser__write_nstring(&browser->b, "", width);

	++row;
	++printed;

show_callchain:
	if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
		struct callchain_print_arg carg = {
			.row_offset = row_offset,
		};

		printed += hist_browser__show_callchain(browser, entry,
					level + 1, row,
					hist_browser__show_callchain_entry, &carg,
					hist_browser__check_output_full);
	}

	return printed;
}

static int advance_hpp_check(struct perf_hpp *hpp, int inc)
{
	advance_hpp(hpp, inc);
@@ -1325,6 +1477,7 @@ static unsigned int hist_browser__refresh(struct ui_browser *browser)
	u16 header_offset = 0;
	struct rb_node *nd;
	struct hist_browser *hb = container_of(browser, struct hist_browser, b);
	int nr_sort = hb->hists->hpp_list->nr_sort_keys;

	if (hb->show_headers) {
		hist_browser__show_headers(hb);
@@ -1335,18 +1488,28 @@ static unsigned int hist_browser__refresh(struct ui_browser *browser)
	hb->he_selection = NULL;
	hb->selection = NULL;

	for (nd = browser->top; nd; nd = rb_next(nd)) {
	for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
		float percent;

		if (h->filtered)
		if (h->filtered) {
			/* let it move to sibling */
			h->unfolded = false;
			continue;
		}

		percent = hist_entry__get_percent_limit(h);
		if (percent < hb->min_pcnt)
			continue;

		if (symbol_conf.report_hierarchy) {
			row += hist_browser__show_hierarchy_entry(hb, h, row,
								  h->depth,
								  nr_sort);
		} else {
			row += hist_browser__show_entry(hb, h, row);
		}

		if (row == browser->rows)
			break;
	}
@@ -1364,7 +1527,14 @@ static struct rb_node *hists__filter_entries(struct rb_node *nd,
		if (!h->filtered && percent >= min_pcnt)
			return nd;

		/*
		 * If it's filtered, its all children also were filtered.
		 * So move to sibling node.
		 */
		if (rb_next(nd))
			nd = rb_next(nd);
		else
			nd = rb_hierarchy_next(nd);
	}

	return NULL;
@@ -1380,7 +1550,7 @@ static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
		if (!h->filtered && percent >= min_pcnt)
			return nd;

		nd = rb_prev(nd);
		nd = rb_hierarchy_prev(nd);
	}

	return NULL;
@@ -1410,8 +1580,8 @@ static void ui_browser__hists_seek(struct ui_browser *browser,
		nd = browser->top;
		goto do_offset;
	case SEEK_END:
		nd = hists__filter_prev_entries(rb_last(browser->entries),
						hb->min_pcnt);
		nd = rb_hierarchy_last(rb_last(browser->entries));
		nd = hists__filter_prev_entries(nd, hb->min_pcnt);
		first = false;
		break;
	default:
@@ -1445,7 +1615,7 @@ static void ui_browser__hists_seek(struct ui_browser *browser,
	if (offset > 0) {
		do {
			h = rb_entry(nd, struct hist_entry, rb_node);
			if (h->unfolded) {
			if (h->unfolded && h->leaf) {
				u16 remaining = h->nr_rows - h->row_offset;
				if (offset > remaining) {
					offset -= remaining;
@@ -1457,7 +1627,8 @@ static void ui_browser__hists_seek(struct ui_browser *browser,
					break;
				}
			}
			nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
			nd = hists__filter_entries(rb_hierarchy_next(nd),
						   hb->min_pcnt);
			if (nd == NULL)
				break;
			--offset;
@@ -1466,7 +1637,7 @@ static void ui_browser__hists_seek(struct ui_browser *browser,
	} else if (offset < 0) {
		while (1) {
			h = rb_entry(nd, struct hist_entry, rb_node);
			if (h->unfolded) {
			if (h->unfolded && h->leaf) {
				if (first) {
					if (-offset > h->row_offset) {
						offset += h->row_offset;
@@ -1490,7 +1661,7 @@ static void ui_browser__hists_seek(struct ui_browser *browser,
				}
			}

			nd = hists__filter_prev_entries(rb_prev(nd),
			nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
							hb->min_pcnt);
			if (nd == NULL)
				break;
@@ -1503,7 +1674,7 @@ static void ui_browser__hists_seek(struct ui_browser *browser,
				 * row_offset at its last entry.
				 */
				h = rb_entry(nd, struct hist_entry, rb_node);
				if (h->unfolded)
				if (h->unfolded && h->leaf)
					h->row_offset = h->nr_rows;
				break;
			}
@@ -1517,13 +1688,14 @@ static void ui_browser__hists_seek(struct ui_browser *browser,
}

static int hist_browser__fprintf_callchain(struct hist_browser *browser,
					   struct hist_entry *he, FILE *fp)
					   struct hist_entry *he, FILE *fp,
					   int level)
{
	struct callchain_print_arg arg  = {
		.fp = fp,
	};

	hist_browser__show_callchain(browser, he, 1, 0,
	hist_browser__show_callchain(browser, he, level, 0,
				     hist_browser__fprintf_callchain_entry, &arg,
				     hist_browser__check_dump_full);
	return arg.printed;
@@ -1566,7 +1738,65 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser,
	printed += fprintf(fp, "%s\n", s);

	if (folded_sign == '-')
		printed += hist_browser__fprintf_callchain(browser, he, fp);
		printed += hist_browser__fprintf_callchain(browser, he, fp, 1);

	return printed;
}


static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
						 struct hist_entry *he,
						 FILE *fp, int level,
						 int nr_sort_keys)
{
	char s[8192];
	int printed = 0;
	char folded_sign = ' ';
	struct perf_hpp hpp = {
		.buf = s,
		.size = sizeof(s),
	};
	struct perf_hpp_fmt *fmt;
	bool first = true;
	int ret;
	int hierarchy_indent = (nr_sort_keys + 1) * HIERARCHY_INDENT;

	printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");

	folded_sign = hist_entry__folded(he);
	printed += fprintf(fp, "%c", folded_sign);

	hists__for_each_format(he->hists, fmt) {
		if (perf_hpp__should_skip(fmt, he->hists))
			continue;

		if (perf_hpp__is_sort_entry(fmt) ||
		    perf_hpp__is_dynamic_entry(fmt))
			break;

		if (!first) {
			ret = scnprintf(hpp.buf, hpp.size, "  ");
			advance_hpp(&hpp, ret);
		} else
			first = false;

		ret = fmt->entry(fmt, &hpp, he);
		advance_hpp(&hpp, ret);
	}

	ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
	advance_hpp(&hpp, ret);

	fmt = he->fmt;
	ret = fmt->entry(fmt, &hpp, he);
	advance_hpp(&hpp, ret);

	printed += fprintf(fp, "%s\n", rtrim(s));

	if (he->leaf && folded_sign == '-') {
		printed += hist_browser__fprintf_callchain(browser, he, fp,
							   he->depth + 1);
	}

	return printed;
}
@@ -1576,12 +1806,22 @@ static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
	struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
						   browser->min_pcnt);
	int printed = 0;
	int nr_sort = browser->hists->hpp_list->nr_sort_keys;

	while (nd) {
		struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);

		if (symbol_conf.report_hierarchy) {
			printed += hist_browser__fprintf_hierarchy_entry(browser,
									 h, fp,
									 h->depth,
									 nr_sort);
		} else {
			printed += hist_browser__fprintf_entry(browser, h, fp);
		nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
		}

		nd = hists__filter_entries(rb_hierarchy_next(nd),
					   browser->min_pcnt);
	}

	return printed;
@@ -2149,12 +2389,12 @@ static void hist_browser__update_percent_limit(struct hist_browser *hb,

	hb->min_pcnt = callchain_param.min_percent = percent;

	if (!symbol_conf.use_callchain)
		return;

	while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
		he = rb_entry(nd, struct hist_entry, rb_node);

		if (!he->leaf || !symbol_conf.use_callchain)
			goto next;

		if (callchain_param.mode == CHAIN_GRAPH_REL) {
			total = he->stat.period;

@@ -2167,11 +2407,17 @@ static void hist_browser__update_percent_limit(struct hist_browser *hb,
		callchain_param.sort(&he->sorted_chain, he->callchain,
				     min_callchain_hits, &callchain_param);

next:
		/*
		 * Tentatively set unfolded so that the rb_hierarchy_next()
		 * can toggle children of folded entries too.
		 */
		he->unfolded = he->has_children;
		nd = rb_hierarchy_next(nd);

		/* force to re-evaluate folding state of callchains */
		he->init_have_children = false;
		hist_entry__set_folding(he, hb, false);

		nd = rb_next(nd);
	}
}