xref: /linux/tools/perf/ui/browsers/c2c-function.c (revision 473f6c8f437b049f8ec015d57cd59bb983b1d85c)
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