xref: /linux/tools/perf/ui/browsers/annotate-data.c (revision c7546e2c3cb739a3c1a2f5acaf9bb629d401afe5)
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 #define FOLDED_SIGN  '+'
18 #define UNFOLD_SIGN  '-'
19 #define NOCHLD_SIGN  ' '
20 
21 struct browser_entry {
22 	struct list_head node;
23 	struct annotated_member *data;
24 	struct type_hist_entry *hists;
25 	struct browser_entry *parent;
26 	struct list_head children;
27 	int indent;  /*indentation level, starts from 0 */
28 	int nr_entries; /* # of visible entries: self + descendents */
29 	bool folded;  /* only can be false when it has children */
30 };
31 
32 struct annotated_data_browser {
33 	struct ui_browser b;
34 	struct list_head entries;
35 	struct browser_entry *curr;
36 	int nr_events;
37 };
38 
39 static struct annotated_data_browser *get_browser(struct ui_browser *uib)
40 {
41 	return container_of(uib, struct annotated_data_browser, b);
42 }
43 
44 static void update_hist_entry(struct type_hist_entry *dst,
45 			      struct type_hist_entry *src)
46 {
47 	dst->nr_samples += src->nr_samples;
48 	dst->period += src->period;
49 }
50 
51 static int get_member_overhead(struct annotated_data_type *adt,
52 			       struct browser_entry *entry,
53 			       struct evsel *leader)
54 {
55 	struct annotated_member *member = entry->data;
56 	int i, k;
57 
58 	for (i = 0; i < member->size; i++) {
59 		struct type_hist *h;
60 		struct evsel *evsel;
61 		int offset = member->offset + i;
62 
63 		k = 0;
64 		for_each_group_evsel(evsel, leader) {
65 			if (symbol_conf.skip_empty &&
66 			    evsel__hists(evsel)->stats.nr_samples == 0)
67 				continue;
68 
69 			h = adt->histograms[evsel->core.idx];
70 			update_hist_entry(&entry->hists[k++], &h->addr[offset]);
71 		}
72 	}
73 	return 0;
74 }
75 
76 static int add_child_entries(struct annotated_data_browser *browser,
77 			     struct browser_entry *parent,
78 			     struct annotated_data_type *adt,
79 			     struct annotated_member *member,
80 			     struct evsel *evsel, int indent)
81 {
82 	struct annotated_member *pos;
83 	struct browser_entry *entry;
84 	struct list_head *parent_list;
85 
86 	entry = zalloc(sizeof(*entry));
87 	if (entry == NULL)
88 		return -1;
89 
90 	entry->hists = calloc(browser->nr_events, sizeof(*entry->hists));
91 	if (entry->hists == NULL) {
92 		free(entry);
93 		return -1;
94 	}
95 
96 	entry->data = member;
97 	entry->parent = parent;
98 	entry->indent = indent;
99 	if (get_member_overhead(adt, entry, evsel) < 0) {
100 		free(entry);
101 		return -1;
102 	}
103 
104 	INIT_LIST_HEAD(&entry->children);
105 	if (parent)
106 		parent_list = &parent->children;
107 	else
108 		parent_list = &browser->entries;
109 
110 	list_add_tail(&entry->node, parent_list);
111 
112 	list_for_each_entry(pos, &member->children, node) {
113 		int nr = add_child_entries(browser, entry, adt, pos, evsel,
114 					   indent + 1);
115 		if (nr < 0)
116 			return nr;
117 	}
118 
119 	/* add an entry for the closing bracket ("}") */
120 	if (!list_empty(&member->children)) {
121 		struct browser_entry *bracket;
122 
123 		bracket = zalloc(sizeof(*bracket));
124 		if (bracket == NULL)
125 			return -1;
126 
127 		bracket->indent = indent;
128 		bracket->parent = entry;
129 		bracket->folded = true;
130 		bracket->nr_entries = 1;
131 
132 		INIT_LIST_HEAD(&bracket->children);
133 		list_add_tail(&bracket->node, &entry->children);
134 	}
135 
136 	/* fold child entries by default */
137 	entry->folded = true;
138 	entry->nr_entries = 1;
139 	return 0;
140 }
141 
142 static u32 count_visible_entries(struct annotated_data_browser *browser)
143 {
144 	int nr = 0;
145 	struct browser_entry *entry;
146 
147 	list_for_each_entry(entry, &browser->entries, node)
148 		nr += entry->nr_entries;
149 
150 	return nr;
151 }
152 
153 static int annotated_data_browser__collect_entries(struct annotated_data_browser *browser)
154 {
155 	struct hist_entry *he = browser->b.priv;
156 	struct annotated_data_type *adt = he->mem_type;
157 	struct evsel *evsel = hists_to_evsel(he->hists);
158 
159 	INIT_LIST_HEAD(&browser->entries);
160 
161 	add_child_entries(browser, /*parent=*/NULL, adt, &adt->self, evsel,
162 			  /*indent=*/0);
163 
164 	browser->b.entries = &browser->entries;
165 	browser->b.nr_entries = count_visible_entries(browser);
166 	return 0;
167 }
168 
169 static void annotated_data_browser__delete_entries(struct annotated_data_browser *browser)
170 {
171 	struct browser_entry *pos, *tmp;
172 
173 	list_for_each_entry_safe(pos, tmp, &browser->entries, node) {
174 		list_del_init(&pos->node);
175 		zfree(&pos->hists);
176 		free(pos);
177 	}
178 }
179 
180 static struct browser_entry *get_first_child(struct browser_entry *entry)
181 {
182 	if (list_empty(&entry->children))
183 		return NULL;
184 
185 	return list_first_entry(&entry->children, struct browser_entry, node);
186 }
187 
188 static struct browser_entry *get_last_child(struct browser_entry *entry)
189 {
190 	if (list_empty(&entry->children))
191 		return NULL;
192 
193 	return list_last_entry(&entry->children, struct browser_entry, node);
194 }
195 
196 static bool is_first_child(struct browser_entry *entry)
197 {
198 	/* This will be checked in a different way */
199 	if (entry->parent == NULL)
200 		return false;
201 
202 	return get_first_child(entry->parent) == entry;
203 }
204 
205 static bool is_last_child(struct browser_entry *entry)
206 {
207 	/* This will be checked in a different way */
208 	if (entry->parent == NULL)
209 		return false;
210 
211 	return get_last_child(entry->parent) == entry;
212 }
213 
214 static struct browser_entry *browser__prev_entry(struct ui_browser *uib,
215 						 struct browser_entry *entry)
216 {
217 	struct annotated_data_browser *browser = get_browser(uib);
218 	struct browser_entry *first;
219 
220 	first = list_first_entry(&browser->entries, struct browser_entry, node);
221 
222 	while (entry != first) {
223 		if (is_first_child(entry))
224 			entry = entry->parent;
225 		else {
226 			entry = list_prev_entry(entry, node);
227 			while (!entry->folded)
228 				entry = get_last_child(entry);
229 		}
230 
231 		if (!uib->filter || !uib->filter(uib, &entry->node))
232 			return entry;
233 	}
234 	return first;
235 }
236 
237 static struct browser_entry *browser__next_entry(struct ui_browser *uib,
238 						 struct browser_entry *entry)
239 {
240 	struct annotated_data_browser *browser = get_browser(uib);
241 	struct browser_entry *last;
242 
243 	last = list_last_entry(&browser->entries, struct browser_entry, node);
244 	while (!last->folded)
245 		last = get_last_child(last);
246 
247 	while (entry != last) {
248 		if (!entry->folded)
249 			entry = get_first_child(entry);
250 		else {
251 			while (is_last_child(entry))
252 				entry = entry->parent;
253 
254 			entry = list_next_entry(entry, node);
255 		}
256 
257 		if (!uib->filter || !uib->filter(uib, &entry->node))
258 			return entry;
259 	}
260 	return last;
261 }
262 
263 static void browser__seek(struct ui_browser *uib, off_t offset, int whence)
264 {
265 	struct annotated_data_browser *browser = get_browser(uib);
266 	struct browser_entry *entry;
267 
268 	if (uib->nr_entries == 0)
269 		return;
270 
271 	switch (whence) {
272 	case SEEK_SET:
273 		entry = list_first_entry(&browser->entries, typeof(*entry), node);
274 		if (uib->filter && uib->filter(uib, &entry->node))
275 			entry = browser__next_entry(uib, entry);
276 		break;
277 	case SEEK_CUR:
278 		entry = list_entry(uib->top, typeof(*entry), node);
279 		break;
280 	case SEEK_END:
281 		entry = list_last_entry(&browser->entries, typeof(*entry), node);
282 		while (!entry->folded)
283 			entry = get_last_child(entry);
284 		if (uib->filter && uib->filter(uib, &entry->node))
285 			entry = browser__prev_entry(uib, entry);
286 		break;
287 	default:
288 		return;
289 	}
290 
291 	assert(entry != NULL);
292 
293 	if (offset > 0) {
294 		while (offset-- != 0)
295 			entry = browser__next_entry(uib, entry);
296 	} else {
297 		while (offset++ != 0)
298 			entry = browser__prev_entry(uib, entry);
299 	}
300 
301 	uib->top = &entry->node;
302 }
303 
304 static unsigned int browser__refresh(struct ui_browser *uib)
305 {
306 	struct annotated_data_browser *browser = get_browser(uib);
307 	struct browser_entry *entry, *next;
308 	int row = 0;
309 
310 	if (uib->top == NULL || uib->top == uib->entries)
311 		browser__seek(uib, SEEK_SET, 0);
312 
313 	entry = list_entry(uib->top, typeof(*entry), node);
314 
315 	while (true) {
316 		if (!uib->filter || !uib->filter(uib, &entry->node)) {
317 			ui_browser__gotorc(uib, row, 0);
318 			uib->write(uib, entry, row);
319 			if (uib->top_idx + row == uib->index)
320 				browser->curr = entry;
321 			if (++row == uib->rows)
322 				break;
323 		}
324 		next = browser__next_entry(uib, entry);
325 		if (next == entry)
326 			break;
327 
328 		entry = next;
329 	}
330 
331 	return row;
332 }
333 
334 static int browser__show(struct ui_browser *uib)
335 {
336 	struct hist_entry *he = uib->priv;
337 	struct annotated_data_type *adt = he->mem_type;
338 	struct annotated_data_browser *browser = get_browser(uib);
339 	const char *help = "Press 'h' for help on key bindings";
340 	char title[256];
341 
342 	snprintf(title, sizeof(title), "Annotate type: '%s' (%d samples)",
343 		 adt->self.type_name, he->stat.nr_events);
344 
345 	if (ui_browser__show(uib, title, help) < 0)
346 		return -1;
347 
348 	/* second line header */
349 	ui_browser__gotorc_title(uib, 0, 0);
350 	ui_browser__set_color(uib, HE_COLORSET_ROOT);
351 
352 	if (symbol_conf.show_total_period)
353 		strcpy(title, "Period");
354 	else if (symbol_conf.show_nr_samples)
355 		strcpy(title, "Samples");
356 	else
357 		strcpy(title, "Percent");
358 
359 	ui_browser__printf(uib, "%*s %10s %10s %10s  %s",
360 			   2 + 11 * (browser->nr_events - 1), "",
361 			   title, "Offset", "Size", "Field");
362 	ui_browser__write_nstring(uib, "", uib->width);
363 	return 0;
364 }
365 
366 static void browser__write_overhead(struct ui_browser *uib,
367 				    struct type_hist *total,
368 				    struct type_hist_entry *hist, int row)
369 {
370 	u64 period = hist->period;
371 	double percent = total->period ? (100.0 * period / total->period) : 0;
372 	bool current = ui_browser__is_current_entry(uib, row);
373 	int nr_samples = 0;
374 
375 	ui_browser__set_percent_color(uib, percent, current);
376 
377 	if (symbol_conf.show_total_period)
378 		ui_browser__printf(uib, " %10" PRIu64, period);
379 	else if (symbol_conf.show_nr_samples)
380 		ui_browser__printf(uib, " %10d", nr_samples);
381 	else
382 		ui_browser__printf(uib, " %10.2f", percent);
383 
384 	ui_browser__set_percent_color(uib, 0, current);
385 }
386 
387 static void browser__write(struct ui_browser *uib, void *entry, int row)
388 {
389 	struct annotated_data_browser *browser = get_browser(uib);
390 	struct browser_entry *be = entry;
391 	struct annotated_member *member = be->data;
392 	struct hist_entry *he = uib->priv;
393 	struct annotated_data_type *adt = he->mem_type;
394 	struct evsel *leader = hists_to_evsel(he->hists);
395 	struct evsel *evsel;
396 	int idx = 0;
397 	bool current = ui_browser__is_current_entry(uib, row);
398 
399 	if (member == NULL) {
400 		/* print the closing bracket */
401 		ui_browser__set_percent_color(uib, 0, current);
402 		ui_browser__printf(uib, "%c ", NOCHLD_SIGN);
403 		ui_browser__write_nstring(uib, "", 11 * browser->nr_events);
404 		ui_browser__printf(uib, " %10s %10s  %*s};",
405 				   "", "", be->indent * 4, "");
406 		ui_browser__write_nstring(uib, "", uib->width);
407 		return;
408 	}
409 
410 	ui_browser__set_percent_color(uib, 0, current);
411 
412 	if (!list_empty(&be->children))
413 		ui_browser__printf(uib, "%c ", be->folded ? FOLDED_SIGN : UNFOLD_SIGN);
414 	else
415 		ui_browser__printf(uib, "%c ", NOCHLD_SIGN);
416 
417 	/* print the number */
418 	for_each_group_evsel(evsel, leader) {
419 		struct type_hist *h = adt->histograms[evsel->core.idx];
420 
421 		if (symbol_conf.skip_empty &&
422 		    evsel__hists(evsel)->stats.nr_samples == 0)
423 			continue;
424 
425 		browser__write_overhead(uib, h, &be->hists[idx++], row);
426 	}
427 
428 	/* print type info */
429 	if (be->indent == 0 && !member->var_name) {
430 		ui_browser__printf(uib, " %#10x %#10x  %s%s",
431 				   member->offset, member->size,
432 				   member->type_name,
433 				   list_empty(&member->children) || be->folded? ";" : " {");
434 	} else {
435 		ui_browser__printf(uib, " %#10x %#10x  %*s%s\t%s%s",
436 				   member->offset, member->size,
437 				   be->indent * 4, "", member->type_name,
438 				   member->var_name ?: "",
439 				   list_empty(&member->children) || be->folded ? ";" : " {");
440 	}
441 	/* fill the rest */
442 	ui_browser__write_nstring(uib, "", uib->width);
443 }
444 
445 static void annotated_data_browser__fold(struct annotated_data_browser *browser,
446 					 struct browser_entry *entry,
447 					 bool recursive)
448 {
449 	struct browser_entry *child;
450 
451 	if (list_empty(&entry->children))
452 		return;
453 	if (entry->folded && !recursive)
454 		return;
455 
456 	if (recursive) {
457 		list_for_each_entry(child, &entry->children, node)
458 			annotated_data_browser__fold(browser, child, true);
459 	}
460 
461 	entry->nr_entries = 1;
462 	entry->folded = true;
463 }
464 
465 static void annotated_data_browser__unfold(struct annotated_data_browser *browser,
466 					   struct browser_entry *entry,
467 					   bool recursive)
468 {
469 	struct browser_entry *child;
470 	int nr_entries;
471 
472 	if (list_empty(&entry->children))
473 		return;
474 	if (!entry->folded && !recursive)
475 		return;
476 
477 	nr_entries = 1; /* for self */
478 	list_for_each_entry(child, &entry->children, node) {
479 		if (recursive)
480 			annotated_data_browser__unfold(browser, child, true);
481 
482 		nr_entries += child->nr_entries;
483 	}
484 
485 	entry->nr_entries = nr_entries;
486 	entry->folded = false;
487 }
488 
489 static void annotated_data_browser__toggle_fold(struct annotated_data_browser *browser,
490 						bool recursive)
491 {
492 	struct browser_entry *curr = browser->curr;
493 	struct browser_entry *parent;
494 
495 	parent = curr->parent;
496 	while (parent) {
497 		parent->nr_entries -= curr->nr_entries;
498 		parent = parent->parent;
499 	}
500 	browser->b.nr_entries -= curr->nr_entries;
501 
502 	if (curr->folded)
503 		annotated_data_browser__unfold(browser, curr, recursive);
504 	else
505 		annotated_data_browser__fold(browser, curr, recursive);
506 
507 	parent = curr->parent;
508 	while (parent) {
509 		parent->nr_entries += curr->nr_entries;
510 		parent = parent->parent;
511 	}
512 	browser->b.nr_entries += curr->nr_entries;
513 
514 	assert(browser->b.nr_entries == count_visible_entries(browser));
515 }
516 
517 static int annotated_data_browser__run(struct annotated_data_browser *browser,
518 				       struct evsel *evsel __maybe_unused,
519 				       struct hist_browser_timer *hbt)
520 {
521 	int delay_secs = hbt ? hbt->refresh : 0;
522 	int key;
523 
524 	if (browser__show(&browser->b) < 0)
525 		return -1;
526 
527 	while (1) {
528 		key = ui_browser__run(&browser->b, delay_secs);
529 
530 		switch (key) {
531 		case K_TIMER:
532 			if (hbt)
533 				hbt->timer(hbt->arg);
534 			continue;
535 		case K_F1:
536 		case 'h':
537 			ui_browser__help_window(&browser->b,
538 		"UP/DOWN/PGUP\n"
539 		"PGDN/SPACE    Navigate\n"
540 		"</>           Move to prev/next symbol\n"
541 		"e             Expand/Collapse current entry\n"
542 		"E             Expand/Collapse all children of the current\n"
543 		"q/ESC/CTRL+C  Exit\n\n");
544 			continue;
545 		case 'e':
546 			annotated_data_browser__toggle_fold(browser,
547 							    /*recursive=*/false);
548 			break;
549 		case 'E':
550 			annotated_data_browser__toggle_fold(browser,
551 							    /*recursive=*/true);
552 			break;
553 		case K_LEFT:
554 		case '<':
555 		case '>':
556 		case K_ESC:
557 		case 'q':
558 		case CTRL('c'):
559 			goto out;
560 		default:
561 			continue;
562 		}
563 	}
564 out:
565 	ui_browser__hide(&browser->b);
566 	return key;
567 }
568 
569 int hist_entry__annotate_data_tui(struct hist_entry *he, struct evsel *evsel,
570 				  struct hist_browser_timer *hbt)
571 {
572 	struct annotated_data_browser browser = {
573 		.b = {
574 			.refresh = browser__refresh,
575 			.seek	 = browser__seek,
576 			.write	 = browser__write,
577 			.priv	 = he,
578 			.extra_title_lines = 1,
579 		},
580 		.nr_events = 1,
581 	};
582 	int ret;
583 
584 	ui_helpline__push("Press ESC to exit");
585 
586 	if (evsel__is_group_event(evsel)) {
587 		struct evsel *pos;
588 		int nr = 0;
589 
590 		for_each_group_evsel(pos, evsel) {
591 			if (!symbol_conf.skip_empty ||
592 			    evsel__hists(pos)->stats.nr_samples)
593 				nr++;
594 		}
595 		browser.nr_events = nr;
596 	}
597 
598 	ret = annotated_data_browser__collect_entries(&browser);
599 	if (ret < 0)
600 		goto out;
601 
602 	/* To get the top and current entry */
603 	browser__refresh(&browser.b);
604 	/* Show the first-level child entries by default */
605 	annotated_data_browser__toggle_fold(&browser, /*recursive=*/false);
606 
607 	ret = annotated_data_browser__run(&browser, evsel, hbt);
608 
609 out:
610 	annotated_data_browser__delete_entries(&browser);
611 
612 	return ret;
613 }
614