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