diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 519353d9f5fb..d4cef68176da 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -477,20 +477,37 @@ static char *callchain_list__sym_name(struct callchain_list *cl, return bf; } +struct callchain_print_arg { + /* for hists browser */ + off_t row_offset; + bool is_current_entry; + + /* for file dump */ + FILE *fp; + int printed; +}; + +typedef void (*print_callchain_entry_fn)(struct hist_browser *browser, + struct callchain_list *chain, + const char *str, int offset, + unsigned short row, + struct callchain_print_arg *arg); + static void hist_browser__show_callchain_entry(struct hist_browser *browser, struct callchain_list *chain, - unsigned short row, int offset, - char folded_sign, const char *str, - bool *is_current_entry) + const char *str, int offset, + unsigned short row, + struct callchain_print_arg *arg) { int color, width; + char folded_sign = callchain_list__folded(chain); color = HE_COLORSET_NORMAL; width = browser->b.width - (offset + 2); if (ui_browser__is_current_entry(&browser->b, row)) { browser->selection = &chain->ms; color = HE_COLORSET_SELECTED; - *is_current_entry = true; + arg->is_current_entry = true; } ui_browser__set_color(&browser->b, color); @@ -500,12 +517,41 @@ static void hist_browser__show_callchain_entry(struct hist_browser *browser, slsmg_write_nstring(str, width); } +static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused, + struct callchain_list *chain, + const char *str, int offset, + unsigned short row __maybe_unused, + struct callchain_print_arg *arg) +{ + char folded_sign = callchain_list__folded(chain); + + arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ", + folded_sign, str); +} + +typedef bool (*check_output_full_fn)(struct hist_browser *browser, + unsigned short row); + +static bool hist_browser__check_output_full(struct hist_browser *browser, + unsigned short row) +{ + return browser->b.rows == row; +} + +static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused, + unsigned short row __maybe_unused) +{ + return false; +} + #define LEVEL_OFFSET_STEP 3 static int hist_browser__show_callchain(struct hist_browser *browser, struct rb_root *root, int level, - unsigned short row, off_t *row_offset, - u64 total, bool *is_current_entry) + unsigned short row, u64 total, + print_callchain_entry_fn print, + struct callchain_print_arg *arg, + check_output_full_fn is_output_full) { struct rb_node *node; int first_row = row, offset = level * LEVEL_OFFSET_STEP; @@ -532,8 +578,8 @@ static int hist_browser__show_callchain(struct hist_browser *browser, extra_offset = LEVEL_OFFSET_STEP; folded_sign = callchain_list__folded(chain); - if (*row_offset != 0) { - --*row_offset; + if (arg->row_offset != 0) { + arg->row_offset--; goto do_next; } @@ -550,13 +596,11 @@ static int hist_browser__show_callchain(struct hist_browser *browser, str = alloc_str; } - hist_browser__show_callchain_entry(browser, chain, row, - offset + extra_offset, - folded_sign, str, - is_current_entry); + print(browser, chain, str, offset + extra_offset, row, arg); + free(alloc_str); - if (++row == browser->b.rows) + if (is_output_full(browser, ++row)) goto out; do_next: if (folded_sign == '+') @@ -572,12 +616,10 @@ do_next: new_total = total; row += hist_browser__show_callchain(browser, &child->rb_root, - new_level, - row, row_offset, - new_total, - is_current_entry); + new_level, row, new_total, + print, arg, is_output_full); } - if (row == browser->b.rows) + if (is_output_full(browser, row)) break; node = next; } @@ -757,16 +799,20 @@ static int hist_browser__show_entry(struct hist_browser *browser, if (folded_sign == '-' && row != browser->b.rows) { u64 total = hists__total_period(entry->hists); + struct callchain_print_arg arg = { + .row_offset = row_offset, + .is_current_entry = current_entry, + }; if (symbol_conf.cumulate_callchain) total = entry->stat_acc->period; printed += hist_browser__show_callchain(browser, - &entry->sorted_chain, - 1, row, &row_offset, - total, ¤t_entry); + &entry->sorted_chain, 1, row, total, + hist_browser__show_callchain_entry, &arg, + hist_browser__check_output_full); - if (current_entry) + if (arg.is_current_entry) browser->he_selection = entry; } @@ -1022,110 +1068,21 @@ do_offset: } } -static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser, - struct callchain_node *chain_node, - u64 total, int level, - FILE *fp) -{ - struct rb_node *node; - int offset = level * LEVEL_OFFSET_STEP; - u64 new_total; - int printed = 0; - - if (callchain_param.mode == CHAIN_GRAPH_REL) - new_total = chain_node->children_hit; - else - new_total = total; - - node = rb_first(&chain_node->rb_root); - while (node) { - struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); - struct rb_node *next = rb_next(node); - u64 cumul = callchain_cumul_hits(child); - struct callchain_list *chain; - char folded_sign = ' '; - int first = true; - int extra_offset = 0; - - list_for_each_entry(chain, &child->val, list) { - char bf[1024], *alloc_str; - const char *str; - bool was_first = first; - - if (first) - first = false; - else - extra_offset = LEVEL_OFFSET_STEP; - - folded_sign = callchain_list__folded(chain); - - alloc_str = NULL; - str = callchain_list__sym_name(chain, bf, sizeof(bf), - browser->show_dso); - if (was_first) { - double percent = cumul * 100.0 / new_total; - - if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) - str = "Not enough memory!"; - else - str = alloc_str; - } - - printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str); - free(alloc_str); - if (folded_sign == '+') - break; - } - - if (folded_sign == '-') { - const int new_level = level + (extra_offset ? 2 : 1); - printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total, - new_level, fp); - } - - node = next; - } - - return printed; -} - -static int hist_browser__fprintf_callchain_node(struct hist_browser *browser, - struct callchain_node *node, - int level, FILE *fp) -{ - struct callchain_list *chain; - int offset = level * LEVEL_OFFSET_STEP; - char folded_sign = ' '; - int printed = 0; - - list_for_each_entry(chain, &node->val, list) { - char bf[1024], *s; - - folded_sign = callchain_list__folded(chain); - s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso); - printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s); - } - - if (folded_sign == '-') - printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node, - browser->hists->stats.total_period, - level + 1, fp); - return printed; -} - static int hist_browser__fprintf_callchain(struct hist_browser *browser, - struct rb_root *chain, int level, FILE *fp) + struct hist_entry *he, FILE *fp) { - struct rb_node *nd; - int printed = 0; + u64 total = hists__total_period(he->hists); + struct callchain_print_arg arg = { + .fp = fp, + }; - for (nd = rb_first(chain); nd; nd = rb_next(nd)) { - struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); + if (symbol_conf.cumulate_callchain) + total = he->stat_acc->period; - printed += hist_browser__fprintf_callchain_node(browser, node, level, fp); - } - - return printed; + hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total, + hist_browser__fprintf_callchain_entry, &arg, + hist_browser__check_dump_full); + return arg.printed; } static int hist_browser__fprintf_entry(struct hist_browser *browser, @@ -1164,7 +1121,7 @@ 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->sorted_chain, 1, fp); + printed += hist_browser__fprintf_callchain(browser, he, fp); return printed; }