mirror of
https://github.com/torvalds/linux.git
synced 2026-09-24 06:24:02 +02:00
Add the browser front end: create/run/delete the hist_browser and add the title. The d shortcut opens the existing per-cacheline detail view for the selected level-3 cacheline. Level-3 entries retain the source cacheline index, so the shortcut can locate the original entry without relying on a potentially ambiguous virtual address. Report a warning when the common model rejects a cacheline coalescing field list without `iaddr`. Without it, the detail histograms may already have merged samples from different functions and cannot support reliable function attribution. Keep visible-row accounting local to the function view by wrapping the generic browser refresh callback and recounting the currently reachable hierarchy before each redraw. This keeps navigation correct when a level-1 row is collapsed while level-3 descendants remain expanded, without adding C2C-specific hooks to the shared hist_browser. Also handle Ctrl-C like the other function-view exit keys. Keep callchains hidden while the function browser runs, restoring the user's setting while opening the cacheline detail view. Wire the builder into perf_c2c__browse_function_view(). Signed-off-by: Jiebin Sun <jiebin.sun@intel.com> Reviewed-by: Tianyou Li <tianyou.li@intel.com> Reviewed-by: Wangyang Guo <wangyang.guo@intel.com> Reviewed-by: Ian Rogers <irogers@google.com> Cc: Dapeng Mi <dapeng1.mi@linux.intel.com> Cc: James Clark <james.clark@linaro.org> Cc: Thomas Falcon <thomas.falcon@intel.com> Signed-off-by: Namhyung Kim <namhyung@kernel.org>
210 lines
5.6 KiB
C
210 lines
5.6 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* C2C function browser - TUI front end for function-level sharing analysis
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <inttypes.h>
|
|
#include <stdlib.h>
|
|
#include <sys/ttydefaults.h>
|
|
#include <linux/rbtree.h>
|
|
#include <linux/zalloc.h>
|
|
|
|
#include "../browser.h"
|
|
#include "../keysyms.h"
|
|
#include "../libslang.h"
|
|
#include "../ui.h"
|
|
#include "../../util/c2c.h"
|
|
#include "../../util/debug.h"
|
|
#include "../../util/hist.h"
|
|
#include "../../util/symbol.h"
|
|
#include "hists.h"
|
|
|
|
struct c2c_function_browser {
|
|
struct hist_browser hb;
|
|
unsigned int (*orig_refresh)(struct ui_browser *browser);
|
|
int (*browse_cacheline)(struct hist_entry *he);
|
|
};
|
|
|
|
/*
|
|
* Count visible entries in @root, descending only through visible, unfolded
|
|
* parents. Match hists__filter_entries(), which drives generic browser
|
|
* navigation, so the count cannot include rows the browser skips.
|
|
*/
|
|
static u64
|
|
c2c_function__nr_visible_rows(struct rb_root_cached *root, float min_pcnt)
|
|
{
|
|
struct rb_node *nd;
|
|
u64 rows = 0;
|
|
|
|
for (nd = rb_first_cached(root); nd; nd = rb_next(nd)) {
|
|
struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
|
|
|
|
/*
|
|
* The generic refresh folds filtered parents and therefore hides
|
|
* their subtree. A percentage-rejected parent is merely skipped;
|
|
* if it is unfolded, qualifying descendants are still rendered.
|
|
*/
|
|
if (he->filtered)
|
|
continue;
|
|
|
|
if (hist_entry__get_percent_limit(he) >= min_pcnt)
|
|
rows++;
|
|
if (he->has_children && he->unfolded)
|
|
rows += c2c_function__nr_visible_rows(&he->hroot_out,
|
|
min_pcnt);
|
|
}
|
|
return rows;
|
|
}
|
|
|
|
static void
|
|
c2c_function_browser__update_nr_entries(struct c2c_function_browser *browser)
|
|
{
|
|
u64 nr_entries;
|
|
|
|
nr_entries = c2c_function__nr_visible_rows(&browser->hb.hists->entries,
|
|
browser->hb.min_pcnt);
|
|
browser->hb.nr_non_filtered_entries = nr_entries;
|
|
browser->hb.b.nr_entries = nr_entries;
|
|
}
|
|
|
|
static unsigned int c2c_function_browser__refresh(struct ui_browser *ui_browser)
|
|
{
|
|
struct hist_browser *hist_browser = container_of(ui_browser, struct hist_browser, b);
|
|
struct c2c_function_browser *browser;
|
|
|
|
browser = container_of(hist_browser, struct c2c_function_browser, hb);
|
|
c2c_function_browser__update_nr_entries(browser);
|
|
return browser->orig_refresh(ui_browser);
|
|
}
|
|
|
|
static int c2c_function_browser__title(struct hist_browser *browser,
|
|
char *bf, size_t size)
|
|
{
|
|
scnprintf(bf, size,
|
|
"Shared Data Functions Table (%" PRIu64 " entries, sorted on Cycles %%)",
|
|
browser->hists->nr_non_filtered_entries);
|
|
return 0;
|
|
}
|
|
|
|
static struct c2c_function_browser *
|
|
c2c_function_browser__new(struct hists *hists,
|
|
int (*browse_cacheline)(struct hist_entry *he))
|
|
{
|
|
struct c2c_function_browser *browser;
|
|
|
|
if (!hists)
|
|
return NULL;
|
|
|
|
browser = zalloc(sizeof(*browser));
|
|
if (!browser)
|
|
return NULL;
|
|
|
|
hist_browser__init(&browser->hb, hists);
|
|
browser->orig_refresh = browser->hb.b.refresh;
|
|
browser->hb.b.refresh = c2c_function_browser__refresh;
|
|
browser->browse_cacheline = browse_cacheline;
|
|
|
|
browser->hb.title = c2c_function_browser__title;
|
|
browser->hb.c2c_filter = true;
|
|
browser->hb.show_headers = true;
|
|
/* Keep title line count consistent with forcing headers on. */
|
|
browser->hb.b.extra_title_lines = hists->hpp_list->nr_header_lines;
|
|
browser->hb.min_pcnt = 0.0;
|
|
|
|
return browser;
|
|
}
|
|
|
|
static void c2c_function_browser__delete(struct c2c_function_browser *browser)
|
|
{
|
|
free(browser);
|
|
}
|
|
|
|
static int
|
|
c2c_browser__browse_cacheline(struct c2c_function_browser *browser,
|
|
struct hist_entry *he_selection)
|
|
{
|
|
struct hist_entry *he = c2c_function__find_cacheline(he_selection);
|
|
|
|
return he ? browser->browse_cacheline(he) : -1;
|
|
}
|
|
|
|
int perf_c2c__browse_function_view(struct c2c_function_view_args *args)
|
|
{
|
|
struct c2c_function_browser *browser;
|
|
struct hists *hists;
|
|
bool saved_use_callchain = symbol_conf.use_callchain;
|
|
int key, ret;
|
|
static const char help[] =
|
|
" d Display details for the selected level-3 cacheline\n"
|
|
" e/+ Expand/collapse the selected entry\n"
|
|
" TAB/ESC/q/^C Return to the cacheline view\n";
|
|
|
|
if (!args || !args->cl_hists || !args->browse_cacheline)
|
|
return -EINVAL;
|
|
|
|
/*
|
|
* Function view does not display callchains; cacheline detail temporarily
|
|
* restores them.
|
|
*/
|
|
symbol_conf.use_callchain = false;
|
|
|
|
ret = c2c_function__build(args->cl_hists, args->cl_sort,
|
|
args->symbol_full, &hists);
|
|
if (ret) {
|
|
if (ret == -EOPNOTSUPP)
|
|
ui__warning("The function view requires iaddr in --coalesce.\n");
|
|
else
|
|
ui__error("Failed to build function view hierarchy (ret=%d)\n", ret);
|
|
goto out;
|
|
}
|
|
|
|
browser = c2c_function_browser__new(hists, args->browse_cacheline);
|
|
if (!browser) {
|
|
ret = -ENOMEM;
|
|
goto out_reset;
|
|
}
|
|
|
|
/* Reset abort key so we can receive Ctrl-C as a key. */
|
|
SLang_reset_tty();
|
|
SLang_init_tty(0, 0, 0);
|
|
SLtty_set_suspend_state(true);
|
|
|
|
while (1) {
|
|
c2c_function_browser__update_nr_entries(browser);
|
|
key = hist_browser__run(&browser->hb, "? - help", true, 0);
|
|
|
|
switch (key) {
|
|
case 'q':
|
|
case K_TAB:
|
|
case K_ESC:
|
|
case CTRL('c'):
|
|
goto browser_done;
|
|
case 'd':
|
|
/* Cacheline detail honors the user's callchain setting. */
|
|
symbol_conf.use_callchain = saved_use_callchain;
|
|
c2c_browser__browse_cacheline(browser, browser->hb.he_selection);
|
|
/*
|
|
* Preserve any toggle made in the detail view, then
|
|
* re-disable callchain for the function view.
|
|
*/
|
|
saved_use_callchain = symbol_conf.use_callchain;
|
|
symbol_conf.use_callchain = false;
|
|
break;
|
|
case '?':
|
|
ui_browser__help_window(&browser->hb.b, help);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
browser_done:
|
|
c2c_function_browser__delete(browser);
|
|
out_reset:
|
|
c2c_function__reset();
|
|
out:
|
|
symbol_conf.use_callchain = saved_use_callchain;
|
|
return ret;
|
|
}
|