18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <stdio.h> 38c2ecf20Sopenharmony_ci#include <stdlib.h> 48c2ecf20Sopenharmony_ci#include <linux/string.h> 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include "../../util/callchain.h" 78c2ecf20Sopenharmony_ci#include "../../util/debug.h" 88c2ecf20Sopenharmony_ci#include "../../util/event.h" 98c2ecf20Sopenharmony_ci#include "../../util/hist.h" 108c2ecf20Sopenharmony_ci#include "../../util/map.h" 118c2ecf20Sopenharmony_ci#include "../../util/maps.h" 128c2ecf20Sopenharmony_ci#include "../../util/symbol.h" 138c2ecf20Sopenharmony_ci#include "../../util/sort.h" 148c2ecf20Sopenharmony_ci#include "../../util/evsel.h" 158c2ecf20Sopenharmony_ci#include "../../util/srcline.h" 168c2ecf20Sopenharmony_ci#include "../../util/string2.h" 178c2ecf20Sopenharmony_ci#include "../../util/thread.h" 188c2ecf20Sopenharmony_ci#include "../../util/block-info.h" 198c2ecf20Sopenharmony_ci#include <linux/ctype.h> 208c2ecf20Sopenharmony_ci#include <linux/zalloc.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci int i; 258c2ecf20Sopenharmony_ci int ret = fprintf(fp, " "); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci for (i = 0; i < left_margin; i++) 288c2ecf20Sopenharmony_ci ret += fprintf(fp, " "); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci return ret; 318c2ecf20Sopenharmony_ci} 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask, 348c2ecf20Sopenharmony_ci int left_margin) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci int i; 378c2ecf20Sopenharmony_ci size_t ret = callchain__fprintf_left_margin(fp, left_margin); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci for (i = 0; i < depth; i++) 408c2ecf20Sopenharmony_ci if (depth_mask & (1 << i)) 418c2ecf20Sopenharmony_ci ret += fprintf(fp, "| "); 428c2ecf20Sopenharmony_ci else 438c2ecf20Sopenharmony_ci ret += fprintf(fp, " "); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci ret += fprintf(fp, "\n"); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci return ret; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic size_t ipchain__fprintf_graph(FILE *fp, struct callchain_node *node, 518c2ecf20Sopenharmony_ci struct callchain_list *chain, 528c2ecf20Sopenharmony_ci int depth, int depth_mask, int period, 538c2ecf20Sopenharmony_ci u64 total_samples, int left_margin) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci int i; 568c2ecf20Sopenharmony_ci size_t ret = 0; 578c2ecf20Sopenharmony_ci char bf[1024], *alloc_str = NULL; 588c2ecf20Sopenharmony_ci char buf[64]; 598c2ecf20Sopenharmony_ci const char *str; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci ret += callchain__fprintf_left_margin(fp, left_margin); 628c2ecf20Sopenharmony_ci for (i = 0; i < depth; i++) { 638c2ecf20Sopenharmony_ci if (depth_mask & (1 << i)) 648c2ecf20Sopenharmony_ci ret += fprintf(fp, "|"); 658c2ecf20Sopenharmony_ci else 668c2ecf20Sopenharmony_ci ret += fprintf(fp, " "); 678c2ecf20Sopenharmony_ci if (!period && i == depth - 1) { 688c2ecf20Sopenharmony_ci ret += fprintf(fp, "--"); 698c2ecf20Sopenharmony_ci ret += callchain_node__fprintf_value(node, fp, total_samples); 708c2ecf20Sopenharmony_ci ret += fprintf(fp, "--"); 718c2ecf20Sopenharmony_ci } else 728c2ecf20Sopenharmony_ci ret += fprintf(fp, "%s", " "); 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci str = callchain_list__sym_name(chain, bf, sizeof(bf), false); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (symbol_conf.show_branchflag_count) { 788c2ecf20Sopenharmony_ci callchain_list_counts__printf_value(chain, NULL, 798c2ecf20Sopenharmony_ci buf, sizeof(buf)); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (asprintf(&alloc_str, "%s%s", str, buf) < 0) 828c2ecf20Sopenharmony_ci str = "Not enough memory!"; 838c2ecf20Sopenharmony_ci else 848c2ecf20Sopenharmony_ci str = alloc_str; 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci fputs(str, fp); 888c2ecf20Sopenharmony_ci fputc('\n', fp); 898c2ecf20Sopenharmony_ci free(alloc_str); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci return ret; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic struct symbol *rem_sq_bracket; 958c2ecf20Sopenharmony_cistatic struct callchain_list rem_hits; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic void init_rem_hits(void) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6); 1008c2ecf20Sopenharmony_ci if (!rem_sq_bracket) { 1018c2ecf20Sopenharmony_ci fprintf(stderr, "Not enough memory to display remaining hits\n"); 1028c2ecf20Sopenharmony_ci return; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci strcpy(rem_sq_bracket->name, "[...]"); 1068c2ecf20Sopenharmony_ci rem_hits.ms.sym = rem_sq_bracket; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root, 1108c2ecf20Sopenharmony_ci u64 total_samples, int depth, 1118c2ecf20Sopenharmony_ci int depth_mask, int left_margin) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci struct rb_node *node, *next; 1148c2ecf20Sopenharmony_ci struct callchain_node *child = NULL; 1158c2ecf20Sopenharmony_ci struct callchain_list *chain; 1168c2ecf20Sopenharmony_ci int new_depth_mask = depth_mask; 1178c2ecf20Sopenharmony_ci u64 remaining; 1188c2ecf20Sopenharmony_ci size_t ret = 0; 1198c2ecf20Sopenharmony_ci int i; 1208c2ecf20Sopenharmony_ci uint entries_printed = 0; 1218c2ecf20Sopenharmony_ci int cumul_count = 0; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci remaining = total_samples; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci node = rb_first(root); 1268c2ecf20Sopenharmony_ci while (node) { 1278c2ecf20Sopenharmony_ci u64 new_total; 1288c2ecf20Sopenharmony_ci u64 cumul; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci child = rb_entry(node, struct callchain_node, rb_node); 1318c2ecf20Sopenharmony_ci cumul = callchain_cumul_hits(child); 1328c2ecf20Sopenharmony_ci remaining -= cumul; 1338c2ecf20Sopenharmony_ci cumul_count += callchain_cumul_counts(child); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* 1368c2ecf20Sopenharmony_ci * The depth mask manages the output of pipes that show 1378c2ecf20Sopenharmony_ci * the depth. We don't want to keep the pipes of the current 1388c2ecf20Sopenharmony_ci * level for the last child of this depth. 1398c2ecf20Sopenharmony_ci * Except if we have remaining filtered hits. They will 1408c2ecf20Sopenharmony_ci * supersede the last child 1418c2ecf20Sopenharmony_ci */ 1428c2ecf20Sopenharmony_ci next = rb_next(node); 1438c2ecf20Sopenharmony_ci if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining)) 1448c2ecf20Sopenharmony_ci new_depth_mask &= ~(1 << (depth - 1)); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci /* 1478c2ecf20Sopenharmony_ci * But we keep the older depth mask for the line separator 1488c2ecf20Sopenharmony_ci * to keep the level link until we reach the last child 1498c2ecf20Sopenharmony_ci */ 1508c2ecf20Sopenharmony_ci ret += ipchain__fprintf_graph_line(fp, depth, depth_mask, 1518c2ecf20Sopenharmony_ci left_margin); 1528c2ecf20Sopenharmony_ci i = 0; 1538c2ecf20Sopenharmony_ci list_for_each_entry(chain, &child->val, list) { 1548c2ecf20Sopenharmony_ci ret += ipchain__fprintf_graph(fp, child, chain, depth, 1558c2ecf20Sopenharmony_ci new_depth_mask, i++, 1568c2ecf20Sopenharmony_ci total_samples, 1578c2ecf20Sopenharmony_ci left_margin); 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (callchain_param.mode == CHAIN_GRAPH_REL) 1618c2ecf20Sopenharmony_ci new_total = child->children_hit; 1628c2ecf20Sopenharmony_ci else 1638c2ecf20Sopenharmony_ci new_total = total_samples; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci ret += __callchain__fprintf_graph(fp, &child->rb_root, new_total, 1668c2ecf20Sopenharmony_ci depth + 1, 1678c2ecf20Sopenharmony_ci new_depth_mask | (1 << depth), 1688c2ecf20Sopenharmony_ci left_margin); 1698c2ecf20Sopenharmony_ci node = next; 1708c2ecf20Sopenharmony_ci if (++entries_printed == callchain_param.print_limit) 1718c2ecf20Sopenharmony_ci break; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (callchain_param.mode == CHAIN_GRAPH_REL && 1758c2ecf20Sopenharmony_ci remaining && remaining != total_samples) { 1768c2ecf20Sopenharmony_ci struct callchain_node rem_node = { 1778c2ecf20Sopenharmony_ci .hit = remaining, 1788c2ecf20Sopenharmony_ci }; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (!rem_sq_bracket) 1818c2ecf20Sopenharmony_ci return ret; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci if (callchain_param.value == CCVAL_COUNT && child && child->parent) { 1848c2ecf20Sopenharmony_ci rem_node.count = child->parent->children_count - cumul_count; 1858c2ecf20Sopenharmony_ci if (rem_node.count <= 0) 1868c2ecf20Sopenharmony_ci return ret; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci new_depth_mask &= ~(1 << (depth - 1)); 1908c2ecf20Sopenharmony_ci ret += ipchain__fprintf_graph(fp, &rem_node, &rem_hits, depth, 1918c2ecf20Sopenharmony_ci new_depth_mask, 0, total_samples, 1928c2ecf20Sopenharmony_ci left_margin); 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return ret; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci/* 1998c2ecf20Sopenharmony_ci * If have one single callchain root, don't bother printing 2008c2ecf20Sopenharmony_ci * its percentage (100 % in fractal mode and the same percentage 2018c2ecf20Sopenharmony_ci * than the hist in graph mode). This also avoid one level of column. 2028c2ecf20Sopenharmony_ci * 2038c2ecf20Sopenharmony_ci * However when percent-limit applied, it's possible that single callchain 2048c2ecf20Sopenharmony_ci * node have different (non-100% in fractal mode) percentage. 2058c2ecf20Sopenharmony_ci */ 2068c2ecf20Sopenharmony_cistatic bool need_percent_display(struct rb_node *node, u64 parent_samples) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci struct callchain_node *cnode; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (rb_next(node)) 2118c2ecf20Sopenharmony_ci return true; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci cnode = rb_entry(node, struct callchain_node, rb_node); 2148c2ecf20Sopenharmony_ci return callchain_cumul_hits(cnode) != parent_samples; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root, 2188c2ecf20Sopenharmony_ci u64 total_samples, u64 parent_samples, 2198c2ecf20Sopenharmony_ci int left_margin) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci struct callchain_node *cnode; 2228c2ecf20Sopenharmony_ci struct callchain_list *chain; 2238c2ecf20Sopenharmony_ci u32 entries_printed = 0; 2248c2ecf20Sopenharmony_ci bool printed = false; 2258c2ecf20Sopenharmony_ci struct rb_node *node; 2268c2ecf20Sopenharmony_ci int i = 0; 2278c2ecf20Sopenharmony_ci int ret = 0; 2288c2ecf20Sopenharmony_ci char bf[1024]; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci node = rb_first(root); 2318c2ecf20Sopenharmony_ci if (node && !need_percent_display(node, parent_samples)) { 2328c2ecf20Sopenharmony_ci cnode = rb_entry(node, struct callchain_node, rb_node); 2338c2ecf20Sopenharmony_ci list_for_each_entry(chain, &cnode->val, list) { 2348c2ecf20Sopenharmony_ci /* 2358c2ecf20Sopenharmony_ci * If we sort by symbol, the first entry is the same than 2368c2ecf20Sopenharmony_ci * the symbol. No need to print it otherwise it appears as 2378c2ecf20Sopenharmony_ci * displayed twice. 2388c2ecf20Sopenharmony_ci */ 2398c2ecf20Sopenharmony_ci if (!i++ && field_order == NULL && 2408c2ecf20Sopenharmony_ci sort_order && strstarts(sort_order, "sym")) 2418c2ecf20Sopenharmony_ci continue; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci if (!printed) { 2448c2ecf20Sopenharmony_ci ret += callchain__fprintf_left_margin(fp, left_margin); 2458c2ecf20Sopenharmony_ci ret += fprintf(fp, "|\n"); 2468c2ecf20Sopenharmony_ci ret += callchain__fprintf_left_margin(fp, left_margin); 2478c2ecf20Sopenharmony_ci ret += fprintf(fp, "---"); 2488c2ecf20Sopenharmony_ci left_margin += 3; 2498c2ecf20Sopenharmony_ci printed = true; 2508c2ecf20Sopenharmony_ci } else 2518c2ecf20Sopenharmony_ci ret += callchain__fprintf_left_margin(fp, left_margin); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci ret += fprintf(fp, "%s", 2548c2ecf20Sopenharmony_ci callchain_list__sym_name(chain, bf, 2558c2ecf20Sopenharmony_ci sizeof(bf), 2568c2ecf20Sopenharmony_ci false)); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (symbol_conf.show_branchflag_count) 2598c2ecf20Sopenharmony_ci ret += callchain_list_counts__printf_value( 2608c2ecf20Sopenharmony_ci chain, fp, NULL, 0); 2618c2ecf20Sopenharmony_ci ret += fprintf(fp, "\n"); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci if (++entries_printed == callchain_param.print_limit) 2648c2ecf20Sopenharmony_ci break; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci root = &cnode->rb_root; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (callchain_param.mode == CHAIN_GRAPH_REL) 2708c2ecf20Sopenharmony_ci total_samples = parent_samples; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci ret += __callchain__fprintf_graph(fp, root, total_samples, 2738c2ecf20Sopenharmony_ci 1, 1, left_margin); 2748c2ecf20Sopenharmony_ci if (ret) { 2758c2ecf20Sopenharmony_ci /* do not add a blank line if it printed nothing */ 2768c2ecf20Sopenharmony_ci ret += fprintf(fp, "\n"); 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci return ret; 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic size_t __callchain__fprintf_flat(FILE *fp, struct callchain_node *node, 2838c2ecf20Sopenharmony_ci u64 total_samples) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci struct callchain_list *chain; 2868c2ecf20Sopenharmony_ci size_t ret = 0; 2878c2ecf20Sopenharmony_ci char bf[1024]; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci if (!node) 2908c2ecf20Sopenharmony_ci return 0; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci ret += __callchain__fprintf_flat(fp, node->parent, total_samples); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci list_for_each_entry(chain, &node->val, list) { 2968c2ecf20Sopenharmony_ci if (chain->ip >= PERF_CONTEXT_MAX) 2978c2ecf20Sopenharmony_ci continue; 2988c2ecf20Sopenharmony_ci ret += fprintf(fp, " %s\n", callchain_list__sym_name(chain, 2998c2ecf20Sopenharmony_ci bf, sizeof(bf), false)); 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci return ret; 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic size_t callchain__fprintf_flat(FILE *fp, struct rb_root *tree, 3068c2ecf20Sopenharmony_ci u64 total_samples) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci size_t ret = 0; 3098c2ecf20Sopenharmony_ci u32 entries_printed = 0; 3108c2ecf20Sopenharmony_ci struct callchain_node *chain; 3118c2ecf20Sopenharmony_ci struct rb_node *rb_node = rb_first(tree); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci while (rb_node) { 3148c2ecf20Sopenharmony_ci chain = rb_entry(rb_node, struct callchain_node, rb_node); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci ret += fprintf(fp, " "); 3178c2ecf20Sopenharmony_ci ret += callchain_node__fprintf_value(chain, fp, total_samples); 3188c2ecf20Sopenharmony_ci ret += fprintf(fp, "\n"); 3198c2ecf20Sopenharmony_ci ret += __callchain__fprintf_flat(fp, chain, total_samples); 3208c2ecf20Sopenharmony_ci ret += fprintf(fp, "\n"); 3218c2ecf20Sopenharmony_ci if (++entries_printed == callchain_param.print_limit) 3228c2ecf20Sopenharmony_ci break; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci rb_node = rb_next(rb_node); 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci return ret; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic size_t __callchain__fprintf_folded(FILE *fp, struct callchain_node *node) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci const char *sep = symbol_conf.field_sep ?: ";"; 3338c2ecf20Sopenharmony_ci struct callchain_list *chain; 3348c2ecf20Sopenharmony_ci size_t ret = 0; 3358c2ecf20Sopenharmony_ci char bf[1024]; 3368c2ecf20Sopenharmony_ci bool first; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (!node) 3398c2ecf20Sopenharmony_ci return 0; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci ret += __callchain__fprintf_folded(fp, node->parent); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci first = (ret == 0); 3448c2ecf20Sopenharmony_ci list_for_each_entry(chain, &node->val, list) { 3458c2ecf20Sopenharmony_ci if (chain->ip >= PERF_CONTEXT_MAX) 3468c2ecf20Sopenharmony_ci continue; 3478c2ecf20Sopenharmony_ci ret += fprintf(fp, "%s%s", first ? "" : sep, 3488c2ecf20Sopenharmony_ci callchain_list__sym_name(chain, 3498c2ecf20Sopenharmony_ci bf, sizeof(bf), false)); 3508c2ecf20Sopenharmony_ci first = false; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci return ret; 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic size_t callchain__fprintf_folded(FILE *fp, struct rb_root *tree, 3578c2ecf20Sopenharmony_ci u64 total_samples) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci size_t ret = 0; 3608c2ecf20Sopenharmony_ci u32 entries_printed = 0; 3618c2ecf20Sopenharmony_ci struct callchain_node *chain; 3628c2ecf20Sopenharmony_ci struct rb_node *rb_node = rb_first(tree); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci while (rb_node) { 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci chain = rb_entry(rb_node, struct callchain_node, rb_node); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci ret += callchain_node__fprintf_value(chain, fp, total_samples); 3698c2ecf20Sopenharmony_ci ret += fprintf(fp, " "); 3708c2ecf20Sopenharmony_ci ret += __callchain__fprintf_folded(fp, chain); 3718c2ecf20Sopenharmony_ci ret += fprintf(fp, "\n"); 3728c2ecf20Sopenharmony_ci if (++entries_printed == callchain_param.print_limit) 3738c2ecf20Sopenharmony_ci break; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci rb_node = rb_next(rb_node); 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci return ret; 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic size_t hist_entry_callchain__fprintf(struct hist_entry *he, 3828c2ecf20Sopenharmony_ci u64 total_samples, int left_margin, 3838c2ecf20Sopenharmony_ci FILE *fp) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci u64 parent_samples = he->stat.period; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci if (symbol_conf.cumulate_callchain) 3888c2ecf20Sopenharmony_ci parent_samples = he->stat_acc->period; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci switch (callchain_param.mode) { 3918c2ecf20Sopenharmony_ci case CHAIN_GRAPH_REL: 3928c2ecf20Sopenharmony_ci return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples, 3938c2ecf20Sopenharmony_ci parent_samples, left_margin); 3948c2ecf20Sopenharmony_ci break; 3958c2ecf20Sopenharmony_ci case CHAIN_GRAPH_ABS: 3968c2ecf20Sopenharmony_ci return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples, 3978c2ecf20Sopenharmony_ci parent_samples, left_margin); 3988c2ecf20Sopenharmony_ci break; 3998c2ecf20Sopenharmony_ci case CHAIN_FLAT: 4008c2ecf20Sopenharmony_ci return callchain__fprintf_flat(fp, &he->sorted_chain, total_samples); 4018c2ecf20Sopenharmony_ci break; 4028c2ecf20Sopenharmony_ci case CHAIN_FOLDED: 4038c2ecf20Sopenharmony_ci return callchain__fprintf_folded(fp, &he->sorted_chain, total_samples); 4048c2ecf20Sopenharmony_ci break; 4058c2ecf20Sopenharmony_ci case CHAIN_NONE: 4068c2ecf20Sopenharmony_ci break; 4078c2ecf20Sopenharmony_ci default: 4088c2ecf20Sopenharmony_ci pr_err("Bad callchain mode\n"); 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci return 0; 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ciint __hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp, 4158c2ecf20Sopenharmony_ci struct perf_hpp_list *hpp_list) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci const char *sep = symbol_conf.field_sep; 4188c2ecf20Sopenharmony_ci struct perf_hpp_fmt *fmt; 4198c2ecf20Sopenharmony_ci char *start = hpp->buf; 4208c2ecf20Sopenharmony_ci int ret; 4218c2ecf20Sopenharmony_ci bool first = true; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (symbol_conf.exclude_other && !he->parent) 4248c2ecf20Sopenharmony_ci return 0; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci perf_hpp_list__for_each_format(hpp_list, fmt) { 4278c2ecf20Sopenharmony_ci if (perf_hpp__should_skip(fmt, he->hists)) 4288c2ecf20Sopenharmony_ci continue; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci /* 4318c2ecf20Sopenharmony_ci * If there's no field_sep, we still need 4328c2ecf20Sopenharmony_ci * to display initial ' '. 4338c2ecf20Sopenharmony_ci */ 4348c2ecf20Sopenharmony_ci if (!sep || !first) { 4358c2ecf20Sopenharmony_ci ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: " "); 4368c2ecf20Sopenharmony_ci advance_hpp(hpp, ret); 4378c2ecf20Sopenharmony_ci } else 4388c2ecf20Sopenharmony_ci first = false; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci if (perf_hpp__use_color() && fmt->color) 4418c2ecf20Sopenharmony_ci ret = fmt->color(fmt, hpp, he); 4428c2ecf20Sopenharmony_ci else 4438c2ecf20Sopenharmony_ci ret = fmt->entry(fmt, hpp, he); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci ret = hist_entry__snprintf_alignment(he, hpp, fmt, ret); 4468c2ecf20Sopenharmony_ci advance_hpp(hpp, ret); 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci return hpp->buf - start; 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_cistatic int hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci return __hist_entry__snprintf(he, hpp, he->hists->hpp_list); 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic int hist_entry__hierarchy_fprintf(struct hist_entry *he, 4588c2ecf20Sopenharmony_ci struct perf_hpp *hpp, 4598c2ecf20Sopenharmony_ci struct hists *hists, 4608c2ecf20Sopenharmony_ci FILE *fp) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci const char *sep = symbol_conf.field_sep; 4638c2ecf20Sopenharmony_ci struct perf_hpp_fmt *fmt; 4648c2ecf20Sopenharmony_ci struct perf_hpp_list_node *fmt_node; 4658c2ecf20Sopenharmony_ci char *buf = hpp->buf; 4668c2ecf20Sopenharmony_ci size_t size = hpp->size; 4678c2ecf20Sopenharmony_ci int ret, printed = 0; 4688c2ecf20Sopenharmony_ci bool first = true; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci if (symbol_conf.exclude_other && !he->parent) 4718c2ecf20Sopenharmony_ci return 0; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci ret = scnprintf(hpp->buf, hpp->size, "%*s", he->depth * HIERARCHY_INDENT, ""); 4748c2ecf20Sopenharmony_ci advance_hpp(hpp, ret); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci /* the first hpp_list_node is for overhead columns */ 4778c2ecf20Sopenharmony_ci fmt_node = list_first_entry(&hists->hpp_formats, 4788c2ecf20Sopenharmony_ci struct perf_hpp_list_node, list); 4798c2ecf20Sopenharmony_ci perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { 4808c2ecf20Sopenharmony_ci /* 4818c2ecf20Sopenharmony_ci * If there's no field_sep, we still need 4828c2ecf20Sopenharmony_ci * to display initial ' '. 4838c2ecf20Sopenharmony_ci */ 4848c2ecf20Sopenharmony_ci if (!sep || !first) { 4858c2ecf20Sopenharmony_ci ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: " "); 4868c2ecf20Sopenharmony_ci advance_hpp(hpp, ret); 4878c2ecf20Sopenharmony_ci } else 4888c2ecf20Sopenharmony_ci first = false; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if (perf_hpp__use_color() && fmt->color) 4918c2ecf20Sopenharmony_ci ret = fmt->color(fmt, hpp, he); 4928c2ecf20Sopenharmony_ci else 4938c2ecf20Sopenharmony_ci ret = fmt->entry(fmt, hpp, he); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci ret = hist_entry__snprintf_alignment(he, hpp, fmt, ret); 4968c2ecf20Sopenharmony_ci advance_hpp(hpp, ret); 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci if (!sep) 5008c2ecf20Sopenharmony_ci ret = scnprintf(hpp->buf, hpp->size, "%*s", 5018c2ecf20Sopenharmony_ci (hists->nr_hpp_node - 2) * HIERARCHY_INDENT, ""); 5028c2ecf20Sopenharmony_ci advance_hpp(hpp, ret); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci printed += fprintf(fp, "%s", buf); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci perf_hpp_list__for_each_format(he->hpp_list, fmt) { 5078c2ecf20Sopenharmony_ci hpp->buf = buf; 5088c2ecf20Sopenharmony_ci hpp->size = size; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci /* 5118c2ecf20Sopenharmony_ci * No need to call hist_entry__snprintf_alignment() since this 5128c2ecf20Sopenharmony_ci * fmt is always the last column in the hierarchy mode. 5138c2ecf20Sopenharmony_ci */ 5148c2ecf20Sopenharmony_ci if (perf_hpp__use_color() && fmt->color) 5158c2ecf20Sopenharmony_ci fmt->color(fmt, hpp, he); 5168c2ecf20Sopenharmony_ci else 5178c2ecf20Sopenharmony_ci fmt->entry(fmt, hpp, he); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci /* 5208c2ecf20Sopenharmony_ci * dynamic entries are right-aligned but we want left-aligned 5218c2ecf20Sopenharmony_ci * in the hierarchy mode 5228c2ecf20Sopenharmony_ci */ 5238c2ecf20Sopenharmony_ci printed += fprintf(fp, "%s%s", sep ?: " ", skip_spaces(buf)); 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci printed += putc('\n', fp); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci if (he->leaf && hist_entry__has_callchains(he) && symbol_conf.use_callchain) { 5288c2ecf20Sopenharmony_ci u64 total = hists__total_period(hists); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci printed += hist_entry_callchain__fprintf(he, total, 0, fp); 5318c2ecf20Sopenharmony_ci goto out; 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ciout: 5358c2ecf20Sopenharmony_ci return printed; 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_cistatic int hist_entry__block_fprintf(struct hist_entry *he, 5398c2ecf20Sopenharmony_ci char *bf, size_t size, 5408c2ecf20Sopenharmony_ci FILE *fp) 5418c2ecf20Sopenharmony_ci{ 5428c2ecf20Sopenharmony_ci struct block_hist *bh = container_of(he, struct block_hist, he); 5438c2ecf20Sopenharmony_ci int ret = 0; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci for (unsigned int i = 0; i < bh->block_hists.nr_entries; i++) { 5468c2ecf20Sopenharmony_ci struct perf_hpp hpp = { 5478c2ecf20Sopenharmony_ci .buf = bf, 5488c2ecf20Sopenharmony_ci .size = size, 5498c2ecf20Sopenharmony_ci .skip = false, 5508c2ecf20Sopenharmony_ci }; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci bh->block_idx = i; 5538c2ecf20Sopenharmony_ci hist_entry__snprintf(he, &hpp); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci if (!hpp.skip) 5568c2ecf20Sopenharmony_ci ret += fprintf(fp, "%s\n", bf); 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci return ret; 5608c2ecf20Sopenharmony_ci} 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_cistatic int hist_entry__individual_block_fprintf(struct hist_entry *he, 5638c2ecf20Sopenharmony_ci char *bf, size_t size, 5648c2ecf20Sopenharmony_ci FILE *fp) 5658c2ecf20Sopenharmony_ci{ 5668c2ecf20Sopenharmony_ci int ret = 0; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci struct perf_hpp hpp = { 5698c2ecf20Sopenharmony_ci .buf = bf, 5708c2ecf20Sopenharmony_ci .size = size, 5718c2ecf20Sopenharmony_ci .skip = false, 5728c2ecf20Sopenharmony_ci }; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci hist_entry__snprintf(he, &hpp); 5758c2ecf20Sopenharmony_ci if (!hpp.skip) 5768c2ecf20Sopenharmony_ci ret += fprintf(fp, "%s\n", bf); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci return ret; 5798c2ecf20Sopenharmony_ci} 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_cistatic int hist_entry__fprintf(struct hist_entry *he, size_t size, 5828c2ecf20Sopenharmony_ci char *bf, size_t bfsz, FILE *fp, 5838c2ecf20Sopenharmony_ci bool ignore_callchains) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci int ret; 5868c2ecf20Sopenharmony_ci int callchain_ret = 0; 5878c2ecf20Sopenharmony_ci struct perf_hpp hpp = { 5888c2ecf20Sopenharmony_ci .buf = bf, 5898c2ecf20Sopenharmony_ci .size = size, 5908c2ecf20Sopenharmony_ci }; 5918c2ecf20Sopenharmony_ci struct hists *hists = he->hists; 5928c2ecf20Sopenharmony_ci u64 total_period = hists->stats.total_period; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci if (size == 0 || size > bfsz) 5958c2ecf20Sopenharmony_ci size = hpp.size = bfsz; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci if (symbol_conf.report_hierarchy) 5988c2ecf20Sopenharmony_ci return hist_entry__hierarchy_fprintf(he, &hpp, hists, fp); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci if (symbol_conf.report_block) 6018c2ecf20Sopenharmony_ci return hist_entry__block_fprintf(he, bf, size, fp); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci if (symbol_conf.report_individual_block) 6048c2ecf20Sopenharmony_ci return hist_entry__individual_block_fprintf(he, bf, size, fp); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci hist_entry__snprintf(he, &hpp); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci ret = fprintf(fp, "%s\n", bf); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci if (hist_entry__has_callchains(he) && !ignore_callchains) 6118c2ecf20Sopenharmony_ci callchain_ret = hist_entry_callchain__fprintf(he, total_period, 6128c2ecf20Sopenharmony_ci 0, fp); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci ret += callchain_ret; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci return ret; 6178c2ecf20Sopenharmony_ci} 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_cistatic int print_hierarchy_indent(const char *sep, int indent, 6208c2ecf20Sopenharmony_ci const char *line, FILE *fp) 6218c2ecf20Sopenharmony_ci{ 6228c2ecf20Sopenharmony_ci int width; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci if (sep != NULL || indent < 2) 6258c2ecf20Sopenharmony_ci return 0; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci width = (indent - 2) * HIERARCHY_INDENT; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci return fprintf(fp, "%-*.*s", width, width, line); 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_cistatic int hists__fprintf_hierarchy_headers(struct hists *hists, 6338c2ecf20Sopenharmony_ci struct perf_hpp *hpp, FILE *fp) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci bool first_node, first_col; 6368c2ecf20Sopenharmony_ci int indent; 6378c2ecf20Sopenharmony_ci int depth; 6388c2ecf20Sopenharmony_ci unsigned width = 0; 6398c2ecf20Sopenharmony_ci unsigned header_width = 0; 6408c2ecf20Sopenharmony_ci struct perf_hpp_fmt *fmt; 6418c2ecf20Sopenharmony_ci struct perf_hpp_list_node *fmt_node; 6428c2ecf20Sopenharmony_ci const char *sep = symbol_conf.field_sep; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci indent = hists->nr_hpp_node; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci /* preserve max indent depth for column headers */ 6478c2ecf20Sopenharmony_ci print_hierarchy_indent(sep, indent, " ", fp); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci /* the first hpp_list_node is for overhead columns */ 6508c2ecf20Sopenharmony_ci fmt_node = list_first_entry(&hists->hpp_formats, 6518c2ecf20Sopenharmony_ci struct perf_hpp_list_node, list); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { 6548c2ecf20Sopenharmony_ci fmt->header(fmt, hpp, hists, 0, NULL); 6558c2ecf20Sopenharmony_ci fprintf(fp, "%s%s", hpp->buf, sep ?: " "); 6568c2ecf20Sopenharmony_ci } 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci /* combine sort headers with ' / ' */ 6598c2ecf20Sopenharmony_ci first_node = true; 6608c2ecf20Sopenharmony_ci list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) { 6618c2ecf20Sopenharmony_ci if (!first_node) 6628c2ecf20Sopenharmony_ci header_width += fprintf(fp, " / "); 6638c2ecf20Sopenharmony_ci first_node = false; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci first_col = true; 6668c2ecf20Sopenharmony_ci perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { 6678c2ecf20Sopenharmony_ci if (perf_hpp__should_skip(fmt, hists)) 6688c2ecf20Sopenharmony_ci continue; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci if (!first_col) 6718c2ecf20Sopenharmony_ci header_width += fprintf(fp, "+"); 6728c2ecf20Sopenharmony_ci first_col = false; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci fmt->header(fmt, hpp, hists, 0, NULL); 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci header_width += fprintf(fp, "%s", strim(hpp->buf)); 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci } 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci fprintf(fp, "\n# "); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci /* preserve max indent depth for initial dots */ 6838c2ecf20Sopenharmony_ci print_hierarchy_indent(sep, indent, dots, fp); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci /* the first hpp_list_node is for overhead columns */ 6868c2ecf20Sopenharmony_ci fmt_node = list_first_entry(&hists->hpp_formats, 6878c2ecf20Sopenharmony_ci struct perf_hpp_list_node, list); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci first_col = true; 6908c2ecf20Sopenharmony_ci perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { 6918c2ecf20Sopenharmony_ci if (!first_col) 6928c2ecf20Sopenharmony_ci fprintf(fp, "%s", sep ?: ".."); 6938c2ecf20Sopenharmony_ci first_col = false; 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci width = fmt->width(fmt, hpp, hists); 6968c2ecf20Sopenharmony_ci fprintf(fp, "%.*s", width, dots); 6978c2ecf20Sopenharmony_ci } 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci depth = 0; 7008c2ecf20Sopenharmony_ci list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) { 7018c2ecf20Sopenharmony_ci first_col = true; 7028c2ecf20Sopenharmony_ci width = depth * HIERARCHY_INDENT; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { 7058c2ecf20Sopenharmony_ci if (perf_hpp__should_skip(fmt, hists)) 7068c2ecf20Sopenharmony_ci continue; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci if (!first_col) 7098c2ecf20Sopenharmony_ci width++; /* for '+' sign between column header */ 7108c2ecf20Sopenharmony_ci first_col = false; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci width += fmt->width(fmt, hpp, hists); 7138c2ecf20Sopenharmony_ci } 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci if (width > header_width) 7168c2ecf20Sopenharmony_ci header_width = width; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci depth++; 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci fprintf(fp, "%s%-.*s", sep ?: " ", header_width, dots); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci fprintf(fp, "\n#\n"); 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci return 2; 7268c2ecf20Sopenharmony_ci} 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_cistatic void fprintf_line(struct hists *hists, struct perf_hpp *hpp, 7298c2ecf20Sopenharmony_ci int line, FILE *fp) 7308c2ecf20Sopenharmony_ci{ 7318c2ecf20Sopenharmony_ci struct perf_hpp_fmt *fmt; 7328c2ecf20Sopenharmony_ci const char *sep = symbol_conf.field_sep; 7338c2ecf20Sopenharmony_ci bool first = true; 7348c2ecf20Sopenharmony_ci int span = 0; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci hists__for_each_format(hists, fmt) { 7378c2ecf20Sopenharmony_ci if (perf_hpp__should_skip(fmt, hists)) 7388c2ecf20Sopenharmony_ci continue; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci if (!first && !span) 7418c2ecf20Sopenharmony_ci fprintf(fp, "%s", sep ?: " "); 7428c2ecf20Sopenharmony_ci else 7438c2ecf20Sopenharmony_ci first = false; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci fmt->header(fmt, hpp, hists, line, &span); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (!span) 7488c2ecf20Sopenharmony_ci fprintf(fp, "%s", hpp->buf); 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci} 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_cistatic int 7538c2ecf20Sopenharmony_cihists__fprintf_standard_headers(struct hists *hists, 7548c2ecf20Sopenharmony_ci struct perf_hpp *hpp, 7558c2ecf20Sopenharmony_ci FILE *fp) 7568c2ecf20Sopenharmony_ci{ 7578c2ecf20Sopenharmony_ci struct perf_hpp_list *hpp_list = hists->hpp_list; 7588c2ecf20Sopenharmony_ci struct perf_hpp_fmt *fmt; 7598c2ecf20Sopenharmony_ci unsigned int width; 7608c2ecf20Sopenharmony_ci const char *sep = symbol_conf.field_sep; 7618c2ecf20Sopenharmony_ci bool first = true; 7628c2ecf20Sopenharmony_ci int line; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci for (line = 0; line < hpp_list->nr_header_lines; line++) { 7658c2ecf20Sopenharmony_ci /* first # is displayed one level up */ 7668c2ecf20Sopenharmony_ci if (line) 7678c2ecf20Sopenharmony_ci fprintf(fp, "# "); 7688c2ecf20Sopenharmony_ci fprintf_line(hists, hpp, line, fp); 7698c2ecf20Sopenharmony_ci fprintf(fp, "\n"); 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci if (sep) 7738c2ecf20Sopenharmony_ci return hpp_list->nr_header_lines; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci first = true; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci fprintf(fp, "# "); 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci hists__for_each_format(hists, fmt) { 7808c2ecf20Sopenharmony_ci unsigned int i; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci if (perf_hpp__should_skip(fmt, hists)) 7838c2ecf20Sopenharmony_ci continue; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci if (!first) 7868c2ecf20Sopenharmony_ci fprintf(fp, "%s", sep ?: " "); 7878c2ecf20Sopenharmony_ci else 7888c2ecf20Sopenharmony_ci first = false; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci width = fmt->width(fmt, hpp, hists); 7918c2ecf20Sopenharmony_ci for (i = 0; i < width; i++) 7928c2ecf20Sopenharmony_ci fprintf(fp, "."); 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci fprintf(fp, "\n"); 7968c2ecf20Sopenharmony_ci fprintf(fp, "#\n"); 7978c2ecf20Sopenharmony_ci return hpp_list->nr_header_lines + 2; 7988c2ecf20Sopenharmony_ci} 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ciint hists__fprintf_headers(struct hists *hists, FILE *fp) 8018c2ecf20Sopenharmony_ci{ 8028c2ecf20Sopenharmony_ci char bf[1024]; 8038c2ecf20Sopenharmony_ci struct perf_hpp dummy_hpp = { 8048c2ecf20Sopenharmony_ci .buf = bf, 8058c2ecf20Sopenharmony_ci .size = sizeof(bf), 8068c2ecf20Sopenharmony_ci }; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci fprintf(fp, "# "); 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci if (symbol_conf.report_hierarchy) 8118c2ecf20Sopenharmony_ci return hists__fprintf_hierarchy_headers(hists, &dummy_hpp, fp); 8128c2ecf20Sopenharmony_ci else 8138c2ecf20Sopenharmony_ci return hists__fprintf_standard_headers(hists, &dummy_hpp, fp); 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci} 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_cisize_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, 8188c2ecf20Sopenharmony_ci int max_cols, float min_pcnt, FILE *fp, 8198c2ecf20Sopenharmony_ci bool ignore_callchains) 8208c2ecf20Sopenharmony_ci{ 8218c2ecf20Sopenharmony_ci struct rb_node *nd; 8228c2ecf20Sopenharmony_ci size_t ret = 0; 8238c2ecf20Sopenharmony_ci const char *sep = symbol_conf.field_sep; 8248c2ecf20Sopenharmony_ci int nr_rows = 0; 8258c2ecf20Sopenharmony_ci size_t linesz; 8268c2ecf20Sopenharmony_ci char *line = NULL; 8278c2ecf20Sopenharmony_ci unsigned indent; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci init_rem_hits(); 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci hists__reset_column_width(hists); 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci if (symbol_conf.col_width_list_str) 8348c2ecf20Sopenharmony_ci perf_hpp__set_user_width(symbol_conf.col_width_list_str); 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci if (show_header) 8378c2ecf20Sopenharmony_ci nr_rows += hists__fprintf_headers(hists, fp); 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci if (max_rows && nr_rows >= max_rows) 8408c2ecf20Sopenharmony_ci goto out; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci linesz = hists__sort_list_width(hists) + 3 + 1; 8438c2ecf20Sopenharmony_ci linesz += perf_hpp__color_overhead(); 8448c2ecf20Sopenharmony_ci line = malloc(linesz); 8458c2ecf20Sopenharmony_ci if (line == NULL) { 8468c2ecf20Sopenharmony_ci ret = -1; 8478c2ecf20Sopenharmony_ci goto out; 8488c2ecf20Sopenharmony_ci } 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci indent = hists__overhead_width(hists) + 4; 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci for (nd = rb_first_cached(&hists->entries); nd; 8538c2ecf20Sopenharmony_ci nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD)) { 8548c2ecf20Sopenharmony_ci struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 8558c2ecf20Sopenharmony_ci float percent; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci if (h->filtered) 8588c2ecf20Sopenharmony_ci continue; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci if (symbol_conf.report_individual_block) 8618c2ecf20Sopenharmony_ci percent = block_info__total_cycles_percent(h); 8628c2ecf20Sopenharmony_ci else 8638c2ecf20Sopenharmony_ci percent = hist_entry__get_percent_limit(h); 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci if (percent < min_pcnt) 8668c2ecf20Sopenharmony_ci continue; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci ret += hist_entry__fprintf(h, max_cols, line, linesz, fp, ignore_callchains); 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci if (max_rows && ++nr_rows >= max_rows) 8718c2ecf20Sopenharmony_ci break; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci /* 8748c2ecf20Sopenharmony_ci * If all children are filtered out or percent-limited, 8758c2ecf20Sopenharmony_ci * display "no entry >= x.xx%" message. 8768c2ecf20Sopenharmony_ci */ 8778c2ecf20Sopenharmony_ci if (!h->leaf && !hist_entry__has_hierarchy_children(h, min_pcnt)) { 8788c2ecf20Sopenharmony_ci int depth = hists->nr_hpp_node + h->depth + 1; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci print_hierarchy_indent(sep, depth, " ", fp); 8818c2ecf20Sopenharmony_ci fprintf(fp, "%*sno entry >= %.2f%%\n", indent, "", min_pcnt); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci if (max_rows && ++nr_rows >= max_rows) 8848c2ecf20Sopenharmony_ci break; 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci if (h->ms.map == NULL && verbose > 1) { 8888c2ecf20Sopenharmony_ci maps__fprintf(h->thread->maps, fp); 8898c2ecf20Sopenharmony_ci fprintf(fp, "%.10s end\n", graph_dotted_line); 8908c2ecf20Sopenharmony_ci } 8918c2ecf20Sopenharmony_ci } 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci free(line); 8948c2ecf20Sopenharmony_ciout: 8958c2ecf20Sopenharmony_ci zfree(&rem_sq_bracket); 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci return ret; 8988c2ecf20Sopenharmony_ci} 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_cisize_t events_stats__fprintf(struct events_stats *stats, FILE *fp) 9018c2ecf20Sopenharmony_ci{ 9028c2ecf20Sopenharmony_ci int i; 9038c2ecf20Sopenharmony_ci size_t ret = 0; 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { 9068c2ecf20Sopenharmony_ci const char *name; 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci name = perf_event__name(i); 9098c2ecf20Sopenharmony_ci if (!strcmp(name, "UNKNOWN")) 9108c2ecf20Sopenharmony_ci continue; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci ret += fprintf(fp, "%16s events: %10d\n", name, stats->nr_events[i]); 9138c2ecf20Sopenharmony_ci } 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci return ret; 9168c2ecf20Sopenharmony_ci} 917