162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <stdio.h> 362306a36Sopenharmony_ci#include <stdlib.h> 462306a36Sopenharmony_ci#include <linux/string.h> 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include "../../util/callchain.h" 762306a36Sopenharmony_ci#include "../../util/debug.h" 862306a36Sopenharmony_ci#include "../../util/event.h" 962306a36Sopenharmony_ci#include "../../util/hist.h" 1062306a36Sopenharmony_ci#include "../../util/map.h" 1162306a36Sopenharmony_ci#include "../../util/maps.h" 1262306a36Sopenharmony_ci#include "../../util/symbol.h" 1362306a36Sopenharmony_ci#include "../../util/sort.h" 1462306a36Sopenharmony_ci#include "../../util/evsel.h" 1562306a36Sopenharmony_ci#include "../../util/srcline.h" 1662306a36Sopenharmony_ci#include "../../util/string2.h" 1762306a36Sopenharmony_ci#include "../../util/thread.h" 1862306a36Sopenharmony_ci#include "../../util/block-info.h" 1962306a36Sopenharmony_ci#include <linux/ctype.h> 2062306a36Sopenharmony_ci#include <linux/zalloc.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci int i; 2562306a36Sopenharmony_ci int ret = fprintf(fp, " "); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci for (i = 0; i < left_margin; i++) 2862306a36Sopenharmony_ci ret += fprintf(fp, " "); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci return ret; 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask, 3462306a36Sopenharmony_ci int left_margin) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci int i; 3762306a36Sopenharmony_ci size_t ret = callchain__fprintf_left_margin(fp, left_margin); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci for (i = 0; i < depth; i++) 4062306a36Sopenharmony_ci if (depth_mask & (1 << i)) 4162306a36Sopenharmony_ci ret += fprintf(fp, "| "); 4262306a36Sopenharmony_ci else 4362306a36Sopenharmony_ci ret += fprintf(fp, " "); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci ret += fprintf(fp, "\n"); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci return ret; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic size_t ipchain__fprintf_graph(FILE *fp, struct callchain_node *node, 5162306a36Sopenharmony_ci struct callchain_list *chain, 5262306a36Sopenharmony_ci int depth, int depth_mask, int period, 5362306a36Sopenharmony_ci u64 total_samples, int left_margin) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci int i; 5662306a36Sopenharmony_ci size_t ret = 0; 5762306a36Sopenharmony_ci char bf[1024], *alloc_str = NULL; 5862306a36Sopenharmony_ci char buf[64]; 5962306a36Sopenharmony_ci const char *str; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci ret += callchain__fprintf_left_margin(fp, left_margin); 6262306a36Sopenharmony_ci for (i = 0; i < depth; i++) { 6362306a36Sopenharmony_ci if (depth_mask & (1 << i)) 6462306a36Sopenharmony_ci ret += fprintf(fp, "|"); 6562306a36Sopenharmony_ci else 6662306a36Sopenharmony_ci ret += fprintf(fp, " "); 6762306a36Sopenharmony_ci if (!period && i == depth - 1) { 6862306a36Sopenharmony_ci ret += fprintf(fp, "--"); 6962306a36Sopenharmony_ci ret += callchain_node__fprintf_value(node, fp, total_samples); 7062306a36Sopenharmony_ci ret += fprintf(fp, "--"); 7162306a36Sopenharmony_ci } else 7262306a36Sopenharmony_ci ret += fprintf(fp, "%s", " "); 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci str = callchain_list__sym_name(chain, bf, sizeof(bf), false); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci if (symbol_conf.show_branchflag_count) { 7862306a36Sopenharmony_ci callchain_list_counts__printf_value(chain, NULL, 7962306a36Sopenharmony_ci buf, sizeof(buf)); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (asprintf(&alloc_str, "%s%s", str, buf) < 0) 8262306a36Sopenharmony_ci str = "Not enough memory!"; 8362306a36Sopenharmony_ci else 8462306a36Sopenharmony_ci str = alloc_str; 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci fputs(str, fp); 8862306a36Sopenharmony_ci fputc('\n', fp); 8962306a36Sopenharmony_ci free(alloc_str); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci return ret; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic struct symbol *rem_sq_bracket; 9562306a36Sopenharmony_cistatic struct callchain_list rem_hits; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic void init_rem_hits(void) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6); 10062306a36Sopenharmony_ci if (!rem_sq_bracket) { 10162306a36Sopenharmony_ci fprintf(stderr, "Not enough memory to display remaining hits\n"); 10262306a36Sopenharmony_ci return; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci strcpy(rem_sq_bracket->name, "[...]"); 10662306a36Sopenharmony_ci rem_hits.ms.sym = rem_sq_bracket; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root, 11062306a36Sopenharmony_ci u64 total_samples, int depth, 11162306a36Sopenharmony_ci int depth_mask, int left_margin) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci struct rb_node *node, *next; 11462306a36Sopenharmony_ci struct callchain_node *child = NULL; 11562306a36Sopenharmony_ci struct callchain_list *chain; 11662306a36Sopenharmony_ci int new_depth_mask = depth_mask; 11762306a36Sopenharmony_ci u64 remaining; 11862306a36Sopenharmony_ci size_t ret = 0; 11962306a36Sopenharmony_ci int i; 12062306a36Sopenharmony_ci uint entries_printed = 0; 12162306a36Sopenharmony_ci int cumul_count = 0; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci remaining = total_samples; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci node = rb_first(root); 12662306a36Sopenharmony_ci while (node) { 12762306a36Sopenharmony_ci u64 new_total; 12862306a36Sopenharmony_ci u64 cumul; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci child = rb_entry(node, struct callchain_node, rb_node); 13162306a36Sopenharmony_ci cumul = callchain_cumul_hits(child); 13262306a36Sopenharmony_ci remaining -= cumul; 13362306a36Sopenharmony_ci cumul_count += callchain_cumul_counts(child); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* 13662306a36Sopenharmony_ci * The depth mask manages the output of pipes that show 13762306a36Sopenharmony_ci * the depth. We don't want to keep the pipes of the current 13862306a36Sopenharmony_ci * level for the last child of this depth. 13962306a36Sopenharmony_ci * Except if we have remaining filtered hits. They will 14062306a36Sopenharmony_ci * supersede the last child 14162306a36Sopenharmony_ci */ 14262306a36Sopenharmony_ci next = rb_next(node); 14362306a36Sopenharmony_ci if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining)) 14462306a36Sopenharmony_ci new_depth_mask &= ~(1 << (depth - 1)); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* 14762306a36Sopenharmony_ci * But we keep the older depth mask for the line separator 14862306a36Sopenharmony_ci * to keep the level link until we reach the last child 14962306a36Sopenharmony_ci */ 15062306a36Sopenharmony_ci ret += ipchain__fprintf_graph_line(fp, depth, depth_mask, 15162306a36Sopenharmony_ci left_margin); 15262306a36Sopenharmony_ci i = 0; 15362306a36Sopenharmony_ci list_for_each_entry(chain, &child->val, list) { 15462306a36Sopenharmony_ci ret += ipchain__fprintf_graph(fp, child, chain, depth, 15562306a36Sopenharmony_ci new_depth_mask, i++, 15662306a36Sopenharmony_ci total_samples, 15762306a36Sopenharmony_ci left_margin); 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (callchain_param.mode == CHAIN_GRAPH_REL) 16162306a36Sopenharmony_ci new_total = child->children_hit; 16262306a36Sopenharmony_ci else 16362306a36Sopenharmony_ci new_total = total_samples; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci ret += __callchain__fprintf_graph(fp, &child->rb_root, new_total, 16662306a36Sopenharmony_ci depth + 1, 16762306a36Sopenharmony_ci new_depth_mask | (1 << depth), 16862306a36Sopenharmony_ci left_margin); 16962306a36Sopenharmony_ci node = next; 17062306a36Sopenharmony_ci if (++entries_printed == callchain_param.print_limit) 17162306a36Sopenharmony_ci break; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (callchain_param.mode == CHAIN_GRAPH_REL && 17562306a36Sopenharmony_ci remaining && remaining != total_samples) { 17662306a36Sopenharmony_ci struct callchain_node rem_node = { 17762306a36Sopenharmony_ci .hit = remaining, 17862306a36Sopenharmony_ci }; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (!rem_sq_bracket) 18162306a36Sopenharmony_ci return ret; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (callchain_param.value == CCVAL_COUNT && child && child->parent) { 18462306a36Sopenharmony_ci rem_node.count = child->parent->children_count - cumul_count; 18562306a36Sopenharmony_ci if (rem_node.count <= 0) 18662306a36Sopenharmony_ci return ret; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci new_depth_mask &= ~(1 << (depth - 1)); 19062306a36Sopenharmony_ci ret += ipchain__fprintf_graph(fp, &rem_node, &rem_hits, depth, 19162306a36Sopenharmony_ci new_depth_mask, 0, total_samples, 19262306a36Sopenharmony_ci left_margin); 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci return ret; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci/* 19962306a36Sopenharmony_ci * If have one single callchain root, don't bother printing 20062306a36Sopenharmony_ci * its percentage (100 % in fractal mode and the same percentage 20162306a36Sopenharmony_ci * than the hist in graph mode). This also avoid one level of column. 20262306a36Sopenharmony_ci * 20362306a36Sopenharmony_ci * However when percent-limit applied, it's possible that single callchain 20462306a36Sopenharmony_ci * node have different (non-100% in fractal mode) percentage. 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_cistatic bool need_percent_display(struct rb_node *node, u64 parent_samples) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci struct callchain_node *cnode; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (rb_next(node)) 21162306a36Sopenharmony_ci return true; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci cnode = rb_entry(node, struct callchain_node, rb_node); 21462306a36Sopenharmony_ci return callchain_cumul_hits(cnode) != parent_samples; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root, 21862306a36Sopenharmony_ci u64 total_samples, u64 parent_samples, 21962306a36Sopenharmony_ci int left_margin) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci struct callchain_node *cnode; 22262306a36Sopenharmony_ci struct callchain_list *chain; 22362306a36Sopenharmony_ci u32 entries_printed = 0; 22462306a36Sopenharmony_ci bool printed = false; 22562306a36Sopenharmony_ci struct rb_node *node; 22662306a36Sopenharmony_ci int i = 0; 22762306a36Sopenharmony_ci int ret = 0; 22862306a36Sopenharmony_ci char bf[1024]; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci node = rb_first(root); 23162306a36Sopenharmony_ci if (node && !need_percent_display(node, parent_samples)) { 23262306a36Sopenharmony_ci cnode = rb_entry(node, struct callchain_node, rb_node); 23362306a36Sopenharmony_ci list_for_each_entry(chain, &cnode->val, list) { 23462306a36Sopenharmony_ci /* 23562306a36Sopenharmony_ci * If we sort by symbol, the first entry is the same than 23662306a36Sopenharmony_ci * the symbol. No need to print it otherwise it appears as 23762306a36Sopenharmony_ci * displayed twice. 23862306a36Sopenharmony_ci */ 23962306a36Sopenharmony_ci if (!i++ && field_order == NULL && 24062306a36Sopenharmony_ci sort_order && strstarts(sort_order, "sym")) 24162306a36Sopenharmony_ci continue; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (!printed) { 24462306a36Sopenharmony_ci ret += callchain__fprintf_left_margin(fp, left_margin); 24562306a36Sopenharmony_ci ret += fprintf(fp, "|\n"); 24662306a36Sopenharmony_ci ret += callchain__fprintf_left_margin(fp, left_margin); 24762306a36Sopenharmony_ci ret += fprintf(fp, "---"); 24862306a36Sopenharmony_ci left_margin += 3; 24962306a36Sopenharmony_ci printed = true; 25062306a36Sopenharmony_ci } else 25162306a36Sopenharmony_ci ret += callchain__fprintf_left_margin(fp, left_margin); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci ret += fprintf(fp, "%s", 25462306a36Sopenharmony_ci callchain_list__sym_name(chain, bf, 25562306a36Sopenharmony_ci sizeof(bf), 25662306a36Sopenharmony_ci false)); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (symbol_conf.show_branchflag_count) 25962306a36Sopenharmony_ci ret += callchain_list_counts__printf_value( 26062306a36Sopenharmony_ci chain, fp, NULL, 0); 26162306a36Sopenharmony_ci ret += fprintf(fp, "\n"); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (++entries_printed == callchain_param.print_limit) 26462306a36Sopenharmony_ci break; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci root = &cnode->rb_root; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (callchain_param.mode == CHAIN_GRAPH_REL) 27062306a36Sopenharmony_ci total_samples = parent_samples; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci ret += __callchain__fprintf_graph(fp, root, total_samples, 27362306a36Sopenharmony_ci 1, 1, left_margin); 27462306a36Sopenharmony_ci if (ret) { 27562306a36Sopenharmony_ci /* do not add a blank line if it printed nothing */ 27662306a36Sopenharmony_ci ret += fprintf(fp, "\n"); 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci return ret; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic size_t __callchain__fprintf_flat(FILE *fp, struct callchain_node *node, 28362306a36Sopenharmony_ci u64 total_samples) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci struct callchain_list *chain; 28662306a36Sopenharmony_ci size_t ret = 0; 28762306a36Sopenharmony_ci char bf[1024]; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (!node) 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci ret += __callchain__fprintf_flat(fp, node->parent, total_samples); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci list_for_each_entry(chain, &node->val, list) { 29662306a36Sopenharmony_ci if (chain->ip >= PERF_CONTEXT_MAX) 29762306a36Sopenharmony_ci continue; 29862306a36Sopenharmony_ci ret += fprintf(fp, " %s\n", callchain_list__sym_name(chain, 29962306a36Sopenharmony_ci bf, sizeof(bf), false)); 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci return ret; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic size_t callchain__fprintf_flat(FILE *fp, struct rb_root *tree, 30662306a36Sopenharmony_ci u64 total_samples) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci size_t ret = 0; 30962306a36Sopenharmony_ci u32 entries_printed = 0; 31062306a36Sopenharmony_ci struct callchain_node *chain; 31162306a36Sopenharmony_ci struct rb_node *rb_node = rb_first(tree); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci while (rb_node) { 31462306a36Sopenharmony_ci chain = rb_entry(rb_node, struct callchain_node, rb_node); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci ret += fprintf(fp, " "); 31762306a36Sopenharmony_ci ret += callchain_node__fprintf_value(chain, fp, total_samples); 31862306a36Sopenharmony_ci ret += fprintf(fp, "\n"); 31962306a36Sopenharmony_ci ret += __callchain__fprintf_flat(fp, chain, total_samples); 32062306a36Sopenharmony_ci ret += fprintf(fp, "\n"); 32162306a36Sopenharmony_ci if (++entries_printed == callchain_param.print_limit) 32262306a36Sopenharmony_ci break; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci rb_node = rb_next(rb_node); 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci return ret; 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic size_t __callchain__fprintf_folded(FILE *fp, struct callchain_node *node) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci const char *sep = symbol_conf.field_sep ?: ";"; 33362306a36Sopenharmony_ci struct callchain_list *chain; 33462306a36Sopenharmony_ci size_t ret = 0; 33562306a36Sopenharmony_ci char bf[1024]; 33662306a36Sopenharmony_ci bool first; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (!node) 33962306a36Sopenharmony_ci return 0; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci ret += __callchain__fprintf_folded(fp, node->parent); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci first = (ret == 0); 34462306a36Sopenharmony_ci list_for_each_entry(chain, &node->val, list) { 34562306a36Sopenharmony_ci if (chain->ip >= PERF_CONTEXT_MAX) 34662306a36Sopenharmony_ci continue; 34762306a36Sopenharmony_ci ret += fprintf(fp, "%s%s", first ? "" : sep, 34862306a36Sopenharmony_ci callchain_list__sym_name(chain, 34962306a36Sopenharmony_ci bf, sizeof(bf), false)); 35062306a36Sopenharmony_ci first = false; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci return ret; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic size_t callchain__fprintf_folded(FILE *fp, struct rb_root *tree, 35762306a36Sopenharmony_ci u64 total_samples) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci size_t ret = 0; 36062306a36Sopenharmony_ci u32 entries_printed = 0; 36162306a36Sopenharmony_ci struct callchain_node *chain; 36262306a36Sopenharmony_ci struct rb_node *rb_node = rb_first(tree); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci while (rb_node) { 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci chain = rb_entry(rb_node, struct callchain_node, rb_node); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci ret += callchain_node__fprintf_value(chain, fp, total_samples); 36962306a36Sopenharmony_ci ret += fprintf(fp, " "); 37062306a36Sopenharmony_ci ret += __callchain__fprintf_folded(fp, chain); 37162306a36Sopenharmony_ci ret += fprintf(fp, "\n"); 37262306a36Sopenharmony_ci if (++entries_printed == callchain_param.print_limit) 37362306a36Sopenharmony_ci break; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci rb_node = rb_next(rb_node); 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci return ret; 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic size_t hist_entry_callchain__fprintf(struct hist_entry *he, 38262306a36Sopenharmony_ci u64 total_samples, int left_margin, 38362306a36Sopenharmony_ci FILE *fp) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci u64 parent_samples = he->stat.period; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci if (symbol_conf.cumulate_callchain) 38862306a36Sopenharmony_ci parent_samples = he->stat_acc->period; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci switch (callchain_param.mode) { 39162306a36Sopenharmony_ci case CHAIN_GRAPH_REL: 39262306a36Sopenharmony_ci return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples, 39362306a36Sopenharmony_ci parent_samples, left_margin); 39462306a36Sopenharmony_ci break; 39562306a36Sopenharmony_ci case CHAIN_GRAPH_ABS: 39662306a36Sopenharmony_ci return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples, 39762306a36Sopenharmony_ci parent_samples, left_margin); 39862306a36Sopenharmony_ci break; 39962306a36Sopenharmony_ci case CHAIN_FLAT: 40062306a36Sopenharmony_ci return callchain__fprintf_flat(fp, &he->sorted_chain, total_samples); 40162306a36Sopenharmony_ci break; 40262306a36Sopenharmony_ci case CHAIN_FOLDED: 40362306a36Sopenharmony_ci return callchain__fprintf_folded(fp, &he->sorted_chain, total_samples); 40462306a36Sopenharmony_ci break; 40562306a36Sopenharmony_ci case CHAIN_NONE: 40662306a36Sopenharmony_ci break; 40762306a36Sopenharmony_ci default: 40862306a36Sopenharmony_ci pr_err("Bad callchain mode\n"); 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci return 0; 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ciint __hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp, 41562306a36Sopenharmony_ci struct perf_hpp_list *hpp_list) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci const char *sep = symbol_conf.field_sep; 41862306a36Sopenharmony_ci struct perf_hpp_fmt *fmt; 41962306a36Sopenharmony_ci char *start = hpp->buf; 42062306a36Sopenharmony_ci int ret; 42162306a36Sopenharmony_ci bool first = true; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (symbol_conf.exclude_other && !he->parent) 42462306a36Sopenharmony_ci return 0; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci perf_hpp_list__for_each_format(hpp_list, fmt) { 42762306a36Sopenharmony_ci if (perf_hpp__should_skip(fmt, he->hists)) 42862306a36Sopenharmony_ci continue; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* 43162306a36Sopenharmony_ci * If there's no field_sep, we still need 43262306a36Sopenharmony_ci * to display initial ' '. 43362306a36Sopenharmony_ci */ 43462306a36Sopenharmony_ci if (!sep || !first) { 43562306a36Sopenharmony_ci ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: " "); 43662306a36Sopenharmony_ci advance_hpp(hpp, ret); 43762306a36Sopenharmony_ci } else 43862306a36Sopenharmony_ci first = false; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (perf_hpp__use_color() && fmt->color) 44162306a36Sopenharmony_ci ret = fmt->color(fmt, hpp, he); 44262306a36Sopenharmony_ci else 44362306a36Sopenharmony_ci ret = fmt->entry(fmt, hpp, he); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci ret = hist_entry__snprintf_alignment(he, hpp, fmt, ret); 44662306a36Sopenharmony_ci advance_hpp(hpp, ret); 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci return hpp->buf - start; 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic int hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci return __hist_entry__snprintf(he, hpp, he->hists->hpp_list); 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic int hist_entry__hierarchy_fprintf(struct hist_entry *he, 45862306a36Sopenharmony_ci struct perf_hpp *hpp, 45962306a36Sopenharmony_ci struct hists *hists, 46062306a36Sopenharmony_ci FILE *fp) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci const char *sep = symbol_conf.field_sep; 46362306a36Sopenharmony_ci struct perf_hpp_fmt *fmt; 46462306a36Sopenharmony_ci struct perf_hpp_list_node *fmt_node; 46562306a36Sopenharmony_ci char *buf = hpp->buf; 46662306a36Sopenharmony_ci size_t size = hpp->size; 46762306a36Sopenharmony_ci int ret, printed = 0; 46862306a36Sopenharmony_ci bool first = true; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci if (symbol_conf.exclude_other && !he->parent) 47162306a36Sopenharmony_ci return 0; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci ret = scnprintf(hpp->buf, hpp->size, "%*s", he->depth * HIERARCHY_INDENT, ""); 47462306a36Sopenharmony_ci advance_hpp(hpp, ret); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci /* the first hpp_list_node is for overhead columns */ 47762306a36Sopenharmony_ci fmt_node = list_first_entry(&hists->hpp_formats, 47862306a36Sopenharmony_ci struct perf_hpp_list_node, list); 47962306a36Sopenharmony_ci perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { 48062306a36Sopenharmony_ci /* 48162306a36Sopenharmony_ci * If there's no field_sep, we still need 48262306a36Sopenharmony_ci * to display initial ' '. 48362306a36Sopenharmony_ci */ 48462306a36Sopenharmony_ci if (!sep || !first) { 48562306a36Sopenharmony_ci ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: " "); 48662306a36Sopenharmony_ci advance_hpp(hpp, ret); 48762306a36Sopenharmony_ci } else 48862306a36Sopenharmony_ci first = false; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (perf_hpp__use_color() && fmt->color) 49162306a36Sopenharmony_ci ret = fmt->color(fmt, hpp, he); 49262306a36Sopenharmony_ci else 49362306a36Sopenharmony_ci ret = fmt->entry(fmt, hpp, he); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci ret = hist_entry__snprintf_alignment(he, hpp, fmt, ret); 49662306a36Sopenharmony_ci advance_hpp(hpp, ret); 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (!sep) 50062306a36Sopenharmony_ci ret = scnprintf(hpp->buf, hpp->size, "%*s", 50162306a36Sopenharmony_ci (hists->nr_hpp_node - 2) * HIERARCHY_INDENT, ""); 50262306a36Sopenharmony_ci advance_hpp(hpp, ret); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci printed += fprintf(fp, "%s", buf); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci perf_hpp_list__for_each_format(he->hpp_list, fmt) { 50762306a36Sopenharmony_ci hpp->buf = buf; 50862306a36Sopenharmony_ci hpp->size = size; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci /* 51162306a36Sopenharmony_ci * No need to call hist_entry__snprintf_alignment() since this 51262306a36Sopenharmony_ci * fmt is always the last column in the hierarchy mode. 51362306a36Sopenharmony_ci */ 51462306a36Sopenharmony_ci if (perf_hpp__use_color() && fmt->color) 51562306a36Sopenharmony_ci fmt->color(fmt, hpp, he); 51662306a36Sopenharmony_ci else 51762306a36Sopenharmony_ci fmt->entry(fmt, hpp, he); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci /* 52062306a36Sopenharmony_ci * dynamic entries are right-aligned but we want left-aligned 52162306a36Sopenharmony_ci * in the hierarchy mode 52262306a36Sopenharmony_ci */ 52362306a36Sopenharmony_ci printed += fprintf(fp, "%s%s", sep ?: " ", skip_spaces(buf)); 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci printed += putc('\n', fp); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci if (he->leaf && hist_entry__has_callchains(he) && symbol_conf.use_callchain) { 52862306a36Sopenharmony_ci u64 total = hists__total_period(hists); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci printed += hist_entry_callchain__fprintf(he, total, 0, fp); 53162306a36Sopenharmony_ci goto out; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ciout: 53562306a36Sopenharmony_ci return printed; 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistatic int hist_entry__block_fprintf(struct hist_entry *he, 53962306a36Sopenharmony_ci char *bf, size_t size, 54062306a36Sopenharmony_ci FILE *fp) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci struct block_hist *bh = container_of(he, struct block_hist, he); 54362306a36Sopenharmony_ci int ret = 0; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci for (unsigned int i = 0; i < bh->block_hists.nr_entries; i++) { 54662306a36Sopenharmony_ci struct perf_hpp hpp = { 54762306a36Sopenharmony_ci .buf = bf, 54862306a36Sopenharmony_ci .size = size, 54962306a36Sopenharmony_ci .skip = false, 55062306a36Sopenharmony_ci }; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci bh->block_idx = i; 55362306a36Sopenharmony_ci hist_entry__snprintf(he, &hpp); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci if (!hpp.skip) 55662306a36Sopenharmony_ci ret += fprintf(fp, "%s\n", bf); 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci return ret; 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cistatic int hist_entry__individual_block_fprintf(struct hist_entry *he, 56362306a36Sopenharmony_ci char *bf, size_t size, 56462306a36Sopenharmony_ci FILE *fp) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci int ret = 0; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci struct perf_hpp hpp = { 56962306a36Sopenharmony_ci .buf = bf, 57062306a36Sopenharmony_ci .size = size, 57162306a36Sopenharmony_ci .skip = false, 57262306a36Sopenharmony_ci }; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci hist_entry__snprintf(he, &hpp); 57562306a36Sopenharmony_ci if (!hpp.skip) 57662306a36Sopenharmony_ci ret += fprintf(fp, "%s\n", bf); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci return ret; 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_cistatic int hist_entry__fprintf(struct hist_entry *he, size_t size, 58262306a36Sopenharmony_ci char *bf, size_t bfsz, FILE *fp, 58362306a36Sopenharmony_ci bool ignore_callchains) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci int ret; 58662306a36Sopenharmony_ci int callchain_ret = 0; 58762306a36Sopenharmony_ci struct perf_hpp hpp = { 58862306a36Sopenharmony_ci .buf = bf, 58962306a36Sopenharmony_ci .size = size, 59062306a36Sopenharmony_ci }; 59162306a36Sopenharmony_ci struct hists *hists = he->hists; 59262306a36Sopenharmony_ci u64 total_period = hists->stats.total_period; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci if (size == 0 || size > bfsz) 59562306a36Sopenharmony_ci size = hpp.size = bfsz; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (symbol_conf.report_hierarchy) 59862306a36Sopenharmony_ci return hist_entry__hierarchy_fprintf(he, &hpp, hists, fp); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci if (symbol_conf.report_block) 60162306a36Sopenharmony_ci return hist_entry__block_fprintf(he, bf, size, fp); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci if (symbol_conf.report_individual_block) 60462306a36Sopenharmony_ci return hist_entry__individual_block_fprintf(he, bf, size, fp); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci hist_entry__snprintf(he, &hpp); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci ret = fprintf(fp, "%s\n", bf); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci if (hist_entry__has_callchains(he) && !ignore_callchains) 61162306a36Sopenharmony_ci callchain_ret = hist_entry_callchain__fprintf(he, total_period, 61262306a36Sopenharmony_ci 0, fp); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci ret += callchain_ret; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci return ret; 61762306a36Sopenharmony_ci} 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_cistatic int print_hierarchy_indent(const char *sep, int indent, 62062306a36Sopenharmony_ci const char *line, FILE *fp) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci int width; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci if (sep != NULL || indent < 2) 62562306a36Sopenharmony_ci return 0; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci width = (indent - 2) * HIERARCHY_INDENT; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci return fprintf(fp, "%-*.*s", width, width, line); 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_cistatic int hists__fprintf_hierarchy_headers(struct hists *hists, 63362306a36Sopenharmony_ci struct perf_hpp *hpp, FILE *fp) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci bool first_node, first_col; 63662306a36Sopenharmony_ci int indent; 63762306a36Sopenharmony_ci int depth; 63862306a36Sopenharmony_ci unsigned width = 0; 63962306a36Sopenharmony_ci unsigned header_width = 0; 64062306a36Sopenharmony_ci struct perf_hpp_fmt *fmt; 64162306a36Sopenharmony_ci struct perf_hpp_list_node *fmt_node; 64262306a36Sopenharmony_ci const char *sep = symbol_conf.field_sep; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci indent = hists->nr_hpp_node; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci /* preserve max indent depth for column headers */ 64762306a36Sopenharmony_ci print_hierarchy_indent(sep, indent, " ", fp); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci /* the first hpp_list_node is for overhead columns */ 65062306a36Sopenharmony_ci fmt_node = list_first_entry(&hists->hpp_formats, 65162306a36Sopenharmony_ci struct perf_hpp_list_node, list); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { 65462306a36Sopenharmony_ci fmt->header(fmt, hpp, hists, 0, NULL); 65562306a36Sopenharmony_ci fprintf(fp, "%s%s", hpp->buf, sep ?: " "); 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci /* combine sort headers with ' / ' */ 65962306a36Sopenharmony_ci first_node = true; 66062306a36Sopenharmony_ci list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) { 66162306a36Sopenharmony_ci if (!first_node) 66262306a36Sopenharmony_ci header_width += fprintf(fp, " / "); 66362306a36Sopenharmony_ci first_node = false; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci first_col = true; 66662306a36Sopenharmony_ci perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { 66762306a36Sopenharmony_ci if (perf_hpp__should_skip(fmt, hists)) 66862306a36Sopenharmony_ci continue; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci if (!first_col) 67162306a36Sopenharmony_ci header_width += fprintf(fp, "+"); 67262306a36Sopenharmony_ci first_col = false; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci fmt->header(fmt, hpp, hists, 0, NULL); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci header_width += fprintf(fp, "%s", strim(hpp->buf)); 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci fprintf(fp, "\n# "); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci /* preserve max indent depth for initial dots */ 68362306a36Sopenharmony_ci print_hierarchy_indent(sep, indent, dots, fp); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci /* the first hpp_list_node is for overhead columns */ 68662306a36Sopenharmony_ci fmt_node = list_first_entry(&hists->hpp_formats, 68762306a36Sopenharmony_ci struct perf_hpp_list_node, list); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci first_col = true; 69062306a36Sopenharmony_ci perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { 69162306a36Sopenharmony_ci if (!first_col) 69262306a36Sopenharmony_ci fprintf(fp, "%s", sep ?: ".."); 69362306a36Sopenharmony_ci first_col = false; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci width = fmt->width(fmt, hpp, hists); 69662306a36Sopenharmony_ci fprintf(fp, "%.*s", width, dots); 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci depth = 0; 70062306a36Sopenharmony_ci list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) { 70162306a36Sopenharmony_ci first_col = true; 70262306a36Sopenharmony_ci width = depth * HIERARCHY_INDENT; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { 70562306a36Sopenharmony_ci if (perf_hpp__should_skip(fmt, hists)) 70662306a36Sopenharmony_ci continue; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci if (!first_col) 70962306a36Sopenharmony_ci width++; /* for '+' sign between column header */ 71062306a36Sopenharmony_ci first_col = false; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci width += fmt->width(fmt, hpp, hists); 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci if (width > header_width) 71662306a36Sopenharmony_ci header_width = width; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci depth++; 71962306a36Sopenharmony_ci } 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci fprintf(fp, "%s%-.*s", sep ?: " ", header_width, dots); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci fprintf(fp, "\n#\n"); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci return 2; 72662306a36Sopenharmony_ci} 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_cistatic void fprintf_line(struct hists *hists, struct perf_hpp *hpp, 72962306a36Sopenharmony_ci int line, FILE *fp) 73062306a36Sopenharmony_ci{ 73162306a36Sopenharmony_ci struct perf_hpp_fmt *fmt; 73262306a36Sopenharmony_ci const char *sep = symbol_conf.field_sep; 73362306a36Sopenharmony_ci bool first = true; 73462306a36Sopenharmony_ci int span = 0; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci hists__for_each_format(hists, fmt) { 73762306a36Sopenharmony_ci if (perf_hpp__should_skip(fmt, hists)) 73862306a36Sopenharmony_ci continue; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci if (!first && !span) 74162306a36Sopenharmony_ci fprintf(fp, "%s", sep ?: " "); 74262306a36Sopenharmony_ci else 74362306a36Sopenharmony_ci first = false; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci fmt->header(fmt, hpp, hists, line, &span); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci if (!span) 74862306a36Sopenharmony_ci fprintf(fp, "%s", hpp->buf); 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci} 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_cistatic int 75362306a36Sopenharmony_cihists__fprintf_standard_headers(struct hists *hists, 75462306a36Sopenharmony_ci struct perf_hpp *hpp, 75562306a36Sopenharmony_ci FILE *fp) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci struct perf_hpp_list *hpp_list = hists->hpp_list; 75862306a36Sopenharmony_ci struct perf_hpp_fmt *fmt; 75962306a36Sopenharmony_ci unsigned int width; 76062306a36Sopenharmony_ci const char *sep = symbol_conf.field_sep; 76162306a36Sopenharmony_ci bool first = true; 76262306a36Sopenharmony_ci int line; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci for (line = 0; line < hpp_list->nr_header_lines; line++) { 76562306a36Sopenharmony_ci /* first # is displayed one level up */ 76662306a36Sopenharmony_ci if (line) 76762306a36Sopenharmony_ci fprintf(fp, "# "); 76862306a36Sopenharmony_ci fprintf_line(hists, hpp, line, fp); 76962306a36Sopenharmony_ci fprintf(fp, "\n"); 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci if (sep) 77362306a36Sopenharmony_ci return hpp_list->nr_header_lines; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci first = true; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci fprintf(fp, "# "); 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci hists__for_each_format(hists, fmt) { 78062306a36Sopenharmony_ci unsigned int i; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci if (perf_hpp__should_skip(fmt, hists)) 78362306a36Sopenharmony_ci continue; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci if (!first) 78662306a36Sopenharmony_ci fprintf(fp, "%s", sep ?: " "); 78762306a36Sopenharmony_ci else 78862306a36Sopenharmony_ci first = false; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci width = fmt->width(fmt, hpp, hists); 79162306a36Sopenharmony_ci for (i = 0; i < width; i++) 79262306a36Sopenharmony_ci fprintf(fp, "."); 79362306a36Sopenharmony_ci } 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci fprintf(fp, "\n"); 79662306a36Sopenharmony_ci fprintf(fp, "#\n"); 79762306a36Sopenharmony_ci return hpp_list->nr_header_lines + 2; 79862306a36Sopenharmony_ci} 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ciint hists__fprintf_headers(struct hists *hists, FILE *fp) 80162306a36Sopenharmony_ci{ 80262306a36Sopenharmony_ci char bf[1024]; 80362306a36Sopenharmony_ci struct perf_hpp dummy_hpp = { 80462306a36Sopenharmony_ci .buf = bf, 80562306a36Sopenharmony_ci .size = sizeof(bf), 80662306a36Sopenharmony_ci }; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci fprintf(fp, "# "); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci if (symbol_conf.report_hierarchy) 81162306a36Sopenharmony_ci return hists__fprintf_hierarchy_headers(hists, &dummy_hpp, fp); 81262306a36Sopenharmony_ci else 81362306a36Sopenharmony_ci return hists__fprintf_standard_headers(hists, &dummy_hpp, fp); 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci} 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_cisize_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, 81862306a36Sopenharmony_ci int max_cols, float min_pcnt, FILE *fp, 81962306a36Sopenharmony_ci bool ignore_callchains) 82062306a36Sopenharmony_ci{ 82162306a36Sopenharmony_ci struct rb_node *nd; 82262306a36Sopenharmony_ci size_t ret = 0; 82362306a36Sopenharmony_ci const char *sep = symbol_conf.field_sep; 82462306a36Sopenharmony_ci int nr_rows = 0; 82562306a36Sopenharmony_ci size_t linesz; 82662306a36Sopenharmony_ci char *line = NULL; 82762306a36Sopenharmony_ci unsigned indent; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci init_rem_hits(); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci hists__reset_column_width(hists); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci if (symbol_conf.col_width_list_str) 83462306a36Sopenharmony_ci perf_hpp__set_user_width(symbol_conf.col_width_list_str); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci if (show_header) 83762306a36Sopenharmony_ci nr_rows += hists__fprintf_headers(hists, fp); 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci if (max_rows && nr_rows >= max_rows) 84062306a36Sopenharmony_ci goto out; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci linesz = hists__sort_list_width(hists) + 3 + 1; 84362306a36Sopenharmony_ci linesz += perf_hpp__color_overhead(); 84462306a36Sopenharmony_ci line = malloc(linesz); 84562306a36Sopenharmony_ci if (line == NULL) { 84662306a36Sopenharmony_ci ret = -1; 84762306a36Sopenharmony_ci goto out; 84862306a36Sopenharmony_ci } 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci indent = hists__overhead_width(hists) + 4; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci for (nd = rb_first_cached(&hists->entries); nd; 85362306a36Sopenharmony_ci nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD)) { 85462306a36Sopenharmony_ci struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 85562306a36Sopenharmony_ci float percent; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci if (h->filtered) 85862306a36Sopenharmony_ci continue; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci if (symbol_conf.report_individual_block) 86162306a36Sopenharmony_ci percent = block_info__total_cycles_percent(h); 86262306a36Sopenharmony_ci else 86362306a36Sopenharmony_ci percent = hist_entry__get_percent_limit(h); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci if (percent < min_pcnt) 86662306a36Sopenharmony_ci continue; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci ret += hist_entry__fprintf(h, max_cols, line, linesz, fp, ignore_callchains); 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci if (max_rows && ++nr_rows >= max_rows) 87162306a36Sopenharmony_ci break; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci /* 87462306a36Sopenharmony_ci * If all children are filtered out or percent-limited, 87562306a36Sopenharmony_ci * display "no entry >= x.xx%" message. 87662306a36Sopenharmony_ci */ 87762306a36Sopenharmony_ci if (!h->leaf && !hist_entry__has_hierarchy_children(h, min_pcnt)) { 87862306a36Sopenharmony_ci int depth = hists->nr_hpp_node + h->depth + 1; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci print_hierarchy_indent(sep, depth, " ", fp); 88162306a36Sopenharmony_ci fprintf(fp, "%*sno entry >= %.2f%%\n", indent, "", min_pcnt); 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci if (max_rows && ++nr_rows >= max_rows) 88462306a36Sopenharmony_ci break; 88562306a36Sopenharmony_ci } 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci if (h->ms.map == NULL && verbose > 1) { 88862306a36Sopenharmony_ci maps__fprintf(thread__maps(h->thread), fp); 88962306a36Sopenharmony_ci fprintf(fp, "%.10s end\n", graph_dotted_line); 89062306a36Sopenharmony_ci } 89162306a36Sopenharmony_ci } 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci free(line); 89462306a36Sopenharmony_ciout: 89562306a36Sopenharmony_ci zfree(&rem_sq_bracket); 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci return ret; 89862306a36Sopenharmony_ci} 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_cisize_t events_stats__fprintf(struct events_stats *stats, FILE *fp, 90162306a36Sopenharmony_ci bool skip_empty) 90262306a36Sopenharmony_ci{ 90362306a36Sopenharmony_ci int i; 90462306a36Sopenharmony_ci size_t ret = 0; 90562306a36Sopenharmony_ci u32 total = stats->nr_events[0]; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { 90862306a36Sopenharmony_ci const char *name; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci name = perf_event__name(i); 91162306a36Sopenharmony_ci if (!strcmp(name, "UNKNOWN")) 91262306a36Sopenharmony_ci continue; 91362306a36Sopenharmony_ci if (skip_empty && !stats->nr_events[i]) 91462306a36Sopenharmony_ci continue; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci if (i && total) { 91762306a36Sopenharmony_ci ret += fprintf(fp, "%16s events: %10d (%4.1f%%)\n", 91862306a36Sopenharmony_ci name, stats->nr_events[i], 91962306a36Sopenharmony_ci 100.0 * stats->nr_events[i] / total); 92062306a36Sopenharmony_ci } else { 92162306a36Sopenharmony_ci ret += fprintf(fp, "%16s events: %10d\n", 92262306a36Sopenharmony_ci name, stats->nr_events[i]); 92362306a36Sopenharmony_ci } 92462306a36Sopenharmony_ci } 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci return ret; 92762306a36Sopenharmony_ci} 928