blob: 12b229bb9dc1bfb974c7b3409b9a0bd5b8409d3a [file] [log] [blame]
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -03001#define _GNU_SOURCE
2#include <stdio.h>
3#undef _GNU_SOURCE
4
5#include <stdlib.h>
6#include <newt.h>
Arnaldo Carvalho de Melo7081e082010-03-12 10:48:12 -03007#include <sys/ttydefaults.h>
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -03008
9#include "cache.h"
10#include "hist.h"
11#include "session.h"
12#include "sort.h"
13#include "symbol.h"
14
Arnaldo Carvalho de Melo7081e082010-03-12 10:48:12 -030015static void newt_form__set_exit_keys(newtComponent self)
16{
17 newtFormAddHotKey(self, NEWT_KEY_ESCAPE);
18 newtFormAddHotKey(self, 'Q');
19 newtFormAddHotKey(self, 'q');
20 newtFormAddHotKey(self, CTRL('c'));
21}
22
23static newtComponent newt_form__new(void)
24{
25 newtComponent self = newtForm(NULL, NULL, 0);
26 if (self)
27 newt_form__set_exit_keys(self);
28 return self;
29}
30
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -030031/*
32 * When debugging newt problems it was useful to be able to "unroll"
33 * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate
34 * a source file with the sequence of calls to these methods, to then
35 * tweak the arrays to get the intended results, so I'm keeping this code
36 * here, may be useful again in the future.
37 */
38#undef NEWT_DEBUG
39
40static void newt_checkbox_tree__add(newtComponent tree, const char *str,
41 void *priv, int *indexes)
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -030042{
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -030043#ifdef NEWT_DEBUG
44 /* Print the newtCheckboxTreeAddArray to tinker with its index arrays */
45 int i = 0, len = 40 - strlen(str);
46
47 fprintf(stderr,
48 "\tnewtCheckboxTreeAddItem(tree, %*.*s\"%s\", (void *)%p, 0, ",
49 len, len, " ", str, priv);
50 while (indexes[i] != NEWT_ARG_LAST) {
51 if (indexes[i] != NEWT_ARG_APPEND)
52 fprintf(stderr, " %d,", indexes[i]);
53 else
54 fprintf(stderr, " %s,", "NEWT_ARG_APPEND");
55 ++i;
56 }
57 fprintf(stderr, " %s", " NEWT_ARG_LAST);\n");
58 fflush(stderr);
59#endif
60 newtCheckboxTreeAddArray(tree, str, priv, 0, indexes);
61}
62
63static char *callchain_list__sym_name(struct callchain_list *self,
64 char *bf, size_t bfsize)
65{
66 if (self->sym)
67 return self->sym->name;
68
69 snprintf(bf, bfsize, "%#Lx", self->ip);
70 return bf;
71}
72
73static void __callchain__append_graph_browser(struct callchain_node *self,
74 newtComponent tree, u64 total,
75 int *indexes, int depth)
76{
77 struct rb_node *node;
78 u64 new_total, remaining;
79 int idx = 0;
80
81 if (callchain_param.mode == CHAIN_GRAPH_REL)
82 new_total = self->children_hit;
83 else
84 new_total = total;
85
86 remaining = new_total;
87 node = rb_first(&self->rb_root);
88 while (node) {
89 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
90 struct rb_node *next = rb_next(node);
91 u64 cumul = cumul_hits(child);
92 struct callchain_list *chain;
93 int first = true, printed = 0;
94 int chain_idx = -1;
95 remaining -= cumul;
96
97 indexes[depth] = NEWT_ARG_APPEND;
98 indexes[depth + 1] = NEWT_ARG_LAST;
99
100 list_for_each_entry(chain, &child->val, list) {
101 char ipstr[BITS_PER_LONG / 4 + 1],
102 *alloc_str = NULL;
103 const char *str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
104
105 if (first) {
106 double percent = cumul * 100.0 / new_total;
107
108 first = false;
109 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
110 str = "Not enough memory!";
111 else
112 str = alloc_str;
113 } else {
114 indexes[depth] = idx;
115 indexes[depth + 1] = NEWT_ARG_APPEND;
116 indexes[depth + 2] = NEWT_ARG_LAST;
117 ++chain_idx;
118 }
119 newt_checkbox_tree__add(tree, str, chain->sym, indexes);
120 free(alloc_str);
121 ++printed;
122 }
123
124 indexes[depth] = idx;
125 if (chain_idx != -1)
126 indexes[depth + 1] = chain_idx;
127 if (printed != 0)
128 ++idx;
129 __callchain__append_graph_browser(child, tree, new_total, indexes,
130 depth + (chain_idx != -1 ? 2 : 1));
131 node = next;
132 }
133}
134
135static void callchain__append_graph_browser(struct callchain_node *self,
136 newtComponent tree, u64 total,
137 int *indexes, int parent_idx)
138{
139 struct callchain_list *chain;
140 int i = 0;
141
142 indexes[1] = NEWT_ARG_APPEND;
143 indexes[2] = NEWT_ARG_LAST;
144
145 list_for_each_entry(chain, &self->val, list) {
146 char ipstr[BITS_PER_LONG / 4 + 1], *str;
147
148 if (chain->ip >= PERF_CONTEXT_MAX)
149 continue;
150
151 if (!i++ && sort__first_dimension == SORT_SYM)
152 continue;
153
154 str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
155 newt_checkbox_tree__add(tree, str, chain->sym, indexes);
156 }
157
158 indexes[1] = parent_idx;
159 indexes[2] = NEWT_ARG_APPEND;
160 indexes[3] = NEWT_ARG_LAST;
161 __callchain__append_graph_browser(self, tree, total, indexes, 2);
162}
163
164static void hist_entry__append_callchain_browser(struct hist_entry *self,
165 newtComponent tree, u64 total, int parent_idx)
166{
167 struct rb_node *rb_node;
168 int indexes[1024] = { [0] = parent_idx, };
169 int idx = 0;
170 struct callchain_node *chain;
171
172 rb_node = rb_first(&self->sorted_chain);
173 while (rb_node) {
174 chain = rb_entry(rb_node, struct callchain_node, rb_node);
175 switch (callchain_param.mode) {
176 case CHAIN_FLAT:
177 break;
178 case CHAIN_GRAPH_ABS: /* falldown */
179 case CHAIN_GRAPH_REL:
180 callchain__append_graph_browser(chain, tree, total, indexes, idx++);
181 break;
182 case CHAIN_NONE:
183 default:
184 break;
185 }
186 rb_node = rb_next(rb_node);
187 }
188}
189
190/*
191 * FIXME: get lib/string.c linked with perf somehow
192 */
193static char *skip_spaces(const char *str)
194{
195 while (isspace(*str))
196 ++str;
197 return (char *)str;
198}
199
200static char *strim(char *s)
201{
202 size_t size;
203 char *end;
204
205 s = skip_spaces(s);
206 size = strlen(s);
207 if (!size)
208 return s;
209
210 end = s + size - 1;
211 while (end >= s && isspace(*end))
212 end--;
213 *(end + 1) = '\0';
214
215 return s;
216}
217
218static size_t hist_entry__append_browser(struct hist_entry *self,
219 newtComponent tree, u64 total)
220{
221 char bf[1024], *s;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300222 FILE *fp;
223
224 if (symbol_conf.exclude_other && !self->parent)
225 return 0;
226
227 fp = fmemopen(bf, sizeof(bf), "w");
228 if (fp == NULL)
229 return 0;
230
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -0300231 hist_entry__fprintf(self, NULL, false, 0, fp, total);
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300232 fclose(fp);
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -0300233
234 /*
235 * FIXME: We shouldn't need to trim, as the printing routines shouldn't
236 * add spaces it in the first place, the stdio output routines should
237 * call a __snprintf method instead of the current __print (that
238 * actually is a __fprintf) one, but get the raw string and _then_ add
239 * the newline, as this is a detail of stdio printing, not needed in
240 * other UIs, e.g. newt.
241 */
242 s = strim(bf);
243
244 if (symbol_conf.use_callchain) {
245 int indexes[2];
246
247 indexes[0] = NEWT_ARG_APPEND;
248 indexes[1] = NEWT_ARG_LAST;
249 newt_checkbox_tree__add(tree, s, self->sym, indexes);
250 } else
251 newtListboxAppendEntry(tree, s, self->sym);
252
253 return strlen(s);
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300254}
255
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -0300256static void symbol__annotate_browser(const struct symbol *self)
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300257{
258 FILE *fp;
Arnaldo Carvalho de Melocb7afb72010-03-12 12:46:47 -0300259 int cols, rows;
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -0300260 newtComponent form, tree;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300261 struct newtExitStruct es;
262 char *str;
263 size_t line_len, max_line_len = 0;
264 size_t max_usable_width;
265 char *line = NULL;
266
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -0300267 if (self == NULL)
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300268 return;
269
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -0300270 if (asprintf(&str, "perf annotate %s 2>&1 | expand", self->name) < 0)
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300271 return;
272
273 fp = popen(str, "r");
274 if (fp == NULL)
275 goto out_free_str;
276
277 newtPushHelpLine("Press ESC to exit");
Arnaldo Carvalho de Melocb7afb72010-03-12 12:46:47 -0300278 newtGetScreenSize(&cols, &rows);
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -0300279 tree = newtListbox(0, 0, rows - 5, NEWT_FLAG_SCROLL);
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300280
281 while (!feof(fp)) {
282 if (getline(&line, &line_len, fp) < 0 || !line_len)
283 break;
284 while (line_len != 0 && isspace(line[line_len - 1]))
285 line[--line_len] = '\0';
286
287 if (line_len > max_line_len)
288 max_line_len = line_len;
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -0300289 newtListboxAppendEntry(tree, line, NULL);
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300290 }
291 fclose(fp);
292 free(line);
293
Arnaldo Carvalho de Melocb7afb72010-03-12 12:46:47 -0300294 max_usable_width = cols - 22;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300295 if (max_line_len > max_usable_width)
296 max_line_len = max_usable_width;
297
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -0300298 newtListboxSetWidth(tree, max_line_len);
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300299
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -0300300 newtCenteredWindow(max_line_len + 2, rows - 5, self->name);
Arnaldo Carvalho de Melo7081e082010-03-12 10:48:12 -0300301 form = newt_form__new();
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -0300302 newtFormAddComponents(form, tree, NULL);
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300303
304 newtFormRun(form, &es);
305 newtFormDestroy(form);
306 newtPopWindow();
307 newtPopHelpLine();
308out_free_str:
309 free(str);
310}
311
312void perf_session__browse_hists(struct rb_root *hists, u64 session_total,
313 const char *helpline)
314{
315 struct sort_entry *se;
316 struct rb_node *nd;
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -0300317 char seq[] = ".";
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300318 unsigned int width;
319 char *col_width = symbol_conf.col_width_list_str;
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -0300320 int rows, cols, idx;
321 int max_len = 0;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300322 char str[1024];
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -0300323 newtComponent form, tree;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300324 struct newtExitStruct es;
325
326 snprintf(str, sizeof(str), "Samples: %Ld", session_total);
327 newtDrawRootText(0, 0, str);
328 newtPushHelpLine(helpline);
329
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -0300330 newtGetScreenSize(&cols, &rows);
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300331
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -0300332 if (symbol_conf.use_callchain)
333 tree = newtCheckboxTreeMulti(0, 0, rows - 5, seq,
334 NEWT_FLAG_SCROLL);
335 else
336 tree = newtListbox(0, 0, rows - 5, (NEWT_FLAG_SCROLL |
337 NEWT_FLAG_RETURNEXIT));
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300338
339 list_for_each_entry(se, &hist_entry__sort_list, list) {
340 if (se->elide)
341 continue;
342 width = strlen(se->header);
343 if (se->width) {
344 if (symbol_conf.col_width_list_str) {
345 if (col_width) {
346 *se->width = atoi(col_width);
347 col_width = strchr(col_width, ',');
348 if (col_width)
349 ++col_width;
350 }
351 }
352 *se->width = max(*se->width, width);
353 }
354 }
355
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -0300356 idx = 0;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300357 for (nd = rb_first(hists); nd; nd = rb_next(nd)) {
358 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -0300359 int len = hist_entry__append_browser(h, tree, session_total);
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300360 if (len > max_len)
361 max_len = len;
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -0300362 if (symbol_conf.use_callchain) {
363 hist_entry__append_callchain_browser(h, tree, session_total, idx++);
364 if (idx > 3300)
365 break;
366 }
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300367 }
368
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -0300369 if (max_len > cols)
370 max_len = cols - 3;
371
372 if (!symbol_conf.use_callchain)
373 newtListboxSetWidth(tree, max_len);
374
375 newtCenteredWindow(max_len + (symbol_conf.use_callchain ? 5 : 0),
376 rows - 5, "Report");
377 form = newt_form__new();
378 newtFormAddHotKey(form, 'A');
379 newtFormAddHotKey(form, 'a');
380 newtFormAddComponents(form, tree, NULL);
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300381
382 while (1) {
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -0300383 const struct symbol *selection;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300384
385 newtFormRun(form, &es);
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -0300386 if (es.reason == NEWT_EXIT_HOTKEY &&
387 toupper(es.u.key) != 'A')
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300388 break;
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -0300389 if (!symbol_conf.use_callchain)
390 selection = newtListboxGetCurrent(tree);
391 else
392 selection = newtCheckboxTreeGetCurrent(tree);
393 symbol__annotate_browser(selection);
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300394 }
395
396 newtFormDestroy(form);
Arnaldo Carvalho de Melo4ded2b22010-03-22 17:52:49 -0300397 newtPopWindow();
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300398}
399
Arnaldo Carvalho de Melof3a1f0e2010-03-22 13:10:25 -0300400static char browser__last_msg[1024];
401
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300402int browser__show_help(const char *format, va_list ap)
403{
404 int ret;
405 static int backlog;
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300406
Arnaldo Carvalho de Melof3a1f0e2010-03-22 13:10:25 -0300407 ret = vsnprintf(browser__last_msg + backlog,
408 sizeof(browser__last_msg) - backlog, format, ap);
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300409 backlog += ret;
410
Arnaldo Carvalho de Melof3a1f0e2010-03-22 13:10:25 -0300411 if (browser__last_msg[backlog - 1] == '\n') {
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300412 newtPopHelpLine();
Arnaldo Carvalho de Melof3a1f0e2010-03-22 13:10:25 -0300413 newtPushHelpLine(browser__last_msg);
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300414 newtRefresh();
415 backlog = 0;
416 }
417
418 return ret;
419}
420
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300421void setup_browser(void)
422{
423 if (!isatty(1))
424 return;
425
426 use_browser = true;
427 newtInit();
428 newtCls();
429 newtPushHelpLine(" ");
430}
431
Arnaldo Carvalho de Melof3a1f0e2010-03-22 13:10:25 -0300432void exit_browser(bool wait_for_ok)
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300433{
Arnaldo Carvalho de Melof3a1f0e2010-03-22 13:10:25 -0300434 if (use_browser) {
435 if (wait_for_ok) {
436 char title[] = "Fatal Error", ok[] = "Ok";
437 newtWinMessage(title, ok, browser__last_msg);
438 }
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300439 newtFinished();
Arnaldo Carvalho de Melof3a1f0e2010-03-22 13:10:25 -0300440 }
Arnaldo Carvalho de Melof9224c52010-03-11 20:12:44 -0300441}