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