perf tools: Fix 64 bit integer format strings
[deliverable/linux.git] / tools / perf / util / ui / browsers / hists.c
CommitLineData
d1b4f249
ACM
1#define _GNU_SOURCE
2#include <stdio.h>
3#undef _GNU_SOURCE
4#include "../libslang.h"
5#include <stdlib.h>
6#include <string.h>
7#include <newt.h>
8#include <linux/rbtree.h>
9
10#include "../../hist.h"
11#include "../../pstack.h"
12#include "../../sort.h"
13#include "../../util.h"
14
15#include "../browser.h"
16#include "../helpline.h"
17#include "../util.h"
18#include "map.h"
19
d1b4f249
ACM
20struct hist_browser {
21 struct ui_browser b;
22 struct hists *hists;
23 struct hist_entry *he_selection;
24 struct map_symbol *selection;
25};
26
27static void hist_browser__refresh_dimensions(struct hist_browser *self)
28{
29 /* 3 == +/- toggle symbol before actual hist_entry rendering */
30 self->b.width = 3 + (hists__sort_list_width(self->hists) +
31 sizeof("[k]"));
32}
33
34static void hist_browser__reset(struct hist_browser *self)
35{
36 self->b.nr_entries = self->hists->nr_entries;
37 hist_browser__refresh_dimensions(self);
38 ui_browser__reset_index(&self->b);
39}
40
41static char tree__folded_sign(bool unfolded)
42{
43 return unfolded ? '-' : '+';
44}
45
46static char map_symbol__folded(const struct map_symbol *self)
47{
48 return self->has_children ? tree__folded_sign(self->unfolded) : ' ';
49}
50
51static char hist_entry__folded(const struct hist_entry *self)
52{
53 return map_symbol__folded(&self->ms);
54}
55
56static char callchain_list__folded(const struct callchain_list *self)
57{
58 return map_symbol__folded(&self->ms);
59}
60
3c916cc2
ACM
61static void map_symbol__set_folding(struct map_symbol *self, bool unfold)
62{
63 self->unfolded = unfold ? self->has_children : false;
64}
65
d1b4f249
ACM
66static int callchain_node__count_rows_rb_tree(struct callchain_node *self)
67{
68 int n = 0;
69 struct rb_node *nd;
70
71 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
72 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
73 struct callchain_list *chain;
74 char folded_sign = ' '; /* No children */
75
76 list_for_each_entry(chain, &child->val, list) {
77 ++n;
78 /* We need this because we may not have children */
79 folded_sign = callchain_list__folded(chain);
80 if (folded_sign == '+')
81 break;
82 }
83
84 if (folded_sign == '-') /* Have children and they're unfolded */
85 n += callchain_node__count_rows_rb_tree(child);
86 }
87
88 return n;
89}
90
91static int callchain_node__count_rows(struct callchain_node *node)
92{
93 struct callchain_list *chain;
94 bool unfolded = false;
95 int n = 0;
96
97 list_for_each_entry(chain, &node->val, list) {
98 ++n;
99 unfolded = chain->ms.unfolded;
100 }
101
102 if (unfolded)
103 n += callchain_node__count_rows_rb_tree(node);
104
105 return n;
106}
107
108static int callchain__count_rows(struct rb_root *chain)
109{
110 struct rb_node *nd;
111 int n = 0;
112
113 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
114 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
115 n += callchain_node__count_rows(node);
116 }
117
118 return n;
119}
120
121static bool map_symbol__toggle_fold(struct map_symbol *self)
122{
123 if (!self->has_children)
124 return false;
125
126 self->unfolded = !self->unfolded;
127 return true;
128}
129
130static void callchain_node__init_have_children_rb_tree(struct callchain_node *self)
131{
132 struct rb_node *nd = rb_first(&self->rb_root);
133
134 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
135 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
136 struct callchain_list *chain;
293db47f 137 bool first = true;
d1b4f249
ACM
138
139 list_for_each_entry(chain, &child->val, list) {
140 if (first) {
141 first = false;
142 chain->ms.has_children = chain->list.next != &child->val ||
293db47f 143 !RB_EMPTY_ROOT(&child->rb_root);
d1b4f249
ACM
144 } else
145 chain->ms.has_children = chain->list.next == &child->val &&
293db47f 146 !RB_EMPTY_ROOT(&child->rb_root);
d1b4f249
ACM
147 }
148
149 callchain_node__init_have_children_rb_tree(child);
150 }
151}
152
153static void callchain_node__init_have_children(struct callchain_node *self)
154{
155 struct callchain_list *chain;
156
157 list_for_each_entry(chain, &self->val, list)
293db47f 158 chain->ms.has_children = !RB_EMPTY_ROOT(&self->rb_root);
d1b4f249
ACM
159
160 callchain_node__init_have_children_rb_tree(self);
161}
162
163static void callchain__init_have_children(struct rb_root *self)
164{
165 struct rb_node *nd;
166
167 for (nd = rb_first(self); nd; nd = rb_next(nd)) {
168 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
169 callchain_node__init_have_children(node);
170 }
171}
172
173static void hist_entry__init_have_children(struct hist_entry *self)
174{
175 if (!self->init_have_children) {
18b308d7 176 self->ms.has_children = !RB_EMPTY_ROOT(&self->sorted_chain);
d1b4f249
ACM
177 callchain__init_have_children(&self->sorted_chain);
178 self->init_have_children = true;
179 }
180}
181
182static bool hist_browser__toggle_fold(struct hist_browser *self)
183{
184 if (map_symbol__toggle_fold(self->selection)) {
185 struct hist_entry *he = self->he_selection;
186
187 hist_entry__init_have_children(he);
188 self->hists->nr_entries -= he->nr_rows;
189
190 if (he->ms.unfolded)
191 he->nr_rows = callchain__count_rows(&he->sorted_chain);
192 else
193 he->nr_rows = 0;
194 self->hists->nr_entries += he->nr_rows;
195 self->b.nr_entries = self->hists->nr_entries;
196
197 return true;
198 }
199
200 /* If it doesn't have children, no toggling performed */
201 return false;
202}
203
3c916cc2
ACM
204static int callchain_node__set_folding_rb_tree(struct callchain_node *self, bool unfold)
205{
206 int n = 0;
207 struct rb_node *nd;
208
209 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
210 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
211 struct callchain_list *chain;
212 bool has_children = false;
213
214 list_for_each_entry(chain, &child->val, list) {
215 ++n;
216 map_symbol__set_folding(&chain->ms, unfold);
217 has_children = chain->ms.has_children;
218 }
219
220 if (has_children)
221 n += callchain_node__set_folding_rb_tree(child, unfold);
222 }
223
224 return n;
225}
226
227static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
228{
229 struct callchain_list *chain;
230 bool has_children = false;
231 int n = 0;
232
233 list_for_each_entry(chain, &node->val, list) {
234 ++n;
235 map_symbol__set_folding(&chain->ms, unfold);
236 has_children = chain->ms.has_children;
237 }
238
239 if (has_children)
240 n += callchain_node__set_folding_rb_tree(node, unfold);
241
242 return n;
243}
244
245static int callchain__set_folding(struct rb_root *chain, bool unfold)
246{
247 struct rb_node *nd;
248 int n = 0;
249
250 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
251 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
252 n += callchain_node__set_folding(node, unfold);
253 }
254
255 return n;
256}
257
258static void hist_entry__set_folding(struct hist_entry *self, bool unfold)
259{
260 hist_entry__init_have_children(self);
261 map_symbol__set_folding(&self->ms, unfold);
262
263 if (self->ms.has_children) {
264 int n = callchain__set_folding(&self->sorted_chain, unfold);
265 self->nr_rows = unfold ? n : 0;
266 } else
267 self->nr_rows = 0;
268}
269
270static void hists__set_folding(struct hists *self, bool unfold)
271{
272 struct rb_node *nd;
273
274 self->nr_entries = 0;
275
276 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
277 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
278 hist_entry__set_folding(he, unfold);
279 self->nr_entries += 1 + he->nr_rows;
280 }
281}
282
283static void hist_browser__set_folding(struct hist_browser *self, bool unfold)
284{
285 hists__set_folding(self->hists, unfold);
286 self->b.nr_entries = self->hists->nr_entries;
287 /* Go to the start, we may be way after valid entries after a collapse */
288 ui_browser__reset_index(&self->b);
289}
290
b50e003d 291static int hist_browser__run(struct hist_browser *self, const char *title)
d1b4f249 292{
b50e003d 293 int key;
3c916cc2
ACM
294 int exit_keys[] = { 'a', '?', 'h', 'C', 'd', 'D', 'E', 't',
295 NEWT_KEY_ENTER, NEWT_KEY_RIGHT, NEWT_KEY_LEFT, 0, };
d1b4f249
ACM
296
297 self->b.entries = &self->hists->entries;
298 self->b.nr_entries = self->hists->nr_entries;
299
300 hist_browser__refresh_dimensions(self);
301
59e8fe32
ACM
302 if (ui_browser__show(&self->b, title,
303 "Press '?' for help on key bindings") < 0)
d1b4f249
ACM
304 return -1;
305
4c1c952e 306 ui_browser__add_exit_keys(&self->b, exit_keys);
d1b4f249
ACM
307
308 while (1) {
b50e003d 309 key = ui_browser__run(&self->b);
d1b4f249 310
b50e003d 311 switch (key) {
4694153c 312 case 'D': { /* Debug */
d1b4f249
ACM
313 static int seq;
314 struct hist_entry *h = rb_entry(self->b.top,
315 struct hist_entry, rb_node);
316 ui_helpline__pop();
317 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
318 seq++, self->b.nr_entries,
319 self->hists->nr_entries,
320 self->b.height,
321 self->b.index,
322 self->b.top_idx,
323 h->row_offset, h->nr_rows);
324 }
3c916cc2
ACM
325 break;
326 case 'C':
327 /* Collapse the whole world. */
328 hist_browser__set_folding(self, false);
329 break;
330 case 'E':
331 /* Expand the whole world. */
332 hist_browser__set_folding(self, true);
333 break;
d1b4f249
ACM
334 case NEWT_KEY_ENTER:
335 if (hist_browser__toggle_fold(self))
336 break;
337 /* fall thru */
338 default:
b50e003d 339 goto out;
d1b4f249
ACM
340 }
341 }
b50e003d 342out:
59e8fe32 343 ui_browser__hide(&self->b);
b50e003d 344 return key;
d1b4f249
ACM
345}
346
347static char *callchain_list__sym_name(struct callchain_list *self,
348 char *bf, size_t bfsize)
349{
350 if (self->ms.sym)
351 return self->ms.sym->name;
352
9486aa38 353 snprintf(bf, bfsize, "%#" PRIx64, self->ip);
d1b4f249
ACM
354 return bf;
355}
356
357#define LEVEL_OFFSET_STEP 3
358
359static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
360 struct callchain_node *chain_node,
361 u64 total, int level,
362 unsigned short row,
363 off_t *row_offset,
364 bool *is_current_entry)
365{
366 struct rb_node *node;
367 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
368 u64 new_total, remaining;
369
370 if (callchain_param.mode == CHAIN_GRAPH_REL)
371 new_total = chain_node->children_hit;
372 else
373 new_total = total;
374
375 remaining = new_total;
376 node = rb_first(&chain_node->rb_root);
377 while (node) {
378 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
379 struct rb_node *next = rb_next(node);
380 u64 cumul = cumul_hits(child);
381 struct callchain_list *chain;
382 char folded_sign = ' ';
383 int first = true;
384 int extra_offset = 0;
385
386 remaining -= cumul;
387
388 list_for_each_entry(chain, &child->val, list) {
389 char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
390 const char *str;
391 int color;
392 bool was_first = first;
393
163caed9 394 if (first)
d1b4f249 395 first = false;
163caed9 396 else
d1b4f249 397 extra_offset = LEVEL_OFFSET_STEP;
d1b4f249
ACM
398
399 folded_sign = callchain_list__folded(chain);
400 if (*row_offset != 0) {
401 --*row_offset;
402 goto do_next;
403 }
404
405 alloc_str = NULL;
406 str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
407 if (was_first) {
408 double percent = cumul * 100.0 / new_total;
409
410 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
411 str = "Not enough memory!";
412 else
413 str = alloc_str;
414 }
415
416 color = HE_COLORSET_NORMAL;
417 width = self->b.width - (offset + extra_offset + 2);
418 if (ui_browser__is_current_entry(&self->b, row)) {
419 self->selection = &chain->ms;
420 color = HE_COLORSET_SELECTED;
421 *is_current_entry = true;
422 }
423
8f9bbc40
ACM
424 ui_browser__set_color(&self->b, color);
425 ui_browser__gotorc(&self->b, row, 0);
d1b4f249
ACM
426 slsmg_write_nstring(" ", offset + extra_offset);
427 slsmg_printf("%c ", folded_sign);
428 slsmg_write_nstring(str, width);
429 free(alloc_str);
430
431 if (++row == self->b.height)
432 goto out;
433do_next:
434 if (folded_sign == '+')
435 break;
436 }
437
438 if (folded_sign == '-') {
439 const int new_level = level + (extra_offset ? 2 : 1);
440 row += hist_browser__show_callchain_node_rb_tree(self, child, new_total,
441 new_level, row, row_offset,
442 is_current_entry);
443 }
444 if (row == self->b.height)
445 goto out;
446 node = next;
447 }
448out:
449 return row - first_row;
450}
451
452static int hist_browser__show_callchain_node(struct hist_browser *self,
453 struct callchain_node *node,
454 int level, unsigned short row,
455 off_t *row_offset,
456 bool *is_current_entry)
457{
458 struct callchain_list *chain;
459 int first_row = row,
460 offset = level * LEVEL_OFFSET_STEP,
461 width = self->b.width - offset;
462 char folded_sign = ' ';
463
464 list_for_each_entry(chain, &node->val, list) {
465 char ipstr[BITS_PER_LONG / 4 + 1], *s;
466 int color;
163caed9 467
d1b4f249
ACM
468 folded_sign = callchain_list__folded(chain);
469
470 if (*row_offset != 0) {
471 --*row_offset;
472 continue;
473 }
474
475 color = HE_COLORSET_NORMAL;
476 if (ui_browser__is_current_entry(&self->b, row)) {
477 self->selection = &chain->ms;
478 color = HE_COLORSET_SELECTED;
479 *is_current_entry = true;
480 }
481
482 s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
8f9bbc40
ACM
483 ui_browser__gotorc(&self->b, row, 0);
484 ui_browser__set_color(&self->b, color);
d1b4f249
ACM
485 slsmg_write_nstring(" ", offset);
486 slsmg_printf("%c ", folded_sign);
487 slsmg_write_nstring(s, width - 2);
488
489 if (++row == self->b.height)
490 goto out;
491 }
492
493 if (folded_sign == '-')
494 row += hist_browser__show_callchain_node_rb_tree(self, node,
495 self->hists->stats.total_period,
496 level + 1, row,
497 row_offset,
498 is_current_entry);
499out:
500 return row - first_row;
501}
502
503static int hist_browser__show_callchain(struct hist_browser *self,
504 struct rb_root *chain,
505 int level, unsigned short row,
506 off_t *row_offset,
507 bool *is_current_entry)
508{
509 struct rb_node *nd;
510 int first_row = row;
511
512 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
513 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
514
515 row += hist_browser__show_callchain_node(self, node, level,
516 row, row_offset,
517 is_current_entry);
518 if (row == self->b.height)
519 break;
520 }
521
522 return row - first_row;
523}
524
525static int hist_browser__show_entry(struct hist_browser *self,
526 struct hist_entry *entry,
527 unsigned short row)
528{
529 char s[256];
530 double percent;
531 int printed = 0;
532 int color, width = self->b.width;
533 char folded_sign = ' ';
534 bool current_entry = ui_browser__is_current_entry(&self->b, row);
535 off_t row_offset = entry->row_offset;
536
537 if (current_entry) {
538 self->he_selection = entry;
539 self->selection = &entry->ms;
540 }
541
542 if (symbol_conf.use_callchain) {
163caed9 543 hist_entry__init_have_children(entry);
d1b4f249
ACM
544 folded_sign = hist_entry__folded(entry);
545 }
546
547 if (row_offset == 0) {
548 hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false,
549 0, false, self->hists->stats.total_period);
550 percent = (entry->period * 100.0) / self->hists->stats.total_period;
551
552 color = HE_COLORSET_SELECTED;
553 if (!current_entry) {
554 if (percent >= MIN_RED)
555 color = HE_COLORSET_TOP;
556 else if (percent >= MIN_GREEN)
557 color = HE_COLORSET_MEDIUM;
558 else
559 color = HE_COLORSET_NORMAL;
560 }
561
8f9bbc40
ACM
562 ui_browser__set_color(&self->b, color);
563 ui_browser__gotorc(&self->b, row, 0);
d1b4f249
ACM
564 if (symbol_conf.use_callchain) {
565 slsmg_printf("%c ", folded_sign);
566 width -= 2;
567 }
568 slsmg_write_nstring(s, width);
569 ++row;
570 ++printed;
571 } else
572 --row_offset;
573
574 if (folded_sign == '-' && row != self->b.height) {
575 printed += hist_browser__show_callchain(self, &entry->sorted_chain,
576 1, row, &row_offset,
577 &current_entry);
578 if (current_entry)
579 self->he_selection = entry;
580 }
581
582 return printed;
583}
584
585static unsigned int hist_browser__refresh(struct ui_browser *self)
586{
587 unsigned row = 0;
588 struct rb_node *nd;
589 struct hist_browser *hb = container_of(self, struct hist_browser, b);
590
591 if (self->top == NULL)
592 self->top = rb_first(&hb->hists->entries);
593
594 for (nd = self->top; nd; nd = rb_next(nd)) {
595 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
596
597 if (h->filtered)
598 continue;
599
600 row += hist_browser__show_entry(hb, h, row);
601 if (row == self->height)
602 break;
603 }
604
605 return row;
606}
607
608static struct rb_node *hists__filter_entries(struct rb_node *nd)
609{
610 while (nd != NULL) {
611 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
612 if (!h->filtered)
613 return nd;
614
615 nd = rb_next(nd);
616 }
617
618 return NULL;
619}
620
621static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
622{
623 while (nd != NULL) {
624 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
625 if (!h->filtered)
626 return nd;
627
628 nd = rb_prev(nd);
629 }
630
631 return NULL;
632}
633
634static void ui_browser__hists_seek(struct ui_browser *self,
635 off_t offset, int whence)
636{
637 struct hist_entry *h;
638 struct rb_node *nd;
639 bool first = true;
640
641 switch (whence) {
642 case SEEK_SET:
643 nd = hists__filter_entries(rb_first(self->entries));
644 break;
645 case SEEK_CUR:
646 nd = self->top;
647 goto do_offset;
648 case SEEK_END:
649 nd = hists__filter_prev_entries(rb_last(self->entries));
650 first = false;
651 break;
652 default:
653 return;
654 }
655
656 /*
657 * Moves not relative to the first visible entry invalidates its
658 * row_offset:
659 */
660 h = rb_entry(self->top, struct hist_entry, rb_node);
661 h->row_offset = 0;
662
663 /*
664 * Here we have to check if nd is expanded (+), if it is we can't go
665 * the next top level hist_entry, instead we must compute an offset of
666 * what _not_ to show and not change the first visible entry.
667 *
668 * This offset increments when we are going from top to bottom and
669 * decreases when we're going from bottom to top.
670 *
671 * As we don't have backpointers to the top level in the callchains
672 * structure, we need to always print the whole hist_entry callchain,
673 * skipping the first ones that are before the first visible entry
674 * and stop when we printed enough lines to fill the screen.
675 */
676do_offset:
677 if (offset > 0) {
678 do {
679 h = rb_entry(nd, struct hist_entry, rb_node);
680 if (h->ms.unfolded) {
681 u16 remaining = h->nr_rows - h->row_offset;
682 if (offset > remaining) {
683 offset -= remaining;
684 h->row_offset = 0;
685 } else {
686 h->row_offset += offset;
687 offset = 0;
688 self->top = nd;
689 break;
690 }
691 }
692 nd = hists__filter_entries(rb_next(nd));
693 if (nd == NULL)
694 break;
695 --offset;
696 self->top = nd;
697 } while (offset != 0);
698 } else if (offset < 0) {
699 while (1) {
700 h = rb_entry(nd, struct hist_entry, rb_node);
701 if (h->ms.unfolded) {
702 if (first) {
703 if (-offset > h->row_offset) {
704 offset += h->row_offset;
705 h->row_offset = 0;
706 } else {
707 h->row_offset += offset;
708 offset = 0;
709 self->top = nd;
710 break;
711 }
712 } else {
713 if (-offset > h->nr_rows) {
714 offset += h->nr_rows;
715 h->row_offset = 0;
716 } else {
717 h->row_offset = h->nr_rows + offset;
718 offset = 0;
719 self->top = nd;
720 break;
721 }
722 }
723 }
724
725 nd = hists__filter_prev_entries(rb_prev(nd));
726 if (nd == NULL)
727 break;
728 ++offset;
729 self->top = nd;
730 if (offset == 0) {
731 /*
732 * Last unfiltered hist_entry, check if it is
733 * unfolded, if it is then we should have
734 * row_offset at its last entry.
735 */
736 h = rb_entry(nd, struct hist_entry, rb_node);
737 if (h->ms.unfolded)
738 h->row_offset = h->nr_rows;
739 break;
740 }
741 first = false;
742 }
743 } else {
744 self->top = nd;
745 h = rb_entry(nd, struct hist_entry, rb_node);
746 h->row_offset = 0;
747 }
748}
749
750static struct hist_browser *hist_browser__new(struct hists *hists)
751{
752 struct hist_browser *self = zalloc(sizeof(*self));
753
754 if (self) {
755 self->hists = hists;
756 self->b.refresh = hist_browser__refresh;
757 self->b.seek = ui_browser__hists_seek;
758 }
759
760 return self;
761}
762
763static void hist_browser__delete(struct hist_browser *self)
764{
d1b4f249
ACM
765 free(self);
766}
767
768static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
769{
770 return self->he_selection;
771}
772
773static struct thread *hist_browser__selected_thread(struct hist_browser *self)
774{
775 return self->he_selection->thread;
776}
777
469917ce
ACM
778static int hists__browser_title(struct hists *self, char *bf, size_t size,
779 const char *ev_name, const struct dso *dso,
780 const struct thread *thread)
d1b4f249 781{
469917ce
ACM
782 char unit;
783 int printed;
784 unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE];
785
786 nr_events = convert_unit(nr_events, &unit);
787 printed = snprintf(bf, size, "Events: %lu%c %s", nr_events, unit, ev_name);
d1b4f249
ACM
788
789 if (thread)
790 printed += snprintf(bf + printed, size - printed,
469917ce
ACM
791 ", Thread: %s(%d)",
792 (thread->comm_set ? thread->comm : ""),
d1b4f249
ACM
793 thread->pid);
794 if (dso)
795 printed += snprintf(bf + printed, size - printed,
469917ce
ACM
796 ", DSO: %s", dso->short_name);
797 return printed;
d1b4f249
ACM
798}
799
800int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
801{
802 struct hist_browser *browser = hist_browser__new(self);
803 struct pstack *fstack;
804 const struct thread *thread_filter = NULL;
805 const struct dso *dso_filter = NULL;
d1b4f249
ACM
806 char msg[160];
807 int key = -1;
808
809 if (browser == NULL)
810 return -1;
811
812 fstack = pstack__new(2);
813 if (fstack == NULL)
814 goto out;
815
816 ui_helpline__push(helpline);
817
469917ce
ACM
818 hists__browser_title(self, msg, sizeof(msg), ev_name,
819 dso_filter, thread_filter);
d1b4f249
ACM
820 while (1) {
821 const struct thread *thread;
822 const struct dso *dso;
823 char *options[16];
824 int nr_options = 0, choice = 0, i,
825 annotate = -2, zoom_dso = -2, zoom_thread = -2,
826 browse_map = -2;
827
b50e003d 828 key = hist_browser__run(browser, msg);
d1b4f249
ACM
829
830 thread = hist_browser__selected_thread(browser);
831 dso = browser->selection->map ? browser->selection->map->dso : NULL;
832
b50e003d 833 switch (key) {
b50e003d
ACM
834 case NEWT_KEY_TAB:
835 case NEWT_KEY_UNTAB:
836 /*
837 * Exit the browser, let hists__browser_tree
838 * go to the next or previous
839 */
840 goto out_free_stack;
841 case 'a':
842 if (browser->selection->map == NULL &&
843 browser->selection->map->dso->annotate_warned)
d1b4f249 844 continue;
b50e003d
ACM
845 goto do_annotate;
846 case 'd':
847 goto zoom_dso;
848 case 't':
849 goto zoom_thread;
3c916cc2 850 case NEWT_KEY_F1:
b50e003d
ACM
851 case 'h':
852 case '?':
b50e003d
ACM
853 ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
854 "<- Zoom out\n"
855 "a Annotate current symbol\n"
856 "h/?/F1 Show this window\n"
3c916cc2
ACM
857 "C Collapse all callchains\n"
858 "E Expand all callchains\n"
b50e003d
ACM
859 "d Zoom into current DSO\n"
860 "t Zoom into current Thread\n"
861 "q/CTRL+C Exit browser");
862 continue;
863 case NEWT_KEY_ENTER:
864 case NEWT_KEY_RIGHT:
865 /* menu */
866 break;
867 case NEWT_KEY_LEFT: {
868 const void *top;
d1b4f249 869
b50e003d 870 if (pstack__empty(fstack))
d1b4f249 871 continue;
b50e003d
ACM
872 top = pstack__pop(fstack);
873 if (top == &dso_filter)
874 goto zoom_out_dso;
875 if (top == &thread_filter)
876 goto zoom_out_thread;
877 continue;
878 }
879 case NEWT_KEY_ESCAPE:
880 if (!ui__dialog_yesno("Do you really want to exit?"))
881 continue;
882 /* Fall thru */
883 default:
884 goto out_free_stack;
d1b4f249
ACM
885 }
886
887 if (browser->selection->sym != NULL &&
888 !browser->selection->map->dso->annotate_warned &&
889 asprintf(&options[nr_options], "Annotate %s",
890 browser->selection->sym->name) > 0)
891 annotate = nr_options++;
892
893 if (thread != NULL &&
894 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
895 (thread_filter ? "out of" : "into"),
896 (thread->comm_set ? thread->comm : ""),
897 thread->pid) > 0)
898 zoom_thread = nr_options++;
899
900 if (dso != NULL &&
901 asprintf(&options[nr_options], "Zoom %s %s DSO",
902 (dso_filter ? "out of" : "into"),
903 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
904 zoom_dso = nr_options++;
905
906 if (browser->selection->map != NULL &&
907 asprintf(&options[nr_options], "Browse map details") > 0)
908 browse_map = nr_options++;
909
910 options[nr_options++] = (char *)"Exit";
911
1e6dd077 912 choice = ui__popup_menu(nr_options, options);
d1b4f249
ACM
913
914 for (i = 0; i < nr_options - 1; ++i)
915 free(options[i]);
916
917 if (choice == nr_options - 1)
918 break;
919
920 if (choice == -1)
921 continue;
922
923 if (choice == annotate) {
924 struct hist_entry *he;
925do_annotate:
926 if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
927 browser->selection->map->dso->annotate_warned = 1;
928 ui_helpline__puts("No vmlinux file found, can't "
929 "annotate with just a "
930 "kallsyms file");
931 continue;
932 }
933
934 he = hist_browser__selected_entry(browser);
935 if (he == NULL)
936 continue;
937
938 hist_entry__tui_annotate(he);
939 } else if (choice == browse_map)
940 map__browse(browser->selection->map);
941 else if (choice == zoom_dso) {
942zoom_dso:
943 if (dso_filter) {
944 pstack__remove(fstack, &dso_filter);
945zoom_out_dso:
946 ui_helpline__pop();
947 dso_filter = NULL;
948 } else {
949 if (dso == NULL)
950 continue;
951 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
952 dso->kernel ? "the Kernel" : dso->short_name);
953 dso_filter = dso;
954 pstack__push(fstack, &dso_filter);
955 }
956 hists__filter_by_dso(self, dso_filter);
469917ce
ACM
957 hists__browser_title(self, msg, sizeof(msg), ev_name,
958 dso_filter, thread_filter);
d1b4f249
ACM
959 hist_browser__reset(browser);
960 } else if (choice == zoom_thread) {
961zoom_thread:
962 if (thread_filter) {
963 pstack__remove(fstack, &thread_filter);
964zoom_out_thread:
965 ui_helpline__pop();
966 thread_filter = NULL;
967 } else {
968 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
969 thread->comm_set ? thread->comm : "",
970 thread->pid);
971 thread_filter = thread;
972 pstack__push(fstack, &thread_filter);
973 }
974 hists__filter_by_thread(self, thread_filter);
469917ce
ACM
975 hists__browser_title(self, msg, sizeof(msg), ev_name,
976 dso_filter, thread_filter);
d1b4f249
ACM
977 hist_browser__reset(browser);
978 }
979 }
980out_free_stack:
981 pstack__delete(fstack);
982out:
983 hist_browser__delete(browser);
984 return key;
985}
986
987int hists__tui_browse_tree(struct rb_root *self, const char *help)
988{
989 struct rb_node *first = rb_first(self), *nd = first, *next;
990 int key = 0;
991
992 while (nd) {
993 struct hists *hists = rb_entry(nd, struct hists, rb_node);
994 const char *ev_name = __event_name(hists->type, hists->config);
995
996 key = hists__browse(hists, help, ev_name);
d1b4f249
ACM
997 switch (key) {
998 case NEWT_KEY_TAB:
999 next = rb_next(nd);
1000 if (next)
1001 nd = next;
1002 break;
1003 case NEWT_KEY_UNTAB:
1004 if (nd == first)
1005 continue;
1006 nd = rb_prev(nd);
1007 default:
b50e003d 1008 return key;
d1b4f249
ACM
1009 }
1010 }
1011
1012 return key;
1013}
This page took 0.111491 seconds and 5 git commands to generate.