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