From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753452AbcBENEW (ORCPT ); Fri, 5 Feb 2016 08:04:22 -0500 Received: from mail-pf0-f194.google.com ([209.85.192.194]:33737 "EHLO mail-pf0-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753427AbcBENER (ORCPT ); Fri, 5 Feb 2016 08:04:17 -0500 From: Namhyung Kim To: Arnaldo Carvalho de Melo Cc: Ingo Molnar , Peter Zijlstra , Jiri Olsa , LKML , David Ahern , Andi Kleen , Stephane Eranian , Wang Nan Subject: [PATCH 18/23] perf hists browser: Implement hierarchy output Date: Fri, 5 Feb 2016 22:01:50 +0900 Message-Id: <1454677315-7515-19-git-send-email-namhyung@kernel.org> X-Mailer: git-send-email 2.7.0 In-Reply-To: <1454677315-7515-1-git-send-email-namhyung@kernel.org> References: <1454677315-7515-1-git-send-email-namhyung@kernel.org> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Implement hierarchy mode in TUI. The output is look like stdio but it also supports to fold/unfold children dynamically. Acked-by: Pekka Enberg Signed-off-by: Namhyung Kim --- tools/perf/ui/browsers/hists.c | 269 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 247 insertions(+), 22 deletions(-) diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index e42ed634e722..8eeaa3d66bda 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -1257,6 +1257,137 @@ 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) +{ + char s[256]; + 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, + }; + struct perf_hpp hpp = { + .buf = s, + .size = sizeof(s), + .ptr = &arg, + }; + 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) { + 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) { + width -= fmt->color(fmt, &hpp, entry); + } else { + width -= fmt->entry(fmt, &hpp, entry); + ui_browser__printf(&browser->b, "%s", s); + } + } + + ui_browser__write_nstring(&browser->b, "", hierarchy_indent); + width -= hierarchy_indent; + + if (column >= browser->b.horiz_scroll) { + 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; + + 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); @@ -1322,6 +1453,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 = perf_hpp__count_sort_keys(); if (hb->show_headers) { hist_browser__show_headers(hb); @@ -1332,18 +1464,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; - row += hist_browser__show_entry(hb, h, row); + 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; } @@ -1361,7 +1503,14 @@ static struct rb_node *hists__filter_entries(struct rb_node *nd, if (!h->filtered && percent >= min_pcnt) return nd; - nd = rb_next(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; @@ -1377,7 +1526,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; @@ -1407,8 +1556,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: @@ -1442,7 +1591,7 @@ do_offset: 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; @@ -1454,7 +1603,8 @@ do_offset: 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; @@ -1463,7 +1613,7 @@ do_offset: } 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; @@ -1487,7 +1637,7 @@ do_offset: } } - nd = hists__filter_prev_entries(rb_prev(nd), + nd = hists__filter_prev_entries(rb_hierarchy_prev(nd), hb->min_pcnt); if (nd == NULL) break; @@ -1500,7 +1650,7 @@ do_offset: * 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; } @@ -1514,13 +1664,14 @@ do_offset: } 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; @@ -1562,7 +1713,65 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser, printed += fprintf(fp, "%s\n", rtrim(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; } @@ -1572,12 +1781,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 = perf_hpp__count_sort_keys(); while (nd) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); - printed += hist_browser__fprintf_entry(browser, h, fp); - nd = hists__filter_entries(rb_next(nd), browser->min_pcnt); + 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_hierarchy_next(nd), + browser->min_pcnt); } return printed; @@ -2145,12 +2364,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; @@ -2163,11 +2382,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); } } -- 2.7.0