aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArnaldo Carvalho de Melo <acme@redhat.com>2010-03-11 18:12:44 -0500
committerIngo Molnar <mingo@elte.hu>2010-03-12 04:28:46 -0500
commitf9224c5c944b60cf709db4adf1f5195264b8d194 (patch)
tree6c40539b5111a6e9a021e0fe6e22e95997217c3c
parentdd2ee78dd8e4c6d6f1a333fd60c3dd27d1b07042 (diff)
perf report: Implement initial UI using newt
Newt has widespread availability and provides a rather simple API as can be seen by the size of this patch. The work needed to support it will benefit other frontends too. In this initial patch it just checks if the output is a tty, if not it falls back to the previous behaviour, also if newt-devel/libnewt-dev is not installed the previous behaviour is maintaned. Pressing enter on a symbol will annotate it, ESC in the annotation window will return to the report symbol list. More work will be done to remove the special casing in color_fprintf, stop using fmemopen/FILE in the printing of hist_entries, etc. Also the annotation doesn't need to be done via spawning "perf annotate" and then browsing its output, we can do better by calling directly the builtin-annotate.c functions, that would then be moved to tools/perf/util/annotate.c and shared with perf top, etc But lets go by baby steps, this patch already improves perf usability by allowing to quickly do annotations on symbols from the report screen and provides a first experimentation with libnewt/TUI integration of tools. Tested on RHEL5 and Fedora12 X86_64 and on Debian PARISC64 to browse a perf.data file collected on a Fedora12 x86_64 box. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Avi Kivity <avi@redhat.com> Cc: Frédéric Weisbecker <fweisbec@gmail.com> Cc: Mike Galbraith <efault@gmx.de> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Paul Mackerras <paulus@samba.org> LKML-Reference: <1268349164-5822-5-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r--tools/perf/Makefile8
-rw-r--r--tools/perf/builtin-report.c47
-rw-r--r--tools/perf/perf.c2
-rw-r--r--tools/perf/util/cache.h14
-rw-r--r--tools/perf/util/color.c5
-rw-r--r--tools/perf/util/debug.c6
-rw-r--r--tools/perf/util/debug.h1
-rw-r--r--tools/perf/util/hist.h5
-rw-r--r--tools/perf/util/newt.c194
-rw-r--r--tools/perf/util/session.h9
10 files changed, 270 insertions, 21 deletions
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 8a8f52db7e38..0abd25ee595f 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -513,6 +513,14 @@ else
513 LIB_OBJS += util/probe-finder.o 513 LIB_OBJS += util/probe-finder.o
514endif 514endif
515 515
516ifneq ($(shell sh -c "(echo '\#include <newt.h>'; echo 'int main(void) { newtInit(); newtCls(); return newtFinished(); }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -lnewt -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y)
517 msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev);
518 BASIC_CFLAGS += -DNO_NEWT_SUPPORT
519else
520 EXTLIBS += -lnewt
521 LIB_OBJS += util/newt.o
522endif
523
516ifndef NO_LIBPERL 524ifndef NO_LIBPERL
517PERL_EMBED_LDOPTS = `perl -MExtUtils::Embed -e ldopts 2>/dev/null` 525PERL_EMBED_LDOPTS = `perl -MExtUtils::Embed -e ldopts 2>/dev/null`
518PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null` 526PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null`
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index f815de25d0fc..1f9f8695f055 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -267,6 +267,7 @@ static int __cmd_report(void)
267 int ret = -EINVAL; 267 int ret = -EINVAL;
268 struct perf_session *session; 268 struct perf_session *session;
269 struct rb_node *next; 269 struct rb_node *next;
270 const char *help = "For a higher level overview, try: perf report --sort comm,dso";
270 271
271 session = perf_session__new(input_name, O_RDONLY, force); 272 session = perf_session__new(input_name, O_RDONLY, force);
272 if (session == NULL) 273 if (session == NULL)
@@ -301,30 +302,38 @@ static int __cmd_report(void)
301 stats = rb_entry(next, struct event_stat_id, rb_node); 302 stats = rb_entry(next, struct event_stat_id, rb_node);
302 perf_session__collapse_resort(&stats->hists); 303 perf_session__collapse_resort(&stats->hists);
303 perf_session__output_resort(&stats->hists, stats->stats.total); 304 perf_session__output_resort(&stats->hists, stats->stats.total);
304 if (rb_first(&session->stats_by_id) ==
305 rb_last(&session->stats_by_id))
306 fprintf(stdout, "# Samples: %Ld\n#\n",
307 stats->stats.total);
308 else
309 fprintf(stdout, "# Samples: %Ld %s\n#\n",
310 stats->stats.total,
311 __event_name(stats->type, stats->config));
312 305
313 perf_session__fprintf_hists(&stats->hists, NULL, false, stdout, 306 if (use_browser)
307 perf_session__browse_hists(&stats->hists,
308 stats->stats.total, help);
309 else {
310 if (rb_first(&session->stats_by_id) ==
311 rb_last(&session->stats_by_id))
312 fprintf(stdout, "# Samples: %Ld\n#\n",
313 stats->stats.total);
314 else
315 fprintf(stdout, "# Samples: %Ld %s\n#\n",
316 stats->stats.total,
317 __event_name(stats->type, stats->config));
318
319 perf_session__fprintf_hists(&stats->hists, NULL, false, stdout,
314 stats->stats.total); 320 stats->stats.total);
315 fprintf(stdout, "\n\n"); 321 fprintf(stdout, "\n\n");
322 }
323
316 next = rb_next(&stats->rb_node); 324 next = rb_next(&stats->rb_node);
317 } 325 }
318 326
319 if (sort_order == default_sort_order && 327 if (!use_browser && sort_order == default_sort_order &&
320 parent_pattern == default_parent_pattern) 328 parent_pattern == default_parent_pattern) {
321 fprintf(stdout, "#\n# (For a higher level overview, try: perf report --sort comm,dso)\n#\n"); 329 fprintf(stdout, "#\n# (%s)\n#\n", help);
322 330
323 if (show_threads) { 331 if (show_threads) {
324 bool raw_printing_style = !strcmp(pretty_printing_style, "raw"); 332 bool style = !strcmp(pretty_printing_style, "raw");
325 perf_read_values_display(stdout, &show_threads_values, 333 perf_read_values_display(stdout, &show_threads_values,
326 raw_printing_style); 334 style);
327 perf_read_values_destroy(&show_threads_values); 335 perf_read_values_destroy(&show_threads_values);
336 }
328 } 337 }
329out_delete: 338out_delete:
330 perf_session__delete(session); 339 perf_session__delete(session);
@@ -447,7 +456,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
447{ 456{
448 argc = parse_options(argc, argv, options, report_usage, 0); 457 argc = parse_options(argc, argv, options, report_usage, 0);
449 458
450 setup_pager(); 459 setup_browser();
451 460
452 if (symbol__init() < 0) 461 if (symbol__init() < 0)
453 return -1; 462 return -1;
diff --git a/tools/perf/perf.c b/tools/perf/perf.c
index 57cb107c1f13..9ff186b57cb7 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -265,6 +265,8 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
265 if (status) 265 if (status)
266 return status & 0xff; 266 return status & 0xff;
267 267
268 exit_browser();
269
268 /* Somebody closed stdout? */ 270 /* Somebody closed stdout? */
269 if (fstat(fileno(stdout), &st)) 271 if (fstat(fileno(stdout), &st))
270 return 0; 272 return 0;
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h
index 918eb376abe3..47b12a3d11bf 100644
--- a/tools/perf/util/cache.h
+++ b/tools/perf/util/cache.h
@@ -1,6 +1,7 @@
1#ifndef __PERF_CACHE_H 1#ifndef __PERF_CACHE_H
2#define __PERF_CACHE_H 2#define __PERF_CACHE_H
3 3
4#include <stdbool.h>
4#include "util.h" 5#include "util.h"
5#include "strbuf.h" 6#include "strbuf.h"
6#include "../perf.h" 7#include "../perf.h"
@@ -69,6 +70,19 @@ extern const char *pager_program;
69extern int pager_in_use(void); 70extern int pager_in_use(void);
70extern int pager_use_color; 71extern int pager_use_color;
71 72
73extern bool use_browser;
74
75#ifdef NO_NEWT_SUPPORT
76static inline void setup_browser(void)
77{
78 setup_pager();
79}
80static inline void exit_browser(void) {}
81#else
82void setup_browser(void);
83void exit_browser(void);
84#endif
85
72extern const char *editor_program; 86extern const char *editor_program;
73extern const char *excludes_file; 87extern const char *excludes_file;
74 88
diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c
index e88bca55a599..9da01914e0af 100644
--- a/tools/perf/util/color.c
+++ b/tools/perf/util/color.c
@@ -203,7 +203,10 @@ int color_fprintf(FILE *fp, const char *color, const char *fmt, ...)
203 int r; 203 int r;
204 204
205 va_start(args, fmt); 205 va_start(args, fmt);
206 r = color_vfprintf(fp, color, fmt, args); 206 if (use_browser)
207 r = vfprintf(fp, fmt, args);
208 else
209 r = color_vfprintf(fp, color, fmt, args);
207 va_end(args); 210 va_end(args);
208 return r; 211 return r;
209} 212}
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c
index 0905600c3851..033d66db863a 100644
--- a/tools/perf/util/debug.c
+++ b/tools/perf/util/debug.c
@@ -6,6 +6,7 @@
6#include <stdarg.h> 6#include <stdarg.h>
7#include <stdio.h> 7#include <stdio.h>
8 8
9#include "cache.h"
9#include "color.h" 10#include "color.h"
10#include "event.h" 11#include "event.h"
11#include "debug.h" 12#include "debug.h"
@@ -21,7 +22,10 @@ int eprintf(int level, const char *fmt, ...)
21 22
22 if (verbose >= level) { 23 if (verbose >= level) {
23 va_start(args, fmt); 24 va_start(args, fmt);
24 ret = vfprintf(stderr, fmt, args); 25 if (use_browser)
26 ret = browser__show_help(fmt, args);
27 else
28 ret = vfprintf(stderr, fmt, args);
25 va_end(args); 29 va_end(args);
26 } 30 }
27 31
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h
index 58720a181591..03accb867996 100644
--- a/tools/perf/util/debug.h
+++ b/tools/perf/util/debug.h
@@ -9,5 +9,6 @@ extern int dump_trace;
9 9
10int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); 10int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
11void trace_event(event_t *event); 11void trace_event(event_t *event);
12int browser__show_help(const char *format, va_list ap);
12 13
13#endif /* __PERF_DEBUG_H */ 14#endif /* __PERF_DEBUG_H */
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 16f360cce5bf..fe366ce5db45 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -18,6 +18,11 @@ struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists,
18 u64 count, bool *hit); 18 u64 count, bool *hit);
19extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); 19extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *);
20extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); 20extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *);
21size_t hist_entry__fprintf(struct hist_entry *self,
22 struct perf_session *pair_session,
23 bool show_displacement,
24 long displacement, FILE *fp,
25 u64 session_total);
21void hist_entry__free(struct hist_entry *); 26void hist_entry__free(struct hist_entry *);
22 27
23void perf_session__output_resort(struct rb_root *hists, u64 total_samples); 28void perf_session__output_resort(struct rb_root *hists, u64 total_samples);
diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c
new file mode 100644
index 000000000000..3d3a936acb67
--- /dev/null
+++ b/tools/perf/util/newt.c
@@ -0,0 +1,194 @@
1#define _GNU_SOURCE
2#include <stdio.h>
3#undef _GNU_SOURCE
4
5#include <stdlib.h>
6#include <newt.h>
7
8#include "cache.h"
9#include "hist.h"
10#include "session.h"
11#include "sort.h"
12#include "symbol.h"
13
14static size_t hist_entry__append_browser(struct hist_entry *self,
15 newtComponent listbox, u64 total)
16{
17 char bf[1024];
18 size_t len;
19 FILE *fp;
20
21 if (symbol_conf.exclude_other && !self->parent)
22 return 0;
23
24 fp = fmemopen(bf, sizeof(bf), "w");
25 if (fp == NULL)
26 return 0;
27
28 len = hist_entry__fprintf(self, NULL, false, 0, fp, total);
29
30 fclose(fp);
31 newtListboxAppendEntry(listbox, bf, self);
32 return len;
33}
34
35static void hist_entry__annotate_browser(struct hist_entry *self)
36{
37 FILE *fp;
38 struct winsize ws;
39 newtComponent form, listbox;
40 struct newtExitStruct es;
41 char *str;
42 size_t line_len, max_line_len = 0;
43 size_t max_usable_width;
44 char *line = NULL;
45
46 if (self->sym == NULL)
47 return;
48
49 if (asprintf(&str, "perf annotate %s | expand", self->sym->name) < 0)
50 return;
51
52 fp = popen(str, "r");
53 if (fp == NULL)
54 goto out_free_str;
55
56 newtPushHelpLine("Press ESC to exit");
57 get_term_dimensions(&ws);
58 listbox = newtListbox(0, 0, ws.ws_row - 5, NEWT_FLAG_SCROLL);
59
60 while (!feof(fp)) {
61 if (getline(&line, &line_len, fp) < 0 || !line_len)
62 break;
63 while (line_len != 0 && isspace(line[line_len - 1]))
64 line[--line_len] = '\0';
65
66 if (line_len > max_line_len)
67 max_line_len = line_len;
68 newtListboxAppendEntry(listbox, line, NULL);
69 }
70 fclose(fp);
71 free(line);
72
73 max_usable_width = ws.ws_col - 22;
74 if (max_line_len > max_usable_width)
75 max_line_len = max_usable_width;
76
77 newtListboxSetWidth(listbox, max_line_len);
78
79 newtCenteredWindow(max_line_len + 2, ws.ws_row - 5, self->sym->name);
80 form = newtForm(NULL, NULL, 0);
81 newtFormAddHotKey(form, NEWT_KEY_ESCAPE);
82 newtFormAddComponents(form, listbox, NULL);
83
84 newtFormRun(form, &es);
85 newtFormDestroy(form);
86 newtPopWindow();
87 newtPopHelpLine();
88out_free_str:
89 free(str);
90}
91
92void perf_session__browse_hists(struct rb_root *hists, u64 session_total,
93 const char *helpline)
94{
95 struct sort_entry *se;
96 struct rb_node *nd;
97 unsigned int width;
98 char *col_width = symbol_conf.col_width_list_str;
99 struct winsize ws;
100 size_t max_len = 0;
101 char str[1024];
102 newtComponent form, listbox;
103 struct newtExitStruct es;
104
105 snprintf(str, sizeof(str), "Samples: %Ld", session_total);
106 newtDrawRootText(0, 0, str);
107 newtPushHelpLine(helpline);
108
109 get_term_dimensions(&ws);
110
111 form = newtForm(NULL, NULL, 0);
112 newtFormAddHotKey(form, NEWT_KEY_ESCAPE);
113
114 listbox = newtListbox(1, 1, ws.ws_row - 2, (NEWT_FLAG_SCROLL |
115 NEWT_FLAG_BORDER |
116 NEWT_FLAG_RETURNEXIT));
117
118 list_for_each_entry(se, &hist_entry__sort_list, list) {
119 if (se->elide)
120 continue;
121 width = strlen(se->header);
122 if (se->width) {
123 if (symbol_conf.col_width_list_str) {
124 if (col_width) {
125 *se->width = atoi(col_width);
126 col_width = strchr(col_width, ',');
127 if (col_width)
128 ++col_width;
129 }
130 }
131 *se->width = max(*se->width, width);
132 }
133 }
134
135 for (nd = rb_first(hists); nd; nd = rb_next(nd)) {
136 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
137 size_t len = hist_entry__append_browser(h, listbox, session_total);
138 if (len > max_len)
139 max_len = len;
140 }
141
142 newtListboxSetWidth(listbox, max_len);
143 newtFormAddComponents(form, listbox, NULL);
144
145 while (1) {
146 struct hist_entry *selection;
147
148 newtFormRun(form, &es);
149 if (es.reason == NEWT_EXIT_HOTKEY)
150 break;
151 selection = newtListboxGetCurrent(listbox);
152 hist_entry__annotate_browser(selection);
153 }
154
155 newtFormDestroy(form);
156}
157
158int browser__show_help(const char *format, va_list ap)
159{
160 int ret;
161 static int backlog;
162 static char msg[1024];
163
164 ret = vsnprintf(msg + backlog, sizeof(msg) - backlog, format, ap);
165 backlog += ret;
166
167 if (msg[backlog - 1] == '\n') {
168 newtPopHelpLine();
169 newtPushHelpLine(msg);
170 newtRefresh();
171 backlog = 0;
172 }
173
174 return ret;
175}
176
177bool use_browser;
178
179void setup_browser(void)
180{
181 if (!isatty(1))
182 return;
183
184 use_browser = true;
185 newtInit();
186 newtCls();
187 newtPushHelpLine(" ");
188}
189
190void exit_browser(void)
191{
192 if (use_browser)
193 newtFinished();
194}
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index 5c33417eebb3..34d73395baac 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -86,4 +86,13 @@ static inline struct map *
86{ 86{
87 return map_groups__new_module(&self->kmaps, start, filename); 87 return map_groups__new_module(&self->kmaps, start, filename);
88} 88}
89
90#ifdef NO_NEWT_SUPPORT
91static inline void perf_session__browse_hists(struct rb_root *hists __used,
92 u64 session_total __used,
93 const char *helpline __used) {}
94#else
95void perf_session__browse_hists(struct rb_root *hists, u64 session_total,
96 const char *helpline);
97#endif
89#endif /* __PERF_SESSION_H */ 98#endif /* __PERF_SESSION_H */