aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/kernel/cpu/perf_event_p4.c9
-rw-r--r--arch/x86/oprofile/nmi_int.c1
-rw-r--r--tools/perf/Makefile31
-rw-r--r--tools/perf/builtin-annotate.c2
-rw-r--r--tools/perf/builtin-report.c31
-rw-r--r--tools/perf/builtin-timechart.c4
-rw-r--r--tools/perf/builtin-trace.c19
-rw-r--r--tools/perf/util/debug.c2
-rw-r--r--tools/perf/util/debug.h9
-rw-r--r--tools/perf/util/hist.c16
-rw-r--r--tools/perf/util/hist.h3
-rw-r--r--tools/perf/util/include/linux/list.h8
-rw-r--r--tools/perf/util/include/linux/types.h12
-rw-r--r--tools/perf/util/probe-event.c11
-rw-r--r--tools/perf/util/probe-finder.c9
-rw-r--r--tools/perf/util/pstack.h2
-rw-r--r--tools/perf/util/sort.c6
-rw-r--r--tools/perf/util/symbol.c40
-rw-r--r--tools/perf/util/symbol.h1
-rw-r--r--tools/perf/util/ui/browser.c329
-rw-r--r--tools/perf/util/ui/browser.h46
-rw-r--r--tools/perf/util/ui/browsers/annotate.c240
-rw-r--r--tools/perf/util/ui/browsers/hists.c (renamed from tools/perf/util/newt.c)1454
-rw-r--r--tools/perf/util/ui/browsers/map.c161
-rw-r--r--tools/perf/util/ui/browsers/map.h6
-rw-r--r--tools/perf/util/ui/helpline.c69
-rw-r--r--tools/perf/util/ui/helpline.h11
-rw-r--r--tools/perf/util/ui/libslang.h27
-rw-r--r--tools/perf/util/ui/progress.c60
-rw-r--r--tools/perf/util/ui/progress.h11
-rw-r--r--tools/perf/util/ui/setup.c42
-rw-r--r--tools/perf/util/ui/util.c114
-rw-r--r--tools/perf/util/ui/util.h10
33 files changed, 1705 insertions, 1091 deletions
diff --git a/arch/x86/kernel/cpu/perf_event_p4.c b/arch/x86/kernel/cpu/perf_event_p4.c
index 107711bf0ee8..febb12cea795 100644
--- a/arch/x86/kernel/cpu/perf_event_p4.c
+++ b/arch/x86/kernel/cpu/perf_event_p4.c
@@ -656,6 +656,7 @@ static int p4_pmu_handle_irq(struct pt_regs *regs)
656 cpuc = &__get_cpu_var(cpu_hw_events); 656 cpuc = &__get_cpu_var(cpu_hw_events);
657 657
658 for (idx = 0; idx < x86_pmu.num_counters; idx++) { 658 for (idx = 0; idx < x86_pmu.num_counters; idx++) {
659 int overflow;
659 660
660 if (!test_bit(idx, cpuc->active_mask)) 661 if (!test_bit(idx, cpuc->active_mask))
661 continue; 662 continue;
@@ -666,12 +667,14 @@ static int p4_pmu_handle_irq(struct pt_regs *regs)
666 WARN_ON_ONCE(hwc->idx != idx); 667 WARN_ON_ONCE(hwc->idx != idx);
667 668
668 /* it might be unflagged overflow */ 669 /* it might be unflagged overflow */
669 handled = p4_pmu_clear_cccr_ovf(hwc); 670 overflow = p4_pmu_clear_cccr_ovf(hwc);
670 671
671 val = x86_perf_event_update(event); 672 val = x86_perf_event_update(event);
672 if (!handled && (val & (1ULL << (x86_pmu.cntval_bits - 1)))) 673 if (!overflow && (val & (1ULL << (x86_pmu.cntval_bits - 1))))
673 continue; 674 continue;
674 675
676 handled += overflow;
677
675 /* event overflow for sure */ 678 /* event overflow for sure */
676 data.period = event->hw.last_period; 679 data.period = event->hw.last_period;
677 680
@@ -687,7 +690,7 @@ static int p4_pmu_handle_irq(struct pt_regs *regs)
687 inc_irq_stat(apic_perf_irqs); 690 inc_irq_stat(apic_perf_irqs);
688 } 691 }
689 692
690 return handled; 693 return handled > 0;
691} 694}
692 695
693/* 696/*
diff --git a/arch/x86/oprofile/nmi_int.c b/arch/x86/oprofile/nmi_int.c
index 1ba67dc8006a..f6b48f6c5951 100644
--- a/arch/x86/oprofile/nmi_int.c
+++ b/arch/x86/oprofile/nmi_int.c
@@ -668,6 +668,7 @@ static int __init ppro_init(char **cpu_type)
668 *cpu_type = "i386/core_2"; 668 *cpu_type = "i386/core_2";
669 break; 669 break;
670 case 0x1a: 670 case 0x1a:
671 case 0x1e:
671 case 0x2e: 672 case 0x2e:
672 spec = &op_arch_perfmon_spec; 673 spec = &op_arch_perfmon_spec;
673 *cpu_type = "i386/core_i7"; 674 *cpu_type = "i386/core_i7";
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 26f626d45a9e..41abb90df50d 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -157,9 +157,8 @@ all::
157# 157#
158# Define NO_DWARF if you do not want debug-info analysis feature at all. 158# Define NO_DWARF if you do not want debug-info analysis feature at all.
159 159
160$(shell sh -c 'mkdir -p $(OUTPUT)scripts/python/Perf-Trace-Util/' 2> /dev/null) 160$(shell sh -c 'mkdir -p $(OUTPUT)scripts/{perl,python}/Perf-Trace-Util/' 2> /dev/null)
161$(shell sh -c 'mkdir -p $(OUTPUT)scripts/perl/Perf-Trace-Util/' 2> /dev/null) 161$(shell sh -c 'mkdir -p $(OUTPUT)util/{ui/browsers,scripting-engines}/' 2> /dev/null)
162$(shell sh -c 'mkdir -p $(OUTPUT)util/scripting-engines/' 2> /dev/null)
163$(shell sh -c 'mkdir $(OUTPUT)bench' 2> /dev/null) 162$(shell sh -c 'mkdir $(OUTPUT)bench' 2> /dev/null)
164 163
165$(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE 164$(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE
@@ -568,7 +567,20 @@ else
568 # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h 567 # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h
569 BASIC_CFLAGS += -I/usr/include/slang 568 BASIC_CFLAGS += -I/usr/include/slang
570 EXTLIBS += -lnewt -lslang 569 EXTLIBS += -lnewt -lslang
571 LIB_OBJS += $(OUTPUT)util/newt.o 570 LIB_OBJS += $(OUTPUT)util/ui/setup.o
571 LIB_OBJS += $(OUTPUT)util/ui/browser.o
572 LIB_OBJS += $(OUTPUT)util/ui/browsers/annotate.o
573 LIB_OBJS += $(OUTPUT)util/ui/browsers/hists.o
574 LIB_OBJS += $(OUTPUT)util/ui/browsers/map.o
575 LIB_OBJS += $(OUTPUT)util/ui/helpline.o
576 LIB_OBJS += $(OUTPUT)util/ui/progress.o
577 LIB_OBJS += $(OUTPUT)util/ui/util.o
578 LIB_H += util/ui/browser.h
579 LIB_H += util/ui/browsers/map.h
580 LIB_H += util/ui/helpline.h
581 LIB_H += util/ui/libslang.h
582 LIB_H += util/ui/progress.h
583 LIB_H += util/ui/util.h
572 endif 584 endif
573endif 585endif
574 586
@@ -966,7 +978,16 @@ $(OUTPUT)builtin-init-db.o: builtin-init-db.c $(OUTPUT)PERF-CFLAGS
966$(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS 978$(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS
967 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< 979 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<
968 980
969$(OUTPUT)util/newt.o: util/newt.c $(OUTPUT)PERF-CFLAGS 981$(OUTPUT)util/ui/browser.o: util/ui/browser.c $(OUTPUT)PERF-CFLAGS
982 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
983
984$(OUTPUT)util/ui/browsers/annotate.o: util/ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS
985 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
986
987$(OUTPUT)util/ui/browsers/hists.o: util/ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS
988 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
989
990$(OUTPUT)util/ui/browsers/map.o: util/ui/browsers/map.c $(OUTPUT)PERF-CFLAGS
970 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< 991 $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $<
971 992
972$(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS 993$(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index fd20670ce986..1478dc64bf15 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -285,7 +285,7 @@ static int hist_entry__tty_annotate(struct hist_entry *he)
285 LIST_HEAD(head); 285 LIST_HEAD(head);
286 struct objdump_line *pos, *n; 286 struct objdump_line *pos, *n;
287 287
288 if (hist_entry__annotate(he, &head) < 0) 288 if (hist_entry__annotate(he, &head, 0) < 0)
289 return -1; 289 return -1;
290 290
291 if (full_paths) 291 if (full_paths)
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 2f4b92925b26..55fc1f46892a 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -348,7 +348,18 @@ static int __cmd_report(void)
348 hists__tty_browse_tree(&session->hists_tree, help); 348 hists__tty_browse_tree(&session->hists_tree, help);
349 349
350out_delete: 350out_delete:
351 perf_session__delete(session); 351 /*
352 * Speed up the exit process, for large files this can
353 * take quite a while.
354 *
355 * XXX Enable this when using valgrind or if we ever
356 * librarize this command.
357 *
358 * Also experiment with obstacks to see how much speed
359 * up we'll get here.
360 *
361 * perf_session__delete(session);
362 */
352 return ret; 363 return ret;
353} 364}
354 365
@@ -478,8 +489,24 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
478 * so don't allocate extra space that won't be used in the stdio 489 * so don't allocate extra space that won't be used in the stdio
479 * implementation. 490 * implementation.
480 */ 491 */
481 if (use_browser > 0) 492 if (use_browser > 0) {
482 symbol_conf.priv_size = sizeof(struct sym_priv); 493 symbol_conf.priv_size = sizeof(struct sym_priv);
494 /*
495 * For searching by name on the "Browse map details".
496 * providing it only in verbose mode not to bloat too
497 * much struct symbol.
498 */
499 if (verbose) {
500 /*
501 * XXX: Need to provide a less kludgy way to ask for
502 * more space per symbol, the u32 is for the index on
503 * the ui browser.
504 * See symbol__browser_index.
505 */
506 symbol_conf.priv_size += sizeof(u32);
507 symbol_conf.sort_by_name = true;
508 }
509 }
483 510
484 if (symbol__init() < 0) 511 if (symbol__init() < 0)
485 return -1; 512 return -1;
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index 5161619d4714..9bcc38f0b706 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -455,8 +455,8 @@ static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te)
455 if (p->current->state != TYPE_NONE) 455 if (p->current->state != TYPE_NONE)
456 pid_put_sample(sw->next_pid, p->current->state, cpu, p->current->state_since, timestamp); 456 pid_put_sample(sw->next_pid, p->current->state, cpu, p->current->state_since, timestamp);
457 457
458 p->current->state_since = timestamp; 458 p->current->state_since = timestamp;
459 p->current->state = TYPE_RUNNING; 459 p->current->state = TYPE_RUNNING;
460 } 460 }
461 461
462 if (prev_p->current) { 462 if (prev_p->current) {
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 294da725a57d..40a6a2992d15 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -1,13 +1,16 @@
1#include "builtin.h" 1#include "builtin.h"
2 2
3#include "util/util.h" 3#include "perf.h"
4#include "util/cache.h" 4#include "util/cache.h"
5#include "util/debug.h"
6#include "util/exec_cmd.h"
7#include "util/header.h"
8#include "util/parse-options.h"
9#include "util/session.h"
5#include "util/symbol.h" 10#include "util/symbol.h"
6#include "util/thread.h" 11#include "util/thread.h"
7#include "util/header.h"
8#include "util/exec_cmd.h"
9#include "util/trace-event.h" 12#include "util/trace-event.h"
10#include "util/session.h" 13#include "util/util.h"
11 14
12static char const *script_name; 15static char const *script_name;
13static char const *generate_script_lang; 16static char const *generate_script_lang;
@@ -59,14 +62,6 @@ static int cleanup_scripting(void)
59 return scripting_ops->stop_script(); 62 return scripting_ops->stop_script();
60} 63}
61 64
62#include "util/parse-options.h"
63
64#include "perf.h"
65#include "util/debug.h"
66
67#include "util/trace-event.h"
68#include "util/exec_cmd.h"
69
70static char const *input_name = "perf.data"; 65static char const *input_name = "perf.data";
71 66
72static int process_sample_event(event_t *event, struct perf_session *session) 67static int process_sample_event(event_t *event, struct perf_session *session)
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c
index 318dab15d177..f9c7e3ad1aa7 100644
--- a/tools/perf/util/debug.c
+++ b/tools/perf/util/debug.c
@@ -23,7 +23,7 @@ int eprintf(int level, const char *fmt, ...)
23 if (verbose >= level) { 23 if (verbose >= level) {
24 va_start(args, fmt); 24 va_start(args, fmt);
25 if (use_browser > 0) 25 if (use_browser > 0)
26 ret = browser__show_help(fmt, args); 26 ret = ui_helpline__show_help(fmt, args);
27 else 27 else
28 ret = vfprintf(stderr, fmt, args); 28 ret = vfprintf(stderr, fmt, args);
29 va_end(args); 29 va_end(args);
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h
index 047ac3324ebe..7a17ee061bcb 100644
--- a/tools/perf/util/debug.h
+++ b/tools/perf/util/debug.h
@@ -14,7 +14,7 @@ void trace_event(event_t *event);
14struct ui_progress; 14struct ui_progress;
15 15
16#ifdef NO_NEWT_SUPPORT 16#ifdef NO_NEWT_SUPPORT
17static inline int browser__show_help(const char *format __used, va_list ap __used) 17static inline int ui_helpline__show_help(const char *format __used, va_list ap __used)
18{ 18{
19 return 0; 19 return 0;
20} 20}
@@ -30,10 +30,9 @@ static inline void ui_progress__update(struct ui_progress *self __used,
30 30
31static inline void ui_progress__delete(struct ui_progress *self __used) {} 31static inline void ui_progress__delete(struct ui_progress *self __used) {}
32#else 32#else
33int browser__show_help(const char *format, va_list ap); 33extern char ui_helpline__last_msg[];
34struct ui_progress *ui_progress__new(const char *title, u64 total); 34int ui_helpline__show_help(const char *format, va_list ap);
35void ui_progress__update(struct ui_progress *self, u64 curr); 35#include "ui/progress.h"
36void ui_progress__delete(struct ui_progress *self);
37#endif 36#endif
38 37
39#endif /* __PERF_DEBUG_H */ 38#endif /* __PERF_DEBUG_H */
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index e7263d49bcf0..be22ae6ef055 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -876,6 +876,9 @@ unsigned int hists__sort_list_width(struct hists *self)
876 if (!se->elide) 876 if (!se->elide)
877 ret += 2 + hists__col_len(self, se->se_width_idx); 877 ret += 2 + hists__col_len(self, se->se_width_idx);
878 878
879 if (verbose) /* Addr + origin */
880 ret += 3 + BITS_PER_LONG / 4;
881
879 return ret; 882 return ret;
880} 883}
881 884
@@ -980,9 +983,9 @@ int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip)
980 return 0; 983 return 0;
981} 984}
982 985
983static struct objdump_line *objdump_line__new(s64 offset, char *line) 986static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize)
984{ 987{
985 struct objdump_line *self = malloc(sizeof(*self)); 988 struct objdump_line *self = malloc(sizeof(*self) + privsize);
986 989
987 if (self != NULL) { 990 if (self != NULL) {
988 self->offset = offset; 991 self->offset = offset;
@@ -1014,7 +1017,7 @@ struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
1014} 1017}
1015 1018
1016static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file, 1019static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file,
1017 struct list_head *head) 1020 struct list_head *head, size_t privsize)
1018{ 1021{
1019 struct symbol *sym = self->ms.sym; 1022 struct symbol *sym = self->ms.sym;
1020 struct objdump_line *objdump_line; 1023 struct objdump_line *objdump_line;
@@ -1065,7 +1068,7 @@ static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file,
1065 offset = -1; 1068 offset = -1;
1066 } 1069 }
1067 1070
1068 objdump_line = objdump_line__new(offset, line); 1071 objdump_line = objdump_line__new(offset, line, privsize);
1069 if (objdump_line == NULL) { 1072 if (objdump_line == NULL) {
1070 free(line); 1073 free(line);
1071 return -1; 1074 return -1;
@@ -1075,7 +1078,8 @@ static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file,
1075 return 0; 1078 return 0;
1076} 1079}
1077 1080
1078int hist_entry__annotate(struct hist_entry *self, struct list_head *head) 1081int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
1082 size_t privsize)
1079{ 1083{
1080 struct symbol *sym = self->ms.sym; 1084 struct symbol *sym = self->ms.sym;
1081 struct map *map = self->ms.map; 1085 struct map *map = self->ms.map;
@@ -1140,7 +1144,7 @@ fallback:
1140 goto out_free_filename; 1144 goto out_free_filename;
1141 1145
1142 while (!feof(file)) 1146 while (!feof(file))
1143 if (hist_entry__parse_objdump_line(self, file, head) < 0) 1147 if (hist_entry__parse_objdump_line(self, file, head, privsize) < 0)
1144 break; 1148 break;
1145 1149
1146 pclose(file); 1150 pclose(file);
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 65a48db46a29..587d375d3430 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -101,7 +101,8 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
101 bool show_displacement, FILE *fp); 101 bool show_displacement, FILE *fp);
102 102
103int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip); 103int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip);
104int hist_entry__annotate(struct hist_entry *self, struct list_head *head); 104int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
105 size_t privsize);
105 106
106void hists__filter_by_dso(struct hists *self, const struct dso *dso); 107void hists__filter_by_dso(struct hists *self, const struct dso *dso);
107void hists__filter_by_thread(struct hists *self, const struct thread *thread); 108void hists__filter_by_thread(struct hists *self, const struct thread *thread);
diff --git a/tools/perf/util/include/linux/list.h b/tools/perf/util/include/linux/list.h
index dbe4b814382a..f5ca26e53fbb 100644
--- a/tools/perf/util/include/linux/list.h
+++ b/tools/perf/util/include/linux/list.h
@@ -15,4 +15,12 @@ static inline void list_del_range(struct list_head *begin,
15 begin->prev->next = end->next; 15 begin->prev->next = end->next;
16 end->next->prev = begin->prev; 16 end->next->prev = begin->prev;
17} 17}
18
19/**
20 * list_for_each_from - iterate over a list from one of its nodes
21 * @pos: the &struct list_head to use as a loop cursor, from where to start
22 * @head: the head for your list.
23 */
24#define list_for_each_from(pos, head) \
25 for (; prefetch(pos->next), pos != (head); pos = pos->next)
18#endif 26#endif
diff --git a/tools/perf/util/include/linux/types.h b/tools/perf/util/include/linux/types.h
index 196862a81a21..12de3b8112f9 100644
--- a/tools/perf/util/include/linux/types.h
+++ b/tools/perf/util/include/linux/types.h
@@ -6,4 +6,16 @@
6#define DECLARE_BITMAP(name,bits) \ 6#define DECLARE_BITMAP(name,bits) \
7 unsigned long name[BITS_TO_LONGS(bits)] 7 unsigned long name[BITS_TO_LONGS(bits)]
8 8
9struct list_head {
10 struct list_head *next, *prev;
11};
12
13struct hlist_head {
14 struct hlist_node *first;
15};
16
17struct hlist_node {
18 struct hlist_node *next, **pprev;
19};
20
9#endif 21#endif
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 2e665cb84055..e72f05c3bef0 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -1606,8 +1606,10 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
1606 1606
1607 /* Init vmlinux path */ 1607 /* Init vmlinux path */
1608 ret = init_vmlinux(); 1608 ret = init_vmlinux();
1609 if (ret < 0) 1609 if (ret < 0) {
1610 free(pkgs);
1610 return ret; 1611 return ret;
1612 }
1611 1613
1612 /* Loop 1: convert all events */ 1614 /* Loop 1: convert all events */
1613 for (i = 0; i < npevs; i++) { 1615 for (i = 0; i < npevs; i++) {
@@ -1625,10 +1627,13 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
1625 ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs, 1627 ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs,
1626 pkgs[i].ntevs, force_add); 1628 pkgs[i].ntevs, force_add);
1627end: 1629end:
1628 /* Loop 3: cleanup trace events */ 1630 /* Loop 3: cleanup and free trace events */
1629 for (i = 0; i < npevs; i++) 1631 for (i = 0; i < npevs; i++) {
1630 for (j = 0; j < pkgs[i].ntevs; j++) 1632 for (j = 0; j < pkgs[i].ntevs; j++)
1631 clear_probe_trace_event(&pkgs[i].tevs[j]); 1633 clear_probe_trace_event(&pkgs[i].tevs[j]);
1634 free(pkgs[i].tevs);
1635 }
1636 free(pkgs);
1632 1637
1633 return ret; 1638 return ret;
1634} 1639}
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 840f1aabbb74..525136684d4e 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -33,7 +33,6 @@
33#include <ctype.h> 33#include <ctype.h>
34#include <dwarf-regs.h> 34#include <dwarf-regs.h>
35 35
36#include "string.h"
37#include "event.h" 36#include "event.h"
38#include "debug.h" 37#include "debug.h"
39#include "util.h" 38#include "util.h"
@@ -706,8 +705,12 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
706 pf->tvar->value = strdup(pf->pvar->var); 705 pf->tvar->value = strdup(pf->pvar->var);
707 if (pf->tvar->value == NULL) 706 if (pf->tvar->value == NULL)
708 return -ENOMEM; 707 return -ENOMEM;
709 else 708 if (pf->pvar->type) {
710 return 0; 709 pf->tvar->type = strdup(pf->pvar->type);
710 if (pf->tvar->type == NULL)
711 return -ENOMEM;
712 }
713 return 0;
711 } 714 }
712 715
713 pr_debug("Searching '%s' variable in context.\n", 716 pr_debug("Searching '%s' variable in context.\n",
diff --git a/tools/perf/util/pstack.h b/tools/perf/util/pstack.h
index 5ad07023504b..4cedea59f518 100644
--- a/tools/perf/util/pstack.h
+++ b/tools/perf/util/pstack.h
@@ -1,6 +1,8 @@
1#ifndef _PERF_PSTACK_ 1#ifndef _PERF_PSTACK_
2#define _PERF_PSTACK_ 2#define _PERF_PSTACK_
3 3
4#include <stdbool.h>
5
4struct pstack; 6struct pstack;
5struct pstack *pstack__new(unsigned short max_nr_entries); 7struct pstack *pstack__new(unsigned short max_nr_entries);
6void pstack__delete(struct pstack *self); 8void pstack__delete(struct pstack *self);
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 1c61a4f4aa8a..b62a553cc67d 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -196,7 +196,8 @@ static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
196 196
197 if (verbose) { 197 if (verbose) {
198 char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!'; 198 char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!';
199 ret += repsep_snprintf(bf, size, "%#018llx %c ", self->ip, o); 199 ret += repsep_snprintf(bf, size, "%*Lx %c ",
200 BITS_PER_LONG / 4, self->ip, o);
200 } 201 }
201 202
202 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level); 203 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level);
@@ -204,7 +205,8 @@ static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
204 ret += repsep_snprintf(bf + ret, size - ret, "%s", 205 ret += repsep_snprintf(bf + ret, size - ret, "%s",
205 self->ms.sym->name); 206 self->ms.sym->name);
206 else 207 else
207 ret += repsep_snprintf(bf + ret, size - ret, "%#016llx", self->ip); 208 ret += repsep_snprintf(bf + ret, size - ret, "%*Lx",
209 BITS_PER_LONG / 4, self->ip);
208 210
209 return ret; 211 return ret;
210} 212}
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 6f0dd90c36ce..1a367734e016 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -131,7 +131,8 @@ static void map_groups__fixup_end(struct map_groups *self)
131 __map_groups__fixup_end(self, i); 131 __map_groups__fixup_end(self, i);
132} 132}
133 133
134static struct symbol *symbol__new(u64 start, u64 len, const char *name) 134static struct symbol *symbol__new(u64 start, u64 len, u8 binding,
135 const char *name)
135{ 136{
136 size_t namelen = strlen(name) + 1; 137 size_t namelen = strlen(name) + 1;
137 struct symbol *self = calloc(1, (symbol_conf.priv_size + 138 struct symbol *self = calloc(1, (symbol_conf.priv_size +
@@ -144,6 +145,7 @@ static struct symbol *symbol__new(u64 start, u64 len, const char *name)
144 145
145 self->start = start; 146 self->start = start;
146 self->end = len ? start + len - 1 : start; 147 self->end = len ? start + len - 1 : start;
148 self->binding = binding;
147 self->namelen = namelen - 1; 149 self->namelen = namelen - 1;
148 150
149 pr_debug4("%s: %s %#Lx-%#Lx\n", __func__, name, start, self->end); 151 pr_debug4("%s: %s %#Lx-%#Lx\n", __func__, name, start, self->end);
@@ -160,8 +162,11 @@ void symbol__delete(struct symbol *self)
160 162
161static size_t symbol__fprintf(struct symbol *self, FILE *fp) 163static size_t symbol__fprintf(struct symbol *self, FILE *fp)
162{ 164{
163 return fprintf(fp, " %llx-%llx %s\n", 165 return fprintf(fp, " %llx-%llx %c %s\n",
164 self->start, self->end, self->name); 166 self->start, self->end,
167 self->binding == STB_GLOBAL ? 'g' :
168 self->binding == STB_LOCAL ? 'l' : 'w',
169 self->name);
165} 170}
166 171
167void dso__set_long_name(struct dso *self, char *name) 172void dso__set_long_name(struct dso *self, char *name)
@@ -453,6 +458,14 @@ struct process_kallsyms_args {
453 struct dso *dso; 458 struct dso *dso;
454}; 459};
455 460
461static u8 kallsyms2elf_type(char type)
462{
463 if (type == 'W')
464 return STB_WEAK;
465
466 return isupper(type) ? STB_GLOBAL : STB_LOCAL;
467}
468
456static int map__process_kallsym_symbol(void *arg, const char *name, 469static int map__process_kallsym_symbol(void *arg, const char *name,
457 char type, u64 start) 470 char type, u64 start)
458{ 471{
@@ -466,7 +479,7 @@ static int map__process_kallsym_symbol(void *arg, const char *name,
466 /* 479 /*
467 * Will fix up the end later, when we have all symbols sorted. 480 * Will fix up the end later, when we have all symbols sorted.
468 */ 481 */
469 sym = symbol__new(start, 0, name); 482 sym = symbol__new(start, 0, kallsyms2elf_type(type), name);
470 483
471 if (sym == NULL) 484 if (sym == NULL)
472 return -ENOMEM; 485 return -ENOMEM;
@@ -661,7 +674,7 @@ static int dso__load_perf_map(struct dso *self, struct map *map,
661 if (len + 2 >= line_len) 674 if (len + 2 >= line_len)
662 continue; 675 continue;
663 676
664 sym = symbol__new(start, size, line + len); 677 sym = symbol__new(start, size, STB_GLOBAL, line + len);
665 678
666 if (sym == NULL) 679 if (sym == NULL)
667 goto out_delete_line; 680 goto out_delete_line;
@@ -873,7 +886,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, struct map *map,
873 "%s@plt", elf_sym__name(&sym, symstrs)); 886 "%s@plt", elf_sym__name(&sym, symstrs));
874 887
875 f = symbol__new(plt_offset, shdr_plt.sh_entsize, 888 f = symbol__new(plt_offset, shdr_plt.sh_entsize,
876 sympltname); 889 STB_GLOBAL, sympltname);
877 if (!f) 890 if (!f)
878 goto out_elf_end; 891 goto out_elf_end;
879 892
@@ -895,7 +908,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, struct map *map,
895 "%s@plt", elf_sym__name(&sym, symstrs)); 908 "%s@plt", elf_sym__name(&sym, symstrs));
896 909
897 f = symbol__new(plt_offset, shdr_plt.sh_entsize, 910 f = symbol__new(plt_offset, shdr_plt.sh_entsize,
898 sympltname); 911 STB_GLOBAL, sympltname);
899 if (!f) 912 if (!f)
900 goto out_elf_end; 913 goto out_elf_end;
901 914
@@ -1066,6 +1079,16 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name,
1066 if (!is_label && !elf_sym__is_a(&sym, map->type)) 1079 if (!is_label && !elf_sym__is_a(&sym, map->type))
1067 continue; 1080 continue;
1068 1081
1082 /* Reject ARM ELF "mapping symbols": these aren't unique and
1083 * don't identify functions, so will confuse the profile
1084 * output: */
1085 if (ehdr.e_machine == EM_ARM) {
1086 if (!strcmp(elf_name, "$a") ||
1087 !strcmp(elf_name, "$d") ||
1088 !strcmp(elf_name, "$t"))
1089 continue;
1090 }
1091
1069 if (opdsec && sym.st_shndx == opdidx) { 1092 if (opdsec && sym.st_shndx == opdidx) {
1070 u32 offset = sym.st_value - opdshdr.sh_addr; 1093 u32 offset = sym.st_value - opdshdr.sh_addr;
1071 u64 *opd = opddata->d_buf + offset; 1094 u64 *opd = opddata->d_buf + offset;
@@ -1146,7 +1169,8 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name,
1146 if (demangled != NULL) 1169 if (demangled != NULL)
1147 elf_name = demangled; 1170 elf_name = demangled;
1148new_symbol: 1171new_symbol:
1149 f = symbol__new(sym.st_value, sym.st_size, elf_name); 1172 f = symbol__new(sym.st_value, sym.st_size,
1173 GELF_ST_BIND(sym.st_info), elf_name);
1150 free(demangled); 1174 free(demangled);
1151 if (!f) 1175 if (!f)
1152 goto out_elf_end; 1176 goto out_elf_end;
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 906be20011d9..b7a8da4af5a0 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -53,6 +53,7 @@ struct symbol {
53 u64 start; 53 u64 start;
54 u64 end; 54 u64 end;
55 u16 namelen; 55 u16 namelen;
56 u8 binding;
56 char name[0]; 57 char name[0];
57}; 58};
58 59
diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c
new file mode 100644
index 000000000000..66f2d583d8c4
--- /dev/null
+++ b/tools/perf/util/ui/browser.c
@@ -0,0 +1,329 @@
1#define _GNU_SOURCE
2#include <stdio.h>
3#undef _GNU_SOURCE
4/*
5 * slang versions <= 2.0.6 have a "#if HAVE_LONG_LONG" that breaks
6 * the build if it isn't defined. Use the equivalent one that glibc
7 * has on features.h.
8 */
9#include <features.h>
10#ifndef HAVE_LONG_LONG
11#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG
12#endif
13#include <slang.h>
14#include <linux/list.h>
15#include <linux/rbtree.h>
16#include <stdlib.h>
17#include <sys/ttydefaults.h>
18#include "browser.h"
19#include "helpline.h"
20#include "../color.h"
21#include "../util.h"
22
23#if SLANG_VERSION < 20104
24#define sltt_set_color(obj, name, fg, bg) \
25 SLtt_set_color(obj,(char *)name, (char *)fg, (char *)bg)
26#else
27#define sltt_set_color SLtt_set_color
28#endif
29
30newtComponent newt_form__new(void);
31
32int ui_browser__percent_color(double percent, bool current)
33{
34 if (current)
35 return HE_COLORSET_SELECTED;
36 if (percent >= MIN_RED)
37 return HE_COLORSET_TOP;
38 if (percent >= MIN_GREEN)
39 return HE_COLORSET_MEDIUM;
40 return HE_COLORSET_NORMAL;
41}
42
43void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence)
44{
45 struct list_head *head = self->entries;
46 struct list_head *pos;
47
48 switch (whence) {
49 case SEEK_SET:
50 pos = head->next;
51 break;
52 case SEEK_CUR:
53 pos = self->top;
54 break;
55 case SEEK_END:
56 pos = head->prev;
57 break;
58 default:
59 return;
60 }
61
62 if (offset > 0) {
63 while (offset-- != 0)
64 pos = pos->next;
65 } else {
66 while (offset++ != 0)
67 pos = pos->prev;
68 }
69
70 self->top = pos;
71}
72
73void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence)
74{
75 struct rb_root *root = self->entries;
76 struct rb_node *nd;
77
78 switch (whence) {
79 case SEEK_SET:
80 nd = rb_first(root);
81 break;
82 case SEEK_CUR:
83 nd = self->top;
84 break;
85 case SEEK_END:
86 nd = rb_last(root);
87 break;
88 default:
89 return;
90 }
91
92 if (offset > 0) {
93 while (offset-- != 0)
94 nd = rb_next(nd);
95 } else {
96 while (offset++ != 0)
97 nd = rb_prev(nd);
98 }
99
100 self->top = nd;
101}
102
103unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self)
104{
105 struct rb_node *nd;
106 int row = 0;
107
108 if (self->top == NULL)
109 self->top = rb_first(self->entries);
110
111 nd = self->top;
112
113 while (nd != NULL) {
114 SLsmg_gotorc(self->y + row, self->x);
115 self->write(self, nd, row);
116 if (++row == self->height)
117 break;
118 nd = rb_next(nd);
119 }
120
121 return row;
122}
123
124bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row)
125{
126 return self->top_idx + row == self->index;
127}
128
129void ui_browser__refresh_dimensions(struct ui_browser *self)
130{
131 int cols, rows;
132 newtGetScreenSize(&cols, &rows);
133
134 if (self->width > cols - 4)
135 self->width = cols - 4;
136 self->height = rows - 5;
137 if (self->height > self->nr_entries)
138 self->height = self->nr_entries;
139 self->y = (rows - self->height) / 2;
140 self->x = (cols - self->width) / 2;
141}
142
143void ui_browser__reset_index(struct ui_browser *self)
144{
145 self->index = self->top_idx = 0;
146 self->seek(self, 0, SEEK_SET);
147}
148
149int ui_browser__show(struct ui_browser *self, const char *title,
150 const char *helpline, ...)
151{
152 va_list ap;
153
154 if (self->form != NULL) {
155 newtFormDestroy(self->form);
156 newtPopWindow();
157 }
158 ui_browser__refresh_dimensions(self);
159 newtCenteredWindow(self->width, self->height, title);
160 self->form = newt_form__new();
161 if (self->form == NULL)
162 return -1;
163
164 self->sb = newtVerticalScrollbar(self->width, 0, self->height,
165 HE_COLORSET_NORMAL,
166 HE_COLORSET_SELECTED);
167 if (self->sb == NULL)
168 return -1;
169
170 newtFormAddHotKey(self->form, NEWT_KEY_UP);
171 newtFormAddHotKey(self->form, NEWT_KEY_DOWN);
172 newtFormAddHotKey(self->form, NEWT_KEY_PGUP);
173 newtFormAddHotKey(self->form, NEWT_KEY_PGDN);
174 newtFormAddHotKey(self->form, NEWT_KEY_HOME);
175 newtFormAddHotKey(self->form, NEWT_KEY_END);
176 newtFormAddHotKey(self->form, ' ');
177 newtFormAddComponent(self->form, self->sb);
178
179 va_start(ap, helpline);
180 ui_helpline__vpush(helpline, ap);
181 va_end(ap);
182 return 0;
183}
184
185void ui_browser__hide(struct ui_browser *self)
186{
187 newtFormDestroy(self->form);
188 newtPopWindow();
189 self->form = NULL;
190 ui_helpline__pop();
191}
192
193int ui_browser__refresh(struct ui_browser *self)
194{
195 int row;
196
197 newtScrollbarSet(self->sb, self->index, self->nr_entries - 1);
198 row = self->refresh(self);
199 SLsmg_set_color(HE_COLORSET_NORMAL);
200 SLsmg_fill_region(self->y + row, self->x,
201 self->height - row, self->width, ' ');
202
203 return 0;
204}
205
206int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es)
207{
208 if (ui_browser__refresh(self) < 0)
209 return -1;
210
211 while (1) {
212 off_t offset;
213
214 newtFormRun(self->form, es);
215
216 if (es->reason != NEWT_EXIT_HOTKEY)
217 break;
218 if (is_exit_key(es->u.key))
219 return es->u.key;
220 switch (es->u.key) {
221 case NEWT_KEY_DOWN:
222 if (self->index == self->nr_entries - 1)
223 break;
224 ++self->index;
225 if (self->index == self->top_idx + self->height) {
226 ++self->top_idx;
227 self->seek(self, +1, SEEK_CUR);
228 }
229 break;
230 case NEWT_KEY_UP:
231 if (self->index == 0)
232 break;
233 --self->index;
234 if (self->index < self->top_idx) {
235 --self->top_idx;
236 self->seek(self, -1, SEEK_CUR);
237 }
238 break;
239 case NEWT_KEY_PGDN:
240 case ' ':
241 if (self->top_idx + self->height > self->nr_entries - 1)
242 break;
243
244 offset = self->height;
245 if (self->index + offset > self->nr_entries - 1)
246 offset = self->nr_entries - 1 - self->index;
247 self->index += offset;
248 self->top_idx += offset;
249 self->seek(self, +offset, SEEK_CUR);
250 break;
251 case NEWT_KEY_PGUP:
252 if (self->top_idx == 0)
253 break;
254
255 if (self->top_idx < self->height)
256 offset = self->top_idx;
257 else
258 offset = self->height;
259
260 self->index -= offset;
261 self->top_idx -= offset;
262 self->seek(self, -offset, SEEK_CUR);
263 break;
264 case NEWT_KEY_HOME:
265 ui_browser__reset_index(self);
266 break;
267 case NEWT_KEY_END:
268 offset = self->height - 1;
269 if (offset >= self->nr_entries)
270 offset = self->nr_entries - 1;
271
272 self->index = self->nr_entries - 1;
273 self->top_idx = self->index - offset;
274 self->seek(self, -offset, SEEK_END);
275 break;
276 default:
277 return es->u.key;
278 }
279 if (ui_browser__refresh(self) < 0)
280 return -1;
281 }
282 return 0;
283}
284
285unsigned int ui_browser__list_head_refresh(struct ui_browser *self)
286{
287 struct list_head *pos;
288 struct list_head *head = self->entries;
289 int row = 0;
290
291 if (self->top == NULL || self->top == self->entries)
292 self->top = head->next;
293
294 pos = self->top;
295
296 list_for_each_from(pos, head) {
297 SLsmg_gotorc(self->y + row, self->x);
298 self->write(self, pos, row);
299 if (++row == self->height)
300 break;
301 }
302
303 return row;
304}
305
306static struct newtPercentTreeColors {
307 const char *topColorFg, *topColorBg;
308 const char *mediumColorFg, *mediumColorBg;
309 const char *normalColorFg, *normalColorBg;
310 const char *selColorFg, *selColorBg;
311 const char *codeColorFg, *codeColorBg;
312} defaultPercentTreeColors = {
313 "red", "lightgray",
314 "green", "lightgray",
315 "black", "lightgray",
316 "lightgray", "magenta",
317 "blue", "lightgray",
318};
319
320void ui_browser__init(void)
321{
322 struct newtPercentTreeColors *c = &defaultPercentTreeColors;
323
324 sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg);
325 sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg);
326 sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg);
327 sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg);
328 sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg);
329}
diff --git a/tools/perf/util/ui/browser.h b/tools/perf/util/ui/browser.h
new file mode 100644
index 000000000000..0b9f829214f7
--- /dev/null
+++ b/tools/perf/util/ui/browser.h
@@ -0,0 +1,46 @@
1#ifndef _PERF_UI_BROWSER_H_
2#define _PERF_UI_BROWSER_H_ 1
3
4#include <stdbool.h>
5#include <newt.h>
6#include <sys/types.h>
7#include "../types.h"
8
9#define HE_COLORSET_TOP 50
10#define HE_COLORSET_MEDIUM 51
11#define HE_COLORSET_NORMAL 52
12#define HE_COLORSET_SELECTED 53
13#define HE_COLORSET_CODE 54
14
15struct ui_browser {
16 newtComponent form, sb;
17 u64 index, top_idx;
18 void *top, *entries;
19 u16 y, x, width, height;
20 void *priv;
21 unsigned int (*refresh)(struct ui_browser *self);
22 void (*write)(struct ui_browser *self, void *entry, int row);
23 void (*seek)(struct ui_browser *self, off_t offset, int whence);
24 u32 nr_entries;
25};
26
27
28int ui_browser__percent_color(double percent, bool current);
29bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row);
30void ui_browser__refresh_dimensions(struct ui_browser *self);
31void ui_browser__reset_index(struct ui_browser *self);
32
33int ui_browser__show(struct ui_browser *self, const char *title,
34 const char *helpline, ...);
35void ui_browser__hide(struct ui_browser *self);
36int ui_browser__refresh(struct ui_browser *self);
37int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es);
38
39void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence);
40unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self);
41
42void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence);
43unsigned int ui_browser__list_head_refresh(struct ui_browser *self);
44
45void ui_browser__init(void);
46#endif /* _PERF_UI_BROWSER_H_ */
diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c
new file mode 100644
index 000000000000..55ff792459ac
--- /dev/null
+++ b/tools/perf/util/ui/browsers/annotate.c
@@ -0,0 +1,240 @@
1#include "../browser.h"
2#include "../helpline.h"
3#include "../libslang.h"
4#include "../../hist.h"
5#include "../../sort.h"
6#include "../../symbol.h"
7
8static void ui__error_window(const char *fmt, ...)
9{
10 va_list ap;
11
12 va_start(ap, fmt);
13 newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap);
14 va_end(ap);
15}
16
17struct annotate_browser {
18 struct ui_browser b;
19 struct rb_root entries;
20 struct rb_node *curr_hot;
21};
22
23struct objdump_line_rb_node {
24 struct rb_node rb_node;
25 double percent;
26 u32 idx;
27};
28
29static inline
30struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self)
31{
32 return (struct objdump_line_rb_node *)(self + 1);
33}
34
35static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
36{
37 struct objdump_line *ol = rb_entry(entry, struct objdump_line, node);
38 bool current_entry = ui_browser__is_current_entry(self, row);
39 int width = self->width;
40
41 if (ol->offset != -1) {
42 struct objdump_line_rb_node *olrb = objdump_line__rb(ol);
43 int color = ui_browser__percent_color(olrb->percent, current_entry);
44 SLsmg_set_color(color);
45 slsmg_printf(" %7.2f ", olrb->percent);
46 if (!current_entry)
47 SLsmg_set_color(HE_COLORSET_CODE);
48 } else {
49 int color = ui_browser__percent_color(0, current_entry);
50 SLsmg_set_color(color);
51 slsmg_write_nstring(" ", 9);
52 }
53
54 SLsmg_write_char(':');
55 slsmg_write_nstring(" ", 8);
56 if (!*ol->line)
57 slsmg_write_nstring(" ", width - 18);
58 else
59 slsmg_write_nstring(ol->line, width - 18);
60}
61
62static double objdump_line__calc_percent(struct objdump_line *self,
63 struct list_head *head,
64 struct symbol *sym)
65{
66 double percent = 0.0;
67
68 if (self->offset != -1) {
69 int len = sym->end - sym->start;
70 unsigned int hits = 0;
71 struct sym_priv *priv = symbol__priv(sym);
72 struct sym_ext *sym_ext = priv->ext;
73 struct sym_hist *h = priv->hist;
74 s64 offset = self->offset;
75 struct objdump_line *next = objdump__get_next_ip_line(head, self);
76
77
78 while (offset < (s64)len &&
79 (next == NULL || offset < next->offset)) {
80 if (sym_ext) {
81 percent += sym_ext[offset].percent;
82 } else
83 hits += h->ip[offset];
84
85 ++offset;
86 }
87
88 if (sym_ext == NULL && h->sum)
89 percent = 100.0 * hits / h->sum;
90 }
91
92 return percent;
93}
94
95static void objdump__insert_line(struct rb_root *self,
96 struct objdump_line_rb_node *line)
97{
98 struct rb_node **p = &self->rb_node;
99 struct rb_node *parent = NULL;
100 struct objdump_line_rb_node *l;
101
102 while (*p != NULL) {
103 parent = *p;
104 l = rb_entry(parent, struct objdump_line_rb_node, rb_node);
105 if (line->percent < l->percent)
106 p = &(*p)->rb_left;
107 else
108 p = &(*p)->rb_right;
109 }
110 rb_link_node(&line->rb_node, parent, p);
111 rb_insert_color(&line->rb_node, self);
112}
113
114static void annotate_browser__set_top(struct annotate_browser *self,
115 struct rb_node *nd)
116{
117 struct objdump_line_rb_node *rbpos;
118 struct objdump_line *pos;
119 unsigned back;
120
121 ui_browser__refresh_dimensions(&self->b);
122 back = self->b.height / 2;
123 rbpos = rb_entry(nd, struct objdump_line_rb_node, rb_node);
124 pos = ((struct objdump_line *)rbpos) - 1;
125 self->b.top_idx = self->b.index = rbpos->idx;
126
127 while (self->b.top_idx != 0 && back != 0) {
128 pos = list_entry(pos->node.prev, struct objdump_line, node);
129
130 --self->b.top_idx;
131 --back;
132 }
133
134 self->b.top = pos;
135 self->curr_hot = nd;
136}
137
138static int annotate_browser__run(struct annotate_browser *self,
139 struct newtExitStruct *es)
140{
141 struct rb_node *nd;
142 struct hist_entry *he = self->b.priv;
143
144 if (ui_browser__show(&self->b, he->ms.sym->name,
145 "<- or ESC: exit, TAB/shift+TAB: cycle thru samples") < 0)
146 return -1;
147
148 newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
149
150 nd = self->curr_hot;
151 if (nd) {
152 newtFormAddHotKey(self->b.form, NEWT_KEY_TAB);
153 newtFormAddHotKey(self->b.form, NEWT_KEY_UNTAB);
154 }
155
156 while (1) {
157 ui_browser__run(&self->b, es);
158
159 if (es->reason != NEWT_EXIT_HOTKEY)
160 break;
161
162 switch (es->u.key) {
163 case NEWT_KEY_TAB:
164 nd = rb_prev(nd);
165 if (nd == NULL)
166 nd = rb_last(&self->entries);
167 annotate_browser__set_top(self, nd);
168 break;
169 case NEWT_KEY_UNTAB:
170 nd = rb_next(nd);
171 if (nd == NULL)
172 nd = rb_first(&self->entries);
173 annotate_browser__set_top(self, nd);
174 break;
175 default:
176 goto out;
177 }
178 }
179out:
180 ui_browser__hide(&self->b);
181 return 0;
182}
183
184int hist_entry__tui_annotate(struct hist_entry *self)
185{
186 struct newtExitStruct es;
187 struct objdump_line *pos, *n;
188 struct objdump_line_rb_node *rbpos;
189 LIST_HEAD(head);
190 struct annotate_browser browser = {
191 .b = {
192 .entries = &head,
193 .refresh = ui_browser__list_head_refresh,
194 .seek = ui_browser__list_head_seek,
195 .write = annotate_browser__write,
196 .priv = self,
197 },
198 };
199 int ret;
200
201 if (self->ms.sym == NULL)
202 return -1;
203
204 if (self->ms.map->dso->annotate_warned)
205 return -1;
206
207 if (hist_entry__annotate(self, &head, sizeof(*rbpos)) < 0) {
208 ui__error_window(ui_helpline__last_msg);
209 return -1;
210 }
211
212 ui_helpline__push("Press <- or ESC to exit");
213
214 list_for_each_entry(pos, &head, node) {
215 size_t line_len = strlen(pos->line);
216 if (browser.b.width < line_len)
217 browser.b.width = line_len;
218 rbpos = objdump_line__rb(pos);
219 rbpos->idx = browser.b.nr_entries++;
220 rbpos->percent = objdump_line__calc_percent(pos, &head, self->ms.sym);
221 if (rbpos->percent < 0.01)
222 continue;
223 objdump__insert_line(&browser.entries, rbpos);
224 }
225
226 /*
227 * Position the browser at the hottest line.
228 */
229 browser.curr_hot = rb_last(&browser.entries);
230 if (browser.curr_hot)
231 annotate_browser__set_top(&browser, browser.curr_hot);
232
233 browser.b.width += 18; /* Percentage */
234 ret = annotate_browser__run(&browser, &es);
235 list_for_each_entry_safe(pos, n, &head, node) {
236 list_del(&pos->node);
237 objdump_line__free(pos);
238 }
239 return ret;
240}
diff --git a/tools/perf/util/newt.c b/tools/perf/util/ui/browsers/hists.c
index 91de99b58445..dafdf6775d77 100644
--- a/tools/perf/util/newt.c
+++ b/tools/perf/util/ui/browsers/hists.c
@@ -1,978 +1,272 @@
1#define _GNU_SOURCE 1#define _GNU_SOURCE
2#include <stdio.h> 2#include <stdio.h>
3#undef _GNU_SOURCE 3#undef _GNU_SOURCE
4/* 4#include "../libslang.h"
5 * slang versions <= 2.0.6 have a "#if HAVE_LONG_LONG" that breaks
6 * the build if it isn't defined. Use the equivalent one that glibc
7 * has on features.h.
8 */
9#include <features.h>
10#ifndef HAVE_LONG_LONG
11#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG
12#endif
13#include <slang.h>
14#include <signal.h>
15#include <stdlib.h> 5#include <stdlib.h>
6#include <string.h>
16#include <newt.h> 7#include <newt.h>
17#include <sys/ttydefaults.h> 8#include <linux/rbtree.h>
18
19#include "cache.h"
20#include "hist.h"
21#include "pstack.h"
22#include "session.h"
23#include "sort.h"
24#include "symbol.h"
25
26#if SLANG_VERSION < 20104
27#define slsmg_printf(msg, args...) SLsmg_printf((char *)msg, ##args)
28#define slsmg_write_nstring(msg, len) SLsmg_write_nstring((char *)msg, len)
29#define sltt_set_color(obj, name, fg, bg) SLtt_set_color(obj,(char *)name,\
30 (char *)fg, (char *)bg)
31#else
32#define slsmg_printf SLsmg_printf
33#define slsmg_write_nstring SLsmg_write_nstring
34#define sltt_set_color SLtt_set_color
35#endif
36
37struct ui_progress {
38 newtComponent form, scale;
39};
40
41struct ui_progress *ui_progress__new(const char *title, u64 total)
42{
43 struct ui_progress *self = malloc(sizeof(*self));
44
45 if (self != NULL) {
46 int cols;
47
48 if (use_browser <= 0)
49 return self;
50 newtGetScreenSize(&cols, NULL);
51 cols -= 4;
52 newtCenteredWindow(cols, 1, title);
53 self->form = newtForm(NULL, NULL, 0);
54 if (self->form == NULL)
55 goto out_free_self;
56 self->scale = newtScale(0, 0, cols, total);
57 if (self->scale == NULL)
58 goto out_free_form;
59 newtFormAddComponent(self->form, self->scale);
60 newtRefresh();
61 }
62
63 return self;
64
65out_free_form:
66 newtFormDestroy(self->form);
67out_free_self:
68 free(self);
69 return NULL;
70}
71
72void ui_progress__update(struct ui_progress *self, u64 curr)
73{
74 /*
75 * FIXME: We should have a per UI backend way of showing progress,
76 * stdio will just show a percentage as NN%, etc.
77 */
78 if (use_browser <= 0)
79 return;
80 newtScaleSet(self->scale, curr);
81 newtRefresh();
82}
83 9
84void ui_progress__delete(struct ui_progress *self) 10#include "../../hist.h"
85{ 11#include "../../pstack.h"
86 if (use_browser > 0) { 12#include "../../sort.h"
87 newtFormDestroy(self->form); 13#include "../../util.h"
88 newtPopWindow();
89 }
90 free(self);
91}
92 14
93static void ui_helpline__pop(void) 15#include "../browser.h"
94{ 16#include "../helpline.h"
95 newtPopHelpLine(); 17#include "../util.h"
96} 18#include "map.h"
97 19
98static void ui_helpline__push(const char *msg) 20struct hist_browser {
99{ 21 struct ui_browser b;
100 newtPushHelpLine(msg); 22 struct hists *hists;
101} 23 struct hist_entry *he_selection;
24 struct map_symbol *selection;
25};
102 26
103static void ui_helpline__vpush(const char *fmt, va_list ap) 27static void hist_browser__refresh_dimensions(struct hist_browser *self)
104{ 28{
105 char *s; 29 /* 3 == +/- toggle symbol before actual hist_entry rendering */
106 30 self->b.width = 3 + (hists__sort_list_width(self->hists) +
107 if (vasprintf(&s, fmt, ap) < 0) 31 sizeof("[k]"));
108 vfprintf(stderr, fmt, ap);
109 else {
110 ui_helpline__push(s);
111 free(s);
112 }
113} 32}
114 33
115static void ui_helpline__fpush(const char *fmt, ...) 34static void hist_browser__reset(struct hist_browser *self)
116{ 35{
117 va_list ap; 36 self->b.nr_entries = self->hists->nr_entries;
118 37 hist_browser__refresh_dimensions(self);
119 va_start(ap, fmt); 38 ui_browser__reset_index(&self->b);
120 ui_helpline__vpush(fmt, ap);
121 va_end(ap);
122} 39}
123 40
124static void ui_helpline__puts(const char *msg) 41static char tree__folded_sign(bool unfolded)
125{ 42{
126 ui_helpline__pop(); 43 return unfolded ? '-' : '+';
127 ui_helpline__push(msg);
128} 44}
129 45
130static char browser__last_msg[1024]; 46static char map_symbol__folded(const struct map_symbol *self)
131
132int browser__show_help(const char *format, va_list ap)
133{ 47{
134 int ret; 48 return self->has_children ? tree__folded_sign(self->unfolded) : ' ';
135 static int backlog;
136
137 ret = vsnprintf(browser__last_msg + backlog,
138 sizeof(browser__last_msg) - backlog, format, ap);
139 backlog += ret;
140
141 if (browser__last_msg[backlog - 1] == '\n') {
142 ui_helpline__puts(browser__last_msg);
143 newtRefresh();
144 backlog = 0;
145 }
146
147 return ret;
148} 49}
149 50
150static void newt_form__set_exit_keys(newtComponent self) 51static char hist_entry__folded(const struct hist_entry *self)
151{ 52{
152 newtFormAddHotKey(self, NEWT_KEY_LEFT); 53 return map_symbol__folded(&self->ms);
153 newtFormAddHotKey(self, NEWT_KEY_ESCAPE);
154 newtFormAddHotKey(self, 'Q');
155 newtFormAddHotKey(self, 'q');
156 newtFormAddHotKey(self, CTRL('c'));
157} 54}
158 55
159static newtComponent newt_form__new(void) 56static char callchain_list__folded(const struct callchain_list *self)
160{ 57{
161 newtComponent self = newtForm(NULL, NULL, 0); 58 return map_symbol__folded(&self->ms);
162 if (self)
163 newt_form__set_exit_keys(self);
164 return self;
165} 59}
166 60
167static int popup_menu(int argc, char * const argv[]) 61static int callchain_node__count_rows_rb_tree(struct callchain_node *self)
168{ 62{
169 struct newtExitStruct es; 63 int n = 0;
170 int i, rc = -1, max_len = 5; 64 struct rb_node *nd;
171 newtComponent listbox, form = newt_form__new();
172
173 if (form == NULL)
174 return -1;
175 65
176 listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT); 66 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
177 if (listbox == NULL) 67 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
178 goto out_destroy_form; 68 struct callchain_list *chain;
69 char folded_sign = ' '; /* No children */
179 70
180 newtFormAddComponent(form, listbox); 71 list_for_each_entry(chain, &child->val, list) {
72 ++n;
73 /* We need this because we may not have children */
74 folded_sign = callchain_list__folded(chain);
75 if (folded_sign == '+')
76 break;
77 }
181 78
182 for (i = 0; i < argc; ++i) { 79 if (folded_sign == '-') /* Have children and they're unfolded */
183 int len = strlen(argv[i]); 80 n += callchain_node__count_rows_rb_tree(child);
184 if (len > max_len)
185 max_len = len;
186 if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i))
187 goto out_destroy_form;
188 } 81 }
189 82
190 newtCenteredWindow(max_len, argc, NULL); 83 return n;
191 newtFormRun(form, &es);
192 rc = newtListboxGetCurrent(listbox) - NULL;
193 if (es.reason == NEWT_EXIT_HOTKEY)
194 rc = -1;
195 newtPopWindow();
196out_destroy_form:
197 newtFormDestroy(form);
198 return rc;
199} 84}
200 85
201static int ui__help_window(const char *text) 86static int callchain_node__count_rows(struct callchain_node *node)
202{ 87{
203 struct newtExitStruct es; 88 struct callchain_list *chain;
204 newtComponent tb, form = newt_form__new(); 89 bool unfolded = false;
205 int rc = -1; 90 int n = 0;
206 int max_len = 0, nr_lines = 0;
207 const char *t;
208
209 if (form == NULL)
210 return -1;
211 91
212 t = text; 92 list_for_each_entry(chain, &node->val, list) {
213 while (1) { 93 ++n;
214 const char *sep = strchr(t, '\n'); 94 unfolded = chain->ms.unfolded;
215 int len;
216
217 if (sep == NULL)
218 sep = strchr(t, '\0');
219 len = sep - t;
220 if (max_len < len)
221 max_len = len;
222 ++nr_lines;
223 if (*sep == '\0')
224 break;
225 t = sep + 1;
226 } 95 }
227 96
228 tb = newtTextbox(0, 0, max_len, nr_lines, 0); 97 if (unfolded)
229 if (tb == NULL) 98 n += callchain_node__count_rows_rb_tree(node);
230 goto out_destroy_form;
231
232 newtTextboxSetText(tb, text);
233 newtFormAddComponent(form, tb);
234 newtCenteredWindow(max_len, nr_lines, NULL);
235 newtFormRun(form, &es);
236 newtPopWindow();
237 rc = 0;
238out_destroy_form:
239 newtFormDestroy(form);
240 return rc;
241}
242
243static bool dialog_yesno(const char *msg)
244{
245 /* newtWinChoice should really be accepting const char pointers... */
246 char yes[] = "Yes", no[] = "No";
247 return newtWinChoice(NULL, yes, no, (char *)msg) == 1;
248}
249
250static void ui__error_window(const char *fmt, ...)
251{
252 va_list ap;
253
254 va_start(ap, fmt);
255 newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap);
256 va_end(ap);
257}
258
259#define HE_COLORSET_TOP 50
260#define HE_COLORSET_MEDIUM 51
261#define HE_COLORSET_NORMAL 52
262#define HE_COLORSET_SELECTED 53
263#define HE_COLORSET_CODE 54
264 99
265static int ui_browser__percent_color(double percent, bool current) 100 return n;
266{
267 if (current)
268 return HE_COLORSET_SELECTED;
269 if (percent >= MIN_RED)
270 return HE_COLORSET_TOP;
271 if (percent >= MIN_GREEN)
272 return HE_COLORSET_MEDIUM;
273 return HE_COLORSET_NORMAL;
274} 101}
275 102
276struct ui_browser { 103static int callchain__count_rows(struct rb_root *chain)
277 newtComponent form, sb;
278 u64 index, first_visible_entry_idx;
279 void *first_visible_entry, *entries;
280 u16 top, left, width, height;
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);
285 u32 nr_entries;
286};
287
288static void ui_browser__list_head_seek(struct ui_browser *self,
289 off_t offset, int whence)
290{ 104{
291 struct list_head *head = self->entries; 105 struct rb_node *nd;
292 struct list_head *pos; 106 int n = 0;
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 107
308 if (offset > 0) { 108 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
309 while (offset-- != 0) 109 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
310 pos = pos->next; 110 n += callchain_node__count_rows(node);
311 } else {
312 while (offset++ != 0)
313 pos = pos->prev;
314 } 111 }
315 112
316 self->first_visible_entry = pos; 113 return n;
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} 114}
323 115
324static void ui_browser__refresh_dimensions(struct ui_browser *self) 116static bool map_symbol__toggle_fold(struct map_symbol *self)
325{ 117{
326 int cols, rows; 118 if (!self->has_children)
327 newtGetScreenSize(&cols, &rows); 119 return false;
328
329 if (self->width > cols - 4)
330 self->width = cols - 4;
331 self->height = rows - 5;
332 if (self->height > self->nr_entries)
333 self->height = self->nr_entries;
334 self->top = (rows - self->height) / 2;
335 self->left = (cols - self->width) / 2;
336}
337 120
338static void ui_browser__reset_index(struct ui_browser *self) 121 self->unfolded = !self->unfolded;
339{ 122 return true;
340 self->index = self->first_visible_entry_idx = 0;
341 self->seek(self, 0, SEEK_SET);
342} 123}
343 124
344static int ui_browser__show(struct ui_browser *self, const char *title) 125static void callchain_node__init_have_children_rb_tree(struct callchain_node *self)
345{ 126{
346 if (self->form != NULL) { 127 struct rb_node *nd = rb_first(&self->rb_root);
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 128
362 newtFormAddHotKey(self->form, NEWT_KEY_UP); 129 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
363 newtFormAddHotKey(self->form, NEWT_KEY_DOWN); 130 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
364 newtFormAddHotKey(self->form, NEWT_KEY_PGUP); 131 struct callchain_list *chain;
365 newtFormAddHotKey(self->form, NEWT_KEY_PGDN); 132 int first = true;
366 newtFormAddHotKey(self->form, NEWT_KEY_HOME);
367 newtFormAddHotKey(self->form, NEWT_KEY_END);
368 newtFormAddComponent(self->form, self->sb);
369 return 0;
370}
371 133
372static int objdump_line__show(struct objdump_line *self, struct list_head *head, 134 list_for_each_entry(chain, &child->val, list) {
373 int width, struct hist_entry *he, int len, 135 if (first) {
374 bool current_entry) 136 first = false;
375{ 137 chain->ms.has_children = chain->list.next != &child->val ||
376 if (self->offset != -1) { 138 rb_first(&child->rb_root) != NULL;
377 struct symbol *sym = he->ms.sym;
378 unsigned int hits = 0;
379 double percent = 0.0;
380 int color;
381 struct sym_priv *priv = symbol__priv(sym);
382 struct sym_ext *sym_ext = priv->ext;
383 struct sym_hist *h = priv->hist;
384 s64 offset = self->offset;
385 struct objdump_line *next = objdump__get_next_ip_line(head, self);
386
387 while (offset < (s64)len &&
388 (next == NULL || offset < next->offset)) {
389 if (sym_ext) {
390 percent += sym_ext[offset].percent;
391 } else 139 } else
392 hits += h->ip[offset]; 140 chain->ms.has_children = chain->list.next == &child->val &&
393 141 rb_first(&child->rb_root) != NULL;
394 ++offset;
395 } 142 }
396 143
397 if (sym_ext == NULL && h->sum) 144 callchain_node__init_have_children_rb_tree(child);
398 percent = 100.0 * hits / h->sum;
399
400 color = ui_browser__percent_color(percent, current_entry);
401 SLsmg_set_color(color);
402 slsmg_printf(" %7.2f ", percent);
403 if (!current_entry)
404 SLsmg_set_color(HE_COLORSET_CODE);
405 } else {
406 int color = ui_browser__percent_color(0, current_entry);
407 SLsmg_set_color(color);
408 slsmg_write_nstring(" ", 9);
409 } 145 }
410
411 SLsmg_write_char(':');
412 slsmg_write_nstring(" ", 8);
413 if (!*self->line)
414 slsmg_write_nstring(" ", width - 18);
415 else
416 slsmg_write_nstring(self->line, width - 18);
417
418 return 0;
419} 146}
420 147
421static int ui_browser__refresh_entries(struct ui_browser *self) 148static void callchain_node__init_have_children(struct callchain_node *self)
422{ 149{
423 int row; 150 struct callchain_list *chain;
424 151
425 newtScrollbarSet(self->sb, self->index, self->nr_entries - 1); 152 list_for_each_entry(chain, &self->val, list)
426 row = self->refresh_entries(self); 153 chain->ms.has_children = rb_first(&self->rb_root) != NULL;
427 SLsmg_set_color(HE_COLORSET_NORMAL);
428 SLsmg_fill_region(self->top + row, self->left,
429 self->height - row, self->width, ' ');
430 154
431 return 0; 155 callchain_node__init_have_children_rb_tree(self);
432} 156}
433 157
434static int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es) 158static void callchain__init_have_children(struct rb_root *self)
435{ 159{
436 if (ui_browser__refresh_entries(self) < 0) 160 struct rb_node *nd;
437 return -1;
438
439 while (1) {
440 off_t offset;
441
442 newtFormRun(self->form, es);
443
444 if (es->reason != NEWT_EXIT_HOTKEY)
445 break;
446 if (is_exit_key(es->u.key))
447 return es->u.key;
448 switch (es->u.key) {
449 case NEWT_KEY_DOWN:
450 if (self->index == self->nr_entries - 1)
451 break;
452 ++self->index;
453 if (self->index == self->first_visible_entry_idx + self->height) {
454 ++self->first_visible_entry_idx;
455 self->seek(self, +1, SEEK_CUR);
456 }
457 break;
458 case NEWT_KEY_UP:
459 if (self->index == 0)
460 break;
461 --self->index;
462 if (self->index < self->first_visible_entry_idx) {
463 --self->first_visible_entry_idx;
464 self->seek(self, -1, SEEK_CUR);
465 }
466 break;
467 case NEWT_KEY_PGDN:
468 case ' ':
469 if (self->first_visible_entry_idx + self->height > self->nr_entries - 1)
470 break;
471
472 offset = self->height;
473 if (self->index + offset > self->nr_entries - 1)
474 offset = self->nr_entries - 1 - self->index;
475 self->index += offset;
476 self->first_visible_entry_idx += offset;
477 self->seek(self, +offset, SEEK_CUR);
478 break;
479 case NEWT_KEY_PGUP:
480 if (self->first_visible_entry_idx == 0)
481 break;
482
483 if (self->first_visible_entry_idx < self->height)
484 offset = self->first_visible_entry_idx;
485 else
486 offset = self->height;
487 161
488 self->index -= offset; 162 for (nd = rb_first(self); nd; nd = rb_next(nd)) {
489 self->first_visible_entry_idx -= offset; 163 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
490 self->seek(self, -offset, SEEK_CUR); 164 callchain_node__init_have_children(node);
491 break;
492 case NEWT_KEY_HOME:
493 ui_browser__reset_index(self);
494 break;
495 case NEWT_KEY_END:
496 offset = self->height - 1;
497 if (offset >= self->nr_entries)
498 offset = self->nr_entries - 1;
499
500 self->index = self->nr_entries - 1;
501 self->first_visible_entry_idx = self->index - offset;
502 self->seek(self, -offset, SEEK_END);
503 break;
504 default:
505 return es->u.key;
506 }
507 if (ui_browser__refresh_entries(self) < 0)
508 return -1;
509 } 165 }
510 return 0;
511}
512
513static char *callchain_list__sym_name(struct callchain_list *self,
514 char *bf, size_t bfsize)
515{
516 if (self->ms.sym)
517 return self->ms.sym->name;
518
519 snprintf(bf, bfsize, "%#Lx", self->ip);
520 return bf;
521} 166}
522 167
523static unsigned int hist_entry__annotate_browser_refresh(struct ui_browser *self) 168static void hist_entry__init_have_children(struct hist_entry *self)
524{ 169{
525 struct objdump_line *pos; 170 if (!self->init_have_children) {
526 struct list_head *head = self->entries; 171 callchain__init_have_children(&self->sorted_chain);
527 struct hist_entry *he = self->priv; 172 self->init_have_children = true;
528 int row = 0;
529 int len = he->ms.sym->end - he->ms.sym->start;
530
531 if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries)
532 self->first_visible_entry = head->next;
533
534 pos = list_entry(self->first_visible_entry, struct objdump_line, node);
535
536 list_for_each_entry_from(pos, head, node) {
537 bool current_entry = ui_browser__is_current_entry(self, row);
538 SLsmg_gotorc(self->top + row, self->left);
539 objdump_line__show(pos, head, self->width,
540 he, len, current_entry);
541 if (++row == self->height)
542 break;
543 } 173 }
544
545 return row;
546} 174}
547 175
548int hist_entry__tui_annotate(struct hist_entry *self) 176static bool hist_browser__toggle_fold(struct hist_browser *self)
549{ 177{
550 struct ui_browser browser; 178 if (map_symbol__toggle_fold(self->selection)) {
551 struct newtExitStruct es; 179 struct hist_entry *he = self->he_selection;
552 struct objdump_line *pos, *n;
553 LIST_HEAD(head);
554 int ret;
555
556 if (self->ms.sym == NULL)
557 return -1;
558 180
559 if (self->ms.map->dso->annotate_warned) 181 hist_entry__init_have_children(he);
560 return -1; 182 self->hists->nr_entries -= he->nr_rows;
561 183
562 if (hist_entry__annotate(self, &head) < 0) { 184 if (he->ms.unfolded)
563 ui__error_window(browser__last_msg); 185 he->nr_rows = callchain__count_rows(&he->sorted_chain);
564 return -1; 186 else
565 } 187 he->nr_rows = 0;
188 self->hists->nr_entries += he->nr_rows;
189 self->b.nr_entries = self->hists->nr_entries;
566 190
567 ui_helpline__push("Press <- or ESC to exit"); 191 return true;
568
569 memset(&browser, 0, sizeof(browser));
570 browser.entries = &head;
571 browser.refresh_entries = hist_entry__annotate_browser_refresh;
572 browser.seek = ui_browser__list_head_seek;
573 browser.priv = self;
574 list_for_each_entry(pos, &head, node) {
575 size_t line_len = strlen(pos->line);
576 if (browser.width < line_len)
577 browser.width = line_len;
578 ++browser.nr_entries;
579 } 192 }
580 193
581 browser.width += 18; /* Percentage */ 194 /* If it doesn't have children, no toggling performed */
582 ui_browser__show(&browser, self->ms.sym->name); 195 return false;
583 newtFormAddHotKey(browser.form, ' ');
584 ret = ui_browser__run(&browser, &es);
585 newtFormDestroy(browser.form);
586 newtPopWindow();
587 list_for_each_entry_safe(pos, n, &head, node) {
588 list_del(&pos->node);
589 objdump_line__free(pos);
590 }
591 ui_helpline__pop();
592 return ret;
593} 196}
594 197
595struct hist_browser {
596 struct ui_browser b;
597 struct hists *hists;
598 struct hist_entry *he_selection;
599 struct map_symbol *selection;
600};
601
602static void hist_browser__reset(struct hist_browser *self);
603static int hist_browser__run(struct hist_browser *self, const char *title, 198static int hist_browser__run(struct hist_browser *self, const char *title,
604 struct newtExitStruct *es); 199 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)
610{
611 struct hist_browser *self = zalloc(sizeof(*self));
612
613 if (self) {
614 self->hists = hists;
615 self->b.refresh_entries = hist_browser__refresh_entries;
616 self->b.seek = ui_browser__hists_seek;
617 }
618
619 return self;
620}
621
622static void hist_browser__delete(struct hist_browser *self)
623{
624 newtFormDestroy(self->b.form);
625 newtPopWindow();
626 free(self);
627}
628
629static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
630{
631 return self->he_selection;
632}
633
634static struct thread *hist_browser__selected_thread(struct hist_browser *self)
635{ 200{
636 return self->he_selection->thread; 201 char str[256], unit;
637} 202 unsigned long nr_events = self->hists->stats.nr_events[PERF_RECORD_SAMPLE];
638 203
639static int hist_browser__title(char *bf, size_t size, const char *ev_name, 204 self->b.entries = &self->hists->entries;
640 const struct dso *dso, const struct thread *thread) 205 self->b.nr_entries = self->hists->nr_entries;
641{
642 int printed = 0;
643 206
644 if (thread) 207 hist_browser__refresh_dimensions(self);
645 printed += snprintf(bf + printed, size - printed,
646 "Thread: %s(%d)",
647 (thread->comm_set ? thread->comm : ""),
648 thread->pid);
649 if (dso)
650 printed += snprintf(bf + printed, size - printed,
651 "%sDSO: %s", thread ? " " : "",
652 dso->short_name);
653 return printed ?: snprintf(bf, size, "Event: %s", ev_name);
654}
655 208
656int hists__browse(struct hists *self, const char *helpline, const char *ev_name) 209 nr_events = convert_unit(nr_events, &unit);
657{ 210 snprintf(str, sizeof(str), "Events: %lu%c ",
658 struct hist_browser *browser = hist_browser__new(self); 211 nr_events, unit);
659 struct pstack *fstack; 212 newtDrawRootText(0, 0, str);
660 const struct thread *thread_filter = NULL;
661 const struct dso *dso_filter = NULL;
662 struct newtExitStruct es;
663 char msg[160];
664 int key = -1;
665 213
666 if (browser == NULL) 214 if (ui_browser__show(&self->b, title,
215 "Press '?' for help on key bindings") < 0)
667 return -1; 216 return -1;
668 217
669 fstack = pstack__new(2); 218 newtFormAddHotKey(self->b.form, 'a');
670 if (fstack == NULL) 219 newtFormAddHotKey(self->b.form, '?');
671 goto out; 220 newtFormAddHotKey(self->b.form, 'h');
672 221 newtFormAddHotKey(self->b.form, 'd');
673 ui_helpline__push(helpline); 222 newtFormAddHotKey(self->b.form, 'D');
223 newtFormAddHotKey(self->b.form, 't');
674 224
675 hist_browser__title(msg, sizeof(msg), ev_name, 225 newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
676 dso_filter, thread_filter); 226 newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT);
227 newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
677 228
678 while (1) { 229 while (1) {
679 const struct thread *thread; 230 ui_browser__run(&self->b, es);
680 const struct dso *dso;
681 char *options[16];
682 int nr_options = 0, choice = 0, i,
683 annotate = -2, zoom_dso = -2, zoom_thread = -2;
684 231
685 if (hist_browser__run(browser, msg, &es)) 232 if (es->reason != NEWT_EXIT_HOTKEY)
686 break; 233 break;
687 234 switch (es->u.key) {
688 thread = hist_browser__selected_thread(browser); 235 case 'D': { /* Debug */
689 dso = browser->selection->map ? browser->selection->map->dso : NULL; 236 static int seq;
690 237 struct hist_entry *h = rb_entry(self->b.top,
691 if (es.reason == NEWT_EXIT_HOTKEY) { 238 struct hist_entry, rb_node);
692 key = es.u.key; 239 ui_helpline__pop();
693 240 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
694 switch (key) { 241 seq++, self->b.nr_entries,
695 case NEWT_KEY_F1: 242 self->hists->nr_entries,
696 goto do_help; 243 self->b.height,
697 case NEWT_KEY_TAB: 244 self->b.index,
698 case NEWT_KEY_UNTAB: 245 self->b.top_idx,
699 /* 246 h->row_offset, h->nr_rows);
700 * Exit the browser, let hists__browser_tree
701 * go to the next or previous
702 */
703 goto out_free_stack;
704 default:;
705 }
706
707 key = toupper(key);
708 switch (key) {
709 case 'A':
710 if (browser->selection->map == NULL &&
711 browser->selection->map->dso->annotate_warned)
712 continue;
713 goto do_annotate;
714 case 'D':
715 goto zoom_dso;
716 case 'T':
717 goto zoom_thread;
718 case 'H':
719 case '?':
720do_help:
721 ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
722 "<- Zoom out\n"
723 "a Annotate current symbol\n"
724 "h/?/F1 Show this window\n"
725 "d Zoom into current DSO\n"
726 "t Zoom into current Thread\n"
727 "q/CTRL+C Exit browser");
728 continue;
729 default:;
730 }
731 if (is_exit_key(key)) {
732 if (key == NEWT_KEY_ESCAPE) {
733 if (dialog_yesno("Do you really want to exit?"))
734 break;
735 else
736 continue;
737 } else
738 break;
739 }
740
741 if (es.u.key == NEWT_KEY_LEFT) {
742 const void *top;
743
744 if (pstack__empty(fstack))
745 continue;
746 top = pstack__pop(fstack);
747 if (top == &dso_filter)
748 goto zoom_out_dso;
749 if (top == &thread_filter)
750 goto zoom_out_thread;
751 continue;
752 }
753 } 247 }
754
755 if (browser->selection->sym != NULL &&
756 !browser->selection->map->dso->annotate_warned &&
757 asprintf(&options[nr_options], "Annotate %s",
758 browser->selection->sym->name) > 0)
759 annotate = nr_options++;
760
761 if (thread != NULL &&
762 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
763 (thread_filter ? "out of" : "into"),
764 (thread->comm_set ? thread->comm : ""),
765 thread->pid) > 0)
766 zoom_thread = nr_options++;
767
768 if (dso != NULL &&
769 asprintf(&options[nr_options], "Zoom %s %s DSO",
770 (dso_filter ? "out of" : "into"),
771 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
772 zoom_dso = nr_options++;
773
774 options[nr_options++] = (char *)"Exit";
775
776 choice = popup_menu(nr_options, options);
777
778 for (i = 0; i < nr_options - 1; ++i)
779 free(options[i]);
780
781 if (choice == nr_options - 1)
782 break;
783
784 if (choice == -1)
785 continue; 248 continue;
786 249 case NEWT_KEY_ENTER:
787 if (choice == annotate) { 250 if (hist_browser__toggle_fold(self))
788 struct hist_entry *he; 251 break;
789do_annotate: 252 /* fall thru */
790 if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
791 browser->selection->map->dso->annotate_warned = 1;
792 ui_helpline__puts("No vmlinux file found, can't "
793 "annotate with just a "
794 "kallsyms file");
795 continue;
796 }
797
798 he = hist_browser__selected_entry(browser);
799 if (he == NULL)
800 continue;
801
802 hist_entry__tui_annotate(he);
803 } else if (choice == zoom_dso) {
804zoom_dso:
805 if (dso_filter) {
806 pstack__remove(fstack, &dso_filter);
807zoom_out_dso:
808 ui_helpline__pop();
809 dso_filter = NULL;
810 } else {
811 if (dso == NULL)
812 continue;
813 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
814 dso->kernel ? "the Kernel" : dso->short_name);
815 dso_filter = dso;
816 pstack__push(fstack, &dso_filter);
817 }
818 hists__filter_by_dso(self, dso_filter);
819 hist_browser__title(msg, sizeof(msg), ev_name,
820 dso_filter, thread_filter);
821 hist_browser__reset(browser);
822 } else if (choice == zoom_thread) {
823zoom_thread:
824 if (thread_filter) {
825 pstack__remove(fstack, &thread_filter);
826zoom_out_thread:
827 ui_helpline__pop();
828 thread_filter = NULL;
829 } else {
830 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
831 thread->comm_set ? thread->comm : "",
832 thread->pid);
833 thread_filter = thread;
834 pstack__push(fstack, &thread_filter);
835 }
836 hists__filter_by_thread(self, thread_filter);
837 hist_browser__title(msg, sizeof(msg), ev_name,
838 dso_filter, thread_filter);
839 hist_browser__reset(browser);
840 }
841 }
842out_free_stack:
843 pstack__delete(fstack);
844out:
845 hist_browser__delete(browser);
846 return key;
847}
848
849int hists__tui_browse_tree(struct rb_root *self, const char *help)
850{
851 struct rb_node *first = rb_first(self), *nd = first, *next;
852 int key = 0;
853
854 while (nd) {
855 struct hists *hists = rb_entry(nd, struct hists, rb_node);
856 const char *ev_name = __event_name(hists->type, hists->config);
857
858 key = hists__browse(hists, help, ev_name);
859
860 if (is_exit_key(key))
861 break;
862
863 switch (key) {
864 case NEWT_KEY_TAB:
865 next = rb_next(nd);
866 if (next)
867 nd = next;
868 break;
869 case NEWT_KEY_UNTAB:
870 if (nd == first)
871 continue;
872 nd = rb_prev(nd);
873 default: 253 default:
874 break; 254 return 0;
875 }
876 }
877
878 return key;
879}
880
881static struct newtPercentTreeColors {
882 const char *topColorFg, *topColorBg;
883 const char *mediumColorFg, *mediumColorBg;
884 const char *normalColorFg, *normalColorBg;
885 const char *selColorFg, *selColorBg;
886 const char *codeColorFg, *codeColorBg;
887} defaultPercentTreeColors = {
888 "red", "lightgray",
889 "green", "lightgray",
890 "black", "lightgray",
891 "lightgray", "magenta",
892 "blue", "lightgray",
893};
894
895static void newt_suspend(void *d __used)
896{
897 newtSuspend();
898 raise(SIGTSTP);
899 newtResume();
900}
901
902void setup_browser(void)
903{
904 struct newtPercentTreeColors *c = &defaultPercentTreeColors;
905
906 if (!isatty(1) || !use_browser || dump_trace) {
907 use_browser = 0;
908 setup_pager();
909 return;
910 }
911
912 use_browser = 1;
913 newtInit();
914 newtCls();
915 newtSetSuspendCallback(newt_suspend, NULL);
916 ui_helpline__puts(" ");
917 sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg);
918 sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg);
919 sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg);
920 sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg);
921 sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg);
922}
923
924void exit_browser(bool wait_for_ok)
925{
926 if (use_browser > 0) {
927 if (wait_for_ok) {
928 char title[] = "Fatal Error", ok[] = "Ok";
929 newtWinMessage(title, ok, browser__last_msg);
930 } 255 }
931 newtFinished();
932 } 256 }
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 257
964static char callchain_list__folded(const struct callchain_list *self) 258 ui_browser__hide(&self->b);
965{ 259 return 0;
966 return map_symbol__folded(&self->ms);
967} 260}
968 261
969static bool map_symbol__toggle_fold(struct map_symbol *self) 262static char *callchain_list__sym_name(struct callchain_list *self,
263 char *bf, size_t bfsize)
970{ 264{
971 if (!self->has_children) 265 if (self->ms.sym)
972 return false; 266 return self->ms.sym->name;
973 267
974 self->unfolded = !self->unfolded; 268 snprintf(bf, bfsize, "%#Lx", self->ip);
975 return true; 269 return bf;
976} 270}
977 271
978#define LEVEL_OFFSET_STEP 3 272#define LEVEL_OFFSET_STEP 3
@@ -1048,7 +342,7 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
1048 } 342 }
1049 343
1050 SLsmg_set_color(color); 344 SLsmg_set_color(color);
1051 SLsmg_gotorc(self->b.top + row, self->b.left); 345 SLsmg_gotorc(self->b.y + row, self->b.x);
1052 slsmg_write_nstring(" ", offset + extra_offset); 346 slsmg_write_nstring(" ", offset + extra_offset);
1053 slsmg_printf("%c ", folded_sign); 347 slsmg_printf("%c ", folded_sign);
1054 slsmg_write_nstring(str, width); 348 slsmg_write_nstring(str, width);
@@ -1111,7 +405,7 @@ static int hist_browser__show_callchain_node(struct hist_browser *self,
1111 } 405 }
1112 406
1113 s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); 407 s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
1114 SLsmg_gotorc(self->b.top + row, self->b.left); 408 SLsmg_gotorc(self->b.y + row, self->b.x);
1115 SLsmg_set_color(color); 409 SLsmg_set_color(color);
1116 slsmg_write_nstring(" ", offset); 410 slsmg_write_nstring(" ", offset);
1117 slsmg_printf("%c ", folded_sign); 411 slsmg_printf("%c ", folded_sign);
@@ -1191,7 +485,7 @@ static int hist_browser__show_entry(struct hist_browser *self,
1191 } 485 }
1192 486
1193 SLsmg_set_color(color); 487 SLsmg_set_color(color);
1194 SLsmg_gotorc(self->b.top + row, self->b.left); 488 SLsmg_gotorc(self->b.y + row, self->b.x);
1195 if (symbol_conf.use_callchain) { 489 if (symbol_conf.use_callchain) {
1196 slsmg_printf("%c ", folded_sign); 490 slsmg_printf("%c ", folded_sign);
1197 width -= 2; 491 width -= 2;
@@ -1213,16 +507,16 @@ static int hist_browser__show_entry(struct hist_browser *self,
1213 return printed; 507 return printed;
1214} 508}
1215 509
1216static unsigned int hist_browser__refresh_entries(struct ui_browser *self) 510static unsigned int hist_browser__refresh(struct ui_browser *self)
1217{ 511{
1218 unsigned row = 0; 512 unsigned row = 0;
1219 struct rb_node *nd; 513 struct rb_node *nd;
1220 struct hist_browser *hb = container_of(self, struct hist_browser, b); 514 struct hist_browser *hb = container_of(self, struct hist_browser, b);
1221 515
1222 if (self->first_visible_entry == NULL) 516 if (self->top == NULL)
1223 self->first_visible_entry = rb_first(&hb->hists->entries); 517 self->top = rb_first(&hb->hists->entries);
1224 518
1225 for (nd = self->first_visible_entry; nd; nd = rb_next(nd)) { 519 for (nd = self->top; nd; nd = rb_next(nd)) {
1226 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 520 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1227 521
1228 if (h->filtered) 522 if (h->filtered)
@@ -1236,57 +530,6 @@ static unsigned int hist_browser__refresh_entries(struct ui_browser *self)
1236 return row; 530 return row;
1237} 531}
1238 532
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) 533static struct rb_node *hists__filter_entries(struct rb_node *nd)
1291{ 534{
1292 while (nd != NULL) { 535 while (nd != NULL) {
@@ -1325,7 +568,7 @@ static void ui_browser__hists_seek(struct ui_browser *self,
1325 nd = hists__filter_entries(rb_first(self->entries)); 568 nd = hists__filter_entries(rb_first(self->entries));
1326 break; 569 break;
1327 case SEEK_CUR: 570 case SEEK_CUR:
1328 nd = self->first_visible_entry; 571 nd = self->top;
1329 goto do_offset; 572 goto do_offset;
1330 case SEEK_END: 573 case SEEK_END:
1331 nd = hists__filter_prev_entries(rb_last(self->entries)); 574 nd = hists__filter_prev_entries(rb_last(self->entries));
@@ -1339,7 +582,7 @@ static void ui_browser__hists_seek(struct ui_browser *self,
1339 * Moves not relative to the first visible entry invalidates its 582 * Moves not relative to the first visible entry invalidates its
1340 * row_offset: 583 * row_offset:
1341 */ 584 */
1342 h = rb_entry(self->first_visible_entry, struct hist_entry, rb_node); 585 h = rb_entry(self->top, struct hist_entry, rb_node);
1343 h->row_offset = 0; 586 h->row_offset = 0;
1344 587
1345 /* 588 /*
@@ -1367,7 +610,7 @@ do_offset:
1367 } else { 610 } else {
1368 h->row_offset += offset; 611 h->row_offset += offset;
1369 offset = 0; 612 offset = 0;
1370 self->first_visible_entry = nd; 613 self->top = nd;
1371 break; 614 break;
1372 } 615 }
1373 } 616 }
@@ -1375,7 +618,7 @@ do_offset:
1375 if (nd == NULL) 618 if (nd == NULL)
1376 break; 619 break;
1377 --offset; 620 --offset;
1378 self->first_visible_entry = nd; 621 self->top = nd;
1379 } while (offset != 0); 622 } while (offset != 0);
1380 } else if (offset < 0) { 623 } else if (offset < 0) {
1381 while (1) { 624 while (1) {
@@ -1388,7 +631,7 @@ do_offset:
1388 } else { 631 } else {
1389 h->row_offset += offset; 632 h->row_offset += offset;
1390 offset = 0; 633 offset = 0;
1391 self->first_visible_entry = nd; 634 self->top = nd;
1392 break; 635 break;
1393 } 636 }
1394 } else { 637 } else {
@@ -1398,7 +641,7 @@ do_offset:
1398 } else { 641 } else {
1399 h->row_offset = h->nr_rows + offset; 642 h->row_offset = h->nr_rows + offset;
1400 offset = 0; 643 offset = 0;
1401 self->first_visible_entry = nd; 644 self->top = nd;
1402 break; 645 break;
1403 } 646 }
1404 } 647 }
@@ -1408,7 +651,7 @@ do_offset:
1408 if (nd == NULL) 651 if (nd == NULL)
1409 break; 652 break;
1410 ++offset; 653 ++offset;
1411 self->first_visible_entry = nd; 654 self->top = nd;
1412 if (offset == 0) { 655 if (offset == 0) {
1413 /* 656 /*
1414 * Last unfiltered hist_entry, check if it is 657 * Last unfiltered hist_entry, check if it is
@@ -1423,146 +666,283 @@ do_offset:
1423 first = false; 666 first = false;
1424 } 667 }
1425 } else { 668 } else {
1426 self->first_visible_entry = nd; 669 self->top = nd;
1427 h = rb_entry(nd, struct hist_entry, rb_node); 670 h = rb_entry(nd, struct hist_entry, rb_node);
1428 h->row_offset = 0; 671 h->row_offset = 0;
1429 } 672 }
1430} 673}
1431 674
1432static int callchain_node__count_rows_rb_tree(struct callchain_node *self) 675static struct hist_browser *hist_browser__new(struct hists *hists)
1433{ 676{
1434 int n = 0; 677 struct hist_browser *self = zalloc(sizeof(*self));
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 678
1450 if (folded_sign == '-') /* Have children and they're unfolded */ 679 if (self) {
1451 n += callchain_node__count_rows_rb_tree(child); 680 self->hists = hists;
681 self->b.refresh = hist_browser__refresh;
682 self->b.seek = ui_browser__hists_seek;
1452 } 683 }
1453 684
1454 return n; 685 return self;
1455} 686}
1456 687
1457static int callchain_node__count_rows(struct callchain_node *node) 688static void hist_browser__delete(struct hist_browser *self)
1458{ 689{
1459 struct callchain_list *chain; 690 newtFormDestroy(self->b.form);
1460 bool unfolded = false; 691 newtPopWindow();
1461 int n = 0; 692 free(self);
1462 693}
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 694
1471 return n; 695static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
696{
697 return self->he_selection;
1472} 698}
1473 699
1474static int callchain__count_rows(struct rb_root *chain) 700static struct thread *hist_browser__selected_thread(struct hist_browser *self)
1475{ 701{
1476 struct rb_node *nd; 702 return self->he_selection->thread;
1477 int n = 0; 703}
1478 704
1479 for (nd = rb_first(chain); nd; nd = rb_next(nd)) { 705static int hist_browser__title(char *bf, size_t size, const char *ev_name,
1480 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); 706 const struct dso *dso, const struct thread *thread)
1481 n += callchain_node__count_rows(node); 707{
1482 } 708 int printed = 0;
1483 709
1484 return n; 710 if (thread)
711 printed += snprintf(bf + printed, size - printed,
712 "Thread: %s(%d)",
713 (thread->comm_set ? thread->comm : ""),
714 thread->pid);
715 if (dso)
716 printed += snprintf(bf + printed, size - printed,
717 "%sDSO: %s", thread ? " " : "",
718 dso->short_name);
719 return printed ?: snprintf(bf, size, "Event: %s", ev_name);
1485} 720}
1486 721
1487static bool hist_browser__toggle_fold(struct hist_browser *self) 722int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
1488{ 723{
1489 if (map_symbol__toggle_fold(self->selection)) { 724 struct hist_browser *browser = hist_browser__new(self);
1490 struct hist_entry *he = self->he_selection; 725 struct pstack *fstack;
726 const struct thread *thread_filter = NULL;
727 const struct dso *dso_filter = NULL;
728 struct newtExitStruct es;
729 char msg[160];
730 int key = -1;
1491 731
1492 hist_entry__init_have_children(he); 732 if (browser == NULL)
1493 self->hists->nr_entries -= he->nr_rows; 733 return -1;
1494 734
1495 if (he->ms.unfolded) 735 fstack = pstack__new(2);
1496 he->nr_rows = callchain__count_rows(&he->sorted_chain); 736 if (fstack == NULL)
1497 else 737 goto out;
1498 he->nr_rows = 0;
1499 self->hists->nr_entries += he->nr_rows;
1500 self->b.nr_entries = self->hists->nr_entries;
1501 738
1502 return true; 739 ui_helpline__push(helpline);
1503 }
1504 740
1505 /* If it doesn't have children, no toggling performed */ 741 hist_browser__title(msg, sizeof(msg), ev_name,
1506 return false; 742 dso_filter, thread_filter);
1507}
1508 743
1509static int hist_browser__run(struct hist_browser *self, const char *title, 744 while (1) {
1510 struct newtExitStruct *es) 745 const struct thread *thread;
1511{ 746 const struct dso *dso;
1512 char str[256], unit; 747 char *options[16];
1513 unsigned long nr_events = self->hists->stats.nr_events[PERF_RECORD_SAMPLE]; 748 int nr_options = 0, choice = 0, i,
749 annotate = -2, zoom_dso = -2, zoom_thread = -2,
750 browse_map = -2;
1514 751
1515 self->b.entries = &self->hists->entries; 752 if (hist_browser__run(browser, msg, &es))
1516 self->b.nr_entries = self->hists->nr_entries; 753 break;
1517 754
1518 hist_browser__refresh_dimensions(self); 755 thread = hist_browser__selected_thread(browser);
756 dso = browser->selection->map ? browser->selection->map->dso : NULL;
1519 757
1520 nr_events = convert_unit(nr_events, &unit); 758 if (es.reason == NEWT_EXIT_HOTKEY) {
1521 snprintf(str, sizeof(str), "Events: %lu%c ", 759 key = es.u.key;
1522 nr_events, unit);
1523 newtDrawRootText(0, 0, str);
1524 760
1525 if (ui_browser__show(&self->b, title) < 0) 761 switch (key) {
1526 return -1; 762 case NEWT_KEY_F1:
763 goto do_help;
764 case NEWT_KEY_TAB:
765 case NEWT_KEY_UNTAB:
766 /*
767 * Exit the browser, let hists__browser_tree
768 * go to the next or previous
769 */
770 goto out_free_stack;
771 default:;
772 }
1527 773
1528 newtFormAddHotKey(self->b.form, 'A'); 774 switch (key) {
1529 newtFormAddHotKey(self->b.form, 'a'); 775 case 'a':
1530 newtFormAddHotKey(self->b.form, '?'); 776 if (browser->selection->map == NULL &&
1531 newtFormAddHotKey(self->b.form, 'h'); 777 browser->selection->map->dso->annotate_warned)
1532 newtFormAddHotKey(self->b.form, 'H'); 778 continue;
1533 newtFormAddHotKey(self->b.form, 'd'); 779 goto do_annotate;
780 case 'd':
781 goto zoom_dso;
782 case 't':
783 goto zoom_thread;
784 case 'h':
785 case '?':
786do_help:
787 ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
788 "<- Zoom out\n"
789 "a Annotate current symbol\n"
790 "h/?/F1 Show this window\n"
791 "d Zoom into current DSO\n"
792 "t Zoom into current Thread\n"
793 "q/CTRL+C Exit browser");
794 continue;
795 default:;
796 }
797 if (is_exit_key(key)) {
798 if (key == NEWT_KEY_ESCAPE &&
799 !ui__dialog_yesno("Do you really want to exit?"))
800 continue;
801 break;
802 }
1534 803
1535 newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT); 804 if (es.u.key == NEWT_KEY_LEFT) {
1536 newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT); 805 const void *top;
1537 newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
1538 806
1539 while (1) { 807 if (pstack__empty(fstack))
1540 ui_browser__run(&self->b, es); 808 continue;
809 top = pstack__pop(fstack);
810 if (top == &dso_filter)
811 goto zoom_out_dso;
812 if (top == &thread_filter)
813 goto zoom_out_thread;
814 continue;
815 }
816 }
1541 817
1542 if (es->reason != NEWT_EXIT_HOTKEY) 818 if (browser->selection->sym != NULL &&
819 !browser->selection->map->dso->annotate_warned &&
820 asprintf(&options[nr_options], "Annotate %s",
821 browser->selection->sym->name) > 0)
822 annotate = nr_options++;
823
824 if (thread != NULL &&
825 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
826 (thread_filter ? "out of" : "into"),
827 (thread->comm_set ? thread->comm : ""),
828 thread->pid) > 0)
829 zoom_thread = nr_options++;
830
831 if (dso != NULL &&
832 asprintf(&options[nr_options], "Zoom %s %s DSO",
833 (dso_filter ? "out of" : "into"),
834 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
835 zoom_dso = nr_options++;
836
837 if (browser->selection->map != NULL &&
838 asprintf(&options[nr_options], "Browse map details") > 0)
839 browse_map = nr_options++;
840
841 options[nr_options++] = (char *)"Exit";
842
843 choice = ui__popup_menu(nr_options, options);
844
845 for (i = 0; i < nr_options - 1; ++i)
846 free(options[i]);
847
848 if (choice == nr_options - 1)
1543 break; 849 break;
1544 switch (es->u.key) { 850
1545 case 'd': { /* Debug */ 851 if (choice == -1)
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; 852 continue;
1559 case NEWT_KEY_ENTER: 853
1560 if (hist_browser__toggle_fold(self)) 854 if (choice == annotate) {
1561 break; 855 struct hist_entry *he;
1562 /* fall thru */ 856do_annotate:
857 if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
858 browser->selection->map->dso->annotate_warned = 1;
859 ui_helpline__puts("No vmlinux file found, can't "
860 "annotate with just a "
861 "kallsyms file");
862 continue;
863 }
864
865 he = hist_browser__selected_entry(browser);
866 if (he == NULL)
867 continue;
868
869 hist_entry__tui_annotate(he);
870 } else if (choice == browse_map)
871 map__browse(browser->selection->map);
872 else if (choice == zoom_dso) {
873zoom_dso:
874 if (dso_filter) {
875 pstack__remove(fstack, &dso_filter);
876zoom_out_dso:
877 ui_helpline__pop();
878 dso_filter = NULL;
879 } else {
880 if (dso == NULL)
881 continue;
882 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
883 dso->kernel ? "the Kernel" : dso->short_name);
884 dso_filter = dso;
885 pstack__push(fstack, &dso_filter);
886 }
887 hists__filter_by_dso(self, dso_filter);
888 hist_browser__title(msg, sizeof(msg), ev_name,
889 dso_filter, thread_filter);
890 hist_browser__reset(browser);
891 } else if (choice == zoom_thread) {
892zoom_thread:
893 if (thread_filter) {
894 pstack__remove(fstack, &thread_filter);
895zoom_out_thread:
896 ui_helpline__pop();
897 thread_filter = NULL;
898 } else {
899 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
900 thread->comm_set ? thread->comm : "",
901 thread->pid);
902 thread_filter = thread;
903 pstack__push(fstack, &thread_filter);
904 }
905 hists__filter_by_thread(self, thread_filter);
906 hist_browser__title(msg, sizeof(msg), ev_name,
907 dso_filter, thread_filter);
908 hist_browser__reset(browser);
909 }
910 }
911out_free_stack:
912 pstack__delete(fstack);
913out:
914 hist_browser__delete(browser);
915 return key;
916}
917
918int hists__tui_browse_tree(struct rb_root *self, const char *help)
919{
920 struct rb_node *first = rb_first(self), *nd = first, *next;
921 int key = 0;
922
923 while (nd) {
924 struct hists *hists = rb_entry(nd, struct hists, rb_node);
925 const char *ev_name = __event_name(hists->type, hists->config);
926
927 key = hists__browse(hists, help, ev_name);
928
929 if (is_exit_key(key))
930 break;
931
932 switch (key) {
933 case NEWT_KEY_TAB:
934 next = rb_next(nd);
935 if (next)
936 nd = next;
937 break;
938 case NEWT_KEY_UNTAB:
939 if (nd == first)
940 continue;
941 nd = rb_prev(nd);
1563 default: 942 default:
1564 return 0; 943 break;
1565 } 944 }
1566 } 945 }
1567 return 0; 946
947 return key;
1568} 948}
diff --git a/tools/perf/util/ui/browsers/map.c b/tools/perf/util/ui/browsers/map.c
new file mode 100644
index 000000000000..142b825b42bf
--- /dev/null
+++ b/tools/perf/util/ui/browsers/map.c
@@ -0,0 +1,161 @@
1#include "../libslang.h"
2#include <elf.h>
3#include <newt.h>
4#include <sys/ttydefaults.h>
5#include <ctype.h>
6#include <string.h>
7#include <linux/bitops.h>
8#include "../../debug.h"
9#include "../../symbol.h"
10#include "../browser.h"
11#include "../helpline.h"
12#include "map.h"
13
14static int ui_entry__read(const char *title, char *bf, size_t size, int width)
15{
16 struct newtExitStruct es;
17 newtComponent form, entry;
18 const char *result;
19 int err = -1;
20
21 newtCenteredWindow(width, 1, title);
22 form = newtForm(NULL, NULL, 0);
23 if (form == NULL)
24 return -1;
25
26 entry = newtEntry(0, 0, "0x", width, &result, NEWT_FLAG_SCROLL);
27 if (entry == NULL)
28 goto out_free_form;
29
30 newtFormAddComponent(form, entry);
31 newtFormAddHotKey(form, NEWT_KEY_ENTER);
32 newtFormAddHotKey(form, NEWT_KEY_ESCAPE);
33 newtFormAddHotKey(form, NEWT_KEY_LEFT);
34 newtFormAddHotKey(form, CTRL('c'));
35 newtFormRun(form, &es);
36
37 if (result != NULL) {
38 strncpy(bf, result, size);
39 err = 0;
40 }
41out_free_form:
42 newtPopWindow();
43 newtFormDestroy(form);
44 return 0;
45}
46
47struct map_browser {
48 struct ui_browser b;
49 struct map *map;
50 u16 namelen;
51 u8 addrlen;
52};
53
54static void map_browser__write(struct ui_browser *self, void *nd, int row)
55{
56 struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
57 struct map_browser *mb = container_of(self, struct map_browser, b);
58 bool current_entry = ui_browser__is_current_entry(self, row);
59 int color = ui_browser__percent_color(0, current_entry);
60
61 SLsmg_set_color(color);
62 slsmg_printf("%*llx %*llx %c ",
63 mb->addrlen, sym->start, mb->addrlen, sym->end,
64 sym->binding == STB_GLOBAL ? 'g' :
65 sym->binding == STB_LOCAL ? 'l' : 'w');
66 slsmg_write_nstring(sym->name, mb->namelen);
67}
68
69/* FIXME uber-kludgy, see comment on cmd_report... */
70static u32 *symbol__browser_index(struct symbol *self)
71{
72 return ((void *)self) - sizeof(struct rb_node) - sizeof(u32);
73}
74
75static int map_browser__search(struct map_browser *self)
76{
77 char target[512];
78 struct symbol *sym;
79 int err = ui_entry__read("Search by name/addr", target, sizeof(target), 40);
80
81 if (err)
82 return err;
83
84 if (target[0] == '0' && tolower(target[1]) == 'x') {
85 u64 addr = strtoull(target, NULL, 16);
86 sym = map__find_symbol(self->map, addr, NULL);
87 } else
88 sym = map__find_symbol_by_name(self->map, target, NULL);
89
90 if (sym != NULL) {
91 u32 *idx = symbol__browser_index(sym);
92
93 self->b.top = &sym->rb_node;
94 self->b.index = self->b.top_idx = *idx;
95 } else
96 ui_helpline__fpush("%s not found!", target);
97
98 return 0;
99}
100
101static int map_browser__run(struct map_browser *self, struct newtExitStruct *es)
102{
103 if (ui_browser__show(&self->b, self->map->dso->long_name,
104 "Press <- or ESC to exit, %s / to search",
105 verbose ? "" : "restart with -v to use") < 0)
106 return -1;
107
108 newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
109 newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
110 if (verbose)
111 newtFormAddHotKey(self->b.form, '/');
112
113 while (1) {
114 ui_browser__run(&self->b, es);
115
116 if (es->reason != NEWT_EXIT_HOTKEY)
117 break;
118 if (verbose && es->u.key == '/')
119 map_browser__search(self);
120 else
121 break;
122 }
123
124 ui_browser__hide(&self->b);
125 return 0;
126}
127
128int map__browse(struct map *self)
129{
130 struct map_browser mb = {
131 .b = {
132 .entries = &self->dso->symbols[self->type],
133 .refresh = ui_browser__rb_tree_refresh,
134 .seek = ui_browser__rb_tree_seek,
135 .write = map_browser__write,
136 },
137 .map = self,
138 };
139 struct newtExitStruct es;
140 struct rb_node *nd;
141 char tmp[BITS_PER_LONG / 4];
142 u64 maxaddr = 0;
143
144 for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) {
145 struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
146
147 if (mb.namelen < pos->namelen)
148 mb.namelen = pos->namelen;
149 if (maxaddr < pos->end)
150 maxaddr = pos->end;
151 if (verbose) {
152 u32 *idx = symbol__browser_index(pos);
153 *idx = mb.b.nr_entries;
154 }
155 ++mb.b.nr_entries;
156 }
157
158 mb.addrlen = snprintf(tmp, sizeof(tmp), "%llx", maxaddr);
159 mb.b.width += mb.addrlen * 2 + 4 + mb.namelen;
160 return map_browser__run(&mb, &es);
161}
diff --git a/tools/perf/util/ui/browsers/map.h b/tools/perf/util/ui/browsers/map.h
new file mode 100644
index 000000000000..df8581a43e17
--- /dev/null
+++ b/tools/perf/util/ui/browsers/map.h
@@ -0,0 +1,6 @@
1#ifndef _PERF_UI_MAP_BROWSER_H_
2#define _PERF_UI_MAP_BROWSER_H_ 1
3struct map;
4
5int map__browse(struct map *self);
6#endif /* _PERF_UI_MAP_BROWSER_H_ */
diff --git a/tools/perf/util/ui/helpline.c b/tools/perf/util/ui/helpline.c
new file mode 100644
index 000000000000..8d79daa4458a
--- /dev/null
+++ b/tools/perf/util/ui/helpline.c
@@ -0,0 +1,69 @@
1#define _GNU_SOURCE
2#include <stdio.h>
3#include <stdlib.h>
4#include <newt.h>
5
6#include "../debug.h"
7#include "helpline.h"
8
9void ui_helpline__pop(void)
10{
11 newtPopHelpLine();
12}
13
14void ui_helpline__push(const char *msg)
15{
16 newtPushHelpLine(msg);
17}
18
19void ui_helpline__vpush(const char *fmt, va_list ap)
20{
21 char *s;
22
23 if (vasprintf(&s, fmt, ap) < 0)
24 vfprintf(stderr, fmt, ap);
25 else {
26 ui_helpline__push(s);
27 free(s);
28 }
29}
30
31void ui_helpline__fpush(const char *fmt, ...)
32{
33 va_list ap;
34
35 va_start(ap, fmt);
36 ui_helpline__vpush(fmt, ap);
37 va_end(ap);
38}
39
40void ui_helpline__puts(const char *msg)
41{
42 ui_helpline__pop();
43 ui_helpline__push(msg);
44}
45
46void ui_helpline__init(void)
47{
48 ui_helpline__puts(" ");
49}
50
51char ui_helpline__last_msg[1024];
52
53int ui_helpline__show_help(const char *format, va_list ap)
54{
55 int ret;
56 static int backlog;
57
58 ret = vsnprintf(ui_helpline__last_msg + backlog,
59 sizeof(ui_helpline__last_msg) - backlog, format, ap);
60 backlog += ret;
61
62 if (ui_helpline__last_msg[backlog - 1] == '\n') {
63 ui_helpline__puts(ui_helpline__last_msg);
64 newtRefresh();
65 backlog = 0;
66 }
67
68 return ret;
69}
diff --git a/tools/perf/util/ui/helpline.h b/tools/perf/util/ui/helpline.h
new file mode 100644
index 000000000000..ab6028d0c401
--- /dev/null
+++ b/tools/perf/util/ui/helpline.h
@@ -0,0 +1,11 @@
1#ifndef _PERF_UI_HELPLINE_H_
2#define _PERF_UI_HELPLINE_H_ 1
3
4void ui_helpline__init(void);
5void ui_helpline__pop(void);
6void ui_helpline__push(const char *msg);
7void ui_helpline__vpush(const char *fmt, va_list ap);
8void ui_helpline__fpush(const char *fmt, ...);
9void ui_helpline__puts(const char *msg);
10
11#endif /* _PERF_UI_HELPLINE_H_ */
diff --git a/tools/perf/util/ui/libslang.h b/tools/perf/util/ui/libslang.h
new file mode 100644
index 000000000000..5623da8e8080
--- /dev/null
+++ b/tools/perf/util/ui/libslang.h
@@ -0,0 +1,27 @@
1#ifndef _PERF_UI_SLANG_H_
2#define _PERF_UI_SLANG_H_ 1
3/*
4 * slang versions <= 2.0.6 have a "#if HAVE_LONG_LONG" that breaks
5 * the build if it isn't defined. Use the equivalent one that glibc
6 * has on features.h.
7 */
8#include <features.h>
9#ifndef HAVE_LONG_LONG
10#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG
11#endif
12#include <slang.h>
13
14#if SLANG_VERSION < 20104
15#define slsmg_printf(msg, args...) \
16 SLsmg_printf((char *)msg, ##args)
17#define slsmg_write_nstring(msg, len) \
18 SLsmg_write_nstring((char *)msg, len)
19#define sltt_set_color(obj, name, fg, bg) \
20 SLtt_set_color(obj,(char *)name, (char *)fg, (char *)bg)
21#else
22#define slsmg_printf SLsmg_printf
23#define slsmg_write_nstring SLsmg_write_nstring
24#define sltt_set_color SLtt_set_color
25#endif
26
27#endif /* _PERF_UI_SLANG_H_ */
diff --git a/tools/perf/util/ui/progress.c b/tools/perf/util/ui/progress.c
new file mode 100644
index 000000000000..d7fc399d36b3
--- /dev/null
+++ b/tools/perf/util/ui/progress.c
@@ -0,0 +1,60 @@
1#include <stdlib.h>
2#include <newt.h>
3#include "../cache.h"
4#include "progress.h"
5
6struct ui_progress {
7 newtComponent form, scale;
8};
9
10struct ui_progress *ui_progress__new(const char *title, u64 total)
11{
12 struct ui_progress *self = malloc(sizeof(*self));
13
14 if (self != NULL) {
15 int cols;
16
17 if (use_browser <= 0)
18 return self;
19 newtGetScreenSize(&cols, NULL);
20 cols -= 4;
21 newtCenteredWindow(cols, 1, title);
22 self->form = newtForm(NULL, NULL, 0);
23 if (self->form == NULL)
24 goto out_free_self;
25 self->scale = newtScale(0, 0, cols, total);
26 if (self->scale == NULL)
27 goto out_free_form;
28 newtFormAddComponent(self->form, self->scale);
29 newtRefresh();
30 }
31
32 return self;
33
34out_free_form:
35 newtFormDestroy(self->form);
36out_free_self:
37 free(self);
38 return NULL;
39}
40
41void ui_progress__update(struct ui_progress *self, u64 curr)
42{
43 /*
44 * FIXME: We should have a per UI backend way of showing progress,
45 * stdio will just show a percentage as NN%, etc.
46 */
47 if (use_browser <= 0)
48 return;
49 newtScaleSet(self->scale, curr);
50 newtRefresh();
51}
52
53void ui_progress__delete(struct ui_progress *self)
54{
55 if (use_browser > 0) {
56 newtFormDestroy(self->form);
57 newtPopWindow();
58 }
59 free(self);
60}
diff --git a/tools/perf/util/ui/progress.h b/tools/perf/util/ui/progress.h
new file mode 100644
index 000000000000..a3820a0beb5b
--- /dev/null
+++ b/tools/perf/util/ui/progress.h
@@ -0,0 +1,11 @@
1#ifndef _PERF_UI_PROGRESS_H_
2#define _PERF_UI_PROGRESS_H_ 1
3
4struct ui_progress;
5
6struct ui_progress *ui_progress__new(const char *title, u64 total);
7void ui_progress__delete(struct ui_progress *self);
8
9void ui_progress__update(struct ui_progress *self, u64 curr);
10
11#endif
diff --git a/tools/perf/util/ui/setup.c b/tools/perf/util/ui/setup.c
new file mode 100644
index 000000000000..662085032eb7
--- /dev/null
+++ b/tools/perf/util/ui/setup.c
@@ -0,0 +1,42 @@
1#include <newt.h>
2#include <signal.h>
3#include <stdbool.h>
4
5#include "../cache.h"
6#include "../debug.h"
7#include "browser.h"
8#include "helpline.h"
9
10static void newt_suspend(void *d __used)
11{
12 newtSuspend();
13 raise(SIGTSTP);
14 newtResume();
15}
16
17void setup_browser(void)
18{
19 if (!isatty(1) || !use_browser || dump_trace) {
20 use_browser = 0;
21 setup_pager();
22 return;
23 }
24
25 use_browser = 1;
26 newtInit();
27 newtCls();
28 newtSetSuspendCallback(newt_suspend, NULL);
29 ui_helpline__init();
30 ui_browser__init();
31}
32
33void exit_browser(bool wait_for_ok)
34{
35 if (use_browser > 0) {
36 if (wait_for_ok) {
37 char title[] = "Fatal Error", ok[] = "Ok";
38 newtWinMessage(title, ok, ui_helpline__last_msg);
39 }
40 newtFinished();
41 }
42}
diff --git a/tools/perf/util/ui/util.c b/tools/perf/util/ui/util.c
new file mode 100644
index 000000000000..04600e26ceea
--- /dev/null
+++ b/tools/perf/util/ui/util.c
@@ -0,0 +1,114 @@
1#include <newt.h>
2#include <signal.h>
3#include <stdio.h>
4#include <stdbool.h>
5#include <string.h>
6#include <sys/ttydefaults.h>
7
8#include "../cache.h"
9#include "../debug.h"
10#include "browser.h"
11#include "helpline.h"
12#include "util.h"
13
14newtComponent newt_form__new(void);
15
16static void newt_form__set_exit_keys(newtComponent self)
17{
18 newtFormAddHotKey(self, NEWT_KEY_LEFT);
19 newtFormAddHotKey(self, NEWT_KEY_ESCAPE);
20 newtFormAddHotKey(self, 'Q');
21 newtFormAddHotKey(self, 'q');
22 newtFormAddHotKey(self, CTRL('c'));
23}
24
25newtComponent newt_form__new(void)
26{
27 newtComponent self = newtForm(NULL, NULL, 0);
28 if (self)
29 newt_form__set_exit_keys(self);
30 return self;
31}
32
33int ui__popup_menu(int argc, char * const argv[])
34{
35 struct newtExitStruct es;
36 int i, rc = -1, max_len = 5;
37 newtComponent listbox, form = newt_form__new();
38
39 if (form == NULL)
40 return -1;
41
42 listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT);
43 if (listbox == NULL)
44 goto out_destroy_form;
45
46 newtFormAddComponent(form, listbox);
47
48 for (i = 0; i < argc; ++i) {
49 int len = strlen(argv[i]);
50 if (len > max_len)
51 max_len = len;
52 if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i))
53 goto out_destroy_form;
54 }
55
56 newtCenteredWindow(max_len, argc, NULL);
57 newtFormRun(form, &es);
58 rc = newtListboxGetCurrent(listbox) - NULL;
59 if (es.reason == NEWT_EXIT_HOTKEY)
60 rc = -1;
61 newtPopWindow();
62out_destroy_form:
63 newtFormDestroy(form);
64 return rc;
65}
66
67int ui__help_window(const char *text)
68{
69 struct newtExitStruct es;
70 newtComponent tb, form = newt_form__new();
71 int rc = -1;
72 int max_len = 0, nr_lines = 0;
73 const char *t;
74
75 if (form == NULL)
76 return -1;
77
78 t = text;
79 while (1) {
80 const char *sep = strchr(t, '\n');
81 int len;
82
83 if (sep == NULL)
84 sep = strchr(t, '\0');
85 len = sep - t;
86 if (max_len < len)
87 max_len = len;
88 ++nr_lines;
89 if (*sep == '\0')
90 break;
91 t = sep + 1;
92 }
93
94 tb = newtTextbox(0, 0, max_len, nr_lines, 0);
95 if (tb == NULL)
96 goto out_destroy_form;
97
98 newtTextboxSetText(tb, text);
99 newtFormAddComponent(form, tb);
100 newtCenteredWindow(max_len, nr_lines, NULL);
101 newtFormRun(form, &es);
102 newtPopWindow();
103 rc = 0;
104out_destroy_form:
105 newtFormDestroy(form);
106 return rc;
107}
108
109bool ui__dialog_yesno(const char *msg)
110{
111 /* newtWinChoice should really be accepting const char pointers... */
112 char yes[] = "Yes", no[] = "No";
113 return newtWinChoice(NULL, yes, no, (char *)msg) == 1;
114}
diff --git a/tools/perf/util/ui/util.h b/tools/perf/util/ui/util.h
new file mode 100644
index 000000000000..afcbc1d99531
--- /dev/null
+++ b/tools/perf/util/ui/util.h
@@ -0,0 +1,10 @@
1#ifndef _PERF_UI_UTIL_H_
2#define _PERF_UI_UTIL_H_ 1
3
4#include <stdbool.h>
5
6int ui__popup_menu(int argc, char * const argv[]);
7int ui__help_window(const char *text);
8bool ui__dialog_yesno(const char *msg);
9
10#endif /* _PERF_UI_UTIL_H_ */