1*1975d27dSJiebin Sun // SPDX-License-Identifier: GPL-2.0 2*1975d27dSJiebin Sun /* 3*1975d27dSJiebin Sun * C2C function browser - TUI front end for function-level sharing analysis 4*1975d27dSJiebin Sun */ 5*1975d27dSJiebin Sun 6*1975d27dSJiebin Sun #include <errno.h> 7*1975d27dSJiebin Sun #include <inttypes.h> 8*1975d27dSJiebin Sun #include <stdlib.h> 9*1975d27dSJiebin Sun #include <sys/ttydefaults.h> 10*1975d27dSJiebin Sun #include <linux/rbtree.h> 11*1975d27dSJiebin Sun #include <linux/zalloc.h> 12*1975d27dSJiebin Sun 13*1975d27dSJiebin Sun #include "../browser.h" 14*1975d27dSJiebin Sun #include "../keysyms.h" 15*1975d27dSJiebin Sun #include "../libslang.h" 16*1975d27dSJiebin Sun #include "../ui.h" 17*1975d27dSJiebin Sun #include "../../util/c2c.h" 18*1975d27dSJiebin Sun #include "../../util/debug.h" 19*1975d27dSJiebin Sun #include "../../util/hist.h" 20*1975d27dSJiebin Sun #include "../../util/symbol.h" 21*1975d27dSJiebin Sun #include "hists.h" 22*1975d27dSJiebin Sun 23*1975d27dSJiebin Sun struct c2c_function_browser { 24*1975d27dSJiebin Sun struct hist_browser hb; 25*1975d27dSJiebin Sun unsigned int (*orig_refresh)(struct ui_browser *browser); 26*1975d27dSJiebin Sun int (*browse_cacheline)(struct hist_entry *he); 27*1975d27dSJiebin Sun }; 28*1975d27dSJiebin Sun 29*1975d27dSJiebin Sun /* 30*1975d27dSJiebin Sun * Count visible entries in @root, descending only through visible, unfolded 31*1975d27dSJiebin Sun * parents. Match hists__filter_entries(), which drives generic browser 32*1975d27dSJiebin Sun * navigation, so the count cannot include rows the browser skips. 33*1975d27dSJiebin Sun */ 34*1975d27dSJiebin Sun static u64 35*1975d27dSJiebin Sun c2c_function__nr_visible_rows(struct rb_root_cached *root, float min_pcnt) 36*1975d27dSJiebin Sun { 37*1975d27dSJiebin Sun struct rb_node *nd; 38*1975d27dSJiebin Sun u64 rows = 0; 39*1975d27dSJiebin Sun 40*1975d27dSJiebin Sun for (nd = rb_first_cached(root); nd; nd = rb_next(nd)) { 41*1975d27dSJiebin Sun struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); 42*1975d27dSJiebin Sun 43*1975d27dSJiebin Sun /* 44*1975d27dSJiebin Sun * The generic refresh folds filtered parents and therefore hides 45*1975d27dSJiebin Sun * their subtree. A percentage-rejected parent is merely skipped; 46*1975d27dSJiebin Sun * if it is unfolded, qualifying descendants are still rendered. 47*1975d27dSJiebin Sun */ 48*1975d27dSJiebin Sun if (he->filtered) 49*1975d27dSJiebin Sun continue; 50*1975d27dSJiebin Sun 51*1975d27dSJiebin Sun if (hist_entry__get_percent_limit(he) >= min_pcnt) 52*1975d27dSJiebin Sun rows++; 53*1975d27dSJiebin Sun if (he->has_children && he->unfolded) 54*1975d27dSJiebin Sun rows += c2c_function__nr_visible_rows(&he->hroot_out, 55*1975d27dSJiebin Sun min_pcnt); 56*1975d27dSJiebin Sun } 57*1975d27dSJiebin Sun return rows; 58*1975d27dSJiebin Sun } 59*1975d27dSJiebin Sun 60*1975d27dSJiebin Sun static void 61*1975d27dSJiebin Sun c2c_function_browser__update_nr_entries(struct c2c_function_browser *browser) 62*1975d27dSJiebin Sun { 63*1975d27dSJiebin Sun u64 nr_entries; 64*1975d27dSJiebin Sun 65*1975d27dSJiebin Sun nr_entries = c2c_function__nr_visible_rows(&browser->hb.hists->entries, 66*1975d27dSJiebin Sun browser->hb.min_pcnt); 67*1975d27dSJiebin Sun browser->hb.nr_non_filtered_entries = nr_entries; 68*1975d27dSJiebin Sun browser->hb.b.nr_entries = nr_entries; 69*1975d27dSJiebin Sun } 70*1975d27dSJiebin Sun 71*1975d27dSJiebin Sun static unsigned int c2c_function_browser__refresh(struct ui_browser *ui_browser) 72*1975d27dSJiebin Sun { 73*1975d27dSJiebin Sun struct hist_browser *hist_browser = container_of(ui_browser, struct hist_browser, b); 74*1975d27dSJiebin Sun struct c2c_function_browser *browser; 75*1975d27dSJiebin Sun 76*1975d27dSJiebin Sun browser = container_of(hist_browser, struct c2c_function_browser, hb); 77*1975d27dSJiebin Sun c2c_function_browser__update_nr_entries(browser); 78*1975d27dSJiebin Sun return browser->orig_refresh(ui_browser); 79*1975d27dSJiebin Sun } 80*1975d27dSJiebin Sun 81*1975d27dSJiebin Sun static int c2c_function_browser__title(struct hist_browser *browser, 82*1975d27dSJiebin Sun char *bf, size_t size) 83*1975d27dSJiebin Sun { 84*1975d27dSJiebin Sun scnprintf(bf, size, 85*1975d27dSJiebin Sun "Shared Data Functions Table (%" PRIu64 " entries, sorted on Cycles %%)", 86*1975d27dSJiebin Sun browser->hists->nr_non_filtered_entries); 87*1975d27dSJiebin Sun return 0; 88*1975d27dSJiebin Sun } 89*1975d27dSJiebin Sun 90*1975d27dSJiebin Sun static struct c2c_function_browser * 91*1975d27dSJiebin Sun c2c_function_browser__new(struct hists *hists, 92*1975d27dSJiebin Sun int (*browse_cacheline)(struct hist_entry *he)) 93*1975d27dSJiebin Sun { 94*1975d27dSJiebin Sun struct c2c_function_browser *browser; 95*1975d27dSJiebin Sun 96*1975d27dSJiebin Sun if (!hists) 97*1975d27dSJiebin Sun return NULL; 98*1975d27dSJiebin Sun 99*1975d27dSJiebin Sun browser = zalloc(sizeof(*browser)); 100*1975d27dSJiebin Sun if (!browser) 101*1975d27dSJiebin Sun return NULL; 102*1975d27dSJiebin Sun 103*1975d27dSJiebin Sun hist_browser__init(&browser->hb, hists); 104*1975d27dSJiebin Sun browser->orig_refresh = browser->hb.b.refresh; 105*1975d27dSJiebin Sun browser->hb.b.refresh = c2c_function_browser__refresh; 106*1975d27dSJiebin Sun browser->browse_cacheline = browse_cacheline; 107*1975d27dSJiebin Sun 108*1975d27dSJiebin Sun browser->hb.title = c2c_function_browser__title; 109*1975d27dSJiebin Sun browser->hb.c2c_filter = true; 110*1975d27dSJiebin Sun browser->hb.show_headers = true; 111*1975d27dSJiebin Sun /* Keep title line count consistent with forcing headers on. */ 112*1975d27dSJiebin Sun browser->hb.b.extra_title_lines = hists->hpp_list->nr_header_lines; 113*1975d27dSJiebin Sun browser->hb.min_pcnt = 0.0; 114*1975d27dSJiebin Sun 115*1975d27dSJiebin Sun return browser; 116*1975d27dSJiebin Sun } 117*1975d27dSJiebin Sun 118*1975d27dSJiebin Sun static void c2c_function_browser__delete(struct c2c_function_browser *browser) 119*1975d27dSJiebin Sun { 120*1975d27dSJiebin Sun free(browser); 121*1975d27dSJiebin Sun } 122*1975d27dSJiebin Sun 123*1975d27dSJiebin Sun static int 124*1975d27dSJiebin Sun c2c_browser__browse_cacheline(struct c2c_function_browser *browser, 125*1975d27dSJiebin Sun struct hist_entry *he_selection) 126*1975d27dSJiebin Sun { 127*1975d27dSJiebin Sun struct hist_entry *he = c2c_function__find_cacheline(he_selection); 128*1975d27dSJiebin Sun 129*1975d27dSJiebin Sun return he ? browser->browse_cacheline(he) : -1; 130*1975d27dSJiebin Sun } 131*1975d27dSJiebin Sun 132*1975d27dSJiebin Sun int perf_c2c__browse_function_view(struct c2c_function_view_args *args) 133*1975d27dSJiebin Sun { 134*1975d27dSJiebin Sun struct c2c_function_browser *browser; 135*1975d27dSJiebin Sun struct hists *hists; 136*1975d27dSJiebin Sun bool saved_use_callchain = symbol_conf.use_callchain; 137*1975d27dSJiebin Sun int key, ret; 138*1975d27dSJiebin Sun static const char help[] = 139*1975d27dSJiebin Sun " d Display details for the selected level-3 cacheline\n" 140*1975d27dSJiebin Sun " e/+ Expand/collapse the selected entry\n" 141*1975d27dSJiebin Sun " TAB/ESC/q/^C Return to the cacheline view\n"; 142*1975d27dSJiebin Sun 143*1975d27dSJiebin Sun if (!args || !args->cl_hists || !args->browse_cacheline) 144*1975d27dSJiebin Sun return -EINVAL; 145*1975d27dSJiebin Sun 146*1975d27dSJiebin Sun /* 147*1975d27dSJiebin Sun * Function view does not display callchains; cacheline detail temporarily 148*1975d27dSJiebin Sun * restores them. 149*1975d27dSJiebin Sun */ 150*1975d27dSJiebin Sun symbol_conf.use_callchain = false; 151*1975d27dSJiebin Sun 152*1975d27dSJiebin Sun ret = c2c_function__build(args->cl_hists, args->cl_sort, 153*1975d27dSJiebin Sun args->symbol_full, &hists); 154*1975d27dSJiebin Sun if (ret) { 155*1975d27dSJiebin Sun if (ret == -EOPNOTSUPP) 156*1975d27dSJiebin Sun ui__warning("The function view requires iaddr in --coalesce.\n"); 157*1975d27dSJiebin Sun else 158*1975d27dSJiebin Sun ui__error("Failed to build function view hierarchy (ret=%d)\n", ret); 159*1975d27dSJiebin Sun goto out; 160*1975d27dSJiebin Sun } 161*1975d27dSJiebin Sun 162*1975d27dSJiebin Sun browser = c2c_function_browser__new(hists, args->browse_cacheline); 163*1975d27dSJiebin Sun if (!browser) { 164*1975d27dSJiebin Sun ret = -ENOMEM; 165*1975d27dSJiebin Sun goto out_reset; 166*1975d27dSJiebin Sun } 167*1975d27dSJiebin Sun 168*1975d27dSJiebin Sun /* Reset abort key so we can receive Ctrl-C as a key. */ 169*1975d27dSJiebin Sun SLang_reset_tty(); 170*1975d27dSJiebin Sun SLang_init_tty(0, 0, 0); 171*1975d27dSJiebin Sun SLtty_set_suspend_state(true); 172*1975d27dSJiebin Sun 173*1975d27dSJiebin Sun while (1) { 174*1975d27dSJiebin Sun c2c_function_browser__update_nr_entries(browser); 175*1975d27dSJiebin Sun key = hist_browser__run(&browser->hb, "? - help", true, 0); 176*1975d27dSJiebin Sun 177*1975d27dSJiebin Sun switch (key) { 178*1975d27dSJiebin Sun case 'q': 179*1975d27dSJiebin Sun case K_TAB: 180*1975d27dSJiebin Sun case K_ESC: 181*1975d27dSJiebin Sun case CTRL('c'): 182*1975d27dSJiebin Sun goto browser_done; 183*1975d27dSJiebin Sun case 'd': 184*1975d27dSJiebin Sun /* Cacheline detail honors the user's callchain setting. */ 185*1975d27dSJiebin Sun symbol_conf.use_callchain = saved_use_callchain; 186*1975d27dSJiebin Sun c2c_browser__browse_cacheline(browser, browser->hb.he_selection); 187*1975d27dSJiebin Sun /* 188*1975d27dSJiebin Sun * Preserve any toggle made in the detail view, then 189*1975d27dSJiebin Sun * re-disable callchain for the function view. 190*1975d27dSJiebin Sun */ 191*1975d27dSJiebin Sun saved_use_callchain = symbol_conf.use_callchain; 192*1975d27dSJiebin Sun symbol_conf.use_callchain = false; 193*1975d27dSJiebin Sun break; 194*1975d27dSJiebin Sun case '?': 195*1975d27dSJiebin Sun ui_browser__help_window(&browser->hb.b, help); 196*1975d27dSJiebin Sun break; 197*1975d27dSJiebin Sun default: 198*1975d27dSJiebin Sun break; 199*1975d27dSJiebin Sun } 200*1975d27dSJiebin Sun } 201*1975d27dSJiebin Sun 202*1975d27dSJiebin Sun browser_done: 203*1975d27dSJiebin Sun c2c_function_browser__delete(browser); 204*1975d27dSJiebin Sun out_reset: 205*1975d27dSJiebin Sun c2c_function__reset(); 206*1975d27dSJiebin Sun out: 207*1975d27dSJiebin Sun symbol_conf.use_callchain = saved_use_callchain; 208*1975d27dSJiebin Sun return ret; 209*1975d27dSJiebin Sun } 210