| 1 | |
| 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 |
|
| 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 |
|
| 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 |
|
| 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 | }
|