aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/util/newt.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util/newt.c')
-rw-r--r--tools/perf/util/newt.c1164
1 files changed, 777 insertions, 387 deletions
diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c
index 7537ca15900b..91de99b58445 100644
--- a/tools/perf/util/newt.c
+++ b/tools/perf/util/newt.c
@@ -11,6 +11,7 @@
11#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG 11#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG
12#endif 12#endif
13#include <slang.h> 13#include <slang.h>
14#include <signal.h>
14#include <stdlib.h> 15#include <stdlib.h>
15#include <newt.h> 16#include <newt.h>
16#include <sys/ttydefaults.h> 17#include <sys/ttydefaults.h>
@@ -278,9 +279,48 @@ struct ui_browser {
278 void *first_visible_entry, *entries; 279 void *first_visible_entry, *entries;
279 u16 top, left, width, height; 280 u16 top, left, width, height;
280 void *priv; 281 void *priv;
282 unsigned int (*refresh_entries)(struct ui_browser *self);
283 void (*seek)(struct ui_browser *self,
284 off_t offset, int whence);
281 u32 nr_entries; 285 u32 nr_entries;
282}; 286};
283 287
288static void ui_browser__list_head_seek(struct ui_browser *self,
289 off_t offset, int whence)
290{
291 struct list_head *head = self->entries;
292 struct list_head *pos;
293
294 switch (whence) {
295 case SEEK_SET:
296 pos = head->next;
297 break;
298 case SEEK_CUR:
299 pos = self->first_visible_entry;
300 break;
301 case SEEK_END:
302 pos = head->prev;
303 break;
304 default:
305 return;
306 }
307
308 if (offset > 0) {
309 while (offset-- != 0)
310 pos = pos->next;
311 } else {
312 while (offset++ != 0)
313 pos = pos->prev;
314 }
315
316 self->first_visible_entry = pos;
317}
318
319static bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row)
320{
321 return (self->first_visible_entry_idx + row) == self->index;
322}
323
284static void ui_browser__refresh_dimensions(struct ui_browser *self) 324static void ui_browser__refresh_dimensions(struct ui_browser *self)
285{ 325{
286 int cols, rows; 326 int cols, rows;
@@ -297,8 +337,36 @@ static void ui_browser__refresh_dimensions(struct ui_browser *self)
297 337
298static void ui_browser__reset_index(struct ui_browser *self) 338static void ui_browser__reset_index(struct ui_browser *self)
299{ 339{
300 self->index = self->first_visible_entry_idx = 0; 340 self->index = self->first_visible_entry_idx = 0;
301 self->first_visible_entry = NULL; 341 self->seek(self, 0, SEEK_SET);
342}
343
344static int ui_browser__show(struct ui_browser *self, const char *title)
345{
346 if (self->form != NULL) {
347 newtFormDestroy(self->form);
348 newtPopWindow();
349 }
350 ui_browser__refresh_dimensions(self);
351 newtCenteredWindow(self->width, self->height, title);
352 self->form = newt_form__new();
353 if (self->form == NULL)
354 return -1;
355
356 self->sb = newtVerticalScrollbar(self->width, 0, self->height,
357 HE_COLORSET_NORMAL,
358 HE_COLORSET_SELECTED);
359 if (self->sb == NULL)
360 return -1;
361
362 newtFormAddHotKey(self->form, NEWT_KEY_UP);
363 newtFormAddHotKey(self->form, NEWT_KEY_DOWN);
364 newtFormAddHotKey(self->form, NEWT_KEY_PGUP);
365 newtFormAddHotKey(self->form, NEWT_KEY_PGDN);
366 newtFormAddHotKey(self->form, NEWT_KEY_HOME);
367 newtFormAddHotKey(self->form, NEWT_KEY_END);
368 newtFormAddComponent(self->form, self->sb);
369 return 0;
302} 370}
303 371
304static int objdump_line__show(struct objdump_line *self, struct list_head *head, 372static int objdump_line__show(struct objdump_line *self, struct list_head *head,
@@ -352,26 +420,10 @@ static int objdump_line__show(struct objdump_line *self, struct list_head *head,
352 420
353static int ui_browser__refresh_entries(struct ui_browser *self) 421static int ui_browser__refresh_entries(struct ui_browser *self)
354{ 422{
355 struct objdump_line *pos; 423 int row;
356 struct list_head *head = self->entries;
357 struct hist_entry *he = self->priv;
358 int row = 0;
359 int len = he->ms.sym->end - he->ms.sym->start;
360
361 if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries)
362 self->first_visible_entry = head->next;
363
364 pos = list_entry(self->first_visible_entry, struct objdump_line, node);
365
366 list_for_each_entry_from(pos, head, node) {
367 bool current_entry = (self->first_visible_entry_idx + row) == self->index;
368 SLsmg_gotorc(self->top + row, self->left);
369 objdump_line__show(pos, head, self->width,
370 he, len, current_entry);
371 if (++row == self->height)
372 break;
373 }
374 424
425 newtScrollbarSet(self->sb, self->index, self->nr_entries - 1);
426 row = self->refresh_entries(self);
375 SLsmg_set_color(HE_COLORSET_NORMAL); 427 SLsmg_set_color(HE_COLORSET_NORMAL);
376 SLsmg_fill_region(self->top + row, self->left, 428 SLsmg_fill_region(self->top + row, self->left,
377 self->height - row, self->width, ' '); 429 self->height - row, self->width, ' ');
@@ -379,42 +431,13 @@ static int ui_browser__refresh_entries(struct ui_browser *self)
379 return 0; 431 return 0;
380} 432}
381 433
382static int ui_browser__run(struct ui_browser *self, const char *title, 434static int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es)
383 struct newtExitStruct *es)
384{ 435{
385 if (self->form) {
386 newtFormDestroy(self->form);
387 newtPopWindow();
388 }
389
390 ui_browser__refresh_dimensions(self);
391 newtCenteredWindow(self->width + 2, self->height, title);
392 self->form = newt_form__new();
393 if (self->form == NULL)
394 return -1;
395
396 self->sb = newtVerticalScrollbar(self->width + 1, 0, self->height,
397 HE_COLORSET_NORMAL,
398 HE_COLORSET_SELECTED);
399 if (self->sb == NULL)
400 return -1;
401
402 newtFormAddHotKey(self->form, NEWT_KEY_UP);
403 newtFormAddHotKey(self->form, NEWT_KEY_DOWN);
404 newtFormAddHotKey(self->form, NEWT_KEY_PGUP);
405 newtFormAddHotKey(self->form, NEWT_KEY_PGDN);
406 newtFormAddHotKey(self->form, ' ');
407 newtFormAddHotKey(self->form, NEWT_KEY_HOME);
408 newtFormAddHotKey(self->form, NEWT_KEY_END);
409 newtFormAddHotKey(self->form, NEWT_KEY_TAB);
410 newtFormAddHotKey(self->form, NEWT_KEY_RIGHT);
411
412 if (ui_browser__refresh_entries(self) < 0) 436 if (ui_browser__refresh_entries(self) < 0)
413 return -1; 437 return -1;
414 newtFormAddComponent(self->form, self->sb);
415 438
416 while (1) { 439 while (1) {
417 unsigned int offset; 440 off_t offset;
418 441
419 newtFormRun(self->form, es); 442 newtFormRun(self->form, es);
420 443
@@ -428,9 +451,8 @@ static int ui_browser__run(struct ui_browser *self, const char *title,
428 break; 451 break;
429 ++self->index; 452 ++self->index;
430 if (self->index == self->first_visible_entry_idx + self->height) { 453 if (self->index == self->first_visible_entry_idx + self->height) {
431 struct list_head *pos = self->first_visible_entry;
432 ++self->first_visible_entry_idx; 454 ++self->first_visible_entry_idx;
433 self->first_visible_entry = pos->next; 455 self->seek(self, +1, SEEK_CUR);
434 } 456 }
435 break; 457 break;
436 case NEWT_KEY_UP: 458 case NEWT_KEY_UP:
@@ -438,9 +460,8 @@ static int ui_browser__run(struct ui_browser *self, const char *title,
438 break; 460 break;
439 --self->index; 461 --self->index;
440 if (self->index < self->first_visible_entry_idx) { 462 if (self->index < self->first_visible_entry_idx) {
441 struct list_head *pos = self->first_visible_entry;
442 --self->first_visible_entry_idx; 463 --self->first_visible_entry_idx;
443 self->first_visible_entry = pos->prev; 464 self->seek(self, -1, SEEK_CUR);
444 } 465 }
445 break; 466 break;
446 case NEWT_KEY_PGDN: 467 case NEWT_KEY_PGDN:
@@ -453,12 +474,7 @@ static int ui_browser__run(struct ui_browser *self, const char *title,
453 offset = self->nr_entries - 1 - self->index; 474 offset = self->nr_entries - 1 - self->index;
454 self->index += offset; 475 self->index += offset;
455 self->first_visible_entry_idx += offset; 476 self->first_visible_entry_idx += offset;
456 477 self->seek(self, +offset, SEEK_CUR);
457 while (offset--) {
458 struct list_head *pos = self->first_visible_entry;
459 self->first_visible_entry = pos->next;
460 }
461
462 break; 478 break;
463 case NEWT_KEY_PGUP: 479 case NEWT_KEY_PGUP:
464 if (self->first_visible_entry_idx == 0) 480 if (self->first_visible_entry_idx == 0)
@@ -471,36 +487,22 @@ static int ui_browser__run(struct ui_browser *self, const char *title,
471 487
472 self->index -= offset; 488 self->index -= offset;
473 self->first_visible_entry_idx -= offset; 489 self->first_visible_entry_idx -= offset;
474 490 self->seek(self, -offset, SEEK_CUR);
475 while (offset--) {
476 struct list_head *pos = self->first_visible_entry;
477 self->first_visible_entry = pos->prev;
478 }
479 break; 491 break;
480 case NEWT_KEY_HOME: 492 case NEWT_KEY_HOME:
481 ui_browser__reset_index(self); 493 ui_browser__reset_index(self);
482 break; 494 break;
483 case NEWT_KEY_END: { 495 case NEWT_KEY_END:
484 struct list_head *head = self->entries;
485 offset = self->height - 1; 496 offset = self->height - 1;
497 if (offset >= self->nr_entries)
498 offset = self->nr_entries - 1;
486 499
487 if (offset > self->nr_entries) 500 self->index = self->nr_entries - 1;
488 offset = self->nr_entries; 501 self->first_visible_entry_idx = self->index - offset;
489 502 self->seek(self, -offset, SEEK_END);
490 self->index = self->first_visible_entry_idx = self->nr_entries - 1 - offset;
491 self->first_visible_entry = head->prev;
492 while (offset-- != 0) {
493 struct list_head *pos = self->first_visible_entry;
494 self->first_visible_entry = pos->prev;
495 }
496 }
497 break; 503 break;
498 case NEWT_KEY_RIGHT:
499 case NEWT_KEY_LEFT:
500 case NEWT_KEY_TAB:
501 return es->u.key;
502 default: 504 default:
503 continue; 505 return es->u.key;
504 } 506 }
505 if (ui_browser__refresh_entries(self) < 0) 507 if (ui_browser__refresh_entries(self) < 0)
506 return -1; 508 return -1;
@@ -508,38 +510,6 @@ static int ui_browser__run(struct ui_browser *self, const char *title,
508 return 0; 510 return 0;
509} 511}
510 512
511/*
512 * When debugging newt problems it was useful to be able to "unroll"
513 * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate
514 * a source file with the sequence of calls to these methods, to then
515 * tweak the arrays to get the intended results, so I'm keeping this code
516 * here, may be useful again in the future.
517 */
518#undef NEWT_DEBUG
519
520static void newt_checkbox_tree__add(newtComponent tree, const char *str,
521 void *priv, int *indexes)
522{
523#ifdef NEWT_DEBUG
524 /* Print the newtCheckboxTreeAddArray to tinker with its index arrays */
525 int i = 0, len = 40 - strlen(str);
526
527 fprintf(stderr,
528 "\tnewtCheckboxTreeAddItem(tree, %*.*s\"%s\", (void *)%p, 0, ",
529 len, len, " ", str, priv);
530 while (indexes[i] != NEWT_ARG_LAST) {
531 if (indexes[i] != NEWT_ARG_APPEND)
532 fprintf(stderr, " %d,", indexes[i]);
533 else
534 fprintf(stderr, " %s,", "NEWT_ARG_APPEND");
535 ++i;
536 }
537 fprintf(stderr, " %s", " NEWT_ARG_LAST);\n");
538 fflush(stderr);
539#endif
540 newtCheckboxTreeAddArray(tree, str, priv, 0, indexes);
541}
542
543static char *callchain_list__sym_name(struct callchain_list *self, 513static char *callchain_list__sym_name(struct callchain_list *self,
544 char *bf, size_t bfsize) 514 char *bf, size_t bfsize)
545{ 515{
@@ -550,144 +520,29 @@ static char *callchain_list__sym_name(struct callchain_list *self,
550 return bf; 520 return bf;
551} 521}
552 522
553static void __callchain__append_graph_browser(struct callchain_node *self, 523static unsigned int hist_entry__annotate_browser_refresh(struct ui_browser *self)
554 newtComponent tree, u64 total,
555 int *indexes, int depth)
556{ 524{
557 struct rb_node *node; 525 struct objdump_line *pos;
558 u64 new_total, remaining; 526 struct list_head *head = self->entries;
559 int idx = 0; 527 struct hist_entry *he = self->priv;
560 528 int row = 0;
561 if (callchain_param.mode == CHAIN_GRAPH_REL) 529 int len = he->ms.sym->end - he->ms.sym->start;
562 new_total = self->children_hit;
563 else
564 new_total = total;
565
566 remaining = new_total;
567 node = rb_first(&self->rb_root);
568 while (node) {
569 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
570 struct rb_node *next = rb_next(node);
571 u64 cumul = cumul_hits(child);
572 struct callchain_list *chain;
573 int first = true, printed = 0;
574 int chain_idx = -1;
575 remaining -= cumul;
576
577 indexes[depth] = NEWT_ARG_APPEND;
578 indexes[depth + 1] = NEWT_ARG_LAST;
579
580 list_for_each_entry(chain, &child->val, list) {
581 char ipstr[BITS_PER_LONG / 4 + 1],
582 *alloc_str = NULL;
583 const char *str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
584
585 if (first) {
586 double percent = cumul * 100.0 / new_total;
587
588 first = false;
589 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
590 str = "Not enough memory!";
591 else
592 str = alloc_str;
593 } else {
594 indexes[depth] = idx;
595 indexes[depth + 1] = NEWT_ARG_APPEND;
596 indexes[depth + 2] = NEWT_ARG_LAST;
597 ++chain_idx;
598 }
599 newt_checkbox_tree__add(tree, str, &chain->ms, indexes);
600 free(alloc_str);
601 ++printed;
602 }
603
604 indexes[depth] = idx;
605 if (chain_idx != -1)
606 indexes[depth + 1] = chain_idx;
607 if (printed != 0)
608 ++idx;
609 __callchain__append_graph_browser(child, tree, new_total, indexes,
610 depth + (chain_idx != -1 ? 2 : 1));
611 node = next;
612 }
613}
614
615static void callchain__append_graph_browser(struct callchain_node *self,
616 newtComponent tree, u64 total,
617 int *indexes, int parent_idx)
618{
619 struct callchain_list *chain;
620 int i = 0;
621
622 indexes[1] = NEWT_ARG_APPEND;
623 indexes[2] = NEWT_ARG_LAST;
624
625 list_for_each_entry(chain, &self->val, list) {
626 char ipstr[BITS_PER_LONG / 4 + 1], *str;
627
628 if (chain->ip >= PERF_CONTEXT_MAX)
629 continue;
630
631 if (!i++ && sort__first_dimension == SORT_SYM)
632 continue;
633 530
634 str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); 531 if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries)
635 newt_checkbox_tree__add(tree, str, &chain->ms, indexes); 532 self->first_visible_entry = head->next;
636 }
637 533
638 indexes[1] = parent_idx; 534 pos = list_entry(self->first_visible_entry, struct objdump_line, node);
639 indexes[2] = NEWT_ARG_APPEND;
640 indexes[3] = NEWT_ARG_LAST;
641 __callchain__append_graph_browser(self, tree, total, indexes, 2);
642}
643 535
644static void hist_entry__append_callchain_browser(struct hist_entry *self, 536 list_for_each_entry_from(pos, head, node) {
645 newtComponent tree, u64 total, int parent_idx) 537 bool current_entry = ui_browser__is_current_entry(self, row);
646{ 538 SLsmg_gotorc(self->top + row, self->left);
647 struct rb_node *rb_node; 539 objdump_line__show(pos, head, self->width,
648 int indexes[1024] = { [0] = parent_idx, }; 540 he, len, current_entry);
649 int idx = 0; 541 if (++row == self->height)
650 struct callchain_node *chain;
651
652 rb_node = rb_first(&self->sorted_chain);
653 while (rb_node) {
654 chain = rb_entry(rb_node, struct callchain_node, rb_node);
655 switch (callchain_param.mode) {
656 case CHAIN_FLAT:
657 break;
658 case CHAIN_GRAPH_ABS: /* falldown */
659 case CHAIN_GRAPH_REL:
660 callchain__append_graph_browser(chain, tree, total, indexes, idx++);
661 break;
662 case CHAIN_NONE:
663 default:
664 break; 542 break;
665 }
666 rb_node = rb_next(rb_node);
667 } 543 }
668}
669
670static size_t hist_entry__append_browser(struct hist_entry *self,
671 newtComponent tree, u64 total)
672{
673 char s[256];
674 size_t ret;
675
676 if (symbol_conf.exclude_other && !self->parent)
677 return 0;
678 544
679 ret = hist_entry__snprintf(self, s, sizeof(s), NULL, 545 return row;
680 false, 0, false, total);
681 if (symbol_conf.use_callchain) {
682 int indexes[2];
683
684 indexes[0] = NEWT_ARG_APPEND;
685 indexes[1] = NEWT_ARG_LAST;
686 newt_checkbox_tree__add(tree, s, &self->ms, indexes);
687 } else
688 newtListboxAppendEntry(tree, s, &self->ms);
689
690 return ret;
691} 546}
692 547
693int hist_entry__tui_annotate(struct hist_entry *self) 548int hist_entry__tui_annotate(struct hist_entry *self)
@@ -712,7 +567,9 @@ int hist_entry__tui_annotate(struct hist_entry *self)
712 ui_helpline__push("Press <- or ESC to exit"); 567 ui_helpline__push("Press <- or ESC to exit");
713 568
714 memset(&browser, 0, sizeof(browser)); 569 memset(&browser, 0, sizeof(browser));
715 browser.entries = &head; 570 browser.entries = &head;
571 browser.refresh_entries = hist_entry__annotate_browser_refresh;
572 browser.seek = ui_browser__list_head_seek;
716 browser.priv = self; 573 browser.priv = self;
717 list_for_each_entry(pos, &head, node) { 574 list_for_each_entry(pos, &head, node) {
718 size_t line_len = strlen(pos->line); 575 size_t line_len = strlen(pos->line);
@@ -722,7 +579,9 @@ int hist_entry__tui_annotate(struct hist_entry *self)
722 } 579 }
723 580
724 browser.width += 18; /* Percentage */ 581 browser.width += 18; /* Percentage */
725 ret = ui_browser__run(&browser, self->ms.sym->name, &es); 582 ui_browser__show(&browser, self->ms.sym->name);
583 newtFormAddHotKey(browser.form, ' ');
584 ret = ui_browser__run(&browser, &es);
726 newtFormDestroy(browser.form); 585 newtFormDestroy(browser.form);
727 newtPopWindow(); 586 newtPopWindow();
728 list_for_each_entry_safe(pos, n, &head, node) { 587 list_for_each_entry_safe(pos, n, &head, node) {
@@ -733,157 +592,48 @@ int hist_entry__tui_annotate(struct hist_entry *self)
733 return ret; 592 return ret;
734} 593}
735 594
736static const void *newt__symbol_tree_get_current(newtComponent self)
737{
738 if (symbol_conf.use_callchain)
739 return newtCheckboxTreeGetCurrent(self);
740 return newtListboxGetCurrent(self);
741}
742
743static void hist_browser__selection(newtComponent self, void *data)
744{
745 const struct map_symbol **symbol_ptr = data;
746 *symbol_ptr = newt__symbol_tree_get_current(self);
747}
748
749struct hist_browser { 595struct hist_browser {
750 newtComponent form, tree; 596 struct ui_browser b;
751 const struct map_symbol *selection; 597 struct hists *hists;
598 struct hist_entry *he_selection;
599 struct map_symbol *selection;
752}; 600};
753 601
754static struct hist_browser *hist_browser__new(void) 602static void hist_browser__reset(struct hist_browser *self);
603static int hist_browser__run(struct hist_browser *self, const char *title,
604 struct newtExitStruct *es);
605static unsigned int hist_browser__refresh_entries(struct ui_browser *self);
606static void ui_browser__hists_seek(struct ui_browser *self,
607 off_t offset, int whence);
608
609static struct hist_browser *hist_browser__new(struct hists *hists)
755{ 610{
756 struct hist_browser *self = malloc(sizeof(*self)); 611 struct hist_browser *self = zalloc(sizeof(*self));
757 612
758 if (self != NULL) 613 if (self) {
759 self->form = NULL; 614 self->hists = hists;
615 self->b.refresh_entries = hist_browser__refresh_entries;
616 self->b.seek = ui_browser__hists_seek;
617 }
760 618
761 return self; 619 return self;
762} 620}
763 621
764static void hist_browser__delete(struct hist_browser *self) 622static void hist_browser__delete(struct hist_browser *self)
765{ 623{
766 newtFormDestroy(self->form); 624 newtFormDestroy(self->b.form);
767 newtPopWindow(); 625 newtPopWindow();
768 free(self); 626 free(self);
769} 627}
770 628
771static int hist_browser__populate(struct hist_browser *self, struct hists *hists,
772 const char *title)
773{
774 int max_len = 0, idx, cols, rows;
775 struct ui_progress *progress;
776 struct rb_node *nd;
777 u64 curr_hist = 0;
778 char seq[] = ".", unit;
779 char str[256];
780 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
781
782 if (self->form) {
783 newtFormDestroy(self->form);
784 newtPopWindow();
785 }
786
787 nr_events = convert_unit(nr_events, &unit);
788 snprintf(str, sizeof(str), "Events: %lu%c ",
789 nr_events, unit);
790 newtDrawRootText(0, 0, str);
791
792 newtGetScreenSize(NULL, &rows);
793
794 if (symbol_conf.use_callchain)
795 self->tree = newtCheckboxTreeMulti(0, 0, rows - 5, seq,
796 NEWT_FLAG_SCROLL);
797 else
798 self->tree = newtListbox(0, 0, rows - 5,
799 (NEWT_FLAG_SCROLL |
800 NEWT_FLAG_RETURNEXIT));
801
802 newtComponentAddCallback(self->tree, hist_browser__selection,
803 &self->selection);
804
805 progress = ui_progress__new("Adding entries to the browser...",
806 hists->nr_entries);
807 if (progress == NULL)
808 return -1;
809
810 idx = 0;
811 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
812 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
813 int len;
814
815 if (h->filtered)
816 continue;
817
818 len = hist_entry__append_browser(h, self->tree, hists->stats.total_period);
819 if (len > max_len)
820 max_len = len;
821 if (symbol_conf.use_callchain)
822 hist_entry__append_callchain_browser(h, self->tree,
823 hists->stats.total_period, idx++);
824 ++curr_hist;
825 if (curr_hist % 5)
826 ui_progress__update(progress, curr_hist);
827 }
828
829 ui_progress__delete(progress);
830
831 newtGetScreenSize(&cols, &rows);
832
833 if (max_len > cols)
834 max_len = cols - 3;
835
836 if (!symbol_conf.use_callchain)
837 newtListboxSetWidth(self->tree, max_len);
838
839 newtCenteredWindow(max_len + (symbol_conf.use_callchain ? 5 : 0),
840 rows - 5, title);
841 self->form = newt_form__new();
842 if (self->form == NULL)
843 return -1;
844
845 newtFormAddHotKey(self->form, 'A');
846 newtFormAddHotKey(self->form, 'a');
847 newtFormAddHotKey(self->form, 'D');
848 newtFormAddHotKey(self->form, 'd');
849 newtFormAddHotKey(self->form, 'T');
850 newtFormAddHotKey(self->form, 't');
851 newtFormAddHotKey(self->form, '?');
852 newtFormAddHotKey(self->form, 'H');
853 newtFormAddHotKey(self->form, 'h');
854 newtFormAddHotKey(self->form, NEWT_KEY_F1);
855 newtFormAddHotKey(self->form, NEWT_KEY_RIGHT);
856 newtFormAddHotKey(self->form, NEWT_KEY_TAB);
857 newtFormAddHotKey(self->form, NEWT_KEY_UNTAB);
858 newtFormAddComponents(self->form, self->tree, NULL);
859 self->selection = newt__symbol_tree_get_current(self->tree);
860
861 return 0;
862}
863
864static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self) 629static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
865{ 630{
866 int *indexes; 631 return self->he_selection;
867
868 if (!symbol_conf.use_callchain)
869 goto out;
870
871 indexes = newtCheckboxTreeFindItem(self->tree, (void *)self->selection);
872 if (indexes) {
873 bool is_hist_entry = indexes[1] == NEWT_ARG_LAST;
874 free(indexes);
875 if (is_hist_entry)
876 goto out;
877 }
878 return NULL;
879out:
880 return container_of(self->selection, struct hist_entry, ms);
881} 632}
882 633
883static struct thread *hist_browser__selected_thread(struct hist_browser *self) 634static struct thread *hist_browser__selected_thread(struct hist_browser *self)
884{ 635{
885 struct hist_entry *he = hist_browser__selected_entry(self); 636 return self->he_selection->thread;
886 return he ? he->thread : NULL;
887} 637}
888 638
889static int hist_browser__title(char *bf, size_t size, const char *ev_name, 639static int hist_browser__title(char *bf, size_t size, const char *ev_name,
@@ -905,7 +655,7 @@ static int hist_browser__title(char *bf, size_t size, const char *ev_name,
905 655
906int hists__browse(struct hists *self, const char *helpline, const char *ev_name) 656int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
907{ 657{
908 struct hist_browser *browser = hist_browser__new(); 658 struct hist_browser *browser = hist_browser__new(self);
909 struct pstack *fstack; 659 struct pstack *fstack;
910 const struct thread *thread_filter = NULL; 660 const struct thread *thread_filter = NULL;
911 const struct dso *dso_filter = NULL; 661 const struct dso *dso_filter = NULL;
@@ -924,8 +674,6 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
924 674
925 hist_browser__title(msg, sizeof(msg), ev_name, 675 hist_browser__title(msg, sizeof(msg), ev_name,
926 dso_filter, thread_filter); 676 dso_filter, thread_filter);
927 if (hist_browser__populate(browser, self, msg) < 0)
928 goto out_free_stack;
929 677
930 while (1) { 678 while (1) {
931 const struct thread *thread; 679 const struct thread *thread;
@@ -934,7 +682,8 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
934 int nr_options = 0, choice = 0, i, 682 int nr_options = 0, choice = 0, i,
935 annotate = -2, zoom_dso = -2, zoom_thread = -2; 683 annotate = -2, zoom_dso = -2, zoom_thread = -2;
936 684
937 newtFormRun(browser->form, &es); 685 if (hist_browser__run(browser, msg, &es))
686 break;
938 687
939 thread = hist_browser__selected_thread(browser); 688 thread = hist_browser__selected_thread(browser);
940 dso = browser->selection->map ? browser->selection->map->dso : NULL; 689 dso = browser->selection->map ? browser->selection->map->dso : NULL;
@@ -1069,8 +818,7 @@ zoom_out_dso:
1069 hists__filter_by_dso(self, dso_filter); 818 hists__filter_by_dso(self, dso_filter);
1070 hist_browser__title(msg, sizeof(msg), ev_name, 819 hist_browser__title(msg, sizeof(msg), ev_name,
1071 dso_filter, thread_filter); 820 dso_filter, thread_filter);
1072 if (hist_browser__populate(browser, self, msg) < 0) 821 hist_browser__reset(browser);
1073 goto out;
1074 } else if (choice == zoom_thread) { 822 } else if (choice == zoom_thread) {
1075zoom_thread: 823zoom_thread:
1076 if (thread_filter) { 824 if (thread_filter) {
@@ -1088,8 +836,7 @@ zoom_out_thread:
1088 hists__filter_by_thread(self, thread_filter); 836 hists__filter_by_thread(self, thread_filter);
1089 hist_browser__title(msg, sizeof(msg), ev_name, 837 hist_browser__title(msg, sizeof(msg), ev_name,
1090 dso_filter, thread_filter); 838 dso_filter, thread_filter);
1091 if (hist_browser__populate(browser, self, msg) < 0) 839 hist_browser__reset(browser);
1092 goto out;
1093 } 840 }
1094 } 841 }
1095out_free_stack: 842out_free_stack:
@@ -1145,6 +892,13 @@ static struct newtPercentTreeColors {
1145 "blue", "lightgray", 892 "blue", "lightgray",
1146}; 893};
1147 894
895static void newt_suspend(void *d __used)
896{
897 newtSuspend();
898 raise(SIGTSTP);
899 newtResume();
900}
901
1148void setup_browser(void) 902void setup_browser(void)
1149{ 903{
1150 struct newtPercentTreeColors *c = &defaultPercentTreeColors; 904 struct newtPercentTreeColors *c = &defaultPercentTreeColors;
@@ -1158,6 +912,7 @@ void setup_browser(void)
1158 use_browser = 1; 912 use_browser = 1;
1159 newtInit(); 913 newtInit();
1160 newtCls(); 914 newtCls();
915 newtSetSuspendCallback(newt_suspend, NULL);
1161 ui_helpline__puts(" "); 916 ui_helpline__puts(" ");
1162 sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg); 917 sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg);
1163 sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg); 918 sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg);
@@ -1176,3 +931,638 @@ void exit_browser(bool wait_for_ok)
1176 newtFinished(); 931 newtFinished();
1177 } 932 }
1178} 933}
934
935static void hist_browser__refresh_dimensions(struct hist_browser *self)
936{
937 /* 3 == +/- toggle symbol before actual hist_entry rendering */
938 self->b.width = 3 + (hists__sort_list_width(self->hists) +
939 sizeof("[k]"));
940}
941
942static void hist_browser__reset(struct hist_browser *self)
943{
944 self->b.nr_entries = self->hists->nr_entries;
945 hist_browser__refresh_dimensions(self);
946 ui_browser__reset_index(&self->b);
947}
948
949static char tree__folded_sign(bool unfolded)
950{
951 return unfolded ? '-' : '+';
952}
953
954static char map_symbol__folded(const struct map_symbol *self)
955{
956 return self->has_children ? tree__folded_sign(self->unfolded) : ' ';
957}
958
959static char hist_entry__folded(const struct hist_entry *self)
960{
961 return map_symbol__folded(&self->ms);
962}
963
964static char callchain_list__folded(const struct callchain_list *self)
965{
966 return map_symbol__folded(&self->ms);
967}
968
969static bool map_symbol__toggle_fold(struct map_symbol *self)
970{
971 if (!self->has_children)
972 return false;
973
974 self->unfolded = !self->unfolded;
975 return true;
976}
977
978#define LEVEL_OFFSET_STEP 3
979
980static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
981 struct callchain_node *chain_node,
982 u64 total, int level,
983 unsigned short row,
984 off_t *row_offset,
985 bool *is_current_entry)
986{
987 struct rb_node *node;
988 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
989 u64 new_total, remaining;
990
991 if (callchain_param.mode == CHAIN_GRAPH_REL)
992 new_total = chain_node->children_hit;
993 else
994 new_total = total;
995
996 remaining = new_total;
997 node = rb_first(&chain_node->rb_root);
998 while (node) {
999 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1000 struct rb_node *next = rb_next(node);
1001 u64 cumul = cumul_hits(child);
1002 struct callchain_list *chain;
1003 char folded_sign = ' ';
1004 int first = true;
1005 int extra_offset = 0;
1006
1007 remaining -= cumul;
1008
1009 list_for_each_entry(chain, &child->val, list) {
1010 char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
1011 const char *str;
1012 int color;
1013 bool was_first = first;
1014
1015 if (first) {
1016 first = false;
1017 chain->ms.has_children = chain->list.next != &child->val ||
1018 rb_first(&child->rb_root) != NULL;
1019 } else {
1020 extra_offset = LEVEL_OFFSET_STEP;
1021 chain->ms.has_children = chain->list.next == &child->val &&
1022 rb_first(&child->rb_root) != NULL;
1023 }
1024
1025 folded_sign = callchain_list__folded(chain);
1026 if (*row_offset != 0) {
1027 --*row_offset;
1028 goto do_next;
1029 }
1030
1031 alloc_str = NULL;
1032 str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
1033 if (was_first) {
1034 double percent = cumul * 100.0 / new_total;
1035
1036 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
1037 str = "Not enough memory!";
1038 else
1039 str = alloc_str;
1040 }
1041
1042 color = HE_COLORSET_NORMAL;
1043 width = self->b.width - (offset + extra_offset + 2);
1044 if (ui_browser__is_current_entry(&self->b, row)) {
1045 self->selection = &chain->ms;
1046 color = HE_COLORSET_SELECTED;
1047 *is_current_entry = true;
1048 }
1049
1050 SLsmg_set_color(color);
1051 SLsmg_gotorc(self->b.top + row, self->b.left);
1052 slsmg_write_nstring(" ", offset + extra_offset);
1053 slsmg_printf("%c ", folded_sign);
1054 slsmg_write_nstring(str, width);
1055 free(alloc_str);
1056
1057 if (++row == self->b.height)
1058 goto out;
1059do_next:
1060 if (folded_sign == '+')
1061 break;
1062 }
1063
1064 if (folded_sign == '-') {
1065 const int new_level = level + (extra_offset ? 2 : 1);
1066 row += hist_browser__show_callchain_node_rb_tree(self, child, new_total,
1067 new_level, row, row_offset,
1068 is_current_entry);
1069 }
1070 if (row == self->b.height)
1071 goto out;
1072 node = next;
1073 }
1074out:
1075 return row - first_row;
1076}
1077
1078static int hist_browser__show_callchain_node(struct hist_browser *self,
1079 struct callchain_node *node,
1080 int level, unsigned short row,
1081 off_t *row_offset,
1082 bool *is_current_entry)
1083{
1084 struct callchain_list *chain;
1085 int first_row = row,
1086 offset = level * LEVEL_OFFSET_STEP,
1087 width = self->b.width - offset;
1088 char folded_sign = ' ';
1089
1090 list_for_each_entry(chain, &node->val, list) {
1091 char ipstr[BITS_PER_LONG / 4 + 1], *s;
1092 int color;
1093 /*
1094 * FIXME: This should be moved to somewhere else,
1095 * probably when the callchain is created, so as not to
1096 * traverse it all over again
1097 */
1098 chain->ms.has_children = rb_first(&node->rb_root) != NULL;
1099 folded_sign = callchain_list__folded(chain);
1100
1101 if (*row_offset != 0) {
1102 --*row_offset;
1103 continue;
1104 }
1105
1106 color = HE_COLORSET_NORMAL;
1107 if (ui_browser__is_current_entry(&self->b, row)) {
1108 self->selection = &chain->ms;
1109 color = HE_COLORSET_SELECTED;
1110 *is_current_entry = true;
1111 }
1112
1113 s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
1114 SLsmg_gotorc(self->b.top + row, self->b.left);
1115 SLsmg_set_color(color);
1116 slsmg_write_nstring(" ", offset);
1117 slsmg_printf("%c ", folded_sign);
1118 slsmg_write_nstring(s, width - 2);
1119
1120 if (++row == self->b.height)
1121 goto out;
1122 }
1123
1124 if (folded_sign == '-')
1125 row += hist_browser__show_callchain_node_rb_tree(self, node,
1126 self->hists->stats.total_period,
1127 level + 1, row,
1128 row_offset,
1129 is_current_entry);
1130out:
1131 return row - first_row;
1132}
1133
1134static int hist_browser__show_callchain(struct hist_browser *self,
1135 struct rb_root *chain,
1136 int level, unsigned short row,
1137 off_t *row_offset,
1138 bool *is_current_entry)
1139{
1140 struct rb_node *nd;
1141 int first_row = row;
1142
1143 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1144 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1145
1146 row += hist_browser__show_callchain_node(self, node, level,
1147 row, row_offset,
1148 is_current_entry);
1149 if (row == self->b.height)
1150 break;
1151 }
1152
1153 return row - first_row;
1154}
1155
1156static int hist_browser__show_entry(struct hist_browser *self,
1157 struct hist_entry *entry,
1158 unsigned short row)
1159{
1160 char s[256];
1161 double percent;
1162 int printed = 0;
1163 int color, width = self->b.width;
1164 char folded_sign = ' ';
1165 bool current_entry = ui_browser__is_current_entry(&self->b, row);
1166 off_t row_offset = entry->row_offset;
1167
1168 if (current_entry) {
1169 self->he_selection = entry;
1170 self->selection = &entry->ms;
1171 }
1172
1173 if (symbol_conf.use_callchain) {
1174 entry->ms.has_children = !RB_EMPTY_ROOT(&entry->sorted_chain);
1175 folded_sign = hist_entry__folded(entry);
1176 }
1177
1178 if (row_offset == 0) {
1179 hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false,
1180 0, false, self->hists->stats.total_period);
1181 percent = (entry->period * 100.0) / self->hists->stats.total_period;
1182
1183 color = HE_COLORSET_SELECTED;
1184 if (!current_entry) {
1185 if (percent >= MIN_RED)
1186 color = HE_COLORSET_TOP;
1187 else if (percent >= MIN_GREEN)
1188 color = HE_COLORSET_MEDIUM;
1189 else
1190 color = HE_COLORSET_NORMAL;
1191 }
1192
1193 SLsmg_set_color(color);
1194 SLsmg_gotorc(self->b.top + row, self->b.left);
1195 if (symbol_conf.use_callchain) {
1196 slsmg_printf("%c ", folded_sign);
1197 width -= 2;
1198 }
1199 slsmg_write_nstring(s, width);
1200 ++row;
1201 ++printed;
1202 } else
1203 --row_offset;
1204
1205 if (folded_sign == '-' && row != self->b.height) {
1206 printed += hist_browser__show_callchain(self, &entry->sorted_chain,
1207 1, row, &row_offset,
1208 &current_entry);
1209 if (current_entry)
1210 self->he_selection = entry;
1211 }
1212
1213 return printed;
1214}
1215
1216static unsigned int hist_browser__refresh_entries(struct ui_browser *self)
1217{
1218 unsigned row = 0;
1219 struct rb_node *nd;
1220 struct hist_browser *hb = container_of(self, struct hist_browser, b);
1221
1222 if (self->first_visible_entry == NULL)
1223 self->first_visible_entry = rb_first(&hb->hists->entries);
1224
1225 for (nd = self->first_visible_entry; nd; nd = rb_next(nd)) {
1226 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1227
1228 if (h->filtered)
1229 continue;
1230
1231 row += hist_browser__show_entry(hb, h, row);
1232 if (row == self->height)
1233 break;
1234 }
1235
1236 return row;
1237}
1238
1239static void callchain_node__init_have_children_rb_tree(struct callchain_node *self)
1240{
1241 struct rb_node *nd = rb_first(&self->rb_root);
1242
1243 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
1244 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
1245 struct callchain_list *chain;
1246 int first = true;
1247
1248 list_for_each_entry(chain, &child->val, list) {
1249 if (first) {
1250 first = false;
1251 chain->ms.has_children = chain->list.next != &child->val ||
1252 rb_first(&child->rb_root) != NULL;
1253 } else
1254 chain->ms.has_children = chain->list.next == &child->val &&
1255 rb_first(&child->rb_root) != NULL;
1256 }
1257
1258 callchain_node__init_have_children_rb_tree(child);
1259 }
1260}
1261
1262static void callchain_node__init_have_children(struct callchain_node *self)
1263{
1264 struct callchain_list *chain;
1265
1266 list_for_each_entry(chain, &self->val, list)
1267 chain->ms.has_children = rb_first(&self->rb_root) != NULL;
1268
1269 callchain_node__init_have_children_rb_tree(self);
1270}
1271
1272static void callchain__init_have_children(struct rb_root *self)
1273{
1274 struct rb_node *nd;
1275
1276 for (nd = rb_first(self); nd; nd = rb_next(nd)) {
1277 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1278 callchain_node__init_have_children(node);
1279 }
1280}
1281
1282static void hist_entry__init_have_children(struct hist_entry *self)
1283{
1284 if (!self->init_have_children) {
1285 callchain__init_have_children(&self->sorted_chain);
1286 self->init_have_children = true;
1287 }
1288}
1289
1290static struct rb_node *hists__filter_entries(struct rb_node *nd)
1291{
1292 while (nd != NULL) {
1293 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1294 if (!h->filtered)
1295 return nd;
1296
1297 nd = rb_next(nd);
1298 }
1299
1300 return NULL;
1301}
1302
1303static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
1304{
1305 while (nd != NULL) {
1306 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1307 if (!h->filtered)
1308 return nd;
1309
1310 nd = rb_prev(nd);
1311 }
1312
1313 return NULL;
1314}
1315
1316static void ui_browser__hists_seek(struct ui_browser *self,
1317 off_t offset, int whence)
1318{
1319 struct hist_entry *h;
1320 struct rb_node *nd;
1321 bool first = true;
1322
1323 switch (whence) {
1324 case SEEK_SET:
1325 nd = hists__filter_entries(rb_first(self->entries));
1326 break;
1327 case SEEK_CUR:
1328 nd = self->first_visible_entry;
1329 goto do_offset;
1330 case SEEK_END:
1331 nd = hists__filter_prev_entries(rb_last(self->entries));
1332 first = false;
1333 break;
1334 default:
1335 return;
1336 }
1337
1338 /*
1339 * Moves not relative to the first visible entry invalidates its
1340 * row_offset:
1341 */
1342 h = rb_entry(self->first_visible_entry, struct hist_entry, rb_node);
1343 h->row_offset = 0;
1344
1345 /*
1346 * Here we have to check if nd is expanded (+), if it is we can't go
1347 * the next top level hist_entry, instead we must compute an offset of
1348 * what _not_ to show and not change the first visible entry.
1349 *
1350 * This offset increments when we are going from top to bottom and
1351 * decreases when we're going from bottom to top.
1352 *
1353 * As we don't have backpointers to the top level in the callchains
1354 * structure, we need to always print the whole hist_entry callchain,
1355 * skipping the first ones that are before the first visible entry
1356 * and stop when we printed enough lines to fill the screen.
1357 */
1358do_offset:
1359 if (offset > 0) {
1360 do {
1361 h = rb_entry(nd, struct hist_entry, rb_node);
1362 if (h->ms.unfolded) {
1363 u16 remaining = h->nr_rows - h->row_offset;
1364 if (offset > remaining) {
1365 offset -= remaining;
1366 h->row_offset = 0;
1367 } else {
1368 h->row_offset += offset;
1369 offset = 0;
1370 self->first_visible_entry = nd;
1371 break;
1372 }
1373 }
1374 nd = hists__filter_entries(rb_next(nd));
1375 if (nd == NULL)
1376 break;
1377 --offset;
1378 self->first_visible_entry = nd;
1379 } while (offset != 0);
1380 } else if (offset < 0) {
1381 while (1) {
1382 h = rb_entry(nd, struct hist_entry, rb_node);
1383 if (h->ms.unfolded) {
1384 if (first) {
1385 if (-offset > h->row_offset) {
1386 offset += h->row_offset;
1387 h->row_offset = 0;
1388 } else {
1389 h->row_offset += offset;
1390 offset = 0;
1391 self->first_visible_entry = nd;
1392 break;
1393 }
1394 } else {
1395 if (-offset > h->nr_rows) {
1396 offset += h->nr_rows;
1397 h->row_offset = 0;
1398 } else {
1399 h->row_offset = h->nr_rows + offset;
1400 offset = 0;
1401 self->first_visible_entry = nd;
1402 break;
1403 }
1404 }
1405 }
1406
1407 nd = hists__filter_prev_entries(rb_prev(nd));
1408 if (nd == NULL)
1409 break;
1410 ++offset;
1411 self->first_visible_entry = nd;
1412 if (offset == 0) {
1413 /*
1414 * Last unfiltered hist_entry, check if it is
1415 * unfolded, if it is then we should have
1416 * row_offset at its last entry.
1417 */
1418 h = rb_entry(nd, struct hist_entry, rb_node);
1419 if (h->ms.unfolded)
1420 h->row_offset = h->nr_rows;
1421 break;
1422 }
1423 first = false;
1424 }
1425 } else {
1426 self->first_visible_entry = nd;
1427 h = rb_entry(nd, struct hist_entry, rb_node);
1428 h->row_offset = 0;
1429 }
1430}
1431
1432static int callchain_node__count_rows_rb_tree(struct callchain_node *self)
1433{
1434 int n = 0;
1435 struct rb_node *nd;
1436
1437 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
1438 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
1439 struct callchain_list *chain;
1440 char folded_sign = ' '; /* No children */
1441
1442 list_for_each_entry(chain, &child->val, list) {
1443 ++n;
1444 /* We need this because we may not have children */
1445 folded_sign = callchain_list__folded(chain);
1446 if (folded_sign == '+')
1447 break;
1448 }
1449
1450 if (folded_sign == '-') /* Have children and they're unfolded */
1451 n += callchain_node__count_rows_rb_tree(child);
1452 }
1453
1454 return n;
1455}
1456
1457static int callchain_node__count_rows(struct callchain_node *node)
1458{
1459 struct callchain_list *chain;
1460 bool unfolded = false;
1461 int n = 0;
1462
1463 list_for_each_entry(chain, &node->val, list) {
1464 ++n;
1465 unfolded = chain->ms.unfolded;
1466 }
1467
1468 if (unfolded)
1469 n += callchain_node__count_rows_rb_tree(node);
1470
1471 return n;
1472}
1473
1474static int callchain__count_rows(struct rb_root *chain)
1475{
1476 struct rb_node *nd;
1477 int n = 0;
1478
1479 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1480 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1481 n += callchain_node__count_rows(node);
1482 }
1483
1484 return n;
1485}
1486
1487static bool hist_browser__toggle_fold(struct hist_browser *self)
1488{
1489 if (map_symbol__toggle_fold(self->selection)) {
1490 struct hist_entry *he = self->he_selection;
1491
1492 hist_entry__init_have_children(he);
1493 self->hists->nr_entries -= he->nr_rows;
1494
1495 if (he->ms.unfolded)
1496 he->nr_rows = callchain__count_rows(&he->sorted_chain);
1497 else
1498 he->nr_rows = 0;
1499 self->hists->nr_entries += he->nr_rows;
1500 self->b.nr_entries = self->hists->nr_entries;
1501
1502 return true;
1503 }
1504
1505 /* If it doesn't have children, no toggling performed */
1506 return false;
1507}
1508
1509static int hist_browser__run(struct hist_browser *self, const char *title,
1510 struct newtExitStruct *es)
1511{
1512 char str[256], unit;
1513 unsigned long nr_events = self->hists->stats.nr_events[PERF_RECORD_SAMPLE];
1514
1515 self->b.entries = &self->hists->entries;
1516 self->b.nr_entries = self->hists->nr_entries;
1517
1518 hist_browser__refresh_dimensions(self);
1519
1520 nr_events = convert_unit(nr_events, &unit);
1521 snprintf(str, sizeof(str), "Events: %lu%c ",
1522 nr_events, unit);
1523 newtDrawRootText(0, 0, str);
1524
1525 if (ui_browser__show(&self->b, title) < 0)
1526 return -1;
1527
1528 newtFormAddHotKey(self->b.form, 'A');
1529 newtFormAddHotKey(self->b.form, 'a');
1530 newtFormAddHotKey(self->b.form, '?');
1531 newtFormAddHotKey(self->b.form, 'h');
1532 newtFormAddHotKey(self->b.form, 'H');
1533 newtFormAddHotKey(self->b.form, 'd');
1534
1535 newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
1536 newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT);
1537 newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
1538
1539 while (1) {
1540 ui_browser__run(&self->b, es);
1541
1542 if (es->reason != NEWT_EXIT_HOTKEY)
1543 break;
1544 switch (es->u.key) {
1545 case 'd': { /* Debug */
1546 static int seq;
1547 struct hist_entry *h = rb_entry(self->b.first_visible_entry,
1548 struct hist_entry, rb_node);
1549 ui_helpline__pop();
1550 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
1551 seq++, self->b.nr_entries,
1552 self->hists->nr_entries,
1553 self->b.height,
1554 self->b.index,
1555 self->b.first_visible_entry_idx,
1556 h->row_offset, h->nr_rows);
1557 }
1558 continue;
1559 case NEWT_KEY_ENTER:
1560 if (hist_browser__toggle_fold(self))
1561 break;
1562 /* fall thru */
1563 default:
1564 return 0;
1565 }
1566 }
1567 return 0;
1568}