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
get_browser(struct ui_browser * uib)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
update_hist_entry(struct type_hist_entry * dst,struct type_hist_entry * src)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
get_member_overhead(struct annotated_data_type * adt,struct browser_entry * entry,struct evsel * leader)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
add_child_entries(struct annotated_data_browser * browser,struct browser_entry * parent,struct annotated_data_type * adt,struct annotated_member * member,struct evsel * evsel,int indent)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
count_visible_entries(struct annotated_data_browser * browser)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
annotated_data_browser__collect_entries(struct annotated_data_browser * browser)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
annotated_data_browser__delete_entries(struct annotated_data_browser * browser)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
get_first_child(struct browser_entry * entry)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
get_last_child(struct browser_entry * entry)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
is_first_child(struct browser_entry * entry)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
is_last_child(struct browser_entry * entry)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
browser__prev_entry(struct ui_browser * uib,struct browser_entry * entry)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
browser__next_entry(struct ui_browser * uib,struct browser_entry * entry)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
browser__seek(struct ui_browser * uib,off_t offset,int whence)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
browser__refresh(struct ui_browser * uib)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
browser__show(struct ui_browser * uib)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
browser__write_overhead(struct ui_browser * uib,struct type_hist * total,struct type_hist_entry * hist,int row)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
browser__write(struct ui_browser * uib,void * entry,int row)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
annotated_data_browser__fold(struct annotated_data_browser * browser,struct browser_entry * entry,bool recursive)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
annotated_data_browser__unfold(struct annotated_data_browser * browser,struct browser_entry * entry,bool recursive)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
annotated_data_browser__toggle_fold(struct annotated_data_browser * browser,bool recursive)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
annotated_data_browser__run(struct annotated_data_browser * browser,struct evsel * evsel __maybe_unused,struct hist_browser_timer * hbt)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
hist_entry__annotate_data_tui(struct hist_entry * he,struct evsel * evsel,struct hist_browser_timer * hbt)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