xref: /linux/tools/perf/ui/browsers/annotate-data.c (revision 040c0f887fdcfe747a3f63c94e9cd29e9ed0b872)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <inttypes.h>
3 #include <string.h>
4 #include <linux/zalloc.h>
5 #include <sys/ttydefaults.h>
6 
7 #include "ui/browser.h"
8 #include "ui/helpline.h"
9 #include "ui/keysyms.h"
10 #include "ui/ui.h"
11 #include "util/annotate.h"
12 #include "util/annotate-data.h"
13 #include "util/evsel.h"
14 #include "util/evlist.h"
15 #include "util/sort.h"
16 
17 struct annotated_data_browser {
18 	struct ui_browser b;
19 	struct list_head entries;
20 	int nr_events;
21 };
22 
23 struct browser_entry {
24 	struct list_head node;
25 	struct annotated_member *data;
26 	struct type_hist_entry *hists;
27 	int indent;
28 };
29 
30 static struct annotated_data_browser *get_browser(struct ui_browser *uib)
31 {
32 	return container_of(uib, struct annotated_data_browser, b);
33 }
34 
35 static void update_hist_entry(struct type_hist_entry *dst,
36 			      struct type_hist_entry *src)
37 {
38 	dst->nr_samples += src->nr_samples;
39 	dst->period += src->period;
40 }
41 
42 static int get_member_overhead(struct annotated_data_type *adt,
43 			       struct browser_entry *entry,
44 			       struct evsel *leader)
45 {
46 	struct annotated_member *member = entry->data;
47 	int i, k;
48 
49 	for (i = 0; i < member->size; i++) {
50 		struct type_hist *h;
51 		struct evsel *evsel;
52 		int offset = member->offset + i;
53 
54 		k = 0;
55 		for_each_group_evsel(evsel, leader) {
56 			if (symbol_conf.skip_empty &&
57 			    evsel__hists(evsel)->stats.nr_samples == 0)
58 				continue;
59 
60 			h = adt->histograms[evsel->core.idx];
61 			update_hist_entry(&entry->hists[k++], &h->addr[offset]);
62 		}
63 	}
64 	return 0;
65 }
66 
67 static int add_child_entries(struct annotated_data_browser *browser,
68 			     struct annotated_data_type *adt,
69 			     struct annotated_member *member,
70 			     struct evsel *evsel, int indent)
71 {
72 	struct annotated_member *pos;
73 	struct browser_entry *entry;
74 	int nr_entries = 0;
75 
76 	entry = zalloc(sizeof(*entry));
77 	if (entry == NULL)
78 		return -1;
79 
80 	entry->hists = calloc(browser->nr_events, sizeof(*entry->hists));
81 	if (entry->hists == NULL) {
82 		free(entry);
83 		return -1;
84 	}
85 
86 	entry->data = member;
87 	entry->indent = indent;
88 	if (get_member_overhead(adt, entry, evsel) < 0) {
89 		free(entry);
90 		return -1;
91 	}
92 
93 	list_add_tail(&entry->node, &browser->entries);
94 	nr_entries++;
95 
96 	list_for_each_entry(pos, &member->children, node) {
97 		int nr = add_child_entries(browser, adt, pos, evsel, indent + 1);
98 
99 		if (nr < 0)
100 			return nr;
101 
102 		nr_entries += nr;
103 	}
104 
105 	/* add an entry for the closing bracket ("}") */
106 	if (!list_empty(&member->children)) {
107 		entry = zalloc(sizeof(*entry));
108 		if (entry == NULL)
109 			return -1;
110 
111 		entry->indent = indent;
112 		list_add_tail(&entry->node, &browser->entries);
113 		nr_entries++;
114 	}
115 
116 	return nr_entries;
117 }
118 
119 static int annotated_data_browser__collect_entries(struct annotated_data_browser *browser)
120 {
121 	struct hist_entry *he = browser->b.priv;
122 	struct annotated_data_type *adt = he->mem_type;
123 	struct evsel *evsel = hists_to_evsel(he->hists);
124 
125 	INIT_LIST_HEAD(&browser->entries);
126 	browser->b.entries = &browser->entries;
127 	browser->b.nr_entries = add_child_entries(browser, adt, &adt->self,
128 						  evsel, /*indent=*/0);
129 	return 0;
130 }
131 
132 static void annotated_data_browser__delete_entries(struct annotated_data_browser *browser)
133 {
134 	struct browser_entry *pos, *tmp;
135 
136 	list_for_each_entry_safe(pos, tmp, &browser->entries, node) {
137 		list_del_init(&pos->node);
138 		zfree(&pos->hists);
139 		free(pos);
140 	}
141 }
142 
143 static unsigned int browser__refresh(struct ui_browser *uib)
144 {
145 	return ui_browser__list_head_refresh(uib);
146 }
147 
148 static int browser__show(struct ui_browser *uib)
149 {
150 	struct hist_entry *he = uib->priv;
151 	struct annotated_data_type *adt = he->mem_type;
152 	struct annotated_data_browser *browser = get_browser(uib);
153 	const char *help = "Press 'h' for help on key bindings";
154 	char title[256];
155 
156 	snprintf(title, sizeof(title), "Annotate type: '%s' (%d samples)",
157 		 adt->self.type_name, he->stat.nr_events);
158 
159 	if (ui_browser__show(uib, title, help) < 0)
160 		return -1;
161 
162 	/* second line header */
163 	ui_browser__gotorc_title(uib, 0, 0);
164 	ui_browser__set_color(uib, HE_COLORSET_ROOT);
165 
166 	if (symbol_conf.show_total_period)
167 		strcpy(title, "Period");
168 	else if (symbol_conf.show_nr_samples)
169 		strcpy(title, "Samples");
170 	else
171 		strcpy(title, "Percent");
172 
173 	ui_browser__printf(uib, "%*s %10s %10s %10s  %s",
174 			   11 * (browser->nr_events - 1), "",
175 			   title, "Offset", "Size", "Field");
176 	ui_browser__write_nstring(uib, "", uib->width);
177 	return 0;
178 }
179 
180 static void browser__write_overhead(struct ui_browser *uib,
181 				    struct type_hist *total,
182 				    struct type_hist_entry *hist, int row)
183 {
184 	u64 period = hist->period;
185 	double percent = total->period ? (100.0 * period / total->period) : 0;
186 	bool current = ui_browser__is_current_entry(uib, row);
187 	int nr_samples = 0;
188 
189 	ui_browser__set_percent_color(uib, percent, current);
190 
191 	if (symbol_conf.show_total_period)
192 		ui_browser__printf(uib, " %10" PRIu64, period);
193 	else if (symbol_conf.show_nr_samples)
194 		ui_browser__printf(uib, " %10d", nr_samples);
195 	else
196 		ui_browser__printf(uib, " %10.2f", percent);
197 
198 	ui_browser__set_percent_color(uib, 0, current);
199 }
200 
201 static void browser__write(struct ui_browser *uib, void *entry, int row)
202 {
203 	struct annotated_data_browser *browser = get_browser(uib);
204 	struct browser_entry *be = entry;
205 	struct annotated_member *member = be->data;
206 	struct hist_entry *he = uib->priv;
207 	struct annotated_data_type *adt = he->mem_type;
208 	struct evsel *leader = hists_to_evsel(he->hists);
209 	struct evsel *evsel;
210 	int idx = 0;
211 
212 	if (member == NULL) {
213 		bool current = ui_browser__is_current_entry(uib, row);
214 
215 		/* print the closing bracket */
216 		ui_browser__set_percent_color(uib, 0, current);
217 		ui_browser__write_nstring(uib, "", 11 * browser->nr_events);
218 		ui_browser__printf(uib, " %10s %10s  %*s};",
219 				   "", "", be->indent * 4, "");
220 		ui_browser__write_nstring(uib, "", uib->width);
221 		return;
222 	}
223 
224 	/* print the number */
225 	for_each_group_evsel(evsel, leader) {
226 		struct type_hist *h = adt->histograms[evsel->core.idx];
227 
228 		if (symbol_conf.skip_empty &&
229 		    evsel__hists(evsel)->stats.nr_samples == 0)
230 			continue;
231 
232 		browser__write_overhead(uib, h, &be->hists[idx++], row);
233 	}
234 
235 	/* print type info */
236 	if (be->indent == 0 && !member->var_name) {
237 		ui_browser__printf(uib, " %10d %10d  %s%s",
238 				   member->offset, member->size,
239 				   member->type_name,
240 				   list_empty(&member->children) ? ";" : " {");
241 	} else {
242 		ui_browser__printf(uib, " %10d %10d  %*s%s\t%s%s",
243 				   member->offset, member->size,
244 				   be->indent * 4, "", member->type_name,
245 				   member->var_name ?: "",
246 				   list_empty(&member->children) ? ";" : " {");
247 	}
248 	/* fill the rest */
249 	ui_browser__write_nstring(uib, "", uib->width);
250 }
251 
252 static int annotated_data_browser__run(struct annotated_data_browser *browser,
253 				       struct evsel *evsel __maybe_unused,
254 				       struct hist_browser_timer *hbt)
255 {
256 	int delay_secs = hbt ? hbt->refresh : 0;
257 	int key;
258 
259 	if (browser__show(&browser->b) < 0)
260 		return -1;
261 
262 	while (1) {
263 		key = ui_browser__run(&browser->b, delay_secs);
264 
265 		switch (key) {
266 		case K_TIMER:
267 			if (hbt)
268 				hbt->timer(hbt->arg);
269 			continue;
270 		case K_F1:
271 		case 'h':
272 			ui_browser__help_window(&browser->b,
273 		"UP/DOWN/PGUP\n"
274 		"PGDN/SPACE    Navigate\n"
275 		"</>           Move to prev/next symbol\n"
276 		"q/ESC/CTRL+C  Exit\n\n");
277 			continue;
278 		case K_LEFT:
279 		case '<':
280 		case '>':
281 		case K_ESC:
282 		case 'q':
283 		case CTRL('c'):
284 			goto out;
285 		default:
286 			continue;
287 		}
288 	}
289 out:
290 	ui_browser__hide(&browser->b);
291 	return key;
292 }
293 
294 int hist_entry__annotate_data_tui(struct hist_entry *he, struct evsel *evsel,
295 				  struct hist_browser_timer *hbt)
296 {
297 	struct annotated_data_browser browser = {
298 		.b = {
299 			.refresh = browser__refresh,
300 			.seek	 = ui_browser__list_head_seek,
301 			.write	 = browser__write,
302 			.priv	 = he,
303 			.extra_title_lines = 1,
304 		},
305 		.nr_events = 1,
306 	};
307 	int ret;
308 
309 	ui_helpline__push("Press ESC to exit");
310 
311 	if (evsel__is_group_event(evsel)) {
312 		struct evsel *pos;
313 		int nr = 0;
314 
315 		for_each_group_evsel(pos, evsel) {
316 			if (!symbol_conf.skip_empty ||
317 			    evsel__hists(pos)->stats.nr_samples)
318 				nr++;
319 		}
320 		browser.nr_events = nr;
321 	}
322 
323 	ret = annotated_data_browser__collect_entries(&browser);
324 	if (ret == 0)
325 		ret = annotated_data_browser__run(&browser, evsel, hbt);
326 
327 	annotated_data_browser__delete_entries(&browser);
328 
329 	return ret;
330 }
331