perf annotate: Fix a build error
[deliverable/linux.git] / tools / perf / util / ui / browsers / hists.c
CommitLineData
d1b4f249 1#include <stdio.h>
d1b4f249
ACM
2#include "../libslang.h"
3#include <stdlib.h>
4#include <string.h>
5#include <newt.h>
6#include <linux/rbtree.h>
7
e248de33
ACM
8#include "../../evsel.h"
9#include "../../evlist.h"
d1b4f249
ACM
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"
4610e413 18#include "../ui.h"
d1b4f249
ACM
19#include "map.h"
20
d1b4f249
ACM
21struct hist_browser {
22 struct ui_browser b;
23 struct hists *hists;
24 struct hist_entry *he_selection;
25 struct map_symbol *selection;
724c9c9f 26 bool has_symbols;
d1b4f249
ACM
27};
28
81cce8de 29static int hists__browser_title(struct hists *self, char *bf, size_t size,
d7b76f09 30 const char *ev_name);
81cce8de 31
d1b4f249
ACM
32static void hist_browser__refresh_dimensions(struct hist_browser *self)
33{
34 /* 3 == +/- toggle symbol before actual hist_entry rendering */
35 self->b.width = 3 + (hists__sort_list_width(self->hists) +
36 sizeof("[k]"));
37}
38
39static void hist_browser__reset(struct hist_browser *self)
40{
41 self->b.nr_entries = self->hists->nr_entries;
42 hist_browser__refresh_dimensions(self);
43 ui_browser__reset_index(&self->b);
44}
45
46static char tree__folded_sign(bool unfolded)
47{
48 return unfolded ? '-' : '+';
49}
50
51static char map_symbol__folded(const struct map_symbol *self)
52{
53 return self->has_children ? tree__folded_sign(self->unfolded) : ' ';
54}
55
56static char hist_entry__folded(const struct hist_entry *self)
57{
58 return map_symbol__folded(&self->ms);
59}
60
61static char callchain_list__folded(const struct callchain_list *self)
62{
63 return map_symbol__folded(&self->ms);
64}
65
3c916cc2
ACM
66static void map_symbol__set_folding(struct map_symbol *self, bool unfold)
67{
68 self->unfolded = unfold ? self->has_children : false;
69}
70
d1b4f249
ACM
71static int callchain_node__count_rows_rb_tree(struct callchain_node *self)
72{
73 int n = 0;
74 struct rb_node *nd;
75
76 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
77 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
78 struct callchain_list *chain;
79 char folded_sign = ' '; /* No children */
80
81 list_for_each_entry(chain, &child->val, list) {
82 ++n;
83 /* We need this because we may not have children */
84 folded_sign = callchain_list__folded(chain);
85 if (folded_sign == '+')
86 break;
87 }
88
89 if (folded_sign == '-') /* Have children and they're unfolded */
90 n += callchain_node__count_rows_rb_tree(child);
91 }
92
93 return n;
94}
95
96static int callchain_node__count_rows(struct callchain_node *node)
97{
98 struct callchain_list *chain;
99 bool unfolded = false;
100 int n = 0;
101
102 list_for_each_entry(chain, &node->val, list) {
103 ++n;
104 unfolded = chain->ms.unfolded;
105 }
106
107 if (unfolded)
108 n += callchain_node__count_rows_rb_tree(node);
109
110 return n;
111}
112
113static int callchain__count_rows(struct rb_root *chain)
114{
115 struct rb_node *nd;
116 int n = 0;
117
118 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
119 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
120 n += callchain_node__count_rows(node);
121 }
122
123 return n;
124}
125
126static bool map_symbol__toggle_fold(struct map_symbol *self)
127{
8493fe1d
JO
128 if (!self)
129 return false;
130
d1b4f249
ACM
131 if (!self->has_children)
132 return false;
133
134 self->unfolded = !self->unfolded;
135 return true;
136}
137
138static void callchain_node__init_have_children_rb_tree(struct callchain_node *self)
139{
140 struct rb_node *nd = rb_first(&self->rb_root);
141
142 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
143 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
144 struct callchain_list *chain;
293db47f 145 bool first = true;
d1b4f249
ACM
146
147 list_for_each_entry(chain, &child->val, list) {
148 if (first) {
149 first = false;
150 chain->ms.has_children = chain->list.next != &child->val ||
293db47f 151 !RB_EMPTY_ROOT(&child->rb_root);
d1b4f249
ACM
152 } else
153 chain->ms.has_children = chain->list.next == &child->val &&
293db47f 154 !RB_EMPTY_ROOT(&child->rb_root);
d1b4f249
ACM
155 }
156
157 callchain_node__init_have_children_rb_tree(child);
158 }
159}
160
161static void callchain_node__init_have_children(struct callchain_node *self)
162{
163 struct callchain_list *chain;
164
165 list_for_each_entry(chain, &self->val, list)
293db47f 166 chain->ms.has_children = !RB_EMPTY_ROOT(&self->rb_root);
d1b4f249
ACM
167
168 callchain_node__init_have_children_rb_tree(self);
169}
170
171static void callchain__init_have_children(struct rb_root *self)
172{
173 struct rb_node *nd;
174
175 for (nd = rb_first(self); nd; nd = rb_next(nd)) {
176 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
177 callchain_node__init_have_children(node);
178 }
179}
180
181static void hist_entry__init_have_children(struct hist_entry *self)
182{
183 if (!self->init_have_children) {
18b308d7 184 self->ms.has_children = !RB_EMPTY_ROOT(&self->sorted_chain);
d1b4f249
ACM
185 callchain__init_have_children(&self->sorted_chain);
186 self->init_have_children = true;
187 }
188}
189
190static bool hist_browser__toggle_fold(struct hist_browser *self)
191{
192 if (map_symbol__toggle_fold(self->selection)) {
193 struct hist_entry *he = self->he_selection;
194
195 hist_entry__init_have_children(he);
196 self->hists->nr_entries -= he->nr_rows;
197
198 if (he->ms.unfolded)
199 he->nr_rows = callchain__count_rows(&he->sorted_chain);
200 else
201 he->nr_rows = 0;
202 self->hists->nr_entries += he->nr_rows;
203 self->b.nr_entries = self->hists->nr_entries;
204
205 return true;
206 }
207
208 /* If it doesn't have children, no toggling performed */
209 return false;
210}
211
3c916cc2
ACM
212static int callchain_node__set_folding_rb_tree(struct callchain_node *self, bool unfold)
213{
214 int n = 0;
215 struct rb_node *nd;
216
217 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
218 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
219 struct callchain_list *chain;
220 bool has_children = false;
221
222 list_for_each_entry(chain, &child->val, list) {
223 ++n;
224 map_symbol__set_folding(&chain->ms, unfold);
225 has_children = chain->ms.has_children;
226 }
227
228 if (has_children)
229 n += callchain_node__set_folding_rb_tree(child, unfold);
230 }
231
232 return n;
233}
234
235static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
236{
237 struct callchain_list *chain;
238 bool has_children = false;
239 int n = 0;
240
241 list_for_each_entry(chain, &node->val, list) {
242 ++n;
243 map_symbol__set_folding(&chain->ms, unfold);
244 has_children = chain->ms.has_children;
245 }
246
247 if (has_children)
248 n += callchain_node__set_folding_rb_tree(node, unfold);
249
250 return n;
251}
252
253static int callchain__set_folding(struct rb_root *chain, bool unfold)
254{
255 struct rb_node *nd;
256 int n = 0;
257
258 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
259 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
260 n += callchain_node__set_folding(node, unfold);
261 }
262
263 return n;
264}
265
266static void hist_entry__set_folding(struct hist_entry *self, bool unfold)
267{
268 hist_entry__init_have_children(self);
269 map_symbol__set_folding(&self->ms, unfold);
270
271 if (self->ms.has_children) {
272 int n = callchain__set_folding(&self->sorted_chain, unfold);
273 self->nr_rows = unfold ? n : 0;
274 } else
275 self->nr_rows = 0;
276}
277
278static void hists__set_folding(struct hists *self, bool unfold)
279{
280 struct rb_node *nd;
281
282 self->nr_entries = 0;
283
284 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
285 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
286 hist_entry__set_folding(he, unfold);
287 self->nr_entries += 1 + he->nr_rows;
288 }
289}
290
291static void hist_browser__set_folding(struct hist_browser *self, bool unfold)
292{
293 hists__set_folding(self->hists, unfold);
294 self->b.nr_entries = self->hists->nr_entries;
295 /* Go to the start, we may be way after valid entries after a collapse */
296 ui_browser__reset_index(&self->b);
297}
298
7b27509f
ACM
299static void ui_browser__warn_lost_events(struct ui_browser *browser)
300{
301 ui_browser__warning(browser, 4,
302 "Events are being lost, check IO/CPU overload!\n\n"
303 "You may want to run 'perf' using a RT scheduler policy:\n\n"
304 " perf top -r 80\n\n"
305 "Or reduce the sampling frequency.");
306}
307
81cce8de
ACM
308static int hist_browser__run(struct hist_browser *self, const char *ev_name,
309 void(*timer)(void *arg), void *arg, int delay_secs)
d1b4f249 310{
b50e003d 311 int key;
81cce8de 312 char title[160];
d1b4f249
ACM
313
314 self->b.entries = &self->hists->entries;
315 self->b.nr_entries = self->hists->nr_entries;
316
317 hist_browser__refresh_dimensions(self);
d7b76f09 318 hists__browser_title(self->hists, title, sizeof(title), ev_name);
d1b4f249 319
59e8fe32
ACM
320 if (ui_browser__show(&self->b, title,
321 "Press '?' for help on key bindings") < 0)
d1b4f249
ACM
322 return -1;
323
d1b4f249 324 while (1) {
3af6e338 325 key = ui_browser__run(&self->b, delay_secs);
d1b4f249 326
b50e003d 327 switch (key) {
13d8f96c 328 case K_TIMER:
81cce8de 329 timer(arg);
900e14a8 330 ui_browser__update_nr_entries(&self->b, self->hists->nr_entries);
7b27509f
ACM
331
332 if (self->hists->stats.nr_lost_warned !=
333 self->hists->stats.nr_events[PERF_RECORD_LOST]) {
334 self->hists->stats.nr_lost_warned =
335 self->hists->stats.nr_events[PERF_RECORD_LOST];
336 ui_browser__warn_lost_events(&self->b);
337 }
338
339 hists__browser_title(self->hists, title, sizeof(title), ev_name);
81cce8de
ACM
340 ui_browser__show_title(&self->b, title);
341 continue;
4694153c 342 case 'D': { /* Debug */
d1b4f249
ACM
343 static int seq;
344 struct hist_entry *h = rb_entry(self->b.top,
345 struct hist_entry, rb_node);
346 ui_helpline__pop();
347 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
348 seq++, self->b.nr_entries,
349 self->hists->nr_entries,
350 self->b.height,
351 self->b.index,
352 self->b.top_idx,
353 h->row_offset, h->nr_rows);
354 }
3c916cc2
ACM
355 break;
356 case 'C':
357 /* Collapse the whole world. */
358 hist_browser__set_folding(self, false);
359 break;
360 case 'E':
361 /* Expand the whole world. */
362 hist_browser__set_folding(self, true);
363 break;
cf958003 364 case K_ENTER:
d1b4f249
ACM
365 if (hist_browser__toggle_fold(self))
366 break;
367 /* fall thru */
368 default:
b50e003d 369 goto out;
d1b4f249
ACM
370 }
371 }
b50e003d 372out:
59e8fe32 373 ui_browser__hide(&self->b);
b50e003d 374 return key;
d1b4f249
ACM
375}
376
377static char *callchain_list__sym_name(struct callchain_list *self,
378 char *bf, size_t bfsize)
379{
380 if (self->ms.sym)
381 return self->ms.sym->name;
382
9486aa38 383 snprintf(bf, bfsize, "%#" PRIx64, self->ip);
d1b4f249
ACM
384 return bf;
385}
386
387#define LEVEL_OFFSET_STEP 3
388
389static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
390 struct callchain_node *chain_node,
391 u64 total, int level,
392 unsigned short row,
393 off_t *row_offset,
394 bool *is_current_entry)
395{
396 struct rb_node *node;
397 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
398 u64 new_total, remaining;
399
400 if (callchain_param.mode == CHAIN_GRAPH_REL)
401 new_total = chain_node->children_hit;
402 else
403 new_total = total;
404
405 remaining = new_total;
406 node = rb_first(&chain_node->rb_root);
407 while (node) {
408 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
409 struct rb_node *next = rb_next(node);
f08c3154 410 u64 cumul = callchain_cumul_hits(child);
d1b4f249
ACM
411 struct callchain_list *chain;
412 char folded_sign = ' ';
413 int first = true;
414 int extra_offset = 0;
415
416 remaining -= cumul;
417
418 list_for_each_entry(chain, &child->val, list) {
419 char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
420 const char *str;
421 int color;
422 bool was_first = first;
423
163caed9 424 if (first)
d1b4f249 425 first = false;
163caed9 426 else
d1b4f249 427 extra_offset = LEVEL_OFFSET_STEP;
d1b4f249
ACM
428
429 folded_sign = callchain_list__folded(chain);
430 if (*row_offset != 0) {
431 --*row_offset;
432 goto do_next;
433 }
434
435 alloc_str = NULL;
436 str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
437 if (was_first) {
438 double percent = cumul * 100.0 / new_total;
439
440 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
441 str = "Not enough memory!";
442 else
443 str = alloc_str;
444 }
445
446 color = HE_COLORSET_NORMAL;
447 width = self->b.width - (offset + extra_offset + 2);
448 if (ui_browser__is_current_entry(&self->b, row)) {
449 self->selection = &chain->ms;
450 color = HE_COLORSET_SELECTED;
451 *is_current_entry = true;
452 }
453
8f9bbc40
ACM
454 ui_browser__set_color(&self->b, color);
455 ui_browser__gotorc(&self->b, row, 0);
d1b4f249
ACM
456 slsmg_write_nstring(" ", offset + extra_offset);
457 slsmg_printf("%c ", folded_sign);
458 slsmg_write_nstring(str, width);
459 free(alloc_str);
460
461 if (++row == self->b.height)
462 goto out;
463do_next:
464 if (folded_sign == '+')
465 break;
466 }
467
468 if (folded_sign == '-') {
469 const int new_level = level + (extra_offset ? 2 : 1);
470 row += hist_browser__show_callchain_node_rb_tree(self, child, new_total,
471 new_level, row, row_offset,
472 is_current_entry);
473 }
474 if (row == self->b.height)
475 goto out;
476 node = next;
477 }
478out:
479 return row - first_row;
480}
481
482static int hist_browser__show_callchain_node(struct hist_browser *self,
483 struct callchain_node *node,
484 int level, unsigned short row,
485 off_t *row_offset,
486 bool *is_current_entry)
487{
488 struct callchain_list *chain;
489 int first_row = row,
490 offset = level * LEVEL_OFFSET_STEP,
491 width = self->b.width - offset;
492 char folded_sign = ' ';
493
494 list_for_each_entry(chain, &node->val, list) {
495 char ipstr[BITS_PER_LONG / 4 + 1], *s;
496 int color;
163caed9 497
d1b4f249
ACM
498 folded_sign = callchain_list__folded(chain);
499
500 if (*row_offset != 0) {
501 --*row_offset;
502 continue;
503 }
504
505 color = HE_COLORSET_NORMAL;
506 if (ui_browser__is_current_entry(&self->b, row)) {
507 self->selection = &chain->ms;
508 color = HE_COLORSET_SELECTED;
509 *is_current_entry = true;
510 }
511
512 s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
8f9bbc40
ACM
513 ui_browser__gotorc(&self->b, row, 0);
514 ui_browser__set_color(&self->b, color);
d1b4f249
ACM
515 slsmg_write_nstring(" ", offset);
516 slsmg_printf("%c ", folded_sign);
517 slsmg_write_nstring(s, width - 2);
518
519 if (++row == self->b.height)
520 goto out;
521 }
522
523 if (folded_sign == '-')
524 row += hist_browser__show_callchain_node_rb_tree(self, node,
525 self->hists->stats.total_period,
526 level + 1, row,
527 row_offset,
528 is_current_entry);
529out:
530 return row - first_row;
531}
532
533static int hist_browser__show_callchain(struct hist_browser *self,
534 struct rb_root *chain,
535 int level, unsigned short row,
536 off_t *row_offset,
537 bool *is_current_entry)
538{
539 struct rb_node *nd;
540 int first_row = row;
541
542 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
543 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
544
545 row += hist_browser__show_callchain_node(self, node, level,
546 row, row_offset,
547 is_current_entry);
548 if (row == self->b.height)
549 break;
550 }
551
552 return row - first_row;
553}
554
555static int hist_browser__show_entry(struct hist_browser *self,
556 struct hist_entry *entry,
557 unsigned short row)
558{
559 char s[256];
560 double percent;
561 int printed = 0;
f1cf602c 562 int width = self->b.width - 6; /* The percentage */
d1b4f249
ACM
563 char folded_sign = ' ';
564 bool current_entry = ui_browser__is_current_entry(&self->b, row);
565 off_t row_offset = entry->row_offset;
566
567 if (current_entry) {
568 self->he_selection = entry;
569 self->selection = &entry->ms;
570 }
571
572 if (symbol_conf.use_callchain) {
163caed9 573 hist_entry__init_have_children(entry);
d1b4f249
ACM
574 folded_sign = hist_entry__folded(entry);
575 }
576
577 if (row_offset == 0) {
f1cf602c 578 hist_entry__snprintf(entry, s, sizeof(s), self->hists);
d1b4f249
ACM
579 percent = (entry->period * 100.0) / self->hists->stats.total_period;
580
c172f742 581 ui_browser__set_percent_color(&self->b, percent, current_entry);
8f9bbc40 582 ui_browser__gotorc(&self->b, row, 0);
d1b4f249
ACM
583 if (symbol_conf.use_callchain) {
584 slsmg_printf("%c ", folded_sign);
585 width -= 2;
586 }
c172f742 587
f1cf602c
ACM
588 slsmg_printf(" %5.2f%%", percent);
589
c172f742
ACM
590 /* The scroll bar isn't being used */
591 if (!self->b.navkeypressed)
592 width += 1;
593
33f62b3f
ACM
594 if (!current_entry || !self->b.navkeypressed)
595 ui_browser__set_color(&self->b, HE_COLORSET_NORMAL);
596
2cf9cebf
ACM
597 if (symbol_conf.show_nr_samples) {
598 slsmg_printf(" %11u", entry->nr_events);
599 width -= 12;
600 }
601
602 if (symbol_conf.show_total_period) {
603 slsmg_printf(" %12" PRIu64, entry->period);
604 width -= 13;
605 }
606
d1b4f249
ACM
607 slsmg_write_nstring(s, width);
608 ++row;
609 ++printed;
610 } else
611 --row_offset;
612
613 if (folded_sign == '-' && row != self->b.height) {
614 printed += hist_browser__show_callchain(self, &entry->sorted_chain,
615 1, row, &row_offset,
616 &current_entry);
617 if (current_entry)
618 self->he_selection = entry;
619 }
620
621 return printed;
622}
623
437cfe7a
ACM
624static void ui_browser__hists_init_top(struct ui_browser *browser)
625{
626 if (browser->top == NULL) {
627 struct hist_browser *hb;
628
629 hb = container_of(browser, struct hist_browser, b);
630 browser->top = rb_first(&hb->hists->entries);
631 }
632}
633
d1b4f249
ACM
634static unsigned int hist_browser__refresh(struct ui_browser *self)
635{
636 unsigned row = 0;
637 struct rb_node *nd;
638 struct hist_browser *hb = container_of(self, struct hist_browser, b);
639
437cfe7a 640 ui_browser__hists_init_top(self);
d1b4f249
ACM
641
642 for (nd = self->top; nd; nd = rb_next(nd)) {
643 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
644
645 if (h->filtered)
646 continue;
647
648 row += hist_browser__show_entry(hb, h, row);
649 if (row == self->height)
650 break;
651 }
652
653 return row;
654}
655
656static struct rb_node *hists__filter_entries(struct rb_node *nd)
657{
658 while (nd != NULL) {
659 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
660 if (!h->filtered)
661 return nd;
662
663 nd = rb_next(nd);
664 }
665
666 return NULL;
667}
668
669static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
670{
671 while (nd != NULL) {
672 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
673 if (!h->filtered)
674 return nd;
675
676 nd = rb_prev(nd);
677 }
678
679 return NULL;
680}
681
682static void ui_browser__hists_seek(struct ui_browser *self,
683 off_t offset, int whence)
684{
685 struct hist_entry *h;
686 struct rb_node *nd;
687 bool first = true;
688
60098917
ACM
689 if (self->nr_entries == 0)
690 return;
691
437cfe7a
ACM
692 ui_browser__hists_init_top(self);
693
d1b4f249
ACM
694 switch (whence) {
695 case SEEK_SET:
696 nd = hists__filter_entries(rb_first(self->entries));
697 break;
698 case SEEK_CUR:
699 nd = self->top;
700 goto do_offset;
701 case SEEK_END:
702 nd = hists__filter_prev_entries(rb_last(self->entries));
703 first = false;
704 break;
705 default:
706 return;
707 }
708
709 /*
710 * Moves not relative to the first visible entry invalidates its
711 * row_offset:
712 */
713 h = rb_entry(self->top, struct hist_entry, rb_node);
714 h->row_offset = 0;
715
716 /*
717 * Here we have to check if nd is expanded (+), if it is we can't go
718 * the next top level hist_entry, instead we must compute an offset of
719 * what _not_ to show and not change the first visible entry.
720 *
721 * This offset increments when we are going from top to bottom and
722 * decreases when we're going from bottom to top.
723 *
724 * As we don't have backpointers to the top level in the callchains
725 * structure, we need to always print the whole hist_entry callchain,
726 * skipping the first ones that are before the first visible entry
727 * and stop when we printed enough lines to fill the screen.
728 */
729do_offset:
730 if (offset > 0) {
731 do {
732 h = rb_entry(nd, struct hist_entry, rb_node);
733 if (h->ms.unfolded) {
734 u16 remaining = h->nr_rows - h->row_offset;
735 if (offset > remaining) {
736 offset -= remaining;
737 h->row_offset = 0;
738 } else {
739 h->row_offset += offset;
740 offset = 0;
741 self->top = nd;
742 break;
743 }
744 }
745 nd = hists__filter_entries(rb_next(nd));
746 if (nd == NULL)
747 break;
748 --offset;
749 self->top = nd;
750 } while (offset != 0);
751 } else if (offset < 0) {
752 while (1) {
753 h = rb_entry(nd, struct hist_entry, rb_node);
754 if (h->ms.unfolded) {
755 if (first) {
756 if (-offset > h->row_offset) {
757 offset += h->row_offset;
758 h->row_offset = 0;
759 } else {
760 h->row_offset += offset;
761 offset = 0;
762 self->top = nd;
763 break;
764 }
765 } else {
766 if (-offset > h->nr_rows) {
767 offset += h->nr_rows;
768 h->row_offset = 0;
769 } else {
770 h->row_offset = h->nr_rows + offset;
771 offset = 0;
772 self->top = nd;
773 break;
774 }
775 }
776 }
777
778 nd = hists__filter_prev_entries(rb_prev(nd));
779 if (nd == NULL)
780 break;
781 ++offset;
782 self->top = nd;
783 if (offset == 0) {
784 /*
785 * Last unfiltered hist_entry, check if it is
786 * unfolded, if it is then we should have
787 * row_offset at its last entry.
788 */
789 h = rb_entry(nd, struct hist_entry, rb_node);
790 if (h->ms.unfolded)
791 h->row_offset = h->nr_rows;
792 break;
793 }
794 first = false;
795 }
796 } else {
797 self->top = nd;
798 h = rb_entry(nd, struct hist_entry, rb_node);
799 h->row_offset = 0;
800 }
801}
802
803static struct hist_browser *hist_browser__new(struct hists *hists)
804{
805 struct hist_browser *self = zalloc(sizeof(*self));
806
807 if (self) {
808 self->hists = hists;
809 self->b.refresh = hist_browser__refresh;
810 self->b.seek = ui_browser__hists_seek;
a68c2c58
SE
811 self->b.use_navkeypressed = true;
812 if (sort__branch_mode == 1)
813 self->has_symbols = sort_sym_from.list.next != NULL;
814 else
815 self->has_symbols = sort_sym.list.next != NULL;
d1b4f249
ACM
816 }
817
818 return self;
819}
820
821static void hist_browser__delete(struct hist_browser *self)
822{
d1b4f249
ACM
823 free(self);
824}
825
826static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
827{
828 return self->he_selection;
829}
830
831static struct thread *hist_browser__selected_thread(struct hist_browser *self)
832{
833 return self->he_selection->thread;
834}
835
469917ce 836static int hists__browser_title(struct hists *self, char *bf, size_t size,
d7b76f09 837 const char *ev_name)
d1b4f249 838{
469917ce
ACM
839 char unit;
840 int printed;
d7b76f09
ACM
841 const struct dso *dso = self->dso_filter;
842 const struct thread *thread = self->thread_filter;
cc686280
AR
843 unsigned long nr_samples = self->stats.nr_events[PERF_RECORD_SAMPLE];
844 u64 nr_events = self->stats.total_period;
845
846 nr_samples = convert_unit(nr_samples, &unit);
847 printed = scnprintf(bf, size,
848 "Samples: %lu%c of event '%s', Event count (approx.): %lu",
849 nr_samples, unit, ev_name, nr_events);
469917ce 850
d1b4f249 851
0d37aa34
ACM
852 if (self->uid_filter_str)
853 printed += snprintf(bf + printed, size - printed,
854 ", UID: %s", self->uid_filter_str);
d1b4f249 855 if (thread)
e7f01d1e 856 printed += scnprintf(bf + printed, size - printed,
469917ce
ACM
857 ", Thread: %s(%d)",
858 (thread->comm_set ? thread->comm : ""),
d1b4f249
ACM
859 thread->pid);
860 if (dso)
e7f01d1e 861 printed += scnprintf(bf + printed, size - printed,
469917ce
ACM
862 ", DSO: %s", dso->short_name);
863 return printed;
d1b4f249
ACM
864}
865
24bff2dc
SE
866static inline void free_popup_options(char **options, int n)
867{
868 int i;
869
870 for (i = 0; i < n; ++i) {
871 free(options[i]);
872 options[i] = NULL;
873 }
874}
875
34958544 876static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
7f0030b2 877 const char *helpline, const char *ev_name,
81cce8de
ACM
878 bool left_exits,
879 void(*timer)(void *arg), void *arg,
880 int delay_secs)
d1b4f249 881{
7f0030b2 882 struct hists *self = &evsel->hists;
d1b4f249 883 struct hist_browser *browser = hist_browser__new(self);
a68c2c58 884 struct branch_info *bi;
d1b4f249 885 struct pstack *fstack;
24bff2dc
SE
886 char *options[16];
887 int nr_options = 0;
d1b4f249 888 int key = -1;
938a23ae 889 char buf[64];
d1b4f249
ACM
890
891 if (browser == NULL)
892 return -1;
893
894 fstack = pstack__new(2);
895 if (fstack == NULL)
896 goto out;
897
898 ui_helpline__push(helpline);
899
24bff2dc
SE
900 memset(options, 0, sizeof(options));
901
d1b4f249 902 while (1) {
60098917
ACM
903 const struct thread *thread = NULL;
904 const struct dso *dso = NULL;
24bff2dc 905 int choice = 0,
d1b4f249 906 annotate = -2, zoom_dso = -2, zoom_thread = -2,
a68c2c58 907 annotate_f = -2, annotate_t = -2, browse_map = -2;
d1b4f249 908
24bff2dc
SE
909 nr_options = 0;
910
81cce8de 911 key = hist_browser__run(browser, ev_name, timer, arg, delay_secs);
d1b4f249 912
60098917
ACM
913 if (browser->he_selection != NULL) {
914 thread = hist_browser__selected_thread(browser);
915 dso = browser->selection->map ? browser->selection->map->dso : NULL;
916 }
b50e003d 917 switch (key) {
cf958003
ACM
918 case K_TAB:
919 case K_UNTAB:
e4419b8e
DA
920 if (nr_events == 1)
921 continue;
b50e003d
ACM
922 /*
923 * Exit the browser, let hists__browser_tree
924 * go to the next or previous
925 */
926 goto out_free_stack;
927 case 'a':
a6e51f9f 928 if (!browser->has_symbols) {
7b27509f 929 ui_browser__warning(&browser->b, delay_secs * 2,
a6e51f9f 930 "Annotation is only available for symbolic views, "
a68c2c58 931 "include \"sym*\" in --sort to use it.");
a6e51f9f
ACM
932 continue;
933 }
934
60098917 935 if (browser->selection == NULL ||
db9a9cbc 936 browser->selection->sym == NULL ||
b50e003d 937 browser->selection->map->dso->annotate_warned)
d1b4f249 938 continue;
b50e003d
ACM
939 goto do_annotate;
940 case 'd':
941 goto zoom_dso;
942 case 't':
943 goto zoom_thread;
938a23ae
NK
944 case 's':
945 if (ui_browser__input_window("Symbol to show",
946 "Please enter the name of symbol you want to see",
947 buf, "ENTER: OK, ESC: Cancel",
948 delay_secs * 2) == K_ENTER) {
949 self->symbol_filter_str = *buf ? buf : NULL;
950 hists__filter_by_symbol(self);
951 hist_browser__reset(browser);
952 }
953 continue;
cf958003 954 case K_F1:
b50e003d
ACM
955 case 'h':
956 case '?':
4610e413
ACM
957 ui_browser__help_window(&browser->b,
958 "h/?/F1 Show this window\n"
2d5646c0
ACM
959 "UP/DOWN/PGUP\n"
960 "PGDN/SPACE Navigate\n"
961 "q/ESC/CTRL+C Exit browser\n\n"
962 "For multiple event sessions:\n\n"
963 "TAB/UNTAB Switch events\n\n"
724c9c9f 964 "For symbolic views (--sort has sym):\n\n"
2d5646c0
ACM
965 "-> Zoom into DSO/Threads & Annotate current symbol\n"
966 "<- Zoom out\n"
967 "a Annotate current symbol\n"
968 "C Collapse all callchains\n"
969 "E Expand all callchains\n"
970 "d Zoom into current DSO\n"
938a23ae
NK
971 "t Zoom into current Thread\n"
972 "s Filter symbol by name");
b50e003d 973 continue;
cf958003
ACM
974 case K_ENTER:
975 case K_RIGHT:
b50e003d
ACM
976 /* menu */
977 break;
cf958003 978 case K_LEFT: {
b50e003d 979 const void *top;
d1b4f249 980
7f0030b2
ACM
981 if (pstack__empty(fstack)) {
982 /*
983 * Go back to the perf_evsel_menu__run or other user
984 */
985 if (left_exits)
986 goto out_free_stack;
d1b4f249 987 continue;
7f0030b2 988 }
b50e003d 989 top = pstack__pop(fstack);
d7b76f09 990 if (top == &browser->hists->dso_filter)
b50e003d 991 goto zoom_out_dso;
d7b76f09 992 if (top == &browser->hists->thread_filter)
b50e003d
ACM
993 goto zoom_out_thread;
994 continue;
995 }
cf958003 996 case K_ESC:
7f0030b2 997 if (!left_exits &&
4610e413
ACM
998 !ui_browser__dialog_yesno(&browser->b,
999 "Do you really want to exit?"))
b50e003d
ACM
1000 continue;
1001 /* Fall thru */
ed7e5662
ACM
1002 case 'q':
1003 case CTRL('c'):
b50e003d 1004 goto out_free_stack;
ed7e5662
ACM
1005 default:
1006 continue;
d1b4f249
ACM
1007 }
1008
724c9c9f
ACM
1009 if (!browser->has_symbols)
1010 goto add_exit_option;
1011
a68c2c58
SE
1012 if (sort__branch_mode == 1) {
1013 bi = browser->he_selection->branch_info;
1014 if (browser->selection != NULL &&
1015 bi &&
1016 bi->from.sym != NULL &&
1017 !bi->from.map->dso->annotate_warned &&
1018 asprintf(&options[nr_options], "Annotate %s",
1019 bi->from.sym->name) > 0)
1020 annotate_f = nr_options++;
1021
1022 if (browser->selection != NULL &&
1023 bi &&
1024 bi->to.sym != NULL &&
1025 !bi->to.map->dso->annotate_warned &&
8bcd65fd
SE
1026 (bi->to.sym != bi->from.sym ||
1027 bi->to.map->dso != bi->from.map->dso) &&
a68c2c58
SE
1028 asprintf(&options[nr_options], "Annotate %s",
1029 bi->to.sym->name) > 0)
1030 annotate_t = nr_options++;
1031 } else {
1032
1033 if (browser->selection != NULL &&
1034 browser->selection->sym != NULL &&
1035 !browser->selection->map->dso->annotate_warned &&
1036 asprintf(&options[nr_options], "Annotate %s",
1037 browser->selection->sym->name) > 0)
1038 annotate = nr_options++;
1039 }
d1b4f249
ACM
1040
1041 if (thread != NULL &&
1042 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
d7b76f09 1043 (browser->hists->thread_filter ? "out of" : "into"),
d1b4f249
ACM
1044 (thread->comm_set ? thread->comm : ""),
1045 thread->pid) > 0)
1046 zoom_thread = nr_options++;
1047
1048 if (dso != NULL &&
1049 asprintf(&options[nr_options], "Zoom %s %s DSO",
d7b76f09 1050 (browser->hists->dso_filter ? "out of" : "into"),
d1b4f249
ACM
1051 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1052 zoom_dso = nr_options++;
1053
60098917
ACM
1054 if (browser->selection != NULL &&
1055 browser->selection->map != NULL &&
d1b4f249
ACM
1056 asprintf(&options[nr_options], "Browse map details") > 0)
1057 browse_map = nr_options++;
724c9c9f 1058add_exit_option:
d1b4f249 1059 options[nr_options++] = (char *)"Exit";
24bff2dc 1060retry_popup_menu:
1e6dd077 1061 choice = ui__popup_menu(nr_options, options);
d1b4f249 1062
d1b4f249
ACM
1063 if (choice == nr_options - 1)
1064 break;
1065
24bff2dc
SE
1066 if (choice == -1) {
1067 free_popup_options(options, nr_options - 1);
d1b4f249 1068 continue;
24bff2dc 1069 }
d1b4f249 1070
a68c2c58 1071 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
d1b4f249 1072 struct hist_entry *he;
4610e413 1073 int err;
d1b4f249 1074do_annotate:
d1b4f249
ACM
1075 he = hist_browser__selected_entry(browser);
1076 if (he == NULL)
1077 continue;
a68c2c58
SE
1078
1079 /*
1080 * we stash the branch_info symbol + map into the
1081 * the ms so we don't have to rewrite all the annotation
1082 * code to use branch_info.
1083 * in branch mode, the ms struct is not used
1084 */
1085 if (choice == annotate_f) {
1086 he->ms.sym = he->branch_info->from.sym;
1087 he->ms.map = he->branch_info->from.map;
1088 } else if (choice == annotate_t) {
1089 he->ms.sym = he->branch_info->to.sym;
1090 he->ms.map = he->branch_info->to.map;
1091 }
1092
df71d95f
ACM
1093 /*
1094 * Don't let this be freed, say, by hists__decay_entry.
1095 */
1096 he->used = true;
d04b35f8 1097 err = hist_entry__tui_annotate(he, evsel->idx,
4610e413 1098 timer, arg, delay_secs);
df71d95f 1099 he->used = false;
24bff2dc
SE
1100 /*
1101 * offer option to annotate the other branch source or target
1102 * (if they exists) when returning from annotate
1103 */
1104 if ((err == 'q' || err == CTRL('c'))
1105 && annotate_t != -2 && annotate_f != -2)
1106 goto retry_popup_menu;
1107
900e14a8 1108 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
4610e413
ACM
1109 if (err)
1110 ui_browser__handle_resize(&browser->b);
24bff2dc 1111
d1b4f249
ACM
1112 } else if (choice == browse_map)
1113 map__browse(browser->selection->map);
1114 else if (choice == zoom_dso) {
1115zoom_dso:
d7b76f09
ACM
1116 if (browser->hists->dso_filter) {
1117 pstack__remove(fstack, &browser->hists->dso_filter);
d1b4f249
ACM
1118zoom_out_dso:
1119 ui_helpline__pop();
d7b76f09 1120 browser->hists->dso_filter = NULL;
cc02c921 1121 sort_dso.elide = false;
d1b4f249
ACM
1122 } else {
1123 if (dso == NULL)
1124 continue;
1125 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1126 dso->kernel ? "the Kernel" : dso->short_name);
d7b76f09 1127 browser->hists->dso_filter = dso;
cc02c921 1128 sort_dso.elide = true;
d7b76f09 1129 pstack__push(fstack, &browser->hists->dso_filter);
d1b4f249 1130 }
d7b76f09 1131 hists__filter_by_dso(self);
d1b4f249
ACM
1132 hist_browser__reset(browser);
1133 } else if (choice == zoom_thread) {
1134zoom_thread:
d7b76f09
ACM
1135 if (browser->hists->thread_filter) {
1136 pstack__remove(fstack, &browser->hists->thread_filter);
d1b4f249
ACM
1137zoom_out_thread:
1138 ui_helpline__pop();
d7b76f09 1139 browser->hists->thread_filter = NULL;
cc02c921 1140 sort_thread.elide = false;
d1b4f249
ACM
1141 } else {
1142 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1143 thread->comm_set ? thread->comm : "",
1144 thread->pid);
d7b76f09 1145 browser->hists->thread_filter = thread;
cc02c921 1146 sort_thread.elide = true;
d7b76f09 1147 pstack__push(fstack, &browser->hists->thread_filter);
d1b4f249 1148 }
d7b76f09 1149 hists__filter_by_thread(self);
d1b4f249
ACM
1150 hist_browser__reset(browser);
1151 }
1152 }
1153out_free_stack:
1154 pstack__delete(fstack);
1155out:
1156 hist_browser__delete(browser);
24bff2dc 1157 free_popup_options(options, nr_options - 1);
d1b4f249
ACM
1158 return key;
1159}
1160
7f0030b2
ACM
1161struct perf_evsel_menu {
1162 struct ui_browser b;
1163 struct perf_evsel *selection;
7b27509f 1164 bool lost_events, lost_events_warned;
7f0030b2
ACM
1165};
1166
1167static void perf_evsel_menu__write(struct ui_browser *browser,
1168 void *entry, int row)
1169{
1170 struct perf_evsel_menu *menu = container_of(browser,
1171 struct perf_evsel_menu, b);
1172 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1173 bool current_entry = ui_browser__is_current_entry(browser, row);
1174 unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1175 const char *ev_name = event_name(evsel);
1176 char bf[256], unit;
7b27509f
ACM
1177 const char *warn = " ";
1178 size_t printed;
7f0030b2
ACM
1179
1180 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1181 HE_COLORSET_NORMAL);
1182
1183 nr_events = convert_unit(nr_events, &unit);
e7f01d1e 1184 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
7b27509f
ACM
1185 unit, unit == ' ' ? "" : " ", ev_name);
1186 slsmg_printf("%s", bf);
1187
1188 nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1189 if (nr_events != 0) {
1190 menu->lost_events = true;
1191 if (!current_entry)
1192 ui_browser__set_color(browser, HE_COLORSET_TOP);
1193 nr_events = convert_unit(nr_events, &unit);
e7f01d1e
ACM
1194 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1195 nr_events, unit, unit == ' ' ? "" : " ");
7b27509f
ACM
1196 warn = bf;
1197 }
1198
1199 slsmg_write_nstring(warn, browser->width - printed);
7f0030b2
ACM
1200
1201 if (current_entry)
1202 menu->selection = evsel;
1203}
1204
34958544
ACM
1205static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1206 int nr_events, const char *help,
81cce8de 1207 void(*timer)(void *arg), void *arg, int delay_secs)
d1b4f249 1208{
7f0030b2 1209 struct perf_evlist *evlist = menu->b.priv;
e248de33 1210 struct perf_evsel *pos;
7f0030b2
ACM
1211 const char *ev_name, *title = "Available samples";
1212 int key;
d1b4f249 1213
7f0030b2
ACM
1214 if (ui_browser__show(&menu->b, title,
1215 "ESC: exit, ENTER|->: Browse histograms") < 0)
1216 return -1;
1217
7f0030b2 1218 while (1) {
3af6e338 1219 key = ui_browser__run(&menu->b, delay_secs);
7f0030b2
ACM
1220
1221 switch (key) {
cf958003 1222 case K_TIMER:
81cce8de 1223 timer(arg);
7b27509f
ACM
1224
1225 if (!menu->lost_events_warned && menu->lost_events) {
1226 ui_browser__warn_lost_events(&menu->b);
1227 menu->lost_events_warned = true;
1228 }
81cce8de 1229 continue;
cf958003
ACM
1230 case K_RIGHT:
1231 case K_ENTER:
7f0030b2
ACM
1232 if (!menu->selection)
1233 continue;
1234 pos = menu->selection;
1235browse_hists:
18eaf0b8
ACM
1236 perf_evlist__set_selected(evlist, pos);
1237 /*
1238 * Give the calling tool a chance to populate the non
1239 * default evsel resorted hists tree.
1240 */
1241 if (timer)
1242 timer(arg);
7f0030b2 1243 ev_name = event_name(pos);
34958544
ACM
1244 key = perf_evsel__hists_browse(pos, nr_events, help,
1245 ev_name, true, timer,
1246 arg, delay_secs);
7f0030b2 1247 ui_browser__show_title(&menu->b, title);
18eaf0b8 1248 switch (key) {
cf958003 1249 case K_TAB:
18eaf0b8
ACM
1250 if (pos->node.next == &evlist->entries)
1251 pos = list_entry(evlist->entries.next, struct perf_evsel, node);
1252 else
1253 pos = list_entry(pos->node.next, struct perf_evsel, node);
1254 goto browse_hists;
cf958003 1255 case K_UNTAB:
18eaf0b8
ACM
1256 if (pos->node.prev == &evlist->entries)
1257 pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
1258 else
1259 pos = list_entry(pos->node.prev, struct perf_evsel, node);
1260 goto browse_hists;
cf958003 1261 case K_ESC:
4610e413
ACM
1262 if (!ui_browser__dialog_yesno(&menu->b,
1263 "Do you really want to exit?"))
18eaf0b8
ACM
1264 continue;
1265 /* Fall thru */
1266 case 'q':
1267 case CTRL('c'):
1268 goto out;
1269 default:
1270 continue;
1271 }
cf958003 1272 case K_LEFT:
7f0030b2 1273 continue;
cf958003 1274 case K_ESC:
4610e413
ACM
1275 if (!ui_browser__dialog_yesno(&menu->b,
1276 "Do you really want to exit?"))
ed7e5662
ACM
1277 continue;
1278 /* Fall thru */
7f0030b2
ACM
1279 case 'q':
1280 case CTRL('c'):
1281 goto out;
d1b4f249 1282 default:
18eaf0b8 1283 continue;
d1b4f249
ACM
1284 }
1285 }
1286
7f0030b2
ACM
1287out:
1288 ui_browser__hide(&menu->b);
1289 return key;
1290}
1291
1292static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
81cce8de
ACM
1293 const char *help,
1294 void(*timer)(void *arg), void *arg,
1295 int delay_secs)
7f0030b2
ACM
1296{
1297 struct perf_evsel *pos;
1298 struct perf_evsel_menu menu = {
1299 .b = {
1300 .entries = &evlist->entries,
1301 .refresh = ui_browser__list_head_refresh,
1302 .seek = ui_browser__list_head_seek,
1303 .write = perf_evsel_menu__write,
1304 .nr_entries = evlist->nr_entries,
1305 .priv = evlist,
1306 },
1307 };
1308
1309 ui_helpline__push("Press ESC to exit");
1310
1311 list_for_each_entry(pos, &evlist->entries, node) {
1312 const char *ev_name = event_name(pos);
1313 size_t line_len = strlen(ev_name) + 7;
1314
1315 if (menu.b.width < line_len)
1316 menu.b.width = line_len;
1317 /*
1318 * Cache the evsel name, tracepoints have a _high_ cost per
1319 * event_name() call.
1320 */
1321 if (pos->name == NULL)
1322 pos->name = strdup(ev_name);
1323 }
1324
34958544
ACM
1325 return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer,
1326 arg, delay_secs);
7f0030b2
ACM
1327}
1328
81cce8de
ACM
1329int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1330 void(*timer)(void *arg), void *arg,
1331 int delay_secs)
7f0030b2
ACM
1332{
1333
1334 if (evlist->nr_entries == 1) {
1335 struct perf_evsel *first = list_entry(evlist->entries.next,
1336 struct perf_evsel, node);
1337 const char *ev_name = event_name(first);
34958544
ACM
1338 return perf_evsel__hists_browse(first, evlist->nr_entries, help,
1339 ev_name, false, timer, arg,
1340 delay_secs);
7f0030b2
ACM
1341 }
1342
81cce8de
ACM
1343 return __perf_evlist__tui_browse_hists(evlist, help,
1344 timer, arg, delay_secs);
d1b4f249 1345}
This page took 0.158993 seconds and 5 git commands to generate.