162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <dirent.h> 362306a36Sopenharmony_ci#include <errno.h> 462306a36Sopenharmony_ci#include <inttypes.h> 562306a36Sopenharmony_ci#include <stdio.h> 662306a36Sopenharmony_ci#include <stdlib.h> 762306a36Sopenharmony_ci#include <string.h> 862306a36Sopenharmony_ci#include <linux/rbtree.h> 962306a36Sopenharmony_ci#include <linux/string.h> 1062306a36Sopenharmony_ci#include <sys/ttydefaults.h> 1162306a36Sopenharmony_ci#include <linux/time64.h> 1262306a36Sopenharmony_ci#include <linux/zalloc.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "../../util/debug.h" 1562306a36Sopenharmony_ci#include "../../util/dso.h" 1662306a36Sopenharmony_ci#include "../../util/callchain.h" 1762306a36Sopenharmony_ci#include "../../util/evsel.h" 1862306a36Sopenharmony_ci#include "../../util/evlist.h" 1962306a36Sopenharmony_ci#include "../../util/header.h" 2062306a36Sopenharmony_ci#include "../../util/hist.h" 2162306a36Sopenharmony_ci#include "../../util/machine.h" 2262306a36Sopenharmony_ci#include "../../util/map.h" 2362306a36Sopenharmony_ci#include "../../util/maps.h" 2462306a36Sopenharmony_ci#include "../../util/symbol.h" 2562306a36Sopenharmony_ci#include "../../util/map_symbol.h" 2662306a36Sopenharmony_ci#include "../../util/branch.h" 2762306a36Sopenharmony_ci#include "../../util/pstack.h" 2862306a36Sopenharmony_ci#include "../../util/sort.h" 2962306a36Sopenharmony_ci#include "../../util/top.h" 3062306a36Sopenharmony_ci#include "../../util/thread.h" 3162306a36Sopenharmony_ci#include "../../util/block-info.h" 3262306a36Sopenharmony_ci#include "../../util/util.h" 3362306a36Sopenharmony_ci#include "../../arch/common.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include "../browsers/hists.h" 3662306a36Sopenharmony_ci#include "../helpline.h" 3762306a36Sopenharmony_ci#include "../util.h" 3862306a36Sopenharmony_ci#include "../ui.h" 3962306a36Sopenharmony_ci#include "map.h" 4062306a36Sopenharmony_ci#include "annotate.h" 4162306a36Sopenharmony_ci#include "srcline.h" 4262306a36Sopenharmony_ci#include "string2.h" 4362306a36Sopenharmony_ci#include "units.h" 4462306a36Sopenharmony_ci#include "time-utils.h" 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#include <linux/ctype.h> 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ciextern void hist_browser__init_hpp(void); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size); 5162306a36Sopenharmony_cistatic void hist_browser__update_nr_entries(struct hist_browser *hb); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic struct rb_node *hists__filter_entries(struct rb_node *nd, 5462306a36Sopenharmony_ci float min_pcnt); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic bool hist_browser__has_filter(struct hist_browser *hb) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter || hb->c2c_filter; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic int hist_browser__get_folding(struct hist_browser *browser) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci struct rb_node *nd; 6462306a36Sopenharmony_ci struct hists *hists = browser->hists; 6562306a36Sopenharmony_ci int unfolded_rows = 0; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci for (nd = rb_first_cached(&hists->entries); 6862306a36Sopenharmony_ci (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL; 6962306a36Sopenharmony_ci nd = rb_hierarchy_next(nd)) { 7062306a36Sopenharmony_ci struct hist_entry *he = 7162306a36Sopenharmony_ci rb_entry(nd, struct hist_entry, rb_node); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (he->leaf && he->unfolded) 7462306a36Sopenharmony_ci unfolded_rows += he->nr_rows; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci return unfolded_rows; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic void hist_browser__set_title_space(struct hist_browser *hb) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct ui_browser *browser = &hb->b; 8262306a36Sopenharmony_ci struct hists *hists = hb->hists; 8362306a36Sopenharmony_ci struct perf_hpp_list *hpp_list = hists->hpp_list; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci browser->extra_title_lines = hb->show_headers ? hpp_list->nr_header_lines : 0; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic u32 hist_browser__nr_entries(struct hist_browser *hb) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci u32 nr_entries; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (symbol_conf.report_hierarchy) 9362306a36Sopenharmony_ci nr_entries = hb->nr_hierarchy_entries; 9462306a36Sopenharmony_ci else if (hist_browser__has_filter(hb)) 9562306a36Sopenharmony_ci nr_entries = hb->nr_non_filtered_entries; 9662306a36Sopenharmony_ci else 9762306a36Sopenharmony_ci nr_entries = hb->hists->nr_entries; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci hb->nr_callchain_rows = hist_browser__get_folding(hb); 10062306a36Sopenharmony_ci return nr_entries + hb->nr_callchain_rows; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic void hist_browser__update_rows(struct hist_browser *hb) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct ui_browser *browser = &hb->b; 10662306a36Sopenharmony_ci struct hists *hists = hb->hists; 10762306a36Sopenharmony_ci struct perf_hpp_list *hpp_list = hists->hpp_list; 10862306a36Sopenharmony_ci u16 index_row; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (!hb->show_headers) { 11162306a36Sopenharmony_ci browser->rows += browser->extra_title_lines; 11262306a36Sopenharmony_ci browser->extra_title_lines = 0; 11362306a36Sopenharmony_ci return; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci browser->extra_title_lines = hpp_list->nr_header_lines; 11762306a36Sopenharmony_ci browser->rows -= browser->extra_title_lines; 11862306a36Sopenharmony_ci /* 11962306a36Sopenharmony_ci * Verify if we were at the last line and that line isn't 12062306a36Sopenharmony_ci * visible because we now show the header line(s). 12162306a36Sopenharmony_ci */ 12262306a36Sopenharmony_ci index_row = browser->index - browser->top_idx; 12362306a36Sopenharmony_ci if (index_row >= browser->rows) 12462306a36Sopenharmony_ci browser->index -= index_row - browser->rows + 1; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic void hist_browser__refresh_dimensions(struct ui_browser *browser) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci struct hist_browser *hb = container_of(browser, struct hist_browser, b); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci /* 3 == +/- toggle symbol before actual hist_entry rendering */ 13262306a36Sopenharmony_ci browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]")); 13362306a36Sopenharmony_ci /* 13462306a36Sopenharmony_ci * FIXME: Just keeping existing behaviour, but this really should be 13562306a36Sopenharmony_ci * before updating browser->width, as it will invalidate the 13662306a36Sopenharmony_ci * calculation above. Fix this and the fallout in another 13762306a36Sopenharmony_ci * changeset. 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_ci ui_browser__refresh_dimensions(browser); 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic void hist_browser__reset(struct hist_browser *browser) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci /* 14562306a36Sopenharmony_ci * The hists__remove_entry_filter() already folds non-filtered 14662306a36Sopenharmony_ci * entries so we can assume it has 0 callchain rows. 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_ci browser->nr_callchain_rows = 0; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci hist_browser__update_nr_entries(browser); 15162306a36Sopenharmony_ci browser->b.nr_entries = hist_browser__nr_entries(browser); 15262306a36Sopenharmony_ci hist_browser__refresh_dimensions(&browser->b); 15362306a36Sopenharmony_ci ui_browser__reset_index(&browser->b); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic char tree__folded_sign(bool unfolded) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci return unfolded ? '-' : '+'; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic char hist_entry__folded(const struct hist_entry *he) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci return he->has_children ? tree__folded_sign(he->unfolded) : ' '; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic char callchain_list__folded(const struct callchain_list *cl) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci return cl->has_children ? tree__folded_sign(cl->unfolded) : ' '; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic void callchain_list__set_folding(struct callchain_list *cl, bool unfold) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci cl->unfolded = unfold ? cl->has_children : false; 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic int callchain_node__count_rows_rb_tree(struct callchain_node *node) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci int n = 0; 17962306a36Sopenharmony_ci struct rb_node *nd; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) { 18262306a36Sopenharmony_ci struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); 18362306a36Sopenharmony_ci struct callchain_list *chain; 18462306a36Sopenharmony_ci char folded_sign = ' '; /* No children */ 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci list_for_each_entry(chain, &child->val, list) { 18762306a36Sopenharmony_ci ++n; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* We need this because we may not have children */ 19062306a36Sopenharmony_ci folded_sign = callchain_list__folded(chain); 19162306a36Sopenharmony_ci if (folded_sign == '+') 19262306a36Sopenharmony_ci break; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (folded_sign == '-') /* Have children and they're unfolded */ 19662306a36Sopenharmony_ci n += callchain_node__count_rows_rb_tree(child); 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci return n; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic int callchain_node__count_flat_rows(struct callchain_node *node) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci struct callchain_list *chain; 20562306a36Sopenharmony_ci char folded_sign = 0; 20662306a36Sopenharmony_ci int n = 0; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci list_for_each_entry(chain, &node->parent_val, list) { 20962306a36Sopenharmony_ci if (!folded_sign) { 21062306a36Sopenharmony_ci /* only check first chain list entry */ 21162306a36Sopenharmony_ci folded_sign = callchain_list__folded(chain); 21262306a36Sopenharmony_ci if (folded_sign == '+') 21362306a36Sopenharmony_ci return 1; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci n++; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci list_for_each_entry(chain, &node->val, list) { 21962306a36Sopenharmony_ci if (!folded_sign) { 22062306a36Sopenharmony_ci /* node->parent_val list might be empty */ 22162306a36Sopenharmony_ci folded_sign = callchain_list__folded(chain); 22262306a36Sopenharmony_ci if (folded_sign == '+') 22362306a36Sopenharmony_ci return 1; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci n++; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci return n; 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci return 1; 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic int callchain_node__count_rows(struct callchain_node *node) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct callchain_list *chain; 23962306a36Sopenharmony_ci bool unfolded = false; 24062306a36Sopenharmony_ci int n = 0; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (callchain_param.mode == CHAIN_FLAT) 24362306a36Sopenharmony_ci return callchain_node__count_flat_rows(node); 24462306a36Sopenharmony_ci else if (callchain_param.mode == CHAIN_FOLDED) 24562306a36Sopenharmony_ci return callchain_node__count_folded_rows(node); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci list_for_each_entry(chain, &node->val, list) { 24862306a36Sopenharmony_ci ++n; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci unfolded = chain->unfolded; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (unfolded) 25462306a36Sopenharmony_ci n += callchain_node__count_rows_rb_tree(node); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci return n; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic int callchain__count_rows(struct rb_root *chain) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci struct rb_node *nd; 26262306a36Sopenharmony_ci int n = 0; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci for (nd = rb_first(chain); nd; nd = rb_next(nd)) { 26562306a36Sopenharmony_ci struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); 26662306a36Sopenharmony_ci n += callchain_node__count_rows(node); 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci return n; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he, 27362306a36Sopenharmony_ci bool include_children) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci int count = 0; 27662306a36Sopenharmony_ci struct rb_node *node; 27762306a36Sopenharmony_ci struct hist_entry *child; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (he->leaf) 28062306a36Sopenharmony_ci return callchain__count_rows(&he->sorted_chain); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (he->has_no_entry) 28362306a36Sopenharmony_ci return 1; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci node = rb_first_cached(&he->hroot_out); 28662306a36Sopenharmony_ci while (node) { 28762306a36Sopenharmony_ci float percent; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci child = rb_entry(node, struct hist_entry, rb_node); 29062306a36Sopenharmony_ci percent = hist_entry__get_percent_limit(child); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (!child->filtered && percent >= hb->min_pcnt) { 29362306a36Sopenharmony_ci count++; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci if (include_children && child->unfolded) 29662306a36Sopenharmony_ci count += hierarchy_count_rows(hb, child, true); 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci node = rb_next(node); 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci return count; 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic bool hist_entry__toggle_fold(struct hist_entry *he) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci if (!he) 30762306a36Sopenharmony_ci return false; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (!he->has_children) 31062306a36Sopenharmony_ci return false; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci he->unfolded = !he->unfolded; 31362306a36Sopenharmony_ci return true; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic bool callchain_list__toggle_fold(struct callchain_list *cl) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci if (!cl) 31962306a36Sopenharmony_ci return false; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (!cl->has_children) 32262306a36Sopenharmony_ci return false; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci cl->unfolded = !cl->unfolded; 32562306a36Sopenharmony_ci return true; 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic void callchain_node__init_have_children_rb_tree(struct callchain_node *node) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci struct rb_node *nd = rb_first(&node->rb_root); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) { 33362306a36Sopenharmony_ci struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); 33462306a36Sopenharmony_ci struct callchain_list *chain; 33562306a36Sopenharmony_ci bool first = true; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci list_for_each_entry(chain, &child->val, list) { 33862306a36Sopenharmony_ci if (first) { 33962306a36Sopenharmony_ci first = false; 34062306a36Sopenharmony_ci chain->has_children = chain->list.next != &child->val || 34162306a36Sopenharmony_ci !RB_EMPTY_ROOT(&child->rb_root); 34262306a36Sopenharmony_ci } else 34362306a36Sopenharmony_ci chain->has_children = chain->list.next == &child->val && 34462306a36Sopenharmony_ci !RB_EMPTY_ROOT(&child->rb_root); 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci callchain_node__init_have_children_rb_tree(child); 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic void callchain_node__init_have_children(struct callchain_node *node, 35262306a36Sopenharmony_ci bool has_sibling) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci struct callchain_list *chain; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci chain = list_entry(node->val.next, struct callchain_list, list); 35762306a36Sopenharmony_ci chain->has_children = has_sibling; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (!list_empty(&node->val)) { 36062306a36Sopenharmony_ci chain = list_entry(node->val.prev, struct callchain_list, list); 36162306a36Sopenharmony_ci chain->has_children = !RB_EMPTY_ROOT(&node->rb_root); 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci callchain_node__init_have_children_rb_tree(node); 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic void callchain__init_have_children(struct rb_root *root) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci struct rb_node *nd = rb_first(root); 37062306a36Sopenharmony_ci bool has_sibling = nd && rb_next(nd); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci for (nd = rb_first(root); nd; nd = rb_next(nd)) { 37362306a36Sopenharmony_ci struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); 37462306a36Sopenharmony_ci callchain_node__init_have_children(node, has_sibling); 37562306a36Sopenharmony_ci if (callchain_param.mode == CHAIN_FLAT || 37662306a36Sopenharmony_ci callchain_param.mode == CHAIN_FOLDED) 37762306a36Sopenharmony_ci callchain_node__make_parent_list(node); 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic void hist_entry__init_have_children(struct hist_entry *he) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci if (he->init_have_children) 38462306a36Sopenharmony_ci return; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (he->leaf) { 38762306a36Sopenharmony_ci he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain); 38862306a36Sopenharmony_ci callchain__init_have_children(&he->sorted_chain); 38962306a36Sopenharmony_ci } else { 39062306a36Sopenharmony_ci he->has_children = !RB_EMPTY_ROOT(&he->hroot_out.rb_root); 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci he->init_have_children = true; 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_cistatic bool hist_browser__selection_has_children(struct hist_browser *browser) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci struct hist_entry *he = browser->he_selection; 39962306a36Sopenharmony_ci struct map_symbol *ms = browser->selection; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci if (!he || !ms) 40262306a36Sopenharmony_ci return false; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (ms == &he->ms) 40562306a36Sopenharmony_ci return he->has_children; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci return container_of(ms, struct callchain_list, ms)->has_children; 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic bool hist_browser__selection_unfolded(struct hist_browser *browser) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci struct hist_entry *he = browser->he_selection; 41362306a36Sopenharmony_ci struct map_symbol *ms = browser->selection; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (!he || !ms) 41662306a36Sopenharmony_ci return false; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (ms == &he->ms) 41962306a36Sopenharmony_ci return he->unfolded; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci return container_of(ms, struct callchain_list, ms)->unfolded; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic char *hist_browser__selection_sym_name(struct hist_browser *browser, char *bf, size_t size) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci struct hist_entry *he = browser->he_selection; 42762306a36Sopenharmony_ci struct map_symbol *ms = browser->selection; 42862306a36Sopenharmony_ci struct callchain_list *callchain_entry; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (!he || !ms) 43162306a36Sopenharmony_ci return NULL; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if (ms == &he->ms) { 43462306a36Sopenharmony_ci hist_entry__sym_snprintf(he, bf, size, 0); 43562306a36Sopenharmony_ci return bf + 4; // skip the level, e.g. '[k] ' 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci callchain_entry = container_of(ms, struct callchain_list, ms); 43962306a36Sopenharmony_ci return callchain_list__sym_name(callchain_entry, bf, size, browser->show_dso); 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_cistatic bool hist_browser__toggle_fold(struct hist_browser *browser) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci struct hist_entry *he = browser->he_selection; 44562306a36Sopenharmony_ci struct map_symbol *ms = browser->selection; 44662306a36Sopenharmony_ci struct callchain_list *cl = container_of(ms, struct callchain_list, ms); 44762306a36Sopenharmony_ci bool has_children; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (!he || !ms) 45062306a36Sopenharmony_ci return false; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci if (ms == &he->ms) 45362306a36Sopenharmony_ci has_children = hist_entry__toggle_fold(he); 45462306a36Sopenharmony_ci else 45562306a36Sopenharmony_ci has_children = callchain_list__toggle_fold(cl); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (has_children) { 45862306a36Sopenharmony_ci int child_rows = 0; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci hist_entry__init_have_children(he); 46162306a36Sopenharmony_ci browser->b.nr_entries -= he->nr_rows; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (he->leaf) 46462306a36Sopenharmony_ci browser->nr_callchain_rows -= he->nr_rows; 46562306a36Sopenharmony_ci else 46662306a36Sopenharmony_ci browser->nr_hierarchy_entries -= he->nr_rows; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci if (symbol_conf.report_hierarchy) 46962306a36Sopenharmony_ci child_rows = hierarchy_count_rows(browser, he, true); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (he->unfolded) { 47262306a36Sopenharmony_ci if (he->leaf) 47362306a36Sopenharmony_ci he->nr_rows = callchain__count_rows( 47462306a36Sopenharmony_ci &he->sorted_chain); 47562306a36Sopenharmony_ci else 47662306a36Sopenharmony_ci he->nr_rows = hierarchy_count_rows(browser, he, false); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci /* account grand children */ 47962306a36Sopenharmony_ci if (symbol_conf.report_hierarchy) 48062306a36Sopenharmony_ci browser->b.nr_entries += child_rows - he->nr_rows; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci if (!he->leaf && he->nr_rows == 0) { 48362306a36Sopenharmony_ci he->has_no_entry = true; 48462306a36Sopenharmony_ci he->nr_rows = 1; 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci } else { 48762306a36Sopenharmony_ci if (symbol_conf.report_hierarchy) 48862306a36Sopenharmony_ci browser->b.nr_entries -= child_rows - he->nr_rows; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (he->has_no_entry) 49162306a36Sopenharmony_ci he->has_no_entry = false; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci he->nr_rows = 0; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci browser->b.nr_entries += he->nr_rows; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci if (he->leaf) 49962306a36Sopenharmony_ci browser->nr_callchain_rows += he->nr_rows; 50062306a36Sopenharmony_ci else 50162306a36Sopenharmony_ci browser->nr_hierarchy_entries += he->nr_rows; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci return true; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci /* If it doesn't have children, no toggling performed */ 50762306a36Sopenharmony_ci return false; 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci int n = 0; 51362306a36Sopenharmony_ci struct rb_node *nd; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) { 51662306a36Sopenharmony_ci struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); 51762306a36Sopenharmony_ci struct callchain_list *chain; 51862306a36Sopenharmony_ci bool has_children = false; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci list_for_each_entry(chain, &child->val, list) { 52162306a36Sopenharmony_ci ++n; 52262306a36Sopenharmony_ci callchain_list__set_folding(chain, unfold); 52362306a36Sopenharmony_ci has_children = chain->has_children; 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci if (has_children) 52762306a36Sopenharmony_ci n += callchain_node__set_folding_rb_tree(child, unfold); 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci return n; 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic int callchain_node__set_folding(struct callchain_node *node, bool unfold) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci struct callchain_list *chain; 53662306a36Sopenharmony_ci bool has_children = false; 53762306a36Sopenharmony_ci int n = 0; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci list_for_each_entry(chain, &node->val, list) { 54062306a36Sopenharmony_ci ++n; 54162306a36Sopenharmony_ci callchain_list__set_folding(chain, unfold); 54262306a36Sopenharmony_ci has_children = chain->has_children; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci if (has_children) 54662306a36Sopenharmony_ci n += callchain_node__set_folding_rb_tree(node, unfold); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci return n; 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_cistatic int callchain__set_folding(struct rb_root *chain, bool unfold) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci struct rb_node *nd; 55462306a36Sopenharmony_ci int n = 0; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci for (nd = rb_first(chain); nd; nd = rb_next(nd)) { 55762306a36Sopenharmony_ci struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); 55862306a36Sopenharmony_ci n += callchain_node__set_folding(node, unfold); 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci return n; 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cistatic int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he, 56562306a36Sopenharmony_ci bool unfold __maybe_unused) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci float percent; 56862306a36Sopenharmony_ci struct rb_node *nd; 56962306a36Sopenharmony_ci struct hist_entry *child; 57062306a36Sopenharmony_ci int n = 0; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci for (nd = rb_first_cached(&he->hroot_out); nd; nd = rb_next(nd)) { 57362306a36Sopenharmony_ci child = rb_entry(nd, struct hist_entry, rb_node); 57462306a36Sopenharmony_ci percent = hist_entry__get_percent_limit(child); 57562306a36Sopenharmony_ci if (!child->filtered && percent >= hb->min_pcnt) 57662306a36Sopenharmony_ci n++; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci return n; 58062306a36Sopenharmony_ci} 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_cistatic void hist_entry__set_folding(struct hist_entry *he, 58362306a36Sopenharmony_ci struct hist_browser *hb, bool unfold) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci hist_entry__init_have_children(he); 58662306a36Sopenharmony_ci he->unfolded = unfold ? he->has_children : false; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if (he->has_children) { 58962306a36Sopenharmony_ci int n; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci if (he->leaf) 59262306a36Sopenharmony_ci n = callchain__set_folding(&he->sorted_chain, unfold); 59362306a36Sopenharmony_ci else 59462306a36Sopenharmony_ci n = hierarchy_set_folding(hb, he, unfold); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci he->nr_rows = unfold ? n : 0; 59762306a36Sopenharmony_ci } else 59862306a36Sopenharmony_ci he->nr_rows = 0; 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_cistatic void 60262306a36Sopenharmony_ci__hist_browser__set_folding(struct hist_browser *browser, bool unfold) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci struct rb_node *nd; 60562306a36Sopenharmony_ci struct hist_entry *he; 60662306a36Sopenharmony_ci double percent; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci nd = rb_first_cached(&browser->hists->entries); 60962306a36Sopenharmony_ci while (nd) { 61062306a36Sopenharmony_ci he = rb_entry(nd, struct hist_entry, rb_node); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci /* set folding state even if it's currently folded */ 61362306a36Sopenharmony_ci nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci hist_entry__set_folding(he, browser, unfold); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci percent = hist_entry__get_percent_limit(he); 61862306a36Sopenharmony_ci if (he->filtered || percent < browser->min_pcnt) 61962306a36Sopenharmony_ci continue; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci if (!he->depth || unfold) 62262306a36Sopenharmony_ci browser->nr_hierarchy_entries++; 62362306a36Sopenharmony_ci if (he->leaf) 62462306a36Sopenharmony_ci browser->nr_callchain_rows += he->nr_rows; 62562306a36Sopenharmony_ci else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) { 62662306a36Sopenharmony_ci browser->nr_hierarchy_entries++; 62762306a36Sopenharmony_ci he->has_no_entry = true; 62862306a36Sopenharmony_ci he->nr_rows = 1; 62962306a36Sopenharmony_ci } else 63062306a36Sopenharmony_ci he->has_no_entry = false; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_cistatic void hist_browser__set_folding(struct hist_browser *browser, bool unfold) 63562306a36Sopenharmony_ci{ 63662306a36Sopenharmony_ci browser->nr_hierarchy_entries = 0; 63762306a36Sopenharmony_ci browser->nr_callchain_rows = 0; 63862306a36Sopenharmony_ci __hist_browser__set_folding(browser, unfold); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci browser->b.nr_entries = hist_browser__nr_entries(browser); 64162306a36Sopenharmony_ci /* Go to the start, we may be way after valid entries after a collapse */ 64262306a36Sopenharmony_ci ui_browser__reset_index(&browser->b); 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_cistatic void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold) 64662306a36Sopenharmony_ci{ 64762306a36Sopenharmony_ci if (!browser->he_selection) 64862306a36Sopenharmony_ci return; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci if (unfold == browser->he_selection->unfolded) 65162306a36Sopenharmony_ci return; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci hist_browser__toggle_fold(browser); 65462306a36Sopenharmony_ci} 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_cistatic void ui_browser__warn_lost_events(struct ui_browser *browser) 65762306a36Sopenharmony_ci{ 65862306a36Sopenharmony_ci ui_browser__warning(browser, 4, 65962306a36Sopenharmony_ci "Events are being lost, check IO/CPU overload!\n\n" 66062306a36Sopenharmony_ci "You may want to run 'perf' using a RT scheduler policy:\n\n" 66162306a36Sopenharmony_ci " perf top -r 80\n\n" 66262306a36Sopenharmony_ci "Or reduce the sampling frequency."); 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_cistatic int hist_browser__title(struct hist_browser *browser, char *bf, size_t size) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci return browser->title ? browser->title(browser, bf, size) : 0; 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_cistatic int hist_browser__handle_hotkey(struct hist_browser *browser, bool warn_lost_event, char *title, size_t size, int key) 67162306a36Sopenharmony_ci{ 67262306a36Sopenharmony_ci switch (key) { 67362306a36Sopenharmony_ci case K_TIMER: { 67462306a36Sopenharmony_ci struct hist_browser_timer *hbt = browser->hbt; 67562306a36Sopenharmony_ci struct evsel *evsel = hists_to_evsel(browser->hists); 67662306a36Sopenharmony_ci u64 nr_entries; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci WARN_ON_ONCE(!hbt); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci if (hbt) 68162306a36Sopenharmony_ci hbt->timer(hbt->arg); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci if (hist_browser__has_filter(browser) || symbol_conf.report_hierarchy) 68462306a36Sopenharmony_ci hist_browser__update_nr_entries(browser); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci nr_entries = hist_browser__nr_entries(browser); 68762306a36Sopenharmony_ci ui_browser__update_nr_entries(&browser->b, nr_entries); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci if (warn_lost_event && 69062306a36Sopenharmony_ci (evsel->evlist->stats.nr_lost_warned != 69162306a36Sopenharmony_ci evsel->evlist->stats.nr_events[PERF_RECORD_LOST])) { 69262306a36Sopenharmony_ci evsel->evlist->stats.nr_lost_warned = 69362306a36Sopenharmony_ci evsel->evlist->stats.nr_events[PERF_RECORD_LOST]; 69462306a36Sopenharmony_ci ui_browser__warn_lost_events(&browser->b); 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci hist_browser__title(browser, title, size); 69862306a36Sopenharmony_ci ui_browser__show_title(&browser->b, title); 69962306a36Sopenharmony_ci break; 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci case 'D': { /* Debug */ 70262306a36Sopenharmony_ci struct hist_entry *h = rb_entry(browser->b.top, struct hist_entry, rb_node); 70362306a36Sopenharmony_ci static int seq; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci ui_helpline__pop(); 70662306a36Sopenharmony_ci ui_helpline__fpush("%d: nr_ent=(%d,%d), etl: %d, rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d", 70762306a36Sopenharmony_ci seq++, browser->b.nr_entries, browser->hists->nr_entries, 70862306a36Sopenharmony_ci browser->b.extra_title_lines, browser->b.rows, 70962306a36Sopenharmony_ci browser->b.index, browser->b.top_idx, h->row_offset, h->nr_rows); 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci break; 71262306a36Sopenharmony_ci case 'C': 71362306a36Sopenharmony_ci /* Collapse the whole world. */ 71462306a36Sopenharmony_ci hist_browser__set_folding(browser, false); 71562306a36Sopenharmony_ci break; 71662306a36Sopenharmony_ci case 'c': 71762306a36Sopenharmony_ci /* Collapse the selected entry. */ 71862306a36Sopenharmony_ci hist_browser__set_folding_selected(browser, false); 71962306a36Sopenharmony_ci break; 72062306a36Sopenharmony_ci case 'E': 72162306a36Sopenharmony_ci /* Expand the whole world. */ 72262306a36Sopenharmony_ci hist_browser__set_folding(browser, true); 72362306a36Sopenharmony_ci break; 72462306a36Sopenharmony_ci case 'e': 72562306a36Sopenharmony_ci /* Toggle expand/collapse the selected entry. */ 72662306a36Sopenharmony_ci hist_browser__toggle_fold(browser); 72762306a36Sopenharmony_ci break; 72862306a36Sopenharmony_ci case 'H': 72962306a36Sopenharmony_ci browser->show_headers = !browser->show_headers; 73062306a36Sopenharmony_ci hist_browser__update_rows(browser); 73162306a36Sopenharmony_ci break; 73262306a36Sopenharmony_ci case '+': 73362306a36Sopenharmony_ci if (hist_browser__toggle_fold(browser)) 73462306a36Sopenharmony_ci break; 73562306a36Sopenharmony_ci /* fall thru */ 73662306a36Sopenharmony_ci default: 73762306a36Sopenharmony_ci return -1; 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci return 0; 74162306a36Sopenharmony_ci} 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ciint hist_browser__run(struct hist_browser *browser, const char *help, 74462306a36Sopenharmony_ci bool warn_lost_event, int key) 74562306a36Sopenharmony_ci{ 74662306a36Sopenharmony_ci char title[160]; 74762306a36Sopenharmony_ci struct hist_browser_timer *hbt = browser->hbt; 74862306a36Sopenharmony_ci int delay_secs = hbt ? hbt->refresh : 0; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci browser->b.entries = &browser->hists->entries; 75162306a36Sopenharmony_ci browser->b.nr_entries = hist_browser__nr_entries(browser); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci hist_browser__title(browser, title, sizeof(title)); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci if (ui_browser__show(&browser->b, title, "%s", help) < 0) 75662306a36Sopenharmony_ci return -1; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci if (key && hist_browser__handle_hotkey(browser, warn_lost_event, title, sizeof(title), key)) 75962306a36Sopenharmony_ci goto out; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci while (1) { 76262306a36Sopenharmony_ci key = ui_browser__run(&browser->b, delay_secs); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci if (hist_browser__handle_hotkey(browser, warn_lost_event, title, sizeof(title), key)) 76562306a36Sopenharmony_ci break; 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ciout: 76862306a36Sopenharmony_ci ui_browser__hide(&browser->b); 76962306a36Sopenharmony_ci return key; 77062306a36Sopenharmony_ci} 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_cistruct callchain_print_arg { 77362306a36Sopenharmony_ci /* for hists browser */ 77462306a36Sopenharmony_ci off_t row_offset; 77562306a36Sopenharmony_ci bool is_current_entry; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci /* for file dump */ 77862306a36Sopenharmony_ci FILE *fp; 77962306a36Sopenharmony_ci int printed; 78062306a36Sopenharmony_ci}; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_citypedef void (*print_callchain_entry_fn)(struct hist_browser *browser, 78362306a36Sopenharmony_ci struct callchain_list *chain, 78462306a36Sopenharmony_ci const char *str, int offset, 78562306a36Sopenharmony_ci unsigned short row, 78662306a36Sopenharmony_ci struct callchain_print_arg *arg); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_cistatic void hist_browser__show_callchain_entry(struct hist_browser *browser, 78962306a36Sopenharmony_ci struct callchain_list *chain, 79062306a36Sopenharmony_ci const char *str, int offset, 79162306a36Sopenharmony_ci unsigned short row, 79262306a36Sopenharmony_ci struct callchain_print_arg *arg) 79362306a36Sopenharmony_ci{ 79462306a36Sopenharmony_ci int color, width; 79562306a36Sopenharmony_ci char folded_sign = callchain_list__folded(chain); 79662306a36Sopenharmony_ci bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci color = HE_COLORSET_NORMAL; 79962306a36Sopenharmony_ci width = browser->b.width - (offset + 2); 80062306a36Sopenharmony_ci if (ui_browser__is_current_entry(&browser->b, row)) { 80162306a36Sopenharmony_ci browser->selection = &chain->ms; 80262306a36Sopenharmony_ci color = HE_COLORSET_SELECTED; 80362306a36Sopenharmony_ci arg->is_current_entry = true; 80462306a36Sopenharmony_ci } 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci ui_browser__set_color(&browser->b, color); 80762306a36Sopenharmony_ci ui_browser__gotorc(&browser->b, row, 0); 80862306a36Sopenharmony_ci ui_browser__write_nstring(&browser->b, " ", offset); 80962306a36Sopenharmony_ci ui_browser__printf(&browser->b, "%c", folded_sign); 81062306a36Sopenharmony_ci ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' '); 81162306a36Sopenharmony_ci ui_browser__write_nstring(&browser->b, str, width); 81262306a36Sopenharmony_ci} 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_cistatic void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused, 81562306a36Sopenharmony_ci struct callchain_list *chain, 81662306a36Sopenharmony_ci const char *str, int offset, 81762306a36Sopenharmony_ci unsigned short row __maybe_unused, 81862306a36Sopenharmony_ci struct callchain_print_arg *arg) 81962306a36Sopenharmony_ci{ 82062306a36Sopenharmony_ci char folded_sign = callchain_list__folded(chain); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ", 82362306a36Sopenharmony_ci folded_sign, str); 82462306a36Sopenharmony_ci} 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_citypedef bool (*check_output_full_fn)(struct hist_browser *browser, 82762306a36Sopenharmony_ci unsigned short row); 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_cistatic bool hist_browser__check_output_full(struct hist_browser *browser, 83062306a36Sopenharmony_ci unsigned short row) 83162306a36Sopenharmony_ci{ 83262306a36Sopenharmony_ci return browser->b.rows == row; 83362306a36Sopenharmony_ci} 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_cistatic bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused, 83662306a36Sopenharmony_ci unsigned short row __maybe_unused) 83762306a36Sopenharmony_ci{ 83862306a36Sopenharmony_ci return false; 83962306a36Sopenharmony_ci} 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci#define LEVEL_OFFSET_STEP 3 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_cistatic int hist_browser__show_callchain_list(struct hist_browser *browser, 84462306a36Sopenharmony_ci struct callchain_node *node, 84562306a36Sopenharmony_ci struct callchain_list *chain, 84662306a36Sopenharmony_ci unsigned short row, u64 total, 84762306a36Sopenharmony_ci bool need_percent, int offset, 84862306a36Sopenharmony_ci print_callchain_entry_fn print, 84962306a36Sopenharmony_ci struct callchain_print_arg *arg) 85062306a36Sopenharmony_ci{ 85162306a36Sopenharmony_ci char bf[1024], *alloc_str; 85262306a36Sopenharmony_ci char buf[64], *alloc_str2; 85362306a36Sopenharmony_ci const char *str; 85462306a36Sopenharmony_ci int ret = 1; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci if (arg->row_offset != 0) { 85762306a36Sopenharmony_ci arg->row_offset--; 85862306a36Sopenharmony_ci return 0; 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci alloc_str = NULL; 86262306a36Sopenharmony_ci alloc_str2 = NULL; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci str = callchain_list__sym_name(chain, bf, sizeof(bf), 86562306a36Sopenharmony_ci browser->show_dso); 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci if (symbol_conf.show_branchflag_count) { 86862306a36Sopenharmony_ci callchain_list_counts__printf_value(chain, NULL, 86962306a36Sopenharmony_ci buf, sizeof(buf)); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci if (asprintf(&alloc_str2, "%s%s", str, buf) < 0) 87262306a36Sopenharmony_ci str = "Not enough memory!"; 87362306a36Sopenharmony_ci else 87462306a36Sopenharmony_ci str = alloc_str2; 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci if (need_percent) { 87862306a36Sopenharmony_ci callchain_node__scnprintf_value(node, buf, sizeof(buf), 87962306a36Sopenharmony_ci total); 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci if (asprintf(&alloc_str, "%s %s", buf, str) < 0) 88262306a36Sopenharmony_ci str = "Not enough memory!"; 88362306a36Sopenharmony_ci else 88462306a36Sopenharmony_ci str = alloc_str; 88562306a36Sopenharmony_ci } 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci print(browser, chain, str, offset, row, arg); 88862306a36Sopenharmony_ci free(alloc_str); 88962306a36Sopenharmony_ci free(alloc_str2); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci return ret; 89262306a36Sopenharmony_ci} 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_cistatic bool check_percent_display(struct rb_node *node, u64 parent_total) 89562306a36Sopenharmony_ci{ 89662306a36Sopenharmony_ci struct callchain_node *child; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci if (node == NULL) 89962306a36Sopenharmony_ci return false; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci if (rb_next(node)) 90262306a36Sopenharmony_ci return true; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci child = rb_entry(node, struct callchain_node, rb_node); 90562306a36Sopenharmony_ci return callchain_cumul_hits(child) != parent_total; 90662306a36Sopenharmony_ci} 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_cistatic int hist_browser__show_callchain_flat(struct hist_browser *browser, 90962306a36Sopenharmony_ci struct rb_root *root, 91062306a36Sopenharmony_ci unsigned short row, u64 total, 91162306a36Sopenharmony_ci u64 parent_total, 91262306a36Sopenharmony_ci print_callchain_entry_fn print, 91362306a36Sopenharmony_ci struct callchain_print_arg *arg, 91462306a36Sopenharmony_ci check_output_full_fn is_output_full) 91562306a36Sopenharmony_ci{ 91662306a36Sopenharmony_ci struct rb_node *node; 91762306a36Sopenharmony_ci int first_row = row, offset = LEVEL_OFFSET_STEP; 91862306a36Sopenharmony_ci bool need_percent; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci node = rb_first(root); 92162306a36Sopenharmony_ci need_percent = check_percent_display(node, parent_total); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci while (node) { 92462306a36Sopenharmony_ci struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); 92562306a36Sopenharmony_ci struct rb_node *next = rb_next(node); 92662306a36Sopenharmony_ci struct callchain_list *chain; 92762306a36Sopenharmony_ci char folded_sign = ' '; 92862306a36Sopenharmony_ci int first = true; 92962306a36Sopenharmony_ci int extra_offset = 0; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci list_for_each_entry(chain, &child->parent_val, list) { 93262306a36Sopenharmony_ci bool was_first = first; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci if (first) 93562306a36Sopenharmony_ci first = false; 93662306a36Sopenharmony_ci else if (need_percent) 93762306a36Sopenharmony_ci extra_offset = LEVEL_OFFSET_STEP; 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci folded_sign = callchain_list__folded(chain); 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci row += hist_browser__show_callchain_list(browser, child, 94262306a36Sopenharmony_ci chain, row, total, 94362306a36Sopenharmony_ci was_first && need_percent, 94462306a36Sopenharmony_ci offset + extra_offset, 94562306a36Sopenharmony_ci print, arg); 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci if (is_output_full(browser, row)) 94862306a36Sopenharmony_ci goto out; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci if (folded_sign == '+') 95162306a36Sopenharmony_ci goto next; 95262306a36Sopenharmony_ci } 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci list_for_each_entry(chain, &child->val, list) { 95562306a36Sopenharmony_ci bool was_first = first; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci if (first) 95862306a36Sopenharmony_ci first = false; 95962306a36Sopenharmony_ci else if (need_percent) 96062306a36Sopenharmony_ci extra_offset = LEVEL_OFFSET_STEP; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci folded_sign = callchain_list__folded(chain); 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci row += hist_browser__show_callchain_list(browser, child, 96562306a36Sopenharmony_ci chain, row, total, 96662306a36Sopenharmony_ci was_first && need_percent, 96762306a36Sopenharmony_ci offset + extra_offset, 96862306a36Sopenharmony_ci print, arg); 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci if (is_output_full(browser, row)) 97162306a36Sopenharmony_ci goto out; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci if (folded_sign == '+') 97462306a36Sopenharmony_ci break; 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_cinext: 97862306a36Sopenharmony_ci if (is_output_full(browser, row)) 97962306a36Sopenharmony_ci break; 98062306a36Sopenharmony_ci node = next; 98162306a36Sopenharmony_ci } 98262306a36Sopenharmony_ciout: 98362306a36Sopenharmony_ci return row - first_row; 98462306a36Sopenharmony_ci} 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_cistatic char *hist_browser__folded_callchain_str(struct hist_browser *browser, 98762306a36Sopenharmony_ci struct callchain_list *chain, 98862306a36Sopenharmony_ci char *value_str, char *old_str) 98962306a36Sopenharmony_ci{ 99062306a36Sopenharmony_ci char bf[1024]; 99162306a36Sopenharmony_ci const char *str; 99262306a36Sopenharmony_ci char *new; 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci str = callchain_list__sym_name(chain, bf, sizeof(bf), 99562306a36Sopenharmony_ci browser->show_dso); 99662306a36Sopenharmony_ci if (old_str) { 99762306a36Sopenharmony_ci if (asprintf(&new, "%s%s%s", old_str, 99862306a36Sopenharmony_ci symbol_conf.field_sep ?: ";", str) < 0) 99962306a36Sopenharmony_ci new = NULL; 100062306a36Sopenharmony_ci } else { 100162306a36Sopenharmony_ci if (value_str) { 100262306a36Sopenharmony_ci if (asprintf(&new, "%s %s", value_str, str) < 0) 100362306a36Sopenharmony_ci new = NULL; 100462306a36Sopenharmony_ci } else { 100562306a36Sopenharmony_ci if (asprintf(&new, "%s", str) < 0) 100662306a36Sopenharmony_ci new = NULL; 100762306a36Sopenharmony_ci } 100862306a36Sopenharmony_ci } 100962306a36Sopenharmony_ci return new; 101062306a36Sopenharmony_ci} 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_cistatic int hist_browser__show_callchain_folded(struct hist_browser *browser, 101362306a36Sopenharmony_ci struct rb_root *root, 101462306a36Sopenharmony_ci unsigned short row, u64 total, 101562306a36Sopenharmony_ci u64 parent_total, 101662306a36Sopenharmony_ci print_callchain_entry_fn print, 101762306a36Sopenharmony_ci struct callchain_print_arg *arg, 101862306a36Sopenharmony_ci check_output_full_fn is_output_full) 101962306a36Sopenharmony_ci{ 102062306a36Sopenharmony_ci struct rb_node *node; 102162306a36Sopenharmony_ci int first_row = row, offset = LEVEL_OFFSET_STEP; 102262306a36Sopenharmony_ci bool need_percent; 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci node = rb_first(root); 102562306a36Sopenharmony_ci need_percent = check_percent_display(node, parent_total); 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci while (node) { 102862306a36Sopenharmony_ci struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); 102962306a36Sopenharmony_ci struct rb_node *next = rb_next(node); 103062306a36Sopenharmony_ci struct callchain_list *chain, *first_chain = NULL; 103162306a36Sopenharmony_ci int first = true; 103262306a36Sopenharmony_ci char *value_str = NULL, *value_str_alloc = NULL; 103362306a36Sopenharmony_ci char *chain_str = NULL, *chain_str_alloc = NULL; 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci if (arg->row_offset != 0) { 103662306a36Sopenharmony_ci arg->row_offset--; 103762306a36Sopenharmony_ci goto next; 103862306a36Sopenharmony_ci } 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci if (need_percent) { 104162306a36Sopenharmony_ci char buf[64]; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci callchain_node__scnprintf_value(child, buf, sizeof(buf), total); 104462306a36Sopenharmony_ci if (asprintf(&value_str, "%s", buf) < 0) { 104562306a36Sopenharmony_ci value_str = (char *)"<...>"; 104662306a36Sopenharmony_ci goto do_print; 104762306a36Sopenharmony_ci } 104862306a36Sopenharmony_ci value_str_alloc = value_str; 104962306a36Sopenharmony_ci } 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci list_for_each_entry(chain, &child->parent_val, list) { 105262306a36Sopenharmony_ci chain_str = hist_browser__folded_callchain_str(browser, 105362306a36Sopenharmony_ci chain, value_str, chain_str); 105462306a36Sopenharmony_ci if (first) { 105562306a36Sopenharmony_ci first = false; 105662306a36Sopenharmony_ci first_chain = chain; 105762306a36Sopenharmony_ci } 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci if (chain_str == NULL) { 106062306a36Sopenharmony_ci chain_str = (char *)"Not enough memory!"; 106162306a36Sopenharmony_ci goto do_print; 106262306a36Sopenharmony_ci } 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci chain_str_alloc = chain_str; 106562306a36Sopenharmony_ci } 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci list_for_each_entry(chain, &child->val, list) { 106862306a36Sopenharmony_ci chain_str = hist_browser__folded_callchain_str(browser, 106962306a36Sopenharmony_ci chain, value_str, chain_str); 107062306a36Sopenharmony_ci if (first) { 107162306a36Sopenharmony_ci first = false; 107262306a36Sopenharmony_ci first_chain = chain; 107362306a36Sopenharmony_ci } 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci if (chain_str == NULL) { 107662306a36Sopenharmony_ci chain_str = (char *)"Not enough memory!"; 107762306a36Sopenharmony_ci goto do_print; 107862306a36Sopenharmony_ci } 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci chain_str_alloc = chain_str; 108162306a36Sopenharmony_ci } 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_cido_print: 108462306a36Sopenharmony_ci print(browser, first_chain, chain_str, offset, row++, arg); 108562306a36Sopenharmony_ci free(value_str_alloc); 108662306a36Sopenharmony_ci free(chain_str_alloc); 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_cinext: 108962306a36Sopenharmony_ci if (is_output_full(browser, row)) 109062306a36Sopenharmony_ci break; 109162306a36Sopenharmony_ci node = next; 109262306a36Sopenharmony_ci } 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci return row - first_row; 109562306a36Sopenharmony_ci} 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_cistatic int hist_browser__show_callchain_graph(struct hist_browser *browser, 109862306a36Sopenharmony_ci struct rb_root *root, int level, 109962306a36Sopenharmony_ci unsigned short row, u64 total, 110062306a36Sopenharmony_ci u64 parent_total, 110162306a36Sopenharmony_ci print_callchain_entry_fn print, 110262306a36Sopenharmony_ci struct callchain_print_arg *arg, 110362306a36Sopenharmony_ci check_output_full_fn is_output_full) 110462306a36Sopenharmony_ci{ 110562306a36Sopenharmony_ci struct rb_node *node; 110662306a36Sopenharmony_ci int first_row = row, offset = level * LEVEL_OFFSET_STEP; 110762306a36Sopenharmony_ci bool need_percent; 110862306a36Sopenharmony_ci u64 percent_total = total; 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci if (callchain_param.mode == CHAIN_GRAPH_REL) 111162306a36Sopenharmony_ci percent_total = parent_total; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci node = rb_first(root); 111462306a36Sopenharmony_ci need_percent = check_percent_display(node, parent_total); 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci while (node) { 111762306a36Sopenharmony_ci struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); 111862306a36Sopenharmony_ci struct rb_node *next = rb_next(node); 111962306a36Sopenharmony_ci struct callchain_list *chain; 112062306a36Sopenharmony_ci char folded_sign = ' '; 112162306a36Sopenharmony_ci int first = true; 112262306a36Sopenharmony_ci int extra_offset = 0; 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci list_for_each_entry(chain, &child->val, list) { 112562306a36Sopenharmony_ci bool was_first = first; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci if (first) 112862306a36Sopenharmony_ci first = false; 112962306a36Sopenharmony_ci else if (need_percent) 113062306a36Sopenharmony_ci extra_offset = LEVEL_OFFSET_STEP; 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci folded_sign = callchain_list__folded(chain); 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci row += hist_browser__show_callchain_list(browser, child, 113562306a36Sopenharmony_ci chain, row, percent_total, 113662306a36Sopenharmony_ci was_first && need_percent, 113762306a36Sopenharmony_ci offset + extra_offset, 113862306a36Sopenharmony_ci print, arg); 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci if (is_output_full(browser, row)) 114162306a36Sopenharmony_ci goto out; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci if (folded_sign == '+') 114462306a36Sopenharmony_ci break; 114562306a36Sopenharmony_ci } 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci if (folded_sign == '-') { 114862306a36Sopenharmony_ci const int new_level = level + (extra_offset ? 2 : 1); 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci row += hist_browser__show_callchain_graph(browser, &child->rb_root, 115162306a36Sopenharmony_ci new_level, row, total, 115262306a36Sopenharmony_ci child->children_hit, 115362306a36Sopenharmony_ci print, arg, is_output_full); 115462306a36Sopenharmony_ci } 115562306a36Sopenharmony_ci if (is_output_full(browser, row)) 115662306a36Sopenharmony_ci break; 115762306a36Sopenharmony_ci node = next; 115862306a36Sopenharmony_ci } 115962306a36Sopenharmony_ciout: 116062306a36Sopenharmony_ci return row - first_row; 116162306a36Sopenharmony_ci} 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_cistatic int hist_browser__show_callchain(struct hist_browser *browser, 116462306a36Sopenharmony_ci struct hist_entry *entry, int level, 116562306a36Sopenharmony_ci unsigned short row, 116662306a36Sopenharmony_ci print_callchain_entry_fn print, 116762306a36Sopenharmony_ci struct callchain_print_arg *arg, 116862306a36Sopenharmony_ci check_output_full_fn is_output_full) 116962306a36Sopenharmony_ci{ 117062306a36Sopenharmony_ci u64 total = hists__total_period(entry->hists); 117162306a36Sopenharmony_ci u64 parent_total; 117262306a36Sopenharmony_ci int printed; 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci if (symbol_conf.cumulate_callchain) 117562306a36Sopenharmony_ci parent_total = entry->stat_acc->period; 117662306a36Sopenharmony_ci else 117762306a36Sopenharmony_ci parent_total = entry->stat.period; 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci if (callchain_param.mode == CHAIN_FLAT) { 118062306a36Sopenharmony_ci printed = hist_browser__show_callchain_flat(browser, 118162306a36Sopenharmony_ci &entry->sorted_chain, row, 118262306a36Sopenharmony_ci total, parent_total, print, arg, 118362306a36Sopenharmony_ci is_output_full); 118462306a36Sopenharmony_ci } else if (callchain_param.mode == CHAIN_FOLDED) { 118562306a36Sopenharmony_ci printed = hist_browser__show_callchain_folded(browser, 118662306a36Sopenharmony_ci &entry->sorted_chain, row, 118762306a36Sopenharmony_ci total, parent_total, print, arg, 118862306a36Sopenharmony_ci is_output_full); 118962306a36Sopenharmony_ci } else { 119062306a36Sopenharmony_ci printed = hist_browser__show_callchain_graph(browser, 119162306a36Sopenharmony_ci &entry->sorted_chain, level, row, 119262306a36Sopenharmony_ci total, parent_total, print, arg, 119362306a36Sopenharmony_ci is_output_full); 119462306a36Sopenharmony_ci } 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci if (arg->is_current_entry) 119762306a36Sopenharmony_ci browser->he_selection = entry; 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci return printed; 120062306a36Sopenharmony_ci} 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_cistruct hpp_arg { 120362306a36Sopenharmony_ci struct ui_browser *b; 120462306a36Sopenharmony_ci char folded_sign; 120562306a36Sopenharmony_ci bool current_entry; 120662306a36Sopenharmony_ci}; 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ciint __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...) 120962306a36Sopenharmony_ci{ 121062306a36Sopenharmony_ci struct hpp_arg *arg = hpp->ptr; 121162306a36Sopenharmony_ci int ret, len; 121262306a36Sopenharmony_ci va_list args; 121362306a36Sopenharmony_ci double percent; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci va_start(args, fmt); 121662306a36Sopenharmony_ci len = va_arg(args, int); 121762306a36Sopenharmony_ci percent = va_arg(args, double); 121862306a36Sopenharmony_ci va_end(args); 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci ui_browser__set_percent_color(arg->b, percent, arg->current_entry); 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent); 122362306a36Sopenharmony_ci ui_browser__printf(arg->b, "%s", hpp->buf); 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci return ret; 122662306a36Sopenharmony_ci} 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci#define __HPP_COLOR_PERCENT_FN(_type, _field) \ 122962306a36Sopenharmony_cistatic u64 __hpp_get_##_field(struct hist_entry *he) \ 123062306a36Sopenharmony_ci{ \ 123162306a36Sopenharmony_ci return he->stat._field; \ 123262306a36Sopenharmony_ci} \ 123362306a36Sopenharmony_ci \ 123462306a36Sopenharmony_cistatic int \ 123562306a36Sopenharmony_cihist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \ 123662306a36Sopenharmony_ci struct perf_hpp *hpp, \ 123762306a36Sopenharmony_ci struct hist_entry *he) \ 123862306a36Sopenharmony_ci{ \ 123962306a36Sopenharmony_ci return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \ 124062306a36Sopenharmony_ci __hpp__slsmg_color_printf, true); \ 124162306a36Sopenharmony_ci} 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \ 124462306a36Sopenharmony_cistatic u64 __hpp_get_acc_##_field(struct hist_entry *he) \ 124562306a36Sopenharmony_ci{ \ 124662306a36Sopenharmony_ci return he->stat_acc->_field; \ 124762306a36Sopenharmony_ci} \ 124862306a36Sopenharmony_ci \ 124962306a36Sopenharmony_cistatic int \ 125062306a36Sopenharmony_cihist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \ 125162306a36Sopenharmony_ci struct perf_hpp *hpp, \ 125262306a36Sopenharmony_ci struct hist_entry *he) \ 125362306a36Sopenharmony_ci{ \ 125462306a36Sopenharmony_ci if (!symbol_conf.cumulate_callchain) { \ 125562306a36Sopenharmony_ci struct hpp_arg *arg = hpp->ptr; \ 125662306a36Sopenharmony_ci int len = fmt->user_len ?: fmt->len; \ 125762306a36Sopenharmony_ci int ret = scnprintf(hpp->buf, hpp->size, \ 125862306a36Sopenharmony_ci "%*s", len, "N/A"); \ 125962306a36Sopenharmony_ci ui_browser__printf(arg->b, "%s", hpp->buf); \ 126062306a36Sopenharmony_ci \ 126162306a36Sopenharmony_ci return ret; \ 126262306a36Sopenharmony_ci } \ 126362306a36Sopenharmony_ci return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \ 126462306a36Sopenharmony_ci " %*.2f%%", __hpp__slsmg_color_printf, true); \ 126562306a36Sopenharmony_ci} 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci__HPP_COLOR_PERCENT_FN(overhead, period) 126862306a36Sopenharmony_ci__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys) 126962306a36Sopenharmony_ci__HPP_COLOR_PERCENT_FN(overhead_us, period_us) 127062306a36Sopenharmony_ci__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys) 127162306a36Sopenharmony_ci__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us) 127262306a36Sopenharmony_ci__HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period) 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci#undef __HPP_COLOR_PERCENT_FN 127562306a36Sopenharmony_ci#undef __HPP_COLOR_ACC_PERCENT_FN 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_civoid hist_browser__init_hpp(void) 127862306a36Sopenharmony_ci{ 127962306a36Sopenharmony_ci perf_hpp__format[PERF_HPP__OVERHEAD].color = 128062306a36Sopenharmony_ci hist_browser__hpp_color_overhead; 128162306a36Sopenharmony_ci perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color = 128262306a36Sopenharmony_ci hist_browser__hpp_color_overhead_sys; 128362306a36Sopenharmony_ci perf_hpp__format[PERF_HPP__OVERHEAD_US].color = 128462306a36Sopenharmony_ci hist_browser__hpp_color_overhead_us; 128562306a36Sopenharmony_ci perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color = 128662306a36Sopenharmony_ci hist_browser__hpp_color_overhead_guest_sys; 128762306a36Sopenharmony_ci perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color = 128862306a36Sopenharmony_ci hist_browser__hpp_color_overhead_guest_us; 128962306a36Sopenharmony_ci perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color = 129062306a36Sopenharmony_ci hist_browser__hpp_color_overhead_acc; 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci res_sample_init(); 129362306a36Sopenharmony_ci} 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_cistatic int hist_browser__show_entry(struct hist_browser *browser, 129662306a36Sopenharmony_ci struct hist_entry *entry, 129762306a36Sopenharmony_ci unsigned short row) 129862306a36Sopenharmony_ci{ 129962306a36Sopenharmony_ci int printed = 0; 130062306a36Sopenharmony_ci int width = browser->b.width; 130162306a36Sopenharmony_ci char folded_sign = ' '; 130262306a36Sopenharmony_ci bool current_entry = ui_browser__is_current_entry(&browser->b, row); 130362306a36Sopenharmony_ci bool use_callchain = hist_entry__has_callchains(entry) && symbol_conf.use_callchain; 130462306a36Sopenharmony_ci off_t row_offset = entry->row_offset; 130562306a36Sopenharmony_ci bool first = true; 130662306a36Sopenharmony_ci struct perf_hpp_fmt *fmt; 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci if (current_entry) { 130962306a36Sopenharmony_ci browser->he_selection = entry; 131062306a36Sopenharmony_ci browser->selection = &entry->ms; 131162306a36Sopenharmony_ci } 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci if (use_callchain) { 131462306a36Sopenharmony_ci hist_entry__init_have_children(entry); 131562306a36Sopenharmony_ci folded_sign = hist_entry__folded(entry); 131662306a36Sopenharmony_ci } 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci if (row_offset == 0) { 131962306a36Sopenharmony_ci struct hpp_arg arg = { 132062306a36Sopenharmony_ci .b = &browser->b, 132162306a36Sopenharmony_ci .folded_sign = folded_sign, 132262306a36Sopenharmony_ci .current_entry = current_entry, 132362306a36Sopenharmony_ci }; 132462306a36Sopenharmony_ci int column = 0; 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci ui_browser__gotorc(&browser->b, row, 0); 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci hists__for_each_format(browser->hists, fmt) { 132962306a36Sopenharmony_ci char s[2048]; 133062306a36Sopenharmony_ci struct perf_hpp hpp = { 133162306a36Sopenharmony_ci .buf = s, 133262306a36Sopenharmony_ci .size = sizeof(s), 133362306a36Sopenharmony_ci .ptr = &arg, 133462306a36Sopenharmony_ci }; 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci if (perf_hpp__should_skip(fmt, entry->hists) || 133762306a36Sopenharmony_ci column++ < browser->b.horiz_scroll) 133862306a36Sopenharmony_ci continue; 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci if (current_entry && browser->b.navkeypressed) { 134162306a36Sopenharmony_ci ui_browser__set_color(&browser->b, 134262306a36Sopenharmony_ci HE_COLORSET_SELECTED); 134362306a36Sopenharmony_ci } else { 134462306a36Sopenharmony_ci ui_browser__set_color(&browser->b, 134562306a36Sopenharmony_ci HE_COLORSET_NORMAL); 134662306a36Sopenharmony_ci } 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci if (first) { 134962306a36Sopenharmony_ci if (use_callchain) { 135062306a36Sopenharmony_ci ui_browser__printf(&browser->b, "%c ", folded_sign); 135162306a36Sopenharmony_ci width -= 2; 135262306a36Sopenharmony_ci } 135362306a36Sopenharmony_ci first = false; 135462306a36Sopenharmony_ci } else { 135562306a36Sopenharmony_ci ui_browser__printf(&browser->b, " "); 135662306a36Sopenharmony_ci width -= 2; 135762306a36Sopenharmony_ci } 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci if (fmt->color) { 136062306a36Sopenharmony_ci int ret = fmt->color(fmt, &hpp, entry); 136162306a36Sopenharmony_ci hist_entry__snprintf_alignment(entry, &hpp, fmt, ret); 136262306a36Sopenharmony_ci /* 136362306a36Sopenharmony_ci * fmt->color() already used ui_browser to 136462306a36Sopenharmony_ci * print the non alignment bits, skip it (+ret): 136562306a36Sopenharmony_ci */ 136662306a36Sopenharmony_ci ui_browser__printf(&browser->b, "%s", s + ret); 136762306a36Sopenharmony_ci } else { 136862306a36Sopenharmony_ci hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry)); 136962306a36Sopenharmony_ci ui_browser__printf(&browser->b, "%s", s); 137062306a36Sopenharmony_ci } 137162306a36Sopenharmony_ci width -= hpp.buf - s; 137262306a36Sopenharmony_ci } 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci /* The scroll bar isn't being used */ 137562306a36Sopenharmony_ci if (!browser->b.navkeypressed) 137662306a36Sopenharmony_ci width += 1; 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci ui_browser__write_nstring(&browser->b, "", width); 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci ++row; 138162306a36Sopenharmony_ci ++printed; 138262306a36Sopenharmony_ci } else 138362306a36Sopenharmony_ci --row_offset; 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci if (folded_sign == '-' && row != browser->b.rows) { 138662306a36Sopenharmony_ci struct callchain_print_arg arg = { 138762306a36Sopenharmony_ci .row_offset = row_offset, 138862306a36Sopenharmony_ci .is_current_entry = current_entry, 138962306a36Sopenharmony_ci }; 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci printed += hist_browser__show_callchain(browser, 139262306a36Sopenharmony_ci entry, 1, row, 139362306a36Sopenharmony_ci hist_browser__show_callchain_entry, 139462306a36Sopenharmony_ci &arg, 139562306a36Sopenharmony_ci hist_browser__check_output_full); 139662306a36Sopenharmony_ci } 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci return printed; 139962306a36Sopenharmony_ci} 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_cistatic int hist_browser__show_hierarchy_entry(struct hist_browser *browser, 140262306a36Sopenharmony_ci struct hist_entry *entry, 140362306a36Sopenharmony_ci unsigned short row, 140462306a36Sopenharmony_ci int level) 140562306a36Sopenharmony_ci{ 140662306a36Sopenharmony_ci int printed = 0; 140762306a36Sopenharmony_ci int width = browser->b.width; 140862306a36Sopenharmony_ci char folded_sign = ' '; 140962306a36Sopenharmony_ci bool current_entry = ui_browser__is_current_entry(&browser->b, row); 141062306a36Sopenharmony_ci off_t row_offset = entry->row_offset; 141162306a36Sopenharmony_ci bool first = true; 141262306a36Sopenharmony_ci struct perf_hpp_fmt *fmt; 141362306a36Sopenharmony_ci struct perf_hpp_list_node *fmt_node; 141462306a36Sopenharmony_ci struct hpp_arg arg = { 141562306a36Sopenharmony_ci .b = &browser->b, 141662306a36Sopenharmony_ci .current_entry = current_entry, 141762306a36Sopenharmony_ci }; 141862306a36Sopenharmony_ci int column = 0; 141962306a36Sopenharmony_ci int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT; 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci if (current_entry) { 142262306a36Sopenharmony_ci browser->he_selection = entry; 142362306a36Sopenharmony_ci browser->selection = &entry->ms; 142462306a36Sopenharmony_ci } 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci hist_entry__init_have_children(entry); 142762306a36Sopenharmony_ci folded_sign = hist_entry__folded(entry); 142862306a36Sopenharmony_ci arg.folded_sign = folded_sign; 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci if (entry->leaf && row_offset) { 143162306a36Sopenharmony_ci row_offset--; 143262306a36Sopenharmony_ci goto show_callchain; 143362306a36Sopenharmony_ci } 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci ui_browser__gotorc(&browser->b, row, 0); 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci if (current_entry && browser->b.navkeypressed) 143862306a36Sopenharmony_ci ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED); 143962306a36Sopenharmony_ci else 144062306a36Sopenharmony_ci ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL); 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT); 144362306a36Sopenharmony_ci width -= level * HIERARCHY_INDENT; 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci /* the first hpp_list_node is for overhead columns */ 144662306a36Sopenharmony_ci fmt_node = list_first_entry(&entry->hists->hpp_formats, 144762306a36Sopenharmony_ci struct perf_hpp_list_node, list); 144862306a36Sopenharmony_ci perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { 144962306a36Sopenharmony_ci char s[2048]; 145062306a36Sopenharmony_ci struct perf_hpp hpp = { 145162306a36Sopenharmony_ci .buf = s, 145262306a36Sopenharmony_ci .size = sizeof(s), 145362306a36Sopenharmony_ci .ptr = &arg, 145462306a36Sopenharmony_ci }; 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci if (perf_hpp__should_skip(fmt, entry->hists) || 145762306a36Sopenharmony_ci column++ < browser->b.horiz_scroll) 145862306a36Sopenharmony_ci continue; 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci if (current_entry && browser->b.navkeypressed) { 146162306a36Sopenharmony_ci ui_browser__set_color(&browser->b, 146262306a36Sopenharmony_ci HE_COLORSET_SELECTED); 146362306a36Sopenharmony_ci } else { 146462306a36Sopenharmony_ci ui_browser__set_color(&browser->b, 146562306a36Sopenharmony_ci HE_COLORSET_NORMAL); 146662306a36Sopenharmony_ci } 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci if (first) { 146962306a36Sopenharmony_ci ui_browser__printf(&browser->b, "%c ", folded_sign); 147062306a36Sopenharmony_ci width -= 2; 147162306a36Sopenharmony_ci first = false; 147262306a36Sopenharmony_ci } else { 147362306a36Sopenharmony_ci ui_browser__printf(&browser->b, " "); 147462306a36Sopenharmony_ci width -= 2; 147562306a36Sopenharmony_ci } 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci if (fmt->color) { 147862306a36Sopenharmony_ci int ret = fmt->color(fmt, &hpp, entry); 147962306a36Sopenharmony_ci hist_entry__snprintf_alignment(entry, &hpp, fmt, ret); 148062306a36Sopenharmony_ci /* 148162306a36Sopenharmony_ci * fmt->color() already used ui_browser to 148262306a36Sopenharmony_ci * print the non alignment bits, skip it (+ret): 148362306a36Sopenharmony_ci */ 148462306a36Sopenharmony_ci ui_browser__printf(&browser->b, "%s", s + ret); 148562306a36Sopenharmony_ci } else { 148662306a36Sopenharmony_ci int ret = fmt->entry(fmt, &hpp, entry); 148762306a36Sopenharmony_ci hist_entry__snprintf_alignment(entry, &hpp, fmt, ret); 148862306a36Sopenharmony_ci ui_browser__printf(&browser->b, "%s", s); 148962306a36Sopenharmony_ci } 149062306a36Sopenharmony_ci width -= hpp.buf - s; 149162306a36Sopenharmony_ci } 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_ci if (!first) { 149462306a36Sopenharmony_ci ui_browser__write_nstring(&browser->b, "", hierarchy_indent); 149562306a36Sopenharmony_ci width -= hierarchy_indent; 149662306a36Sopenharmony_ci } 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci if (column >= browser->b.horiz_scroll) { 149962306a36Sopenharmony_ci char s[2048]; 150062306a36Sopenharmony_ci struct perf_hpp hpp = { 150162306a36Sopenharmony_ci .buf = s, 150262306a36Sopenharmony_ci .size = sizeof(s), 150362306a36Sopenharmony_ci .ptr = &arg, 150462306a36Sopenharmony_ci }; 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci if (current_entry && browser->b.navkeypressed) { 150762306a36Sopenharmony_ci ui_browser__set_color(&browser->b, 150862306a36Sopenharmony_ci HE_COLORSET_SELECTED); 150962306a36Sopenharmony_ci } else { 151062306a36Sopenharmony_ci ui_browser__set_color(&browser->b, 151162306a36Sopenharmony_ci HE_COLORSET_NORMAL); 151262306a36Sopenharmony_ci } 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci perf_hpp_list__for_each_format(entry->hpp_list, fmt) { 151562306a36Sopenharmony_ci if (first) { 151662306a36Sopenharmony_ci ui_browser__printf(&browser->b, "%c ", folded_sign); 151762306a36Sopenharmony_ci first = false; 151862306a36Sopenharmony_ci } else { 151962306a36Sopenharmony_ci ui_browser__write_nstring(&browser->b, "", 2); 152062306a36Sopenharmony_ci } 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci width -= 2; 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci /* 152562306a36Sopenharmony_ci * No need to call hist_entry__snprintf_alignment() 152662306a36Sopenharmony_ci * since this fmt is always the last column in the 152762306a36Sopenharmony_ci * hierarchy mode. 152862306a36Sopenharmony_ci */ 152962306a36Sopenharmony_ci if (fmt->color) { 153062306a36Sopenharmony_ci width -= fmt->color(fmt, &hpp, entry); 153162306a36Sopenharmony_ci } else { 153262306a36Sopenharmony_ci int i = 0; 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci width -= fmt->entry(fmt, &hpp, entry); 153562306a36Sopenharmony_ci ui_browser__printf(&browser->b, "%s", skip_spaces(s)); 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci while (isspace(s[i++])) 153862306a36Sopenharmony_ci width++; 153962306a36Sopenharmony_ci } 154062306a36Sopenharmony_ci } 154162306a36Sopenharmony_ci } 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci /* The scroll bar isn't being used */ 154462306a36Sopenharmony_ci if (!browser->b.navkeypressed) 154562306a36Sopenharmony_ci width += 1; 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci ui_browser__write_nstring(&browser->b, "", width); 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci ++row; 155062306a36Sopenharmony_ci ++printed; 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_cishow_callchain: 155362306a36Sopenharmony_ci if (entry->leaf && folded_sign == '-' && row != browser->b.rows) { 155462306a36Sopenharmony_ci struct callchain_print_arg carg = { 155562306a36Sopenharmony_ci .row_offset = row_offset, 155662306a36Sopenharmony_ci }; 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci printed += hist_browser__show_callchain(browser, entry, 155962306a36Sopenharmony_ci level + 1, row, 156062306a36Sopenharmony_ci hist_browser__show_callchain_entry, &carg, 156162306a36Sopenharmony_ci hist_browser__check_output_full); 156262306a36Sopenharmony_ci } 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci return printed; 156562306a36Sopenharmony_ci} 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_cistatic int hist_browser__show_no_entry(struct hist_browser *browser, 156862306a36Sopenharmony_ci unsigned short row, int level) 156962306a36Sopenharmony_ci{ 157062306a36Sopenharmony_ci int width = browser->b.width; 157162306a36Sopenharmony_ci bool current_entry = ui_browser__is_current_entry(&browser->b, row); 157262306a36Sopenharmony_ci bool first = true; 157362306a36Sopenharmony_ci int column = 0; 157462306a36Sopenharmony_ci int ret; 157562306a36Sopenharmony_ci struct perf_hpp_fmt *fmt; 157662306a36Sopenharmony_ci struct perf_hpp_list_node *fmt_node; 157762306a36Sopenharmony_ci int indent = browser->hists->nr_hpp_node - 2; 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci if (current_entry) { 158062306a36Sopenharmony_ci browser->he_selection = NULL; 158162306a36Sopenharmony_ci browser->selection = NULL; 158262306a36Sopenharmony_ci } 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci ui_browser__gotorc(&browser->b, row, 0); 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci if (current_entry && browser->b.navkeypressed) 158762306a36Sopenharmony_ci ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED); 158862306a36Sopenharmony_ci else 158962306a36Sopenharmony_ci ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL); 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT); 159262306a36Sopenharmony_ci width -= level * HIERARCHY_INDENT; 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci /* the first hpp_list_node is for overhead columns */ 159562306a36Sopenharmony_ci fmt_node = list_first_entry(&browser->hists->hpp_formats, 159662306a36Sopenharmony_ci struct perf_hpp_list_node, list); 159762306a36Sopenharmony_ci perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { 159862306a36Sopenharmony_ci if (perf_hpp__should_skip(fmt, browser->hists) || 159962306a36Sopenharmony_ci column++ < browser->b.horiz_scroll) 160062306a36Sopenharmony_ci continue; 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci ret = fmt->width(fmt, NULL, browser->hists); 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci if (first) { 160562306a36Sopenharmony_ci /* for folded sign */ 160662306a36Sopenharmony_ci first = false; 160762306a36Sopenharmony_ci ret++; 160862306a36Sopenharmony_ci } else { 160962306a36Sopenharmony_ci /* space between columns */ 161062306a36Sopenharmony_ci ret += 2; 161162306a36Sopenharmony_ci } 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci ui_browser__write_nstring(&browser->b, "", ret); 161462306a36Sopenharmony_ci width -= ret; 161562306a36Sopenharmony_ci } 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT); 161862306a36Sopenharmony_ci width -= indent * HIERARCHY_INDENT; 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci if (column >= browser->b.horiz_scroll) { 162162306a36Sopenharmony_ci char buf[32]; 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt); 162462306a36Sopenharmony_ci ui_browser__printf(&browser->b, " %s", buf); 162562306a36Sopenharmony_ci width -= ret + 2; 162662306a36Sopenharmony_ci } 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci /* The scroll bar isn't being used */ 162962306a36Sopenharmony_ci if (!browser->b.navkeypressed) 163062306a36Sopenharmony_ci width += 1; 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci ui_browser__write_nstring(&browser->b, "", width); 163362306a36Sopenharmony_ci return 1; 163462306a36Sopenharmony_ci} 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_cistatic int advance_hpp_check(struct perf_hpp *hpp, int inc) 163762306a36Sopenharmony_ci{ 163862306a36Sopenharmony_ci advance_hpp(hpp, inc); 163962306a36Sopenharmony_ci return hpp->size <= 0; 164062306a36Sopenharmony_ci} 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_cistatic int 164362306a36Sopenharmony_cihists_browser__scnprintf_headers(struct hist_browser *browser, char *buf, 164462306a36Sopenharmony_ci size_t size, int line) 164562306a36Sopenharmony_ci{ 164662306a36Sopenharmony_ci struct hists *hists = browser->hists; 164762306a36Sopenharmony_ci struct perf_hpp dummy_hpp = { 164862306a36Sopenharmony_ci .buf = buf, 164962306a36Sopenharmony_ci .size = size, 165062306a36Sopenharmony_ci }; 165162306a36Sopenharmony_ci struct perf_hpp_fmt *fmt; 165262306a36Sopenharmony_ci size_t ret = 0; 165362306a36Sopenharmony_ci int column = 0; 165462306a36Sopenharmony_ci int span = 0; 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_ci if (hists__has_callchains(hists) && symbol_conf.use_callchain) { 165762306a36Sopenharmony_ci ret = scnprintf(buf, size, " "); 165862306a36Sopenharmony_ci if (advance_hpp_check(&dummy_hpp, ret)) 165962306a36Sopenharmony_ci return ret; 166062306a36Sopenharmony_ci } 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_ci hists__for_each_format(browser->hists, fmt) { 166362306a36Sopenharmony_ci if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll) 166462306a36Sopenharmony_ci continue; 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ci ret = fmt->header(fmt, &dummy_hpp, hists, line, &span); 166762306a36Sopenharmony_ci if (advance_hpp_check(&dummy_hpp, ret)) 166862306a36Sopenharmony_ci break; 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci if (span) 167162306a36Sopenharmony_ci continue; 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_ci ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " "); 167462306a36Sopenharmony_ci if (advance_hpp_check(&dummy_hpp, ret)) 167562306a36Sopenharmony_ci break; 167662306a36Sopenharmony_ci } 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci return ret; 167962306a36Sopenharmony_ci} 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_cistatic int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size) 168262306a36Sopenharmony_ci{ 168362306a36Sopenharmony_ci struct hists *hists = browser->hists; 168462306a36Sopenharmony_ci struct perf_hpp dummy_hpp = { 168562306a36Sopenharmony_ci .buf = buf, 168662306a36Sopenharmony_ci .size = size, 168762306a36Sopenharmony_ci }; 168862306a36Sopenharmony_ci struct perf_hpp_fmt *fmt; 168962306a36Sopenharmony_ci struct perf_hpp_list_node *fmt_node; 169062306a36Sopenharmony_ci size_t ret = 0; 169162306a36Sopenharmony_ci int column = 0; 169262306a36Sopenharmony_ci int indent = hists->nr_hpp_node - 2; 169362306a36Sopenharmony_ci bool first_node, first_col; 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci ret = scnprintf(buf, size, " "); 169662306a36Sopenharmony_ci if (advance_hpp_check(&dummy_hpp, ret)) 169762306a36Sopenharmony_ci return ret; 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci first_node = true; 170062306a36Sopenharmony_ci /* the first hpp_list_node is for overhead columns */ 170162306a36Sopenharmony_ci fmt_node = list_first_entry(&hists->hpp_formats, 170262306a36Sopenharmony_ci struct perf_hpp_list_node, list); 170362306a36Sopenharmony_ci perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { 170462306a36Sopenharmony_ci if (column++ < browser->b.horiz_scroll) 170562306a36Sopenharmony_ci continue; 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL); 170862306a36Sopenharmony_ci if (advance_hpp_check(&dummy_hpp, ret)) 170962306a36Sopenharmony_ci break; 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_ci ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " "); 171262306a36Sopenharmony_ci if (advance_hpp_check(&dummy_hpp, ret)) 171362306a36Sopenharmony_ci break; 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci first_node = false; 171662306a36Sopenharmony_ci } 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_ci if (!first_node) { 171962306a36Sopenharmony_ci ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s", 172062306a36Sopenharmony_ci indent * HIERARCHY_INDENT, ""); 172162306a36Sopenharmony_ci if (advance_hpp_check(&dummy_hpp, ret)) 172262306a36Sopenharmony_ci return ret; 172362306a36Sopenharmony_ci } 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_ci first_node = true; 172662306a36Sopenharmony_ci list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) { 172762306a36Sopenharmony_ci if (!first_node) { 172862306a36Sopenharmony_ci ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / "); 172962306a36Sopenharmony_ci if (advance_hpp_check(&dummy_hpp, ret)) 173062306a36Sopenharmony_ci break; 173162306a36Sopenharmony_ci } 173262306a36Sopenharmony_ci first_node = false; 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_ci first_col = true; 173562306a36Sopenharmony_ci perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { 173662306a36Sopenharmony_ci char *start; 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_ci if (perf_hpp__should_skip(fmt, hists)) 173962306a36Sopenharmony_ci continue; 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci if (!first_col) { 174262306a36Sopenharmony_ci ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+"); 174362306a36Sopenharmony_ci if (advance_hpp_check(&dummy_hpp, ret)) 174462306a36Sopenharmony_ci break; 174562306a36Sopenharmony_ci } 174662306a36Sopenharmony_ci first_col = false; 174762306a36Sopenharmony_ci 174862306a36Sopenharmony_ci ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL); 174962306a36Sopenharmony_ci dummy_hpp.buf[ret] = '\0'; 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_ci start = strim(dummy_hpp.buf); 175262306a36Sopenharmony_ci ret = strlen(start); 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_ci if (start != dummy_hpp.buf) 175562306a36Sopenharmony_ci memmove(dummy_hpp.buf, start, ret + 1); 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci if (advance_hpp_check(&dummy_hpp, ret)) 175862306a36Sopenharmony_ci break; 175962306a36Sopenharmony_ci } 176062306a36Sopenharmony_ci } 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_ci return ret; 176362306a36Sopenharmony_ci} 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_cistatic void hists_browser__hierarchy_headers(struct hist_browser *browser) 176662306a36Sopenharmony_ci{ 176762306a36Sopenharmony_ci char headers[1024]; 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ci hists_browser__scnprintf_hierarchy_headers(browser, headers, 177062306a36Sopenharmony_ci sizeof(headers)); 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci ui_browser__gotorc_title(&browser->b, 0, 0); 177362306a36Sopenharmony_ci ui_browser__set_color(&browser->b, HE_COLORSET_ROOT); 177462306a36Sopenharmony_ci ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1); 177562306a36Sopenharmony_ci} 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_cistatic void hists_browser__headers(struct hist_browser *browser) 177862306a36Sopenharmony_ci{ 177962306a36Sopenharmony_ci struct hists *hists = browser->hists; 178062306a36Sopenharmony_ci struct perf_hpp_list *hpp_list = hists->hpp_list; 178162306a36Sopenharmony_ci 178262306a36Sopenharmony_ci int line; 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ci for (line = 0; line < hpp_list->nr_header_lines; line++) { 178562306a36Sopenharmony_ci char headers[1024]; 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_ci hists_browser__scnprintf_headers(browser, headers, 178862306a36Sopenharmony_ci sizeof(headers), line); 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci ui_browser__gotorc_title(&browser->b, line, 0); 179162306a36Sopenharmony_ci ui_browser__set_color(&browser->b, HE_COLORSET_ROOT); 179262306a36Sopenharmony_ci ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1); 179362306a36Sopenharmony_ci } 179462306a36Sopenharmony_ci} 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_cistatic void hist_browser__show_headers(struct hist_browser *browser) 179762306a36Sopenharmony_ci{ 179862306a36Sopenharmony_ci if (symbol_conf.report_hierarchy) 179962306a36Sopenharmony_ci hists_browser__hierarchy_headers(browser); 180062306a36Sopenharmony_ci else 180162306a36Sopenharmony_ci hists_browser__headers(browser); 180262306a36Sopenharmony_ci} 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_cistatic void ui_browser__hists_init_top(struct ui_browser *browser) 180562306a36Sopenharmony_ci{ 180662306a36Sopenharmony_ci if (browser->top == NULL) { 180762306a36Sopenharmony_ci struct hist_browser *hb; 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci hb = container_of(browser, struct hist_browser, b); 181062306a36Sopenharmony_ci browser->top = rb_first_cached(&hb->hists->entries); 181162306a36Sopenharmony_ci } 181262306a36Sopenharmony_ci} 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_cistatic unsigned int hist_browser__refresh(struct ui_browser *browser) 181562306a36Sopenharmony_ci{ 181662306a36Sopenharmony_ci unsigned row = 0; 181762306a36Sopenharmony_ci struct rb_node *nd; 181862306a36Sopenharmony_ci struct hist_browser *hb = container_of(browser, struct hist_browser, b); 181962306a36Sopenharmony_ci 182062306a36Sopenharmony_ci if (hb->show_headers) 182162306a36Sopenharmony_ci hist_browser__show_headers(hb); 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_ci ui_browser__hists_init_top(browser); 182462306a36Sopenharmony_ci hb->he_selection = NULL; 182562306a36Sopenharmony_ci hb->selection = NULL; 182662306a36Sopenharmony_ci 182762306a36Sopenharmony_ci for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) { 182862306a36Sopenharmony_ci struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 182962306a36Sopenharmony_ci float percent; 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_ci if (h->filtered) { 183262306a36Sopenharmony_ci /* let it move to sibling */ 183362306a36Sopenharmony_ci h->unfolded = false; 183462306a36Sopenharmony_ci continue; 183562306a36Sopenharmony_ci } 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci if (symbol_conf.report_individual_block) 183862306a36Sopenharmony_ci percent = block_info__total_cycles_percent(h); 183962306a36Sopenharmony_ci else 184062306a36Sopenharmony_ci percent = hist_entry__get_percent_limit(h); 184162306a36Sopenharmony_ci 184262306a36Sopenharmony_ci if (percent < hb->min_pcnt) 184362306a36Sopenharmony_ci continue; 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci if (symbol_conf.report_hierarchy) { 184662306a36Sopenharmony_ci row += hist_browser__show_hierarchy_entry(hb, h, row, 184762306a36Sopenharmony_ci h->depth); 184862306a36Sopenharmony_ci if (row == browser->rows) 184962306a36Sopenharmony_ci break; 185062306a36Sopenharmony_ci 185162306a36Sopenharmony_ci if (h->has_no_entry) { 185262306a36Sopenharmony_ci hist_browser__show_no_entry(hb, row, h->depth + 1); 185362306a36Sopenharmony_ci row++; 185462306a36Sopenharmony_ci } 185562306a36Sopenharmony_ci } else { 185662306a36Sopenharmony_ci row += hist_browser__show_entry(hb, h, row); 185762306a36Sopenharmony_ci } 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_ci if (row == browser->rows) 186062306a36Sopenharmony_ci break; 186162306a36Sopenharmony_ci } 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_ci return row; 186462306a36Sopenharmony_ci} 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_cistatic struct rb_node *hists__filter_entries(struct rb_node *nd, 186762306a36Sopenharmony_ci float min_pcnt) 186862306a36Sopenharmony_ci{ 186962306a36Sopenharmony_ci while (nd != NULL) { 187062306a36Sopenharmony_ci struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 187162306a36Sopenharmony_ci float percent = hist_entry__get_percent_limit(h); 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_ci if (!h->filtered && percent >= min_pcnt) 187462306a36Sopenharmony_ci return nd; 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_ci /* 187762306a36Sopenharmony_ci * If it's filtered, its all children also were filtered. 187862306a36Sopenharmony_ci * So move to sibling node. 187962306a36Sopenharmony_ci */ 188062306a36Sopenharmony_ci if (rb_next(nd)) 188162306a36Sopenharmony_ci nd = rb_next(nd); 188262306a36Sopenharmony_ci else 188362306a36Sopenharmony_ci nd = rb_hierarchy_next(nd); 188462306a36Sopenharmony_ci } 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci return NULL; 188762306a36Sopenharmony_ci} 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_cistatic struct rb_node *hists__filter_prev_entries(struct rb_node *nd, 189062306a36Sopenharmony_ci float min_pcnt) 189162306a36Sopenharmony_ci{ 189262306a36Sopenharmony_ci while (nd != NULL) { 189362306a36Sopenharmony_ci struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 189462306a36Sopenharmony_ci float percent = hist_entry__get_percent_limit(h); 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_ci if (!h->filtered && percent >= min_pcnt) 189762306a36Sopenharmony_ci return nd; 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci nd = rb_hierarchy_prev(nd); 190062306a36Sopenharmony_ci } 190162306a36Sopenharmony_ci 190262306a36Sopenharmony_ci return NULL; 190362306a36Sopenharmony_ci} 190462306a36Sopenharmony_ci 190562306a36Sopenharmony_cistatic void ui_browser__hists_seek(struct ui_browser *browser, 190662306a36Sopenharmony_ci off_t offset, int whence) 190762306a36Sopenharmony_ci{ 190862306a36Sopenharmony_ci struct hist_entry *h; 190962306a36Sopenharmony_ci struct rb_node *nd; 191062306a36Sopenharmony_ci bool first = true; 191162306a36Sopenharmony_ci struct hist_browser *hb; 191262306a36Sopenharmony_ci 191362306a36Sopenharmony_ci hb = container_of(browser, struct hist_browser, b); 191462306a36Sopenharmony_ci 191562306a36Sopenharmony_ci if (browser->nr_entries == 0) 191662306a36Sopenharmony_ci return; 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ci ui_browser__hists_init_top(browser); 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci switch (whence) { 192162306a36Sopenharmony_ci case SEEK_SET: 192262306a36Sopenharmony_ci nd = hists__filter_entries(rb_first(browser->entries), 192362306a36Sopenharmony_ci hb->min_pcnt); 192462306a36Sopenharmony_ci break; 192562306a36Sopenharmony_ci case SEEK_CUR: 192662306a36Sopenharmony_ci nd = browser->top; 192762306a36Sopenharmony_ci goto do_offset; 192862306a36Sopenharmony_ci case SEEK_END: 192962306a36Sopenharmony_ci nd = rb_hierarchy_last(rb_last(browser->entries)); 193062306a36Sopenharmony_ci nd = hists__filter_prev_entries(nd, hb->min_pcnt); 193162306a36Sopenharmony_ci first = false; 193262306a36Sopenharmony_ci break; 193362306a36Sopenharmony_ci default: 193462306a36Sopenharmony_ci return; 193562306a36Sopenharmony_ci } 193662306a36Sopenharmony_ci 193762306a36Sopenharmony_ci /* 193862306a36Sopenharmony_ci * Moves not relative to the first visible entry invalidates its 193962306a36Sopenharmony_ci * row_offset: 194062306a36Sopenharmony_ci */ 194162306a36Sopenharmony_ci h = rb_entry(browser->top, struct hist_entry, rb_node); 194262306a36Sopenharmony_ci h->row_offset = 0; 194362306a36Sopenharmony_ci 194462306a36Sopenharmony_ci /* 194562306a36Sopenharmony_ci * Here we have to check if nd is expanded (+), if it is we can't go 194662306a36Sopenharmony_ci * the next top level hist_entry, instead we must compute an offset of 194762306a36Sopenharmony_ci * what _not_ to show and not change the first visible entry. 194862306a36Sopenharmony_ci * 194962306a36Sopenharmony_ci * This offset increments when we are going from top to bottom and 195062306a36Sopenharmony_ci * decreases when we're going from bottom to top. 195162306a36Sopenharmony_ci * 195262306a36Sopenharmony_ci * As we don't have backpointers to the top level in the callchains 195362306a36Sopenharmony_ci * structure, we need to always print the whole hist_entry callchain, 195462306a36Sopenharmony_ci * skipping the first ones that are before the first visible entry 195562306a36Sopenharmony_ci * and stop when we printed enough lines to fill the screen. 195662306a36Sopenharmony_ci */ 195762306a36Sopenharmony_cido_offset: 195862306a36Sopenharmony_ci if (!nd) 195962306a36Sopenharmony_ci return; 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_ci if (offset > 0) { 196262306a36Sopenharmony_ci do { 196362306a36Sopenharmony_ci h = rb_entry(nd, struct hist_entry, rb_node); 196462306a36Sopenharmony_ci if (h->unfolded && h->leaf) { 196562306a36Sopenharmony_ci u16 remaining = h->nr_rows - h->row_offset; 196662306a36Sopenharmony_ci if (offset > remaining) { 196762306a36Sopenharmony_ci offset -= remaining; 196862306a36Sopenharmony_ci h->row_offset = 0; 196962306a36Sopenharmony_ci } else { 197062306a36Sopenharmony_ci h->row_offset += offset; 197162306a36Sopenharmony_ci offset = 0; 197262306a36Sopenharmony_ci browser->top = nd; 197362306a36Sopenharmony_ci break; 197462306a36Sopenharmony_ci } 197562306a36Sopenharmony_ci } 197662306a36Sopenharmony_ci nd = hists__filter_entries(rb_hierarchy_next(nd), 197762306a36Sopenharmony_ci hb->min_pcnt); 197862306a36Sopenharmony_ci if (nd == NULL) 197962306a36Sopenharmony_ci break; 198062306a36Sopenharmony_ci --offset; 198162306a36Sopenharmony_ci browser->top = nd; 198262306a36Sopenharmony_ci } while (offset != 0); 198362306a36Sopenharmony_ci } else if (offset < 0) { 198462306a36Sopenharmony_ci while (1) { 198562306a36Sopenharmony_ci h = rb_entry(nd, struct hist_entry, rb_node); 198662306a36Sopenharmony_ci if (h->unfolded && h->leaf) { 198762306a36Sopenharmony_ci if (first) { 198862306a36Sopenharmony_ci if (-offset > h->row_offset) { 198962306a36Sopenharmony_ci offset += h->row_offset; 199062306a36Sopenharmony_ci h->row_offset = 0; 199162306a36Sopenharmony_ci } else { 199262306a36Sopenharmony_ci h->row_offset += offset; 199362306a36Sopenharmony_ci offset = 0; 199462306a36Sopenharmony_ci browser->top = nd; 199562306a36Sopenharmony_ci break; 199662306a36Sopenharmony_ci } 199762306a36Sopenharmony_ci } else { 199862306a36Sopenharmony_ci if (-offset > h->nr_rows) { 199962306a36Sopenharmony_ci offset += h->nr_rows; 200062306a36Sopenharmony_ci h->row_offset = 0; 200162306a36Sopenharmony_ci } else { 200262306a36Sopenharmony_ci h->row_offset = h->nr_rows + offset; 200362306a36Sopenharmony_ci offset = 0; 200462306a36Sopenharmony_ci browser->top = nd; 200562306a36Sopenharmony_ci break; 200662306a36Sopenharmony_ci } 200762306a36Sopenharmony_ci } 200862306a36Sopenharmony_ci } 200962306a36Sopenharmony_ci 201062306a36Sopenharmony_ci nd = hists__filter_prev_entries(rb_hierarchy_prev(nd), 201162306a36Sopenharmony_ci hb->min_pcnt); 201262306a36Sopenharmony_ci if (nd == NULL) 201362306a36Sopenharmony_ci break; 201462306a36Sopenharmony_ci ++offset; 201562306a36Sopenharmony_ci browser->top = nd; 201662306a36Sopenharmony_ci if (offset == 0) { 201762306a36Sopenharmony_ci /* 201862306a36Sopenharmony_ci * Last unfiltered hist_entry, check if it is 201962306a36Sopenharmony_ci * unfolded, if it is then we should have 202062306a36Sopenharmony_ci * row_offset at its last entry. 202162306a36Sopenharmony_ci */ 202262306a36Sopenharmony_ci h = rb_entry(nd, struct hist_entry, rb_node); 202362306a36Sopenharmony_ci if (h->unfolded && h->leaf) 202462306a36Sopenharmony_ci h->row_offset = h->nr_rows; 202562306a36Sopenharmony_ci break; 202662306a36Sopenharmony_ci } 202762306a36Sopenharmony_ci first = false; 202862306a36Sopenharmony_ci } 202962306a36Sopenharmony_ci } else { 203062306a36Sopenharmony_ci browser->top = nd; 203162306a36Sopenharmony_ci h = rb_entry(nd, struct hist_entry, rb_node); 203262306a36Sopenharmony_ci h->row_offset = 0; 203362306a36Sopenharmony_ci } 203462306a36Sopenharmony_ci} 203562306a36Sopenharmony_ci 203662306a36Sopenharmony_cistatic int hist_browser__fprintf_callchain(struct hist_browser *browser, 203762306a36Sopenharmony_ci struct hist_entry *he, FILE *fp, 203862306a36Sopenharmony_ci int level) 203962306a36Sopenharmony_ci{ 204062306a36Sopenharmony_ci struct callchain_print_arg arg = { 204162306a36Sopenharmony_ci .fp = fp, 204262306a36Sopenharmony_ci }; 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_ci hist_browser__show_callchain(browser, he, level, 0, 204562306a36Sopenharmony_ci hist_browser__fprintf_callchain_entry, &arg, 204662306a36Sopenharmony_ci hist_browser__check_dump_full); 204762306a36Sopenharmony_ci return arg.printed; 204862306a36Sopenharmony_ci} 204962306a36Sopenharmony_ci 205062306a36Sopenharmony_cistatic int hist_browser__fprintf_entry(struct hist_browser *browser, 205162306a36Sopenharmony_ci struct hist_entry *he, FILE *fp) 205262306a36Sopenharmony_ci{ 205362306a36Sopenharmony_ci char s[8192]; 205462306a36Sopenharmony_ci int printed = 0; 205562306a36Sopenharmony_ci char folded_sign = ' '; 205662306a36Sopenharmony_ci struct perf_hpp hpp = { 205762306a36Sopenharmony_ci .buf = s, 205862306a36Sopenharmony_ci .size = sizeof(s), 205962306a36Sopenharmony_ci }; 206062306a36Sopenharmony_ci struct perf_hpp_fmt *fmt; 206162306a36Sopenharmony_ci bool first = true; 206262306a36Sopenharmony_ci int ret; 206362306a36Sopenharmony_ci 206462306a36Sopenharmony_ci if (hist_entry__has_callchains(he) && symbol_conf.use_callchain) { 206562306a36Sopenharmony_ci folded_sign = hist_entry__folded(he); 206662306a36Sopenharmony_ci printed += fprintf(fp, "%c ", folded_sign); 206762306a36Sopenharmony_ci } 206862306a36Sopenharmony_ci 206962306a36Sopenharmony_ci hists__for_each_format(browser->hists, fmt) { 207062306a36Sopenharmony_ci if (perf_hpp__should_skip(fmt, he->hists)) 207162306a36Sopenharmony_ci continue; 207262306a36Sopenharmony_ci 207362306a36Sopenharmony_ci if (!first) { 207462306a36Sopenharmony_ci ret = scnprintf(hpp.buf, hpp.size, " "); 207562306a36Sopenharmony_ci advance_hpp(&hpp, ret); 207662306a36Sopenharmony_ci } else 207762306a36Sopenharmony_ci first = false; 207862306a36Sopenharmony_ci 207962306a36Sopenharmony_ci ret = fmt->entry(fmt, &hpp, he); 208062306a36Sopenharmony_ci ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret); 208162306a36Sopenharmony_ci advance_hpp(&hpp, ret); 208262306a36Sopenharmony_ci } 208362306a36Sopenharmony_ci printed += fprintf(fp, "%s\n", s); 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_ci if (folded_sign == '-') 208662306a36Sopenharmony_ci printed += hist_browser__fprintf_callchain(browser, he, fp, 1); 208762306a36Sopenharmony_ci 208862306a36Sopenharmony_ci return printed; 208962306a36Sopenharmony_ci} 209062306a36Sopenharmony_ci 209162306a36Sopenharmony_ci 209262306a36Sopenharmony_cistatic int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser, 209362306a36Sopenharmony_ci struct hist_entry *he, 209462306a36Sopenharmony_ci FILE *fp, int level) 209562306a36Sopenharmony_ci{ 209662306a36Sopenharmony_ci char s[8192]; 209762306a36Sopenharmony_ci int printed = 0; 209862306a36Sopenharmony_ci char folded_sign = ' '; 209962306a36Sopenharmony_ci struct perf_hpp hpp = { 210062306a36Sopenharmony_ci .buf = s, 210162306a36Sopenharmony_ci .size = sizeof(s), 210262306a36Sopenharmony_ci }; 210362306a36Sopenharmony_ci struct perf_hpp_fmt *fmt; 210462306a36Sopenharmony_ci struct perf_hpp_list_node *fmt_node; 210562306a36Sopenharmony_ci bool first = true; 210662306a36Sopenharmony_ci int ret; 210762306a36Sopenharmony_ci int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT; 210862306a36Sopenharmony_ci 210962306a36Sopenharmony_ci printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, ""); 211062306a36Sopenharmony_ci 211162306a36Sopenharmony_ci folded_sign = hist_entry__folded(he); 211262306a36Sopenharmony_ci printed += fprintf(fp, "%c", folded_sign); 211362306a36Sopenharmony_ci 211462306a36Sopenharmony_ci /* the first hpp_list_node is for overhead columns */ 211562306a36Sopenharmony_ci fmt_node = list_first_entry(&he->hists->hpp_formats, 211662306a36Sopenharmony_ci struct perf_hpp_list_node, list); 211762306a36Sopenharmony_ci perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) { 211862306a36Sopenharmony_ci if (!first) { 211962306a36Sopenharmony_ci ret = scnprintf(hpp.buf, hpp.size, " "); 212062306a36Sopenharmony_ci advance_hpp(&hpp, ret); 212162306a36Sopenharmony_ci } else 212262306a36Sopenharmony_ci first = false; 212362306a36Sopenharmony_ci 212462306a36Sopenharmony_ci ret = fmt->entry(fmt, &hpp, he); 212562306a36Sopenharmony_ci advance_hpp(&hpp, ret); 212662306a36Sopenharmony_ci } 212762306a36Sopenharmony_ci 212862306a36Sopenharmony_ci ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, ""); 212962306a36Sopenharmony_ci advance_hpp(&hpp, ret); 213062306a36Sopenharmony_ci 213162306a36Sopenharmony_ci perf_hpp_list__for_each_format(he->hpp_list, fmt) { 213262306a36Sopenharmony_ci ret = scnprintf(hpp.buf, hpp.size, " "); 213362306a36Sopenharmony_ci advance_hpp(&hpp, ret); 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_ci ret = fmt->entry(fmt, &hpp, he); 213662306a36Sopenharmony_ci advance_hpp(&hpp, ret); 213762306a36Sopenharmony_ci } 213862306a36Sopenharmony_ci 213962306a36Sopenharmony_ci strim(s); 214062306a36Sopenharmony_ci printed += fprintf(fp, "%s\n", s); 214162306a36Sopenharmony_ci 214262306a36Sopenharmony_ci if (he->leaf && folded_sign == '-') { 214362306a36Sopenharmony_ci printed += hist_browser__fprintf_callchain(browser, he, fp, 214462306a36Sopenharmony_ci he->depth + 1); 214562306a36Sopenharmony_ci } 214662306a36Sopenharmony_ci 214762306a36Sopenharmony_ci return printed; 214862306a36Sopenharmony_ci} 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_cistatic int hist_browser__fprintf(struct hist_browser *browser, FILE *fp) 215162306a36Sopenharmony_ci{ 215262306a36Sopenharmony_ci struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries), 215362306a36Sopenharmony_ci browser->min_pcnt); 215462306a36Sopenharmony_ci int printed = 0; 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ci while (nd) { 215762306a36Sopenharmony_ci struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 215862306a36Sopenharmony_ci 215962306a36Sopenharmony_ci if (symbol_conf.report_hierarchy) { 216062306a36Sopenharmony_ci printed += hist_browser__fprintf_hierarchy_entry(browser, 216162306a36Sopenharmony_ci h, fp, 216262306a36Sopenharmony_ci h->depth); 216362306a36Sopenharmony_ci } else { 216462306a36Sopenharmony_ci printed += hist_browser__fprintf_entry(browser, h, fp); 216562306a36Sopenharmony_ci } 216662306a36Sopenharmony_ci 216762306a36Sopenharmony_ci nd = hists__filter_entries(rb_hierarchy_next(nd), 216862306a36Sopenharmony_ci browser->min_pcnt); 216962306a36Sopenharmony_ci } 217062306a36Sopenharmony_ci 217162306a36Sopenharmony_ci return printed; 217262306a36Sopenharmony_ci} 217362306a36Sopenharmony_ci 217462306a36Sopenharmony_cistatic int hist_browser__dump(struct hist_browser *browser) 217562306a36Sopenharmony_ci{ 217662306a36Sopenharmony_ci char filename[64]; 217762306a36Sopenharmony_ci FILE *fp; 217862306a36Sopenharmony_ci 217962306a36Sopenharmony_ci while (1) { 218062306a36Sopenharmony_ci scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq); 218162306a36Sopenharmony_ci if (access(filename, F_OK)) 218262306a36Sopenharmony_ci break; 218362306a36Sopenharmony_ci /* 218462306a36Sopenharmony_ci * XXX: Just an arbitrary lazy upper limit 218562306a36Sopenharmony_ci */ 218662306a36Sopenharmony_ci if (++browser->print_seq == 8192) { 218762306a36Sopenharmony_ci ui_helpline__fpush("Too many perf.hist.N files, nothing written!"); 218862306a36Sopenharmony_ci return -1; 218962306a36Sopenharmony_ci } 219062306a36Sopenharmony_ci } 219162306a36Sopenharmony_ci 219262306a36Sopenharmony_ci fp = fopen(filename, "w"); 219362306a36Sopenharmony_ci if (fp == NULL) { 219462306a36Sopenharmony_ci char bf[64]; 219562306a36Sopenharmony_ci const char *err = str_error_r(errno, bf, sizeof(bf)); 219662306a36Sopenharmony_ci ui_helpline__fpush("Couldn't write to %s: %s", filename, err); 219762306a36Sopenharmony_ci return -1; 219862306a36Sopenharmony_ci } 219962306a36Sopenharmony_ci 220062306a36Sopenharmony_ci ++browser->print_seq; 220162306a36Sopenharmony_ci hist_browser__fprintf(browser, fp); 220262306a36Sopenharmony_ci fclose(fp); 220362306a36Sopenharmony_ci ui_helpline__fpush("%s written!", filename); 220462306a36Sopenharmony_ci 220562306a36Sopenharmony_ci return 0; 220662306a36Sopenharmony_ci} 220762306a36Sopenharmony_ci 220862306a36Sopenharmony_civoid hist_browser__init(struct hist_browser *browser, 220962306a36Sopenharmony_ci struct hists *hists) 221062306a36Sopenharmony_ci{ 221162306a36Sopenharmony_ci struct perf_hpp_fmt *fmt; 221262306a36Sopenharmony_ci 221362306a36Sopenharmony_ci browser->hists = hists; 221462306a36Sopenharmony_ci browser->b.refresh = hist_browser__refresh; 221562306a36Sopenharmony_ci browser->b.refresh_dimensions = hist_browser__refresh_dimensions; 221662306a36Sopenharmony_ci browser->b.seek = ui_browser__hists_seek; 221762306a36Sopenharmony_ci browser->b.use_navkeypressed = true; 221862306a36Sopenharmony_ci browser->show_headers = symbol_conf.show_hist_headers; 221962306a36Sopenharmony_ci hist_browser__set_title_space(browser); 222062306a36Sopenharmony_ci 222162306a36Sopenharmony_ci if (symbol_conf.report_hierarchy) { 222262306a36Sopenharmony_ci struct perf_hpp_list_node *fmt_node; 222362306a36Sopenharmony_ci 222462306a36Sopenharmony_ci /* count overhead columns (in the first node) */ 222562306a36Sopenharmony_ci fmt_node = list_first_entry(&hists->hpp_formats, 222662306a36Sopenharmony_ci struct perf_hpp_list_node, list); 222762306a36Sopenharmony_ci perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) 222862306a36Sopenharmony_ci ++browser->b.columns; 222962306a36Sopenharmony_ci 223062306a36Sopenharmony_ci /* add a single column for whole hierarchy sort keys*/ 223162306a36Sopenharmony_ci ++browser->b.columns; 223262306a36Sopenharmony_ci } else { 223362306a36Sopenharmony_ci hists__for_each_format(hists, fmt) 223462306a36Sopenharmony_ci ++browser->b.columns; 223562306a36Sopenharmony_ci } 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_ci hists__reset_column_width(hists); 223862306a36Sopenharmony_ci} 223962306a36Sopenharmony_ci 224062306a36Sopenharmony_cistruct hist_browser *hist_browser__new(struct hists *hists) 224162306a36Sopenharmony_ci{ 224262306a36Sopenharmony_ci struct hist_browser *browser = zalloc(sizeof(*browser)); 224362306a36Sopenharmony_ci 224462306a36Sopenharmony_ci if (browser) 224562306a36Sopenharmony_ci hist_browser__init(browser, hists); 224662306a36Sopenharmony_ci 224762306a36Sopenharmony_ci return browser; 224862306a36Sopenharmony_ci} 224962306a36Sopenharmony_ci 225062306a36Sopenharmony_cistatic struct hist_browser * 225162306a36Sopenharmony_ciperf_evsel_browser__new(struct evsel *evsel, 225262306a36Sopenharmony_ci struct hist_browser_timer *hbt, 225362306a36Sopenharmony_ci struct perf_env *env, 225462306a36Sopenharmony_ci struct annotation_options *annotation_opts) 225562306a36Sopenharmony_ci{ 225662306a36Sopenharmony_ci struct hist_browser *browser = hist_browser__new(evsel__hists(evsel)); 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_ci if (browser) { 225962306a36Sopenharmony_ci browser->hbt = hbt; 226062306a36Sopenharmony_ci browser->env = env; 226162306a36Sopenharmony_ci browser->title = hists_browser__scnprintf_title; 226262306a36Sopenharmony_ci browser->annotation_opts = annotation_opts; 226362306a36Sopenharmony_ci } 226462306a36Sopenharmony_ci return browser; 226562306a36Sopenharmony_ci} 226662306a36Sopenharmony_ci 226762306a36Sopenharmony_civoid hist_browser__delete(struct hist_browser *browser) 226862306a36Sopenharmony_ci{ 226962306a36Sopenharmony_ci free(browser); 227062306a36Sopenharmony_ci} 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_cistatic struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser) 227362306a36Sopenharmony_ci{ 227462306a36Sopenharmony_ci return browser->he_selection; 227562306a36Sopenharmony_ci} 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_cistatic struct thread *hist_browser__selected_thread(struct hist_browser *browser) 227862306a36Sopenharmony_ci{ 227962306a36Sopenharmony_ci return browser->he_selection->thread; 228062306a36Sopenharmony_ci} 228162306a36Sopenharmony_ci 228262306a36Sopenharmony_cistatic struct res_sample *hist_browser__selected_res_sample(struct hist_browser *browser) 228362306a36Sopenharmony_ci{ 228462306a36Sopenharmony_ci return browser->he_selection ? browser->he_selection->res_samples : NULL; 228562306a36Sopenharmony_ci} 228662306a36Sopenharmony_ci 228762306a36Sopenharmony_ci/* Check whether the browser is for 'top' or 'report' */ 228862306a36Sopenharmony_cistatic inline bool is_report_browser(void *timer) 228962306a36Sopenharmony_ci{ 229062306a36Sopenharmony_ci return timer == NULL; 229162306a36Sopenharmony_ci} 229262306a36Sopenharmony_ci 229362306a36Sopenharmony_cistatic int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size) 229462306a36Sopenharmony_ci{ 229562306a36Sopenharmony_ci struct hist_browser_timer *hbt = browser->hbt; 229662306a36Sopenharmony_ci int printed = __hists__scnprintf_title(browser->hists, bf, size, !is_report_browser(hbt)); 229762306a36Sopenharmony_ci 229862306a36Sopenharmony_ci if (!is_report_browser(hbt)) { 229962306a36Sopenharmony_ci struct perf_top *top = hbt->arg; 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_ci printed += scnprintf(bf + printed, size - printed, 230262306a36Sopenharmony_ci " lost: %" PRIu64 "/%" PRIu64, 230362306a36Sopenharmony_ci top->lost, top->lost_total); 230462306a36Sopenharmony_ci 230562306a36Sopenharmony_ci printed += scnprintf(bf + printed, size - printed, 230662306a36Sopenharmony_ci " drop: %" PRIu64 "/%" PRIu64, 230762306a36Sopenharmony_ci top->drop, top->drop_total); 230862306a36Sopenharmony_ci 230962306a36Sopenharmony_ci if (top->zero) 231062306a36Sopenharmony_ci printed += scnprintf(bf + printed, size - printed, " [z]"); 231162306a36Sopenharmony_ci 231262306a36Sopenharmony_ci perf_top__reset_sample_counters(top); 231362306a36Sopenharmony_ci } 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_ci 231662306a36Sopenharmony_ci return printed; 231762306a36Sopenharmony_ci} 231862306a36Sopenharmony_ci 231962306a36Sopenharmony_cistatic inline void free_popup_options(char **options, int n) 232062306a36Sopenharmony_ci{ 232162306a36Sopenharmony_ci int i; 232262306a36Sopenharmony_ci 232362306a36Sopenharmony_ci for (i = 0; i < n; ++i) 232462306a36Sopenharmony_ci zfree(&options[i]); 232562306a36Sopenharmony_ci} 232662306a36Sopenharmony_ci 232762306a36Sopenharmony_ci/* 232862306a36Sopenharmony_ci * Only runtime switching of perf data file will make "input_name" point 232962306a36Sopenharmony_ci * to a malloced buffer. So add "is_input_name_malloced" flag to decide 233062306a36Sopenharmony_ci * whether we need to call free() for current "input_name" during the switch. 233162306a36Sopenharmony_ci */ 233262306a36Sopenharmony_cistatic bool is_input_name_malloced = false; 233362306a36Sopenharmony_ci 233462306a36Sopenharmony_cistatic int switch_data_file(void) 233562306a36Sopenharmony_ci{ 233662306a36Sopenharmony_ci char *pwd, *options[32], *abs_path[32], *tmp; 233762306a36Sopenharmony_ci DIR *pwd_dir; 233862306a36Sopenharmony_ci int nr_options = 0, choice = -1, ret = -1; 233962306a36Sopenharmony_ci struct dirent *dent; 234062306a36Sopenharmony_ci 234162306a36Sopenharmony_ci pwd = getenv("PWD"); 234262306a36Sopenharmony_ci if (!pwd) 234362306a36Sopenharmony_ci return ret; 234462306a36Sopenharmony_ci 234562306a36Sopenharmony_ci pwd_dir = opendir(pwd); 234662306a36Sopenharmony_ci if (!pwd_dir) 234762306a36Sopenharmony_ci return ret; 234862306a36Sopenharmony_ci 234962306a36Sopenharmony_ci memset(options, 0, sizeof(options)); 235062306a36Sopenharmony_ci memset(abs_path, 0, sizeof(abs_path)); 235162306a36Sopenharmony_ci 235262306a36Sopenharmony_ci while ((dent = readdir(pwd_dir))) { 235362306a36Sopenharmony_ci char path[PATH_MAX]; 235462306a36Sopenharmony_ci u64 magic; 235562306a36Sopenharmony_ci char *name = dent->d_name; 235662306a36Sopenharmony_ci FILE *file; 235762306a36Sopenharmony_ci 235862306a36Sopenharmony_ci if (!(dent->d_type == DT_REG)) 235962306a36Sopenharmony_ci continue; 236062306a36Sopenharmony_ci 236162306a36Sopenharmony_ci snprintf(path, sizeof(path), "%s/%s", pwd, name); 236262306a36Sopenharmony_ci 236362306a36Sopenharmony_ci file = fopen(path, "r"); 236462306a36Sopenharmony_ci if (!file) 236562306a36Sopenharmony_ci continue; 236662306a36Sopenharmony_ci 236762306a36Sopenharmony_ci if (fread(&magic, 1, 8, file) < 8) 236862306a36Sopenharmony_ci goto close_file_and_continue; 236962306a36Sopenharmony_ci 237062306a36Sopenharmony_ci if (is_perf_magic(magic)) { 237162306a36Sopenharmony_ci options[nr_options] = strdup(name); 237262306a36Sopenharmony_ci if (!options[nr_options]) 237362306a36Sopenharmony_ci goto close_file_and_continue; 237462306a36Sopenharmony_ci 237562306a36Sopenharmony_ci abs_path[nr_options] = strdup(path); 237662306a36Sopenharmony_ci if (!abs_path[nr_options]) { 237762306a36Sopenharmony_ci zfree(&options[nr_options]); 237862306a36Sopenharmony_ci ui__warning("Can't search all data files due to memory shortage.\n"); 237962306a36Sopenharmony_ci fclose(file); 238062306a36Sopenharmony_ci break; 238162306a36Sopenharmony_ci } 238262306a36Sopenharmony_ci 238362306a36Sopenharmony_ci nr_options++; 238462306a36Sopenharmony_ci } 238562306a36Sopenharmony_ci 238662306a36Sopenharmony_ciclose_file_and_continue: 238762306a36Sopenharmony_ci fclose(file); 238862306a36Sopenharmony_ci if (nr_options >= 32) { 238962306a36Sopenharmony_ci ui__warning("Too many perf data files in PWD!\n" 239062306a36Sopenharmony_ci "Only the first 32 files will be listed.\n"); 239162306a36Sopenharmony_ci break; 239262306a36Sopenharmony_ci } 239362306a36Sopenharmony_ci } 239462306a36Sopenharmony_ci closedir(pwd_dir); 239562306a36Sopenharmony_ci 239662306a36Sopenharmony_ci if (nr_options) { 239762306a36Sopenharmony_ci choice = ui__popup_menu(nr_options, options, NULL); 239862306a36Sopenharmony_ci if (choice < nr_options && choice >= 0) { 239962306a36Sopenharmony_ci tmp = strdup(abs_path[choice]); 240062306a36Sopenharmony_ci if (tmp) { 240162306a36Sopenharmony_ci if (is_input_name_malloced) 240262306a36Sopenharmony_ci free((void *)input_name); 240362306a36Sopenharmony_ci input_name = tmp; 240462306a36Sopenharmony_ci is_input_name_malloced = true; 240562306a36Sopenharmony_ci ret = 0; 240662306a36Sopenharmony_ci } else 240762306a36Sopenharmony_ci ui__warning("Data switch failed due to memory shortage!\n"); 240862306a36Sopenharmony_ci } 240962306a36Sopenharmony_ci } 241062306a36Sopenharmony_ci 241162306a36Sopenharmony_ci free_popup_options(options, nr_options); 241262306a36Sopenharmony_ci free_popup_options(abs_path, nr_options); 241362306a36Sopenharmony_ci return ret; 241462306a36Sopenharmony_ci} 241562306a36Sopenharmony_ci 241662306a36Sopenharmony_cistruct popup_action { 241762306a36Sopenharmony_ci unsigned long time; 241862306a36Sopenharmony_ci struct thread *thread; 241962306a36Sopenharmony_ci struct map_symbol ms; 242062306a36Sopenharmony_ci int socket; 242162306a36Sopenharmony_ci struct evsel *evsel; 242262306a36Sopenharmony_ci enum rstype rstype; 242362306a36Sopenharmony_ci 242462306a36Sopenharmony_ci int (*fn)(struct hist_browser *browser, struct popup_action *act); 242562306a36Sopenharmony_ci}; 242662306a36Sopenharmony_ci 242762306a36Sopenharmony_cistatic int 242862306a36Sopenharmony_cido_annotate(struct hist_browser *browser, struct popup_action *act) 242962306a36Sopenharmony_ci{ 243062306a36Sopenharmony_ci struct evsel *evsel; 243162306a36Sopenharmony_ci struct annotation *notes; 243262306a36Sopenharmony_ci struct hist_entry *he; 243362306a36Sopenharmony_ci int err; 243462306a36Sopenharmony_ci 243562306a36Sopenharmony_ci if (!browser->annotation_opts->objdump_path && 243662306a36Sopenharmony_ci perf_env__lookup_objdump(browser->env, &browser->annotation_opts->objdump_path)) 243762306a36Sopenharmony_ci return 0; 243862306a36Sopenharmony_ci 243962306a36Sopenharmony_ci notes = symbol__annotation(act->ms.sym); 244062306a36Sopenharmony_ci if (!notes->src) 244162306a36Sopenharmony_ci return 0; 244262306a36Sopenharmony_ci 244362306a36Sopenharmony_ci if (browser->block_evsel) 244462306a36Sopenharmony_ci evsel = browser->block_evsel; 244562306a36Sopenharmony_ci else 244662306a36Sopenharmony_ci evsel = hists_to_evsel(browser->hists); 244762306a36Sopenharmony_ci 244862306a36Sopenharmony_ci err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt, 244962306a36Sopenharmony_ci browser->annotation_opts); 245062306a36Sopenharmony_ci he = hist_browser__selected_entry(browser); 245162306a36Sopenharmony_ci /* 245262306a36Sopenharmony_ci * offer option to annotate the other branch source or target 245362306a36Sopenharmony_ci * (if they exists) when returning from annotate 245462306a36Sopenharmony_ci */ 245562306a36Sopenharmony_ci if ((err == 'q' || err == CTRL('c')) && he->branch_info) 245662306a36Sopenharmony_ci return 1; 245762306a36Sopenharmony_ci 245862306a36Sopenharmony_ci ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); 245962306a36Sopenharmony_ci if (err) 246062306a36Sopenharmony_ci ui_browser__handle_resize(&browser->b); 246162306a36Sopenharmony_ci return 0; 246262306a36Sopenharmony_ci} 246362306a36Sopenharmony_ci 246462306a36Sopenharmony_cistatic struct symbol *symbol__new_unresolved(u64 addr, struct map *map) 246562306a36Sopenharmony_ci{ 246662306a36Sopenharmony_ci struct annotated_source *src; 246762306a36Sopenharmony_ci struct symbol *sym; 246862306a36Sopenharmony_ci char name[64]; 246962306a36Sopenharmony_ci 247062306a36Sopenharmony_ci snprintf(name, sizeof(name), "%.*" PRIx64, BITS_PER_LONG / 4, addr); 247162306a36Sopenharmony_ci 247262306a36Sopenharmony_ci sym = symbol__new(addr, ANNOTATION_DUMMY_LEN, 0, 0, name); 247362306a36Sopenharmony_ci if (sym) { 247462306a36Sopenharmony_ci src = symbol__hists(sym, 1); 247562306a36Sopenharmony_ci if (!src) { 247662306a36Sopenharmony_ci symbol__delete(sym); 247762306a36Sopenharmony_ci return NULL; 247862306a36Sopenharmony_ci } 247962306a36Sopenharmony_ci 248062306a36Sopenharmony_ci dso__insert_symbol(map__dso(map), sym); 248162306a36Sopenharmony_ci } 248262306a36Sopenharmony_ci 248362306a36Sopenharmony_ci return sym; 248462306a36Sopenharmony_ci} 248562306a36Sopenharmony_ci 248662306a36Sopenharmony_cistatic int 248762306a36Sopenharmony_ciadd_annotate_opt(struct hist_browser *browser __maybe_unused, 248862306a36Sopenharmony_ci struct popup_action *act, char **optstr, 248962306a36Sopenharmony_ci struct map_symbol *ms, 249062306a36Sopenharmony_ci u64 addr) 249162306a36Sopenharmony_ci{ 249262306a36Sopenharmony_ci struct dso *dso; 249362306a36Sopenharmony_ci 249462306a36Sopenharmony_ci if (!ms->map || (dso = map__dso(ms->map)) == NULL || dso->annotate_warned) 249562306a36Sopenharmony_ci return 0; 249662306a36Sopenharmony_ci 249762306a36Sopenharmony_ci if (!ms->sym) 249862306a36Sopenharmony_ci ms->sym = symbol__new_unresolved(addr, ms->map); 249962306a36Sopenharmony_ci 250062306a36Sopenharmony_ci if (ms->sym == NULL || symbol__annotation(ms->sym)->src == NULL) 250162306a36Sopenharmony_ci return 0; 250262306a36Sopenharmony_ci 250362306a36Sopenharmony_ci if (asprintf(optstr, "Annotate %s", ms->sym->name) < 0) 250462306a36Sopenharmony_ci return 0; 250562306a36Sopenharmony_ci 250662306a36Sopenharmony_ci act->ms = *ms; 250762306a36Sopenharmony_ci act->fn = do_annotate; 250862306a36Sopenharmony_ci return 1; 250962306a36Sopenharmony_ci} 251062306a36Sopenharmony_ci 251162306a36Sopenharmony_cistatic int 251262306a36Sopenharmony_cido_zoom_thread(struct hist_browser *browser, struct popup_action *act) 251362306a36Sopenharmony_ci{ 251462306a36Sopenharmony_ci struct thread *thread = act->thread; 251562306a36Sopenharmony_ci 251662306a36Sopenharmony_ci if ((!hists__has(browser->hists, thread) && 251762306a36Sopenharmony_ci !hists__has(browser->hists, comm)) || thread == NULL) 251862306a36Sopenharmony_ci return 0; 251962306a36Sopenharmony_ci 252062306a36Sopenharmony_ci if (browser->hists->thread_filter) { 252162306a36Sopenharmony_ci pstack__remove(browser->pstack, &browser->hists->thread_filter); 252262306a36Sopenharmony_ci perf_hpp__set_elide(HISTC_THREAD, false); 252362306a36Sopenharmony_ci thread__zput(browser->hists->thread_filter); 252462306a36Sopenharmony_ci ui_helpline__pop(); 252562306a36Sopenharmony_ci } else { 252662306a36Sopenharmony_ci const char *comm_set_str = 252762306a36Sopenharmony_ci thread__comm_set(thread) ? thread__comm_str(thread) : ""; 252862306a36Sopenharmony_ci 252962306a36Sopenharmony_ci if (hists__has(browser->hists, thread)) { 253062306a36Sopenharmony_ci ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"", 253162306a36Sopenharmony_ci comm_set_str, thread__tid(thread)); 253262306a36Sopenharmony_ci } else { 253362306a36Sopenharmony_ci ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"", 253462306a36Sopenharmony_ci comm_set_str); 253562306a36Sopenharmony_ci } 253662306a36Sopenharmony_ci 253762306a36Sopenharmony_ci browser->hists->thread_filter = thread__get(thread); 253862306a36Sopenharmony_ci perf_hpp__set_elide(HISTC_THREAD, false); 253962306a36Sopenharmony_ci pstack__push(browser->pstack, &browser->hists->thread_filter); 254062306a36Sopenharmony_ci } 254162306a36Sopenharmony_ci 254262306a36Sopenharmony_ci hists__filter_by_thread(browser->hists); 254362306a36Sopenharmony_ci hist_browser__reset(browser); 254462306a36Sopenharmony_ci return 0; 254562306a36Sopenharmony_ci} 254662306a36Sopenharmony_ci 254762306a36Sopenharmony_cistatic int 254862306a36Sopenharmony_ciadd_thread_opt(struct hist_browser *browser, struct popup_action *act, 254962306a36Sopenharmony_ci char **optstr, struct thread *thread) 255062306a36Sopenharmony_ci{ 255162306a36Sopenharmony_ci int ret; 255262306a36Sopenharmony_ci const char *comm_set_str, *in_out; 255362306a36Sopenharmony_ci 255462306a36Sopenharmony_ci if ((!hists__has(browser->hists, thread) && 255562306a36Sopenharmony_ci !hists__has(browser->hists, comm)) || thread == NULL) 255662306a36Sopenharmony_ci return 0; 255762306a36Sopenharmony_ci 255862306a36Sopenharmony_ci in_out = browser->hists->thread_filter ? "out of" : "into"; 255962306a36Sopenharmony_ci comm_set_str = thread__comm_set(thread) ? thread__comm_str(thread) : ""; 256062306a36Sopenharmony_ci if (hists__has(browser->hists, thread)) { 256162306a36Sopenharmony_ci ret = asprintf(optstr, "Zoom %s %s(%d) thread", 256262306a36Sopenharmony_ci in_out, comm_set_str, thread__tid(thread)); 256362306a36Sopenharmony_ci } else { 256462306a36Sopenharmony_ci ret = asprintf(optstr, "Zoom %s %s thread", in_out, comm_set_str); 256562306a36Sopenharmony_ci } 256662306a36Sopenharmony_ci if (ret < 0) 256762306a36Sopenharmony_ci return 0; 256862306a36Sopenharmony_ci 256962306a36Sopenharmony_ci act->thread = thread; 257062306a36Sopenharmony_ci act->fn = do_zoom_thread; 257162306a36Sopenharmony_ci return 1; 257262306a36Sopenharmony_ci} 257362306a36Sopenharmony_ci 257462306a36Sopenharmony_cistatic int hists_browser__zoom_map(struct hist_browser *browser, struct map *map) 257562306a36Sopenharmony_ci{ 257662306a36Sopenharmony_ci if (!hists__has(browser->hists, dso) || map == NULL) 257762306a36Sopenharmony_ci return 0; 257862306a36Sopenharmony_ci 257962306a36Sopenharmony_ci if (browser->hists->dso_filter) { 258062306a36Sopenharmony_ci pstack__remove(browser->pstack, &browser->hists->dso_filter); 258162306a36Sopenharmony_ci perf_hpp__set_elide(HISTC_DSO, false); 258262306a36Sopenharmony_ci browser->hists->dso_filter = NULL; 258362306a36Sopenharmony_ci ui_helpline__pop(); 258462306a36Sopenharmony_ci } else { 258562306a36Sopenharmony_ci struct dso *dso = map__dso(map); 258662306a36Sopenharmony_ci ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"", 258762306a36Sopenharmony_ci __map__is_kernel(map) ? "the Kernel" : dso->short_name); 258862306a36Sopenharmony_ci browser->hists->dso_filter = dso; 258962306a36Sopenharmony_ci perf_hpp__set_elide(HISTC_DSO, true); 259062306a36Sopenharmony_ci pstack__push(browser->pstack, &browser->hists->dso_filter); 259162306a36Sopenharmony_ci } 259262306a36Sopenharmony_ci 259362306a36Sopenharmony_ci hists__filter_by_dso(browser->hists); 259462306a36Sopenharmony_ci hist_browser__reset(browser); 259562306a36Sopenharmony_ci return 0; 259662306a36Sopenharmony_ci} 259762306a36Sopenharmony_ci 259862306a36Sopenharmony_cistatic int 259962306a36Sopenharmony_cido_zoom_dso(struct hist_browser *browser, struct popup_action *act) 260062306a36Sopenharmony_ci{ 260162306a36Sopenharmony_ci return hists_browser__zoom_map(browser, act->ms.map); 260262306a36Sopenharmony_ci} 260362306a36Sopenharmony_ci 260462306a36Sopenharmony_cistatic int 260562306a36Sopenharmony_ciadd_dso_opt(struct hist_browser *browser, struct popup_action *act, 260662306a36Sopenharmony_ci char **optstr, struct map *map) 260762306a36Sopenharmony_ci{ 260862306a36Sopenharmony_ci if (!hists__has(browser->hists, dso) || map == NULL) 260962306a36Sopenharmony_ci return 0; 261062306a36Sopenharmony_ci 261162306a36Sopenharmony_ci if (asprintf(optstr, "Zoom %s %s DSO (use the 'k' hotkey to zoom directly into the kernel)", 261262306a36Sopenharmony_ci browser->hists->dso_filter ? "out of" : "into", 261362306a36Sopenharmony_ci __map__is_kernel(map) ? "the Kernel" : map__dso(map)->short_name) < 0) 261462306a36Sopenharmony_ci return 0; 261562306a36Sopenharmony_ci 261662306a36Sopenharmony_ci act->ms.map = map; 261762306a36Sopenharmony_ci act->fn = do_zoom_dso; 261862306a36Sopenharmony_ci return 1; 261962306a36Sopenharmony_ci} 262062306a36Sopenharmony_ci 262162306a36Sopenharmony_cistatic int do_toggle_callchain(struct hist_browser *browser, struct popup_action *act __maybe_unused) 262262306a36Sopenharmony_ci{ 262362306a36Sopenharmony_ci hist_browser__toggle_fold(browser); 262462306a36Sopenharmony_ci return 0; 262562306a36Sopenharmony_ci} 262662306a36Sopenharmony_ci 262762306a36Sopenharmony_cistatic int add_callchain_toggle_opt(struct hist_browser *browser, struct popup_action *act, char **optstr) 262862306a36Sopenharmony_ci{ 262962306a36Sopenharmony_ci char sym_name[512]; 263062306a36Sopenharmony_ci 263162306a36Sopenharmony_ci if (!hist_browser__selection_has_children(browser)) 263262306a36Sopenharmony_ci return 0; 263362306a36Sopenharmony_ci 263462306a36Sopenharmony_ci if (asprintf(optstr, "%s [%s] callchain (one level, same as '+' hotkey, use 'e'/'c' for the whole main level entry)", 263562306a36Sopenharmony_ci hist_browser__selection_unfolded(browser) ? "Collapse" : "Expand", 263662306a36Sopenharmony_ci hist_browser__selection_sym_name(browser, sym_name, sizeof(sym_name))) < 0) 263762306a36Sopenharmony_ci return 0; 263862306a36Sopenharmony_ci 263962306a36Sopenharmony_ci act->fn = do_toggle_callchain; 264062306a36Sopenharmony_ci return 1; 264162306a36Sopenharmony_ci} 264262306a36Sopenharmony_ci 264362306a36Sopenharmony_cistatic int 264462306a36Sopenharmony_cido_browse_map(struct hist_browser *browser __maybe_unused, 264562306a36Sopenharmony_ci struct popup_action *act) 264662306a36Sopenharmony_ci{ 264762306a36Sopenharmony_ci map__browse(act->ms.map); 264862306a36Sopenharmony_ci return 0; 264962306a36Sopenharmony_ci} 265062306a36Sopenharmony_ci 265162306a36Sopenharmony_cistatic int 265262306a36Sopenharmony_ciadd_map_opt(struct hist_browser *browser, 265362306a36Sopenharmony_ci struct popup_action *act, char **optstr, struct map *map) 265462306a36Sopenharmony_ci{ 265562306a36Sopenharmony_ci if (!hists__has(browser->hists, dso) || map == NULL) 265662306a36Sopenharmony_ci return 0; 265762306a36Sopenharmony_ci 265862306a36Sopenharmony_ci if (asprintf(optstr, "Browse map details") < 0) 265962306a36Sopenharmony_ci return 0; 266062306a36Sopenharmony_ci 266162306a36Sopenharmony_ci act->ms.map = map; 266262306a36Sopenharmony_ci act->fn = do_browse_map; 266362306a36Sopenharmony_ci return 1; 266462306a36Sopenharmony_ci} 266562306a36Sopenharmony_ci 266662306a36Sopenharmony_cistatic int 266762306a36Sopenharmony_cido_run_script(struct hist_browser *browser __maybe_unused, 266862306a36Sopenharmony_ci struct popup_action *act) 266962306a36Sopenharmony_ci{ 267062306a36Sopenharmony_ci char *script_opt; 267162306a36Sopenharmony_ci int len; 267262306a36Sopenharmony_ci int n = 0; 267362306a36Sopenharmony_ci 267462306a36Sopenharmony_ci len = 100; 267562306a36Sopenharmony_ci if (act->thread) 267662306a36Sopenharmony_ci len += strlen(thread__comm_str(act->thread)); 267762306a36Sopenharmony_ci else if (act->ms.sym) 267862306a36Sopenharmony_ci len += strlen(act->ms.sym->name); 267962306a36Sopenharmony_ci script_opt = malloc(len); 268062306a36Sopenharmony_ci if (!script_opt) 268162306a36Sopenharmony_ci return -1; 268262306a36Sopenharmony_ci 268362306a36Sopenharmony_ci script_opt[0] = 0; 268462306a36Sopenharmony_ci if (act->thread) { 268562306a36Sopenharmony_ci n = scnprintf(script_opt, len, " -c %s ", 268662306a36Sopenharmony_ci thread__comm_str(act->thread)); 268762306a36Sopenharmony_ci } else if (act->ms.sym) { 268862306a36Sopenharmony_ci n = scnprintf(script_opt, len, " -S %s ", 268962306a36Sopenharmony_ci act->ms.sym->name); 269062306a36Sopenharmony_ci } 269162306a36Sopenharmony_ci 269262306a36Sopenharmony_ci if (act->time) { 269362306a36Sopenharmony_ci char start[32], end[32]; 269462306a36Sopenharmony_ci unsigned long starttime = act->time; 269562306a36Sopenharmony_ci unsigned long endtime = act->time + symbol_conf.time_quantum; 269662306a36Sopenharmony_ci 269762306a36Sopenharmony_ci if (starttime == endtime) { /* Display 1ms as fallback */ 269862306a36Sopenharmony_ci starttime -= 1*NSEC_PER_MSEC; 269962306a36Sopenharmony_ci endtime += 1*NSEC_PER_MSEC; 270062306a36Sopenharmony_ci } 270162306a36Sopenharmony_ci timestamp__scnprintf_usec(starttime, start, sizeof start); 270262306a36Sopenharmony_ci timestamp__scnprintf_usec(endtime, end, sizeof end); 270362306a36Sopenharmony_ci n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end); 270462306a36Sopenharmony_ci } 270562306a36Sopenharmony_ci 270662306a36Sopenharmony_ci script_browse(script_opt, act->evsel); 270762306a36Sopenharmony_ci free(script_opt); 270862306a36Sopenharmony_ci return 0; 270962306a36Sopenharmony_ci} 271062306a36Sopenharmony_ci 271162306a36Sopenharmony_cistatic int 271262306a36Sopenharmony_cido_res_sample_script(struct hist_browser *browser __maybe_unused, 271362306a36Sopenharmony_ci struct popup_action *act) 271462306a36Sopenharmony_ci{ 271562306a36Sopenharmony_ci struct hist_entry *he; 271662306a36Sopenharmony_ci 271762306a36Sopenharmony_ci he = hist_browser__selected_entry(browser); 271862306a36Sopenharmony_ci res_sample_browse(he->res_samples, he->num_res, act->evsel, act->rstype); 271962306a36Sopenharmony_ci return 0; 272062306a36Sopenharmony_ci} 272162306a36Sopenharmony_ci 272262306a36Sopenharmony_cistatic int 272362306a36Sopenharmony_ciadd_script_opt_2(struct hist_browser *browser __maybe_unused, 272462306a36Sopenharmony_ci struct popup_action *act, char **optstr, 272562306a36Sopenharmony_ci struct thread *thread, struct symbol *sym, 272662306a36Sopenharmony_ci struct evsel *evsel, const char *tstr) 272762306a36Sopenharmony_ci{ 272862306a36Sopenharmony_ci 272962306a36Sopenharmony_ci if (thread) { 273062306a36Sopenharmony_ci if (asprintf(optstr, "Run scripts for samples of thread [%s]%s", 273162306a36Sopenharmony_ci thread__comm_str(thread), tstr) < 0) 273262306a36Sopenharmony_ci return 0; 273362306a36Sopenharmony_ci } else if (sym) { 273462306a36Sopenharmony_ci if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s", 273562306a36Sopenharmony_ci sym->name, tstr) < 0) 273662306a36Sopenharmony_ci return 0; 273762306a36Sopenharmony_ci } else { 273862306a36Sopenharmony_ci if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0) 273962306a36Sopenharmony_ci return 0; 274062306a36Sopenharmony_ci } 274162306a36Sopenharmony_ci 274262306a36Sopenharmony_ci act->thread = thread; 274362306a36Sopenharmony_ci act->ms.sym = sym; 274462306a36Sopenharmony_ci act->evsel = evsel; 274562306a36Sopenharmony_ci act->fn = do_run_script; 274662306a36Sopenharmony_ci return 1; 274762306a36Sopenharmony_ci} 274862306a36Sopenharmony_ci 274962306a36Sopenharmony_cistatic int 275062306a36Sopenharmony_ciadd_script_opt(struct hist_browser *browser, 275162306a36Sopenharmony_ci struct popup_action *act, char **optstr, 275262306a36Sopenharmony_ci struct thread *thread, struct symbol *sym, 275362306a36Sopenharmony_ci struct evsel *evsel) 275462306a36Sopenharmony_ci{ 275562306a36Sopenharmony_ci int n, j; 275662306a36Sopenharmony_ci struct hist_entry *he; 275762306a36Sopenharmony_ci 275862306a36Sopenharmony_ci n = add_script_opt_2(browser, act, optstr, thread, sym, evsel, ""); 275962306a36Sopenharmony_ci 276062306a36Sopenharmony_ci he = hist_browser__selected_entry(browser); 276162306a36Sopenharmony_ci if (sort_order && strstr(sort_order, "time")) { 276262306a36Sopenharmony_ci char tstr[128]; 276362306a36Sopenharmony_ci 276462306a36Sopenharmony_ci optstr++; 276562306a36Sopenharmony_ci act++; 276662306a36Sopenharmony_ci j = sprintf(tstr, " in "); 276762306a36Sopenharmony_ci j += timestamp__scnprintf_usec(he->time, tstr + j, 276862306a36Sopenharmony_ci sizeof tstr - j); 276962306a36Sopenharmony_ci j += sprintf(tstr + j, "-"); 277062306a36Sopenharmony_ci timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum, 277162306a36Sopenharmony_ci tstr + j, sizeof tstr - j); 277262306a36Sopenharmony_ci n += add_script_opt_2(browser, act, optstr, thread, sym, 277362306a36Sopenharmony_ci evsel, tstr); 277462306a36Sopenharmony_ci act->time = he->time; 277562306a36Sopenharmony_ci } 277662306a36Sopenharmony_ci return n; 277762306a36Sopenharmony_ci} 277862306a36Sopenharmony_ci 277962306a36Sopenharmony_cistatic int 278062306a36Sopenharmony_ciadd_res_sample_opt(struct hist_browser *browser __maybe_unused, 278162306a36Sopenharmony_ci struct popup_action *act, char **optstr, 278262306a36Sopenharmony_ci struct res_sample *res_sample, 278362306a36Sopenharmony_ci struct evsel *evsel, 278462306a36Sopenharmony_ci enum rstype type) 278562306a36Sopenharmony_ci{ 278662306a36Sopenharmony_ci if (!res_sample) 278762306a36Sopenharmony_ci return 0; 278862306a36Sopenharmony_ci 278962306a36Sopenharmony_ci if (asprintf(optstr, "Show context for individual samples %s", 279062306a36Sopenharmony_ci type == A_ASM ? "with assembler" : 279162306a36Sopenharmony_ci type == A_SOURCE ? "with source" : "") < 0) 279262306a36Sopenharmony_ci return 0; 279362306a36Sopenharmony_ci 279462306a36Sopenharmony_ci act->fn = do_res_sample_script; 279562306a36Sopenharmony_ci act->evsel = evsel; 279662306a36Sopenharmony_ci act->rstype = type; 279762306a36Sopenharmony_ci return 1; 279862306a36Sopenharmony_ci} 279962306a36Sopenharmony_ci 280062306a36Sopenharmony_cistatic int 280162306a36Sopenharmony_cido_switch_data(struct hist_browser *browser __maybe_unused, 280262306a36Sopenharmony_ci struct popup_action *act __maybe_unused) 280362306a36Sopenharmony_ci{ 280462306a36Sopenharmony_ci if (switch_data_file()) { 280562306a36Sopenharmony_ci ui__warning("Won't switch the data files due to\n" 280662306a36Sopenharmony_ci "no valid data file get selected!\n"); 280762306a36Sopenharmony_ci return 0; 280862306a36Sopenharmony_ci } 280962306a36Sopenharmony_ci 281062306a36Sopenharmony_ci return K_SWITCH_INPUT_DATA; 281162306a36Sopenharmony_ci} 281262306a36Sopenharmony_ci 281362306a36Sopenharmony_cistatic int 281462306a36Sopenharmony_ciadd_switch_opt(struct hist_browser *browser, 281562306a36Sopenharmony_ci struct popup_action *act, char **optstr) 281662306a36Sopenharmony_ci{ 281762306a36Sopenharmony_ci if (!is_report_browser(browser->hbt)) 281862306a36Sopenharmony_ci return 0; 281962306a36Sopenharmony_ci 282062306a36Sopenharmony_ci if (asprintf(optstr, "Switch to another data file in PWD") < 0) 282162306a36Sopenharmony_ci return 0; 282262306a36Sopenharmony_ci 282362306a36Sopenharmony_ci act->fn = do_switch_data; 282462306a36Sopenharmony_ci return 1; 282562306a36Sopenharmony_ci} 282662306a36Sopenharmony_ci 282762306a36Sopenharmony_cistatic int 282862306a36Sopenharmony_cido_exit_browser(struct hist_browser *browser __maybe_unused, 282962306a36Sopenharmony_ci struct popup_action *act __maybe_unused) 283062306a36Sopenharmony_ci{ 283162306a36Sopenharmony_ci return 0; 283262306a36Sopenharmony_ci} 283362306a36Sopenharmony_ci 283462306a36Sopenharmony_cistatic int 283562306a36Sopenharmony_ciadd_exit_opt(struct hist_browser *browser __maybe_unused, 283662306a36Sopenharmony_ci struct popup_action *act, char **optstr) 283762306a36Sopenharmony_ci{ 283862306a36Sopenharmony_ci if (asprintf(optstr, "Exit") < 0) 283962306a36Sopenharmony_ci return 0; 284062306a36Sopenharmony_ci 284162306a36Sopenharmony_ci act->fn = do_exit_browser; 284262306a36Sopenharmony_ci return 1; 284362306a36Sopenharmony_ci} 284462306a36Sopenharmony_ci 284562306a36Sopenharmony_cistatic int 284662306a36Sopenharmony_cido_zoom_socket(struct hist_browser *browser, struct popup_action *act) 284762306a36Sopenharmony_ci{ 284862306a36Sopenharmony_ci if (!hists__has(browser->hists, socket) || act->socket < 0) 284962306a36Sopenharmony_ci return 0; 285062306a36Sopenharmony_ci 285162306a36Sopenharmony_ci if (browser->hists->socket_filter > -1) { 285262306a36Sopenharmony_ci pstack__remove(browser->pstack, &browser->hists->socket_filter); 285362306a36Sopenharmony_ci browser->hists->socket_filter = -1; 285462306a36Sopenharmony_ci perf_hpp__set_elide(HISTC_SOCKET, false); 285562306a36Sopenharmony_ci } else { 285662306a36Sopenharmony_ci browser->hists->socket_filter = act->socket; 285762306a36Sopenharmony_ci perf_hpp__set_elide(HISTC_SOCKET, true); 285862306a36Sopenharmony_ci pstack__push(browser->pstack, &browser->hists->socket_filter); 285962306a36Sopenharmony_ci } 286062306a36Sopenharmony_ci 286162306a36Sopenharmony_ci hists__filter_by_socket(browser->hists); 286262306a36Sopenharmony_ci hist_browser__reset(browser); 286362306a36Sopenharmony_ci return 0; 286462306a36Sopenharmony_ci} 286562306a36Sopenharmony_ci 286662306a36Sopenharmony_cistatic int 286762306a36Sopenharmony_ciadd_socket_opt(struct hist_browser *browser, struct popup_action *act, 286862306a36Sopenharmony_ci char **optstr, int socket_id) 286962306a36Sopenharmony_ci{ 287062306a36Sopenharmony_ci if (!hists__has(browser->hists, socket) || socket_id < 0) 287162306a36Sopenharmony_ci return 0; 287262306a36Sopenharmony_ci 287362306a36Sopenharmony_ci if (asprintf(optstr, "Zoom %s Processor Socket %d", 287462306a36Sopenharmony_ci (browser->hists->socket_filter > -1) ? "out of" : "into", 287562306a36Sopenharmony_ci socket_id) < 0) 287662306a36Sopenharmony_ci return 0; 287762306a36Sopenharmony_ci 287862306a36Sopenharmony_ci act->socket = socket_id; 287962306a36Sopenharmony_ci act->fn = do_zoom_socket; 288062306a36Sopenharmony_ci return 1; 288162306a36Sopenharmony_ci} 288262306a36Sopenharmony_ci 288362306a36Sopenharmony_cistatic void hist_browser__update_nr_entries(struct hist_browser *hb) 288462306a36Sopenharmony_ci{ 288562306a36Sopenharmony_ci u64 nr_entries = 0; 288662306a36Sopenharmony_ci struct rb_node *nd = rb_first_cached(&hb->hists->entries); 288762306a36Sopenharmony_ci 288862306a36Sopenharmony_ci if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) { 288962306a36Sopenharmony_ci hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries; 289062306a36Sopenharmony_ci return; 289162306a36Sopenharmony_ci } 289262306a36Sopenharmony_ci 289362306a36Sopenharmony_ci while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) { 289462306a36Sopenharmony_ci nr_entries++; 289562306a36Sopenharmony_ci nd = rb_hierarchy_next(nd); 289662306a36Sopenharmony_ci } 289762306a36Sopenharmony_ci 289862306a36Sopenharmony_ci hb->nr_non_filtered_entries = nr_entries; 289962306a36Sopenharmony_ci hb->nr_hierarchy_entries = nr_entries; 290062306a36Sopenharmony_ci} 290162306a36Sopenharmony_ci 290262306a36Sopenharmony_cistatic void hist_browser__update_percent_limit(struct hist_browser *hb, 290362306a36Sopenharmony_ci double percent) 290462306a36Sopenharmony_ci{ 290562306a36Sopenharmony_ci struct hist_entry *he; 290662306a36Sopenharmony_ci struct rb_node *nd = rb_first_cached(&hb->hists->entries); 290762306a36Sopenharmony_ci u64 total = hists__total_period(hb->hists); 290862306a36Sopenharmony_ci u64 min_callchain_hits = total * (percent / 100); 290962306a36Sopenharmony_ci 291062306a36Sopenharmony_ci hb->min_pcnt = callchain_param.min_percent = percent; 291162306a36Sopenharmony_ci 291262306a36Sopenharmony_ci while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) { 291362306a36Sopenharmony_ci he = rb_entry(nd, struct hist_entry, rb_node); 291462306a36Sopenharmony_ci 291562306a36Sopenharmony_ci if (he->has_no_entry) { 291662306a36Sopenharmony_ci he->has_no_entry = false; 291762306a36Sopenharmony_ci he->nr_rows = 0; 291862306a36Sopenharmony_ci } 291962306a36Sopenharmony_ci 292062306a36Sopenharmony_ci if (!he->leaf || !hist_entry__has_callchains(he) || !symbol_conf.use_callchain) 292162306a36Sopenharmony_ci goto next; 292262306a36Sopenharmony_ci 292362306a36Sopenharmony_ci if (callchain_param.mode == CHAIN_GRAPH_REL) { 292462306a36Sopenharmony_ci total = he->stat.period; 292562306a36Sopenharmony_ci 292662306a36Sopenharmony_ci if (symbol_conf.cumulate_callchain) 292762306a36Sopenharmony_ci total = he->stat_acc->period; 292862306a36Sopenharmony_ci 292962306a36Sopenharmony_ci min_callchain_hits = total * (percent / 100); 293062306a36Sopenharmony_ci } 293162306a36Sopenharmony_ci 293262306a36Sopenharmony_ci callchain_param.sort(&he->sorted_chain, he->callchain, 293362306a36Sopenharmony_ci min_callchain_hits, &callchain_param); 293462306a36Sopenharmony_ci 293562306a36Sopenharmony_cinext: 293662306a36Sopenharmony_ci nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD); 293762306a36Sopenharmony_ci 293862306a36Sopenharmony_ci /* force to re-evaluate folding state of callchains */ 293962306a36Sopenharmony_ci he->init_have_children = false; 294062306a36Sopenharmony_ci hist_entry__set_folding(he, hb, false); 294162306a36Sopenharmony_ci } 294262306a36Sopenharmony_ci} 294362306a36Sopenharmony_ci 294462306a36Sopenharmony_cistatic int evsel__hists_browse(struct evsel *evsel, int nr_events, const char *helpline, 294562306a36Sopenharmony_ci bool left_exits, struct hist_browser_timer *hbt, float min_pcnt, 294662306a36Sopenharmony_ci struct perf_env *env, bool warn_lost_event, 294762306a36Sopenharmony_ci struct annotation_options *annotation_opts) 294862306a36Sopenharmony_ci{ 294962306a36Sopenharmony_ci struct hists *hists = evsel__hists(evsel); 295062306a36Sopenharmony_ci struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env, annotation_opts); 295162306a36Sopenharmony_ci struct branch_info *bi = NULL; 295262306a36Sopenharmony_ci#define MAX_OPTIONS 16 295362306a36Sopenharmony_ci char *options[MAX_OPTIONS]; 295462306a36Sopenharmony_ci struct popup_action actions[MAX_OPTIONS]; 295562306a36Sopenharmony_ci int nr_options = 0; 295662306a36Sopenharmony_ci int key = -1; 295762306a36Sopenharmony_ci char buf[128]; 295862306a36Sopenharmony_ci int delay_secs = hbt ? hbt->refresh : 0; 295962306a36Sopenharmony_ci 296062306a36Sopenharmony_ci#define HIST_BROWSER_HELP_COMMON \ 296162306a36Sopenharmony_ci "h/?/F1 Show this window\n" \ 296262306a36Sopenharmony_ci "UP/DOWN/PGUP\n" \ 296362306a36Sopenharmony_ci "PGDN/SPACE Navigate\n" \ 296462306a36Sopenharmony_ci "q/ESC/CTRL+C Exit browser or go back to previous screen\n\n" \ 296562306a36Sopenharmony_ci "For multiple event sessions:\n\n" \ 296662306a36Sopenharmony_ci "TAB/UNTAB Switch events\n\n" \ 296762306a36Sopenharmony_ci "For symbolic views (--sort has sym):\n\n" \ 296862306a36Sopenharmony_ci "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \ 296962306a36Sopenharmony_ci "ESC Zoom out\n" \ 297062306a36Sopenharmony_ci "+ Expand/Collapse one callchain level\n" \ 297162306a36Sopenharmony_ci "a Annotate current symbol\n" \ 297262306a36Sopenharmony_ci "C Collapse all callchains\n" \ 297362306a36Sopenharmony_ci "d Zoom into current DSO\n" \ 297462306a36Sopenharmony_ci "e Expand/Collapse main entry callchains\n" \ 297562306a36Sopenharmony_ci "E Expand all callchains\n" \ 297662306a36Sopenharmony_ci "F Toggle percentage of filtered entries\n" \ 297762306a36Sopenharmony_ci "H Display column headers\n" \ 297862306a36Sopenharmony_ci "k Zoom into the kernel map\n" \ 297962306a36Sopenharmony_ci "L Change percent limit\n" \ 298062306a36Sopenharmony_ci "m Display context menu\n" \ 298162306a36Sopenharmony_ci "S Zoom into current Processor Socket\n" \ 298262306a36Sopenharmony_ci 298362306a36Sopenharmony_ci /* help messages are sorted by lexical order of the hotkey */ 298462306a36Sopenharmony_ci static const char report_help[] = HIST_BROWSER_HELP_COMMON 298562306a36Sopenharmony_ci "i Show header information\n" 298662306a36Sopenharmony_ci "P Print histograms to perf.hist.N\n" 298762306a36Sopenharmony_ci "r Run available scripts\n" 298862306a36Sopenharmony_ci "s Switch to another data file in PWD\n" 298962306a36Sopenharmony_ci "t Zoom into current Thread\n" 299062306a36Sopenharmony_ci "V Verbose (DSO names in callchains, etc)\n" 299162306a36Sopenharmony_ci "/ Filter symbol by name\n" 299262306a36Sopenharmony_ci "0-9 Sort by event n in group"; 299362306a36Sopenharmony_ci static const char top_help[] = HIST_BROWSER_HELP_COMMON 299462306a36Sopenharmony_ci "P Print histograms to perf.hist.N\n" 299562306a36Sopenharmony_ci "t Zoom into current Thread\n" 299662306a36Sopenharmony_ci "V Verbose (DSO names in callchains, etc)\n" 299762306a36Sopenharmony_ci "z Toggle zeroing of samples\n" 299862306a36Sopenharmony_ci "f Enable/Disable events\n" 299962306a36Sopenharmony_ci "/ Filter symbol by name"; 300062306a36Sopenharmony_ci 300162306a36Sopenharmony_ci if (browser == NULL) 300262306a36Sopenharmony_ci return -1; 300362306a36Sopenharmony_ci 300462306a36Sopenharmony_ci /* reset abort key so that it can get Ctrl-C as a key */ 300562306a36Sopenharmony_ci SLang_reset_tty(); 300662306a36Sopenharmony_ci SLang_init_tty(0, 0, 0); 300762306a36Sopenharmony_ci 300862306a36Sopenharmony_ci if (min_pcnt) 300962306a36Sopenharmony_ci browser->min_pcnt = min_pcnt; 301062306a36Sopenharmony_ci hist_browser__update_nr_entries(browser); 301162306a36Sopenharmony_ci 301262306a36Sopenharmony_ci browser->pstack = pstack__new(3); 301362306a36Sopenharmony_ci if (browser->pstack == NULL) 301462306a36Sopenharmony_ci goto out; 301562306a36Sopenharmony_ci 301662306a36Sopenharmony_ci ui_helpline__push(helpline); 301762306a36Sopenharmony_ci 301862306a36Sopenharmony_ci memset(options, 0, sizeof(options)); 301962306a36Sopenharmony_ci memset(actions, 0, sizeof(actions)); 302062306a36Sopenharmony_ci 302162306a36Sopenharmony_ci if (symbol_conf.col_width_list_str) 302262306a36Sopenharmony_ci perf_hpp__set_user_width(symbol_conf.col_width_list_str); 302362306a36Sopenharmony_ci 302462306a36Sopenharmony_ci if (!is_report_browser(hbt)) 302562306a36Sopenharmony_ci browser->b.no_samples_msg = "Collecting samples..."; 302662306a36Sopenharmony_ci 302762306a36Sopenharmony_ci while (1) { 302862306a36Sopenharmony_ci struct thread *thread = NULL; 302962306a36Sopenharmony_ci struct map *map = NULL; 303062306a36Sopenharmony_ci int choice; 303162306a36Sopenharmony_ci int socked_id = -1; 303262306a36Sopenharmony_ci 303362306a36Sopenharmony_ci key = 0; // reset key 303462306a36Sopenharmony_cido_hotkey: // key came straight from options ui__popup_menu() 303562306a36Sopenharmony_ci choice = nr_options = 0; 303662306a36Sopenharmony_ci key = hist_browser__run(browser, helpline, warn_lost_event, key); 303762306a36Sopenharmony_ci 303862306a36Sopenharmony_ci if (browser->he_selection != NULL) { 303962306a36Sopenharmony_ci thread = hist_browser__selected_thread(browser); 304062306a36Sopenharmony_ci map = browser->selection->map; 304162306a36Sopenharmony_ci socked_id = browser->he_selection->socket; 304262306a36Sopenharmony_ci } 304362306a36Sopenharmony_ci switch (key) { 304462306a36Sopenharmony_ci case K_TAB: 304562306a36Sopenharmony_ci case K_UNTAB: 304662306a36Sopenharmony_ci if (nr_events == 1) 304762306a36Sopenharmony_ci continue; 304862306a36Sopenharmony_ci /* 304962306a36Sopenharmony_ci * Exit the browser, let hists__browser_tree 305062306a36Sopenharmony_ci * go to the next or previous 305162306a36Sopenharmony_ci */ 305262306a36Sopenharmony_ci goto out_free_stack; 305362306a36Sopenharmony_ci case '0' ... '9': 305462306a36Sopenharmony_ci if (!symbol_conf.event_group || 305562306a36Sopenharmony_ci evsel->core.nr_members < 2) { 305662306a36Sopenharmony_ci snprintf(buf, sizeof(buf), 305762306a36Sopenharmony_ci "Sort by index only available with group events!"); 305862306a36Sopenharmony_ci helpline = buf; 305962306a36Sopenharmony_ci continue; 306062306a36Sopenharmony_ci } 306162306a36Sopenharmony_ci 306262306a36Sopenharmony_ci if (key - '0' == symbol_conf.group_sort_idx) 306362306a36Sopenharmony_ci continue; 306462306a36Sopenharmony_ci 306562306a36Sopenharmony_ci symbol_conf.group_sort_idx = key - '0'; 306662306a36Sopenharmony_ci 306762306a36Sopenharmony_ci if (symbol_conf.group_sort_idx >= evsel->core.nr_members) { 306862306a36Sopenharmony_ci snprintf(buf, sizeof(buf), 306962306a36Sopenharmony_ci "Max event group index to sort is %d (index from 0 to %d)", 307062306a36Sopenharmony_ci evsel->core.nr_members - 1, 307162306a36Sopenharmony_ci evsel->core.nr_members - 1); 307262306a36Sopenharmony_ci helpline = buf; 307362306a36Sopenharmony_ci continue; 307462306a36Sopenharmony_ci } 307562306a36Sopenharmony_ci 307662306a36Sopenharmony_ci key = K_RELOAD; 307762306a36Sopenharmony_ci goto out_free_stack; 307862306a36Sopenharmony_ci case 'a': 307962306a36Sopenharmony_ci if (!hists__has(hists, sym)) { 308062306a36Sopenharmony_ci ui_browser__warning(&browser->b, delay_secs * 2, 308162306a36Sopenharmony_ci "Annotation is only available for symbolic views, " 308262306a36Sopenharmony_ci "include \"sym*\" in --sort to use it."); 308362306a36Sopenharmony_ci continue; 308462306a36Sopenharmony_ci } 308562306a36Sopenharmony_ci 308662306a36Sopenharmony_ci if (!browser->selection || 308762306a36Sopenharmony_ci !browser->selection->map || 308862306a36Sopenharmony_ci !map__dso(browser->selection->map) || 308962306a36Sopenharmony_ci map__dso(browser->selection->map)->annotate_warned) { 309062306a36Sopenharmony_ci continue; 309162306a36Sopenharmony_ci } 309262306a36Sopenharmony_ci 309362306a36Sopenharmony_ci if (!browser->selection->sym) { 309462306a36Sopenharmony_ci if (!browser->he_selection) 309562306a36Sopenharmony_ci continue; 309662306a36Sopenharmony_ci 309762306a36Sopenharmony_ci if (sort__mode == SORT_MODE__BRANCH) { 309862306a36Sopenharmony_ci bi = browser->he_selection->branch_info; 309962306a36Sopenharmony_ci if (!bi || !bi->to.ms.map) 310062306a36Sopenharmony_ci continue; 310162306a36Sopenharmony_ci 310262306a36Sopenharmony_ci actions->ms.sym = symbol__new_unresolved(bi->to.al_addr, bi->to.ms.map); 310362306a36Sopenharmony_ci actions->ms.map = bi->to.ms.map; 310462306a36Sopenharmony_ci } else { 310562306a36Sopenharmony_ci actions->ms.sym = symbol__new_unresolved(browser->he_selection->ip, 310662306a36Sopenharmony_ci browser->selection->map); 310762306a36Sopenharmony_ci actions->ms.map = browser->selection->map; 310862306a36Sopenharmony_ci } 310962306a36Sopenharmony_ci 311062306a36Sopenharmony_ci if (!actions->ms.sym) 311162306a36Sopenharmony_ci continue; 311262306a36Sopenharmony_ci } else { 311362306a36Sopenharmony_ci if (symbol__annotation(browser->selection->sym)->src == NULL) { 311462306a36Sopenharmony_ci ui_browser__warning(&browser->b, delay_secs * 2, 311562306a36Sopenharmony_ci "No samples for the \"%s\" symbol.\n\n" 311662306a36Sopenharmony_ci "Probably appeared just in a callchain", 311762306a36Sopenharmony_ci browser->selection->sym->name); 311862306a36Sopenharmony_ci continue; 311962306a36Sopenharmony_ci } 312062306a36Sopenharmony_ci 312162306a36Sopenharmony_ci actions->ms.map = browser->selection->map; 312262306a36Sopenharmony_ci actions->ms.sym = browser->selection->sym; 312362306a36Sopenharmony_ci } 312462306a36Sopenharmony_ci 312562306a36Sopenharmony_ci do_annotate(browser, actions); 312662306a36Sopenharmony_ci continue; 312762306a36Sopenharmony_ci case 'P': 312862306a36Sopenharmony_ci hist_browser__dump(browser); 312962306a36Sopenharmony_ci continue; 313062306a36Sopenharmony_ci case 'd': 313162306a36Sopenharmony_ci actions->ms.map = map; 313262306a36Sopenharmony_ci do_zoom_dso(browser, actions); 313362306a36Sopenharmony_ci continue; 313462306a36Sopenharmony_ci case 'k': 313562306a36Sopenharmony_ci if (browser->selection != NULL) 313662306a36Sopenharmony_ci hists_browser__zoom_map(browser, 313762306a36Sopenharmony_ci maps__machine(browser->selection->maps)->vmlinux_map); 313862306a36Sopenharmony_ci continue; 313962306a36Sopenharmony_ci case 'V': 314062306a36Sopenharmony_ci verbose = (verbose + 1) % 4; 314162306a36Sopenharmony_ci browser->show_dso = verbose > 0; 314262306a36Sopenharmony_ci ui_helpline__fpush("Verbosity level set to %d\n", 314362306a36Sopenharmony_ci verbose); 314462306a36Sopenharmony_ci continue; 314562306a36Sopenharmony_ci case 't': 314662306a36Sopenharmony_ci actions->thread = thread; 314762306a36Sopenharmony_ci do_zoom_thread(browser, actions); 314862306a36Sopenharmony_ci continue; 314962306a36Sopenharmony_ci case 'S': 315062306a36Sopenharmony_ci actions->socket = socked_id; 315162306a36Sopenharmony_ci do_zoom_socket(browser, actions); 315262306a36Sopenharmony_ci continue; 315362306a36Sopenharmony_ci case '/': 315462306a36Sopenharmony_ci if (ui_browser__input_window("Symbol to show", 315562306a36Sopenharmony_ci "Please enter the name of symbol you want to see.\n" 315662306a36Sopenharmony_ci "To remove the filter later, press / + ENTER.", 315762306a36Sopenharmony_ci buf, "ENTER: OK, ESC: Cancel", 315862306a36Sopenharmony_ci delay_secs * 2) == K_ENTER) { 315962306a36Sopenharmony_ci hists->symbol_filter_str = *buf ? buf : NULL; 316062306a36Sopenharmony_ci hists__filter_by_symbol(hists); 316162306a36Sopenharmony_ci hist_browser__reset(browser); 316262306a36Sopenharmony_ci } 316362306a36Sopenharmony_ci continue; 316462306a36Sopenharmony_ci case 'r': 316562306a36Sopenharmony_ci if (is_report_browser(hbt)) { 316662306a36Sopenharmony_ci actions->thread = NULL; 316762306a36Sopenharmony_ci actions->ms.sym = NULL; 316862306a36Sopenharmony_ci do_run_script(browser, actions); 316962306a36Sopenharmony_ci } 317062306a36Sopenharmony_ci continue; 317162306a36Sopenharmony_ci case 's': 317262306a36Sopenharmony_ci if (is_report_browser(hbt)) { 317362306a36Sopenharmony_ci key = do_switch_data(browser, actions); 317462306a36Sopenharmony_ci if (key == K_SWITCH_INPUT_DATA) 317562306a36Sopenharmony_ci goto out_free_stack; 317662306a36Sopenharmony_ci } 317762306a36Sopenharmony_ci continue; 317862306a36Sopenharmony_ci case 'i': 317962306a36Sopenharmony_ci /* env->arch is NULL for live-mode (i.e. perf top) */ 318062306a36Sopenharmony_ci if (env->arch) 318162306a36Sopenharmony_ci tui__header_window(env); 318262306a36Sopenharmony_ci continue; 318362306a36Sopenharmony_ci case 'F': 318462306a36Sopenharmony_ci symbol_conf.filter_relative ^= 1; 318562306a36Sopenharmony_ci continue; 318662306a36Sopenharmony_ci case 'z': 318762306a36Sopenharmony_ci if (!is_report_browser(hbt)) { 318862306a36Sopenharmony_ci struct perf_top *top = hbt->arg; 318962306a36Sopenharmony_ci 319062306a36Sopenharmony_ci top->zero = !top->zero; 319162306a36Sopenharmony_ci } 319262306a36Sopenharmony_ci continue; 319362306a36Sopenharmony_ci case 'L': 319462306a36Sopenharmony_ci if (ui_browser__input_window("Percent Limit", 319562306a36Sopenharmony_ci "Please enter the value you want to hide entries under that percent.", 319662306a36Sopenharmony_ci buf, "ENTER: OK, ESC: Cancel", 319762306a36Sopenharmony_ci delay_secs * 2) == K_ENTER) { 319862306a36Sopenharmony_ci char *end; 319962306a36Sopenharmony_ci double new_percent = strtod(buf, &end); 320062306a36Sopenharmony_ci 320162306a36Sopenharmony_ci if (new_percent < 0 || new_percent > 100) { 320262306a36Sopenharmony_ci ui_browser__warning(&browser->b, delay_secs * 2, 320362306a36Sopenharmony_ci "Invalid percent: %.2f", new_percent); 320462306a36Sopenharmony_ci continue; 320562306a36Sopenharmony_ci } 320662306a36Sopenharmony_ci 320762306a36Sopenharmony_ci hist_browser__update_percent_limit(browser, new_percent); 320862306a36Sopenharmony_ci hist_browser__reset(browser); 320962306a36Sopenharmony_ci } 321062306a36Sopenharmony_ci continue; 321162306a36Sopenharmony_ci case K_F1: 321262306a36Sopenharmony_ci case 'h': 321362306a36Sopenharmony_ci case '?': 321462306a36Sopenharmony_ci ui_browser__help_window(&browser->b, 321562306a36Sopenharmony_ci is_report_browser(hbt) ? report_help : top_help); 321662306a36Sopenharmony_ci continue; 321762306a36Sopenharmony_ci case K_ENTER: 321862306a36Sopenharmony_ci case K_RIGHT: 321962306a36Sopenharmony_ci case 'm': 322062306a36Sopenharmony_ci /* menu */ 322162306a36Sopenharmony_ci break; 322262306a36Sopenharmony_ci case K_ESC: 322362306a36Sopenharmony_ci case K_LEFT: { 322462306a36Sopenharmony_ci const void *top; 322562306a36Sopenharmony_ci 322662306a36Sopenharmony_ci if (pstack__empty(browser->pstack)) { 322762306a36Sopenharmony_ci /* 322862306a36Sopenharmony_ci * Go back to the perf_evsel_menu__run or other user 322962306a36Sopenharmony_ci */ 323062306a36Sopenharmony_ci if (left_exits) 323162306a36Sopenharmony_ci goto out_free_stack; 323262306a36Sopenharmony_ci 323362306a36Sopenharmony_ci if (key == K_ESC && 323462306a36Sopenharmony_ci ui_browser__dialog_yesno(&browser->b, 323562306a36Sopenharmony_ci "Do you really want to exit?")) 323662306a36Sopenharmony_ci goto out_free_stack; 323762306a36Sopenharmony_ci 323862306a36Sopenharmony_ci continue; 323962306a36Sopenharmony_ci } 324062306a36Sopenharmony_ci actions->ms.map = map; 324162306a36Sopenharmony_ci top = pstack__peek(browser->pstack); 324262306a36Sopenharmony_ci if (top == &browser->hists->dso_filter) { 324362306a36Sopenharmony_ci /* 324462306a36Sopenharmony_ci * No need to set actions->dso here since 324562306a36Sopenharmony_ci * it's just to remove the current filter. 324662306a36Sopenharmony_ci * Ditto for thread below. 324762306a36Sopenharmony_ci */ 324862306a36Sopenharmony_ci do_zoom_dso(browser, actions); 324962306a36Sopenharmony_ci } else if (top == &browser->hists->thread_filter) { 325062306a36Sopenharmony_ci do_zoom_thread(browser, actions); 325162306a36Sopenharmony_ci } else if (top == &browser->hists->socket_filter) { 325262306a36Sopenharmony_ci do_zoom_socket(browser, actions); 325362306a36Sopenharmony_ci } 325462306a36Sopenharmony_ci continue; 325562306a36Sopenharmony_ci } 325662306a36Sopenharmony_ci case 'q': 325762306a36Sopenharmony_ci case CTRL('c'): 325862306a36Sopenharmony_ci goto out_free_stack; 325962306a36Sopenharmony_ci case 'f': 326062306a36Sopenharmony_ci if (!is_report_browser(hbt)) { 326162306a36Sopenharmony_ci struct perf_top *top = hbt->arg; 326262306a36Sopenharmony_ci 326362306a36Sopenharmony_ci evlist__toggle_enable(top->evlist); 326462306a36Sopenharmony_ci /* 326562306a36Sopenharmony_ci * No need to refresh, resort/decay histogram 326662306a36Sopenharmony_ci * entries if we are not collecting samples: 326762306a36Sopenharmony_ci */ 326862306a36Sopenharmony_ci if (top->evlist->enabled) { 326962306a36Sopenharmony_ci helpline = "Press 'f' to disable the events or 'h' to see other hotkeys"; 327062306a36Sopenharmony_ci hbt->refresh = delay_secs; 327162306a36Sopenharmony_ci } else { 327262306a36Sopenharmony_ci helpline = "Press 'f' again to re-enable the events"; 327362306a36Sopenharmony_ci hbt->refresh = 0; 327462306a36Sopenharmony_ci } 327562306a36Sopenharmony_ci continue; 327662306a36Sopenharmony_ci } 327762306a36Sopenharmony_ci /* Fall thru */ 327862306a36Sopenharmony_ci default: 327962306a36Sopenharmony_ci helpline = "Press '?' for help on key bindings"; 328062306a36Sopenharmony_ci continue; 328162306a36Sopenharmony_ci } 328262306a36Sopenharmony_ci 328362306a36Sopenharmony_ci if (!hists__has(hists, sym) || browser->selection == NULL) 328462306a36Sopenharmony_ci goto skip_annotation; 328562306a36Sopenharmony_ci 328662306a36Sopenharmony_ci if (sort__mode == SORT_MODE__BRANCH) { 328762306a36Sopenharmony_ci 328862306a36Sopenharmony_ci if (browser->he_selection) 328962306a36Sopenharmony_ci bi = browser->he_selection->branch_info; 329062306a36Sopenharmony_ci 329162306a36Sopenharmony_ci if (bi == NULL) 329262306a36Sopenharmony_ci goto skip_annotation; 329362306a36Sopenharmony_ci 329462306a36Sopenharmony_ci nr_options += add_annotate_opt(browser, 329562306a36Sopenharmony_ci &actions[nr_options], 329662306a36Sopenharmony_ci &options[nr_options], 329762306a36Sopenharmony_ci &bi->from.ms, 329862306a36Sopenharmony_ci bi->from.al_addr); 329962306a36Sopenharmony_ci if (bi->to.ms.sym != bi->from.ms.sym) 330062306a36Sopenharmony_ci nr_options += add_annotate_opt(browser, 330162306a36Sopenharmony_ci &actions[nr_options], 330262306a36Sopenharmony_ci &options[nr_options], 330362306a36Sopenharmony_ci &bi->to.ms, 330462306a36Sopenharmony_ci bi->to.al_addr); 330562306a36Sopenharmony_ci } else { 330662306a36Sopenharmony_ci nr_options += add_annotate_opt(browser, 330762306a36Sopenharmony_ci &actions[nr_options], 330862306a36Sopenharmony_ci &options[nr_options], 330962306a36Sopenharmony_ci browser->selection, 331062306a36Sopenharmony_ci browser->he_selection->ip); 331162306a36Sopenharmony_ci } 331262306a36Sopenharmony_ciskip_annotation: 331362306a36Sopenharmony_ci nr_options += add_thread_opt(browser, &actions[nr_options], 331462306a36Sopenharmony_ci &options[nr_options], thread); 331562306a36Sopenharmony_ci nr_options += add_dso_opt(browser, &actions[nr_options], 331662306a36Sopenharmony_ci &options[nr_options], map); 331762306a36Sopenharmony_ci nr_options += add_callchain_toggle_opt(browser, &actions[nr_options], &options[nr_options]); 331862306a36Sopenharmony_ci nr_options += add_map_opt(browser, &actions[nr_options], 331962306a36Sopenharmony_ci &options[nr_options], 332062306a36Sopenharmony_ci browser->selection ? 332162306a36Sopenharmony_ci browser->selection->map : NULL); 332262306a36Sopenharmony_ci nr_options += add_socket_opt(browser, &actions[nr_options], 332362306a36Sopenharmony_ci &options[nr_options], 332462306a36Sopenharmony_ci socked_id); 332562306a36Sopenharmony_ci /* perf script support */ 332662306a36Sopenharmony_ci if (!is_report_browser(hbt)) 332762306a36Sopenharmony_ci goto skip_scripting; 332862306a36Sopenharmony_ci 332962306a36Sopenharmony_ci if (browser->he_selection) { 333062306a36Sopenharmony_ci if (hists__has(hists, thread) && thread) { 333162306a36Sopenharmony_ci nr_options += add_script_opt(browser, 333262306a36Sopenharmony_ci &actions[nr_options], 333362306a36Sopenharmony_ci &options[nr_options], 333462306a36Sopenharmony_ci thread, NULL, evsel); 333562306a36Sopenharmony_ci } 333662306a36Sopenharmony_ci /* 333762306a36Sopenharmony_ci * Note that browser->selection != NULL 333862306a36Sopenharmony_ci * when browser->he_selection is not NULL, 333962306a36Sopenharmony_ci * so we don't need to check browser->selection 334062306a36Sopenharmony_ci * before fetching browser->selection->sym like what 334162306a36Sopenharmony_ci * we do before fetching browser->selection->map. 334262306a36Sopenharmony_ci * 334362306a36Sopenharmony_ci * See hist_browser__show_entry. 334462306a36Sopenharmony_ci */ 334562306a36Sopenharmony_ci if (hists__has(hists, sym) && browser->selection->sym) { 334662306a36Sopenharmony_ci nr_options += add_script_opt(browser, 334762306a36Sopenharmony_ci &actions[nr_options], 334862306a36Sopenharmony_ci &options[nr_options], 334962306a36Sopenharmony_ci NULL, browser->selection->sym, 335062306a36Sopenharmony_ci evsel); 335162306a36Sopenharmony_ci } 335262306a36Sopenharmony_ci } 335362306a36Sopenharmony_ci nr_options += add_script_opt(browser, &actions[nr_options], 335462306a36Sopenharmony_ci &options[nr_options], NULL, NULL, evsel); 335562306a36Sopenharmony_ci nr_options += add_res_sample_opt(browser, &actions[nr_options], 335662306a36Sopenharmony_ci &options[nr_options], 335762306a36Sopenharmony_ci hist_browser__selected_res_sample(browser), 335862306a36Sopenharmony_ci evsel, A_NORMAL); 335962306a36Sopenharmony_ci nr_options += add_res_sample_opt(browser, &actions[nr_options], 336062306a36Sopenharmony_ci &options[nr_options], 336162306a36Sopenharmony_ci hist_browser__selected_res_sample(browser), 336262306a36Sopenharmony_ci evsel, A_ASM); 336362306a36Sopenharmony_ci nr_options += add_res_sample_opt(browser, &actions[nr_options], 336462306a36Sopenharmony_ci &options[nr_options], 336562306a36Sopenharmony_ci hist_browser__selected_res_sample(browser), 336662306a36Sopenharmony_ci evsel, A_SOURCE); 336762306a36Sopenharmony_ci nr_options += add_switch_opt(browser, &actions[nr_options], 336862306a36Sopenharmony_ci &options[nr_options]); 336962306a36Sopenharmony_ciskip_scripting: 337062306a36Sopenharmony_ci nr_options += add_exit_opt(browser, &actions[nr_options], 337162306a36Sopenharmony_ci &options[nr_options]); 337262306a36Sopenharmony_ci 337362306a36Sopenharmony_ci do { 337462306a36Sopenharmony_ci struct popup_action *act; 337562306a36Sopenharmony_ci 337662306a36Sopenharmony_ci choice = ui__popup_menu(nr_options, options, &key); 337762306a36Sopenharmony_ci if (choice == -1) 337862306a36Sopenharmony_ci break; 337962306a36Sopenharmony_ci 338062306a36Sopenharmony_ci if (choice == nr_options) 338162306a36Sopenharmony_ci goto do_hotkey; 338262306a36Sopenharmony_ci 338362306a36Sopenharmony_ci act = &actions[choice]; 338462306a36Sopenharmony_ci key = act->fn(browser, act); 338562306a36Sopenharmony_ci } while (key == 1); 338662306a36Sopenharmony_ci 338762306a36Sopenharmony_ci if (key == K_SWITCH_INPUT_DATA) 338862306a36Sopenharmony_ci break; 338962306a36Sopenharmony_ci } 339062306a36Sopenharmony_ciout_free_stack: 339162306a36Sopenharmony_ci pstack__delete(browser->pstack); 339262306a36Sopenharmony_ciout: 339362306a36Sopenharmony_ci hist_browser__delete(browser); 339462306a36Sopenharmony_ci free_popup_options(options, MAX_OPTIONS); 339562306a36Sopenharmony_ci return key; 339662306a36Sopenharmony_ci} 339762306a36Sopenharmony_ci 339862306a36Sopenharmony_cistruct evsel_menu { 339962306a36Sopenharmony_ci struct ui_browser b; 340062306a36Sopenharmony_ci struct evsel *selection; 340162306a36Sopenharmony_ci struct annotation_options *annotation_opts; 340262306a36Sopenharmony_ci bool lost_events, lost_events_warned; 340362306a36Sopenharmony_ci float min_pcnt; 340462306a36Sopenharmony_ci struct perf_env *env; 340562306a36Sopenharmony_ci}; 340662306a36Sopenharmony_ci 340762306a36Sopenharmony_cistatic void perf_evsel_menu__write(struct ui_browser *browser, 340862306a36Sopenharmony_ci void *entry, int row) 340962306a36Sopenharmony_ci{ 341062306a36Sopenharmony_ci struct evsel_menu *menu = container_of(browser, 341162306a36Sopenharmony_ci struct evsel_menu, b); 341262306a36Sopenharmony_ci struct evsel *evsel = list_entry(entry, struct evsel, core.node); 341362306a36Sopenharmony_ci struct hists *hists = evsel__hists(evsel); 341462306a36Sopenharmony_ci bool current_entry = ui_browser__is_current_entry(browser, row); 341562306a36Sopenharmony_ci unsigned long nr_events = hists->stats.nr_samples; 341662306a36Sopenharmony_ci const char *ev_name = evsel__name(evsel); 341762306a36Sopenharmony_ci char bf[256], unit; 341862306a36Sopenharmony_ci const char *warn = " "; 341962306a36Sopenharmony_ci size_t printed; 342062306a36Sopenharmony_ci 342162306a36Sopenharmony_ci ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : 342262306a36Sopenharmony_ci HE_COLORSET_NORMAL); 342362306a36Sopenharmony_ci 342462306a36Sopenharmony_ci if (evsel__is_group_event(evsel)) { 342562306a36Sopenharmony_ci struct evsel *pos; 342662306a36Sopenharmony_ci 342762306a36Sopenharmony_ci ev_name = evsel__group_name(evsel); 342862306a36Sopenharmony_ci 342962306a36Sopenharmony_ci for_each_group_member(pos, evsel) { 343062306a36Sopenharmony_ci struct hists *pos_hists = evsel__hists(pos); 343162306a36Sopenharmony_ci nr_events += pos_hists->stats.nr_samples; 343262306a36Sopenharmony_ci } 343362306a36Sopenharmony_ci } 343462306a36Sopenharmony_ci 343562306a36Sopenharmony_ci nr_events = convert_unit(nr_events, &unit); 343662306a36Sopenharmony_ci printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, 343762306a36Sopenharmony_ci unit, unit == ' ' ? "" : " ", ev_name); 343862306a36Sopenharmony_ci ui_browser__printf(browser, "%s", bf); 343962306a36Sopenharmony_ci 344062306a36Sopenharmony_ci nr_events = evsel->evlist->stats.nr_events[PERF_RECORD_LOST]; 344162306a36Sopenharmony_ci if (nr_events != 0) { 344262306a36Sopenharmony_ci menu->lost_events = true; 344362306a36Sopenharmony_ci if (!current_entry) 344462306a36Sopenharmony_ci ui_browser__set_color(browser, HE_COLORSET_TOP); 344562306a36Sopenharmony_ci nr_events = convert_unit(nr_events, &unit); 344662306a36Sopenharmony_ci printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!", 344762306a36Sopenharmony_ci nr_events, unit, unit == ' ' ? "" : " "); 344862306a36Sopenharmony_ci warn = bf; 344962306a36Sopenharmony_ci } 345062306a36Sopenharmony_ci 345162306a36Sopenharmony_ci ui_browser__write_nstring(browser, warn, browser->width - printed); 345262306a36Sopenharmony_ci 345362306a36Sopenharmony_ci if (current_entry) 345462306a36Sopenharmony_ci menu->selection = evsel; 345562306a36Sopenharmony_ci} 345662306a36Sopenharmony_ci 345762306a36Sopenharmony_cistatic int perf_evsel_menu__run(struct evsel_menu *menu, 345862306a36Sopenharmony_ci int nr_events, const char *help, 345962306a36Sopenharmony_ci struct hist_browser_timer *hbt, 346062306a36Sopenharmony_ci bool warn_lost_event) 346162306a36Sopenharmony_ci{ 346262306a36Sopenharmony_ci struct evlist *evlist = menu->b.priv; 346362306a36Sopenharmony_ci struct evsel *pos; 346462306a36Sopenharmony_ci const char *title = "Available samples"; 346562306a36Sopenharmony_ci int delay_secs = hbt ? hbt->refresh : 0; 346662306a36Sopenharmony_ci int key; 346762306a36Sopenharmony_ci 346862306a36Sopenharmony_ci if (ui_browser__show(&menu->b, title, 346962306a36Sopenharmony_ci "ESC: exit, ENTER|->: Browse histograms") < 0) 347062306a36Sopenharmony_ci return -1; 347162306a36Sopenharmony_ci 347262306a36Sopenharmony_ci while (1) { 347362306a36Sopenharmony_ci key = ui_browser__run(&menu->b, delay_secs); 347462306a36Sopenharmony_ci 347562306a36Sopenharmony_ci switch (key) { 347662306a36Sopenharmony_ci case K_TIMER: 347762306a36Sopenharmony_ci if (hbt) 347862306a36Sopenharmony_ci hbt->timer(hbt->arg); 347962306a36Sopenharmony_ci 348062306a36Sopenharmony_ci if (!menu->lost_events_warned && 348162306a36Sopenharmony_ci menu->lost_events && 348262306a36Sopenharmony_ci warn_lost_event) { 348362306a36Sopenharmony_ci ui_browser__warn_lost_events(&menu->b); 348462306a36Sopenharmony_ci menu->lost_events_warned = true; 348562306a36Sopenharmony_ci } 348662306a36Sopenharmony_ci continue; 348762306a36Sopenharmony_ci case K_RIGHT: 348862306a36Sopenharmony_ci case K_ENTER: 348962306a36Sopenharmony_ci if (!menu->selection) 349062306a36Sopenharmony_ci continue; 349162306a36Sopenharmony_ci pos = menu->selection; 349262306a36Sopenharmony_cibrowse_hists: 349362306a36Sopenharmony_ci evlist__set_selected(evlist, pos); 349462306a36Sopenharmony_ci /* 349562306a36Sopenharmony_ci * Give the calling tool a chance to populate the non 349662306a36Sopenharmony_ci * default evsel resorted hists tree. 349762306a36Sopenharmony_ci */ 349862306a36Sopenharmony_ci if (hbt) 349962306a36Sopenharmony_ci hbt->timer(hbt->arg); 350062306a36Sopenharmony_ci key = evsel__hists_browse(pos, nr_events, help, true, hbt, 350162306a36Sopenharmony_ci menu->min_pcnt, menu->env, 350262306a36Sopenharmony_ci warn_lost_event, 350362306a36Sopenharmony_ci menu->annotation_opts); 350462306a36Sopenharmony_ci ui_browser__show_title(&menu->b, title); 350562306a36Sopenharmony_ci switch (key) { 350662306a36Sopenharmony_ci case K_TAB: 350762306a36Sopenharmony_ci if (pos->core.node.next == &evlist->core.entries) 350862306a36Sopenharmony_ci pos = evlist__first(evlist); 350962306a36Sopenharmony_ci else 351062306a36Sopenharmony_ci pos = evsel__next(pos); 351162306a36Sopenharmony_ci goto browse_hists; 351262306a36Sopenharmony_ci case K_UNTAB: 351362306a36Sopenharmony_ci if (pos->core.node.prev == &evlist->core.entries) 351462306a36Sopenharmony_ci pos = evlist__last(evlist); 351562306a36Sopenharmony_ci else 351662306a36Sopenharmony_ci pos = evsel__prev(pos); 351762306a36Sopenharmony_ci goto browse_hists; 351862306a36Sopenharmony_ci case K_SWITCH_INPUT_DATA: 351962306a36Sopenharmony_ci case K_RELOAD: 352062306a36Sopenharmony_ci case 'q': 352162306a36Sopenharmony_ci case CTRL('c'): 352262306a36Sopenharmony_ci goto out; 352362306a36Sopenharmony_ci case K_ESC: 352462306a36Sopenharmony_ci default: 352562306a36Sopenharmony_ci continue; 352662306a36Sopenharmony_ci } 352762306a36Sopenharmony_ci case K_LEFT: 352862306a36Sopenharmony_ci continue; 352962306a36Sopenharmony_ci case K_ESC: 353062306a36Sopenharmony_ci if (!ui_browser__dialog_yesno(&menu->b, 353162306a36Sopenharmony_ci "Do you really want to exit?")) 353262306a36Sopenharmony_ci continue; 353362306a36Sopenharmony_ci /* Fall thru */ 353462306a36Sopenharmony_ci case 'q': 353562306a36Sopenharmony_ci case CTRL('c'): 353662306a36Sopenharmony_ci goto out; 353762306a36Sopenharmony_ci default: 353862306a36Sopenharmony_ci continue; 353962306a36Sopenharmony_ci } 354062306a36Sopenharmony_ci } 354162306a36Sopenharmony_ci 354262306a36Sopenharmony_ciout: 354362306a36Sopenharmony_ci ui_browser__hide(&menu->b); 354462306a36Sopenharmony_ci return key; 354562306a36Sopenharmony_ci} 354662306a36Sopenharmony_ci 354762306a36Sopenharmony_cistatic bool filter_group_entries(struct ui_browser *browser __maybe_unused, 354862306a36Sopenharmony_ci void *entry) 354962306a36Sopenharmony_ci{ 355062306a36Sopenharmony_ci struct evsel *evsel = list_entry(entry, struct evsel, core.node); 355162306a36Sopenharmony_ci 355262306a36Sopenharmony_ci if (symbol_conf.event_group && !evsel__is_group_leader(evsel)) 355362306a36Sopenharmony_ci return true; 355462306a36Sopenharmony_ci 355562306a36Sopenharmony_ci return false; 355662306a36Sopenharmony_ci} 355762306a36Sopenharmony_ci 355862306a36Sopenharmony_cistatic int __evlist__tui_browse_hists(struct evlist *evlist, int nr_entries, const char *help, 355962306a36Sopenharmony_ci struct hist_browser_timer *hbt, float min_pcnt, struct perf_env *env, 356062306a36Sopenharmony_ci bool warn_lost_event, struct annotation_options *annotation_opts) 356162306a36Sopenharmony_ci{ 356262306a36Sopenharmony_ci struct evsel *pos; 356362306a36Sopenharmony_ci struct evsel_menu menu = { 356462306a36Sopenharmony_ci .b = { 356562306a36Sopenharmony_ci .entries = &evlist->core.entries, 356662306a36Sopenharmony_ci .refresh = ui_browser__list_head_refresh, 356762306a36Sopenharmony_ci .seek = ui_browser__list_head_seek, 356862306a36Sopenharmony_ci .write = perf_evsel_menu__write, 356962306a36Sopenharmony_ci .filter = filter_group_entries, 357062306a36Sopenharmony_ci .nr_entries = nr_entries, 357162306a36Sopenharmony_ci .priv = evlist, 357262306a36Sopenharmony_ci }, 357362306a36Sopenharmony_ci .min_pcnt = min_pcnt, 357462306a36Sopenharmony_ci .env = env, 357562306a36Sopenharmony_ci .annotation_opts = annotation_opts, 357662306a36Sopenharmony_ci }; 357762306a36Sopenharmony_ci 357862306a36Sopenharmony_ci ui_helpline__push("Press ESC to exit"); 357962306a36Sopenharmony_ci 358062306a36Sopenharmony_ci evlist__for_each_entry(evlist, pos) { 358162306a36Sopenharmony_ci const char *ev_name = evsel__name(pos); 358262306a36Sopenharmony_ci size_t line_len = strlen(ev_name) + 7; 358362306a36Sopenharmony_ci 358462306a36Sopenharmony_ci if (menu.b.width < line_len) 358562306a36Sopenharmony_ci menu.b.width = line_len; 358662306a36Sopenharmony_ci } 358762306a36Sopenharmony_ci 358862306a36Sopenharmony_ci return perf_evsel_menu__run(&menu, nr_entries, help, 358962306a36Sopenharmony_ci hbt, warn_lost_event); 359062306a36Sopenharmony_ci} 359162306a36Sopenharmony_ci 359262306a36Sopenharmony_cistatic bool evlist__single_entry(struct evlist *evlist) 359362306a36Sopenharmony_ci{ 359462306a36Sopenharmony_ci int nr_entries = evlist->core.nr_entries; 359562306a36Sopenharmony_ci 359662306a36Sopenharmony_ci if (nr_entries == 1) 359762306a36Sopenharmony_ci return true; 359862306a36Sopenharmony_ci 359962306a36Sopenharmony_ci if (nr_entries == 2) { 360062306a36Sopenharmony_ci struct evsel *last = evlist__last(evlist); 360162306a36Sopenharmony_ci 360262306a36Sopenharmony_ci if (evsel__is_dummy_event(last)) 360362306a36Sopenharmony_ci return true; 360462306a36Sopenharmony_ci } 360562306a36Sopenharmony_ci 360662306a36Sopenharmony_ci return false; 360762306a36Sopenharmony_ci} 360862306a36Sopenharmony_ci 360962306a36Sopenharmony_ciint evlist__tui_browse_hists(struct evlist *evlist, const char *help, struct hist_browser_timer *hbt, 361062306a36Sopenharmony_ci float min_pcnt, struct perf_env *env, bool warn_lost_event, 361162306a36Sopenharmony_ci struct annotation_options *annotation_opts) 361262306a36Sopenharmony_ci{ 361362306a36Sopenharmony_ci int nr_entries = evlist->core.nr_entries; 361462306a36Sopenharmony_ci 361562306a36Sopenharmony_ci if (evlist__single_entry(evlist)) { 361662306a36Sopenharmony_cisingle_entry: { 361762306a36Sopenharmony_ci struct evsel *first = evlist__first(evlist); 361862306a36Sopenharmony_ci 361962306a36Sopenharmony_ci return evsel__hists_browse(first, nr_entries, help, false, hbt, min_pcnt, 362062306a36Sopenharmony_ci env, warn_lost_event, annotation_opts); 362162306a36Sopenharmony_ci } 362262306a36Sopenharmony_ci } 362362306a36Sopenharmony_ci 362462306a36Sopenharmony_ci if (symbol_conf.event_group) { 362562306a36Sopenharmony_ci struct evsel *pos; 362662306a36Sopenharmony_ci 362762306a36Sopenharmony_ci nr_entries = 0; 362862306a36Sopenharmony_ci evlist__for_each_entry(evlist, pos) { 362962306a36Sopenharmony_ci if (evsel__is_group_leader(pos)) 363062306a36Sopenharmony_ci nr_entries++; 363162306a36Sopenharmony_ci } 363262306a36Sopenharmony_ci 363362306a36Sopenharmony_ci if (nr_entries == 1) 363462306a36Sopenharmony_ci goto single_entry; 363562306a36Sopenharmony_ci } 363662306a36Sopenharmony_ci 363762306a36Sopenharmony_ci return __evlist__tui_browse_hists(evlist, nr_entries, help, hbt, min_pcnt, env, 363862306a36Sopenharmony_ci warn_lost_event, annotation_opts); 363962306a36Sopenharmony_ci} 364062306a36Sopenharmony_ci 364162306a36Sopenharmony_cistatic int block_hists_browser__title(struct hist_browser *browser, char *bf, 364262306a36Sopenharmony_ci size_t size) 364362306a36Sopenharmony_ci{ 364462306a36Sopenharmony_ci struct hists *hists = evsel__hists(browser->block_evsel); 364562306a36Sopenharmony_ci const char *evname = evsel__name(browser->block_evsel); 364662306a36Sopenharmony_ci unsigned long nr_samples = hists->stats.nr_samples; 364762306a36Sopenharmony_ci int ret; 364862306a36Sopenharmony_ci 364962306a36Sopenharmony_ci ret = scnprintf(bf, size, "# Samples: %lu", nr_samples); 365062306a36Sopenharmony_ci if (evname) 365162306a36Sopenharmony_ci scnprintf(bf + ret, size - ret, " of event '%s'", evname); 365262306a36Sopenharmony_ci 365362306a36Sopenharmony_ci return 0; 365462306a36Sopenharmony_ci} 365562306a36Sopenharmony_ci 365662306a36Sopenharmony_ciint block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel, 365762306a36Sopenharmony_ci float min_percent, struct perf_env *env, 365862306a36Sopenharmony_ci struct annotation_options *annotation_opts) 365962306a36Sopenharmony_ci{ 366062306a36Sopenharmony_ci struct hists *hists = &bh->block_hists; 366162306a36Sopenharmony_ci struct hist_browser *browser; 366262306a36Sopenharmony_ci int key = -1; 366362306a36Sopenharmony_ci struct popup_action action; 366462306a36Sopenharmony_ci static const char help[] = 366562306a36Sopenharmony_ci " q Quit \n"; 366662306a36Sopenharmony_ci 366762306a36Sopenharmony_ci browser = hist_browser__new(hists); 366862306a36Sopenharmony_ci if (!browser) 366962306a36Sopenharmony_ci return -1; 367062306a36Sopenharmony_ci 367162306a36Sopenharmony_ci browser->block_evsel = evsel; 367262306a36Sopenharmony_ci browser->title = block_hists_browser__title; 367362306a36Sopenharmony_ci browser->min_pcnt = min_percent; 367462306a36Sopenharmony_ci browser->env = env; 367562306a36Sopenharmony_ci browser->annotation_opts = annotation_opts; 367662306a36Sopenharmony_ci 367762306a36Sopenharmony_ci /* reset abort key so that it can get Ctrl-C as a key */ 367862306a36Sopenharmony_ci SLang_reset_tty(); 367962306a36Sopenharmony_ci SLang_init_tty(0, 0, 0); 368062306a36Sopenharmony_ci 368162306a36Sopenharmony_ci memset(&action, 0, sizeof(action)); 368262306a36Sopenharmony_ci 368362306a36Sopenharmony_ci while (1) { 368462306a36Sopenharmony_ci key = hist_browser__run(browser, "? - help", true, 0); 368562306a36Sopenharmony_ci 368662306a36Sopenharmony_ci switch (key) { 368762306a36Sopenharmony_ci case 'q': 368862306a36Sopenharmony_ci goto out; 368962306a36Sopenharmony_ci case '?': 369062306a36Sopenharmony_ci ui_browser__help_window(&browser->b, help); 369162306a36Sopenharmony_ci break; 369262306a36Sopenharmony_ci case 'a': 369362306a36Sopenharmony_ci case K_ENTER: 369462306a36Sopenharmony_ci if (!browser->selection || 369562306a36Sopenharmony_ci !browser->selection->sym) { 369662306a36Sopenharmony_ci continue; 369762306a36Sopenharmony_ci } 369862306a36Sopenharmony_ci 369962306a36Sopenharmony_ci action.ms.map = browser->selection->map; 370062306a36Sopenharmony_ci action.ms.sym = browser->selection->sym; 370162306a36Sopenharmony_ci do_annotate(browser, &action); 370262306a36Sopenharmony_ci continue; 370362306a36Sopenharmony_ci default: 370462306a36Sopenharmony_ci break; 370562306a36Sopenharmony_ci } 370662306a36Sopenharmony_ci } 370762306a36Sopenharmony_ci 370862306a36Sopenharmony_ciout: 370962306a36Sopenharmony_ci hist_browser__delete(browser); 371062306a36Sopenharmony_ci return 0; 371162306a36Sopenharmony_ci} 3712