perf report: Implement Newt callgraphs
[linux-2.6.git] / tools / perf / util / newt.c
1 #define _GNU_SOURCE
2 #include <stdio.h>
3 #undef _GNU_SOURCE
4
5 #include <stdlib.h>
6 #include <newt.h>
7 #include <sys/ttydefaults.h>
8
9 #include "cache.h"
10 #include "hist.h"
11 #include "session.h"
12 #include "sort.h"
13 #include "symbol.h"
14
15 static 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
23 static 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
31 /*
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
40 static void newt_checkbox_tree__add(newtComponent tree, const char *str,
41                                     void *priv, int *indexes)
42 {
43 #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
63 static 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
73 static 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
135 static 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
164 static 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  */
193 static char *skip_spaces(const char *str)
194 {
195         while (isspace(*str))
196                 ++str;
197         return (char *)str;
198 }
199
200 static 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
218 static size_t hist_entry__append_browser(struct hist_entry *self,
219                                          newtComponent tree, u64 total)
220 {
221         char bf[1024], *s;
222         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
231         hist_entry__fprintf(self, NULL, false, 0, fp, total);
232         fclose(fp);
233
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);
254 }
255
256 static void symbol__annotate_browser(const struct symbol *self)
257 {
258         FILE *fp;
259         int cols, rows;
260         newtComponent form, tree;
261         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
267         if (self == NULL)
268                 return;
269
270         if (asprintf(&str, "perf annotate %s 2>&1 | expand", self->name) < 0)
271                 return;
272
273         fp = popen(str, "r");
274         if (fp == NULL)
275                 goto out_free_str;
276
277         newtPushHelpLine("Press ESC to exit");
278         newtGetScreenSize(&cols, &rows);
279         tree = newtListbox(0, 0, rows - 5, NEWT_FLAG_SCROLL);
280
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;
289                 newtListboxAppendEntry(tree, line, NULL);
290         }
291         fclose(fp);
292         free(line);
293
294         max_usable_width = cols - 22;
295         if (max_line_len > max_usable_width)
296                 max_line_len = max_usable_width;
297
298         newtListboxSetWidth(tree, max_line_len);
299
300         newtCenteredWindow(max_line_len + 2, rows - 5, self->name);
301         form = newt_form__new();
302         newtFormAddComponents(form, tree, NULL);
303
304         newtFormRun(form, &es);
305         newtFormDestroy(form);
306         newtPopWindow();
307         newtPopHelpLine();
308 out_free_str:
309         free(str);
310 }
311
312 void 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;
317         char seq[] = ".";
318         unsigned int width;
319         char *col_width = symbol_conf.col_width_list_str;
320         int rows, cols, idx;
321         int max_len = 0;
322         char str[1024];
323         newtComponent form, tree;
324         struct newtExitStruct es;
325
326         snprintf(str, sizeof(str), "Samples: %Ld", session_total);
327         newtDrawRootText(0, 0, str);
328         newtPushHelpLine(helpline);
329
330         newtGetScreenSize(&cols, &rows);
331
332         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));
338
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
356         idx = 0;
357         for (nd = rb_first(hists); nd; nd = rb_next(nd)) {
358                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
359                 int len = hist_entry__append_browser(h, tree, session_total);
360                 if (len > max_len)
361                         max_len = len;
362                 if (symbol_conf.use_callchain) {
363                         hist_entry__append_callchain_browser(h, tree, session_total, idx++);
364                         if (idx > 3300)
365                                 break;
366                 }
367         }
368
369         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);
381
382         while (1) {
383                 const struct symbol *selection;
384
385                 newtFormRun(form, &es);
386                 if (es.reason == NEWT_EXIT_HOTKEY &&
387                     toupper(es.u.key) != 'A')
388                         break;
389                 if (!symbol_conf.use_callchain)
390                         selection = newtListboxGetCurrent(tree);
391                 else
392                         selection = newtCheckboxTreeGetCurrent(tree);
393                 symbol__annotate_browser(selection);
394         }
395
396         newtFormDestroy(form);
397         newtPopWindow();
398 }
399
400 static char browser__last_msg[1024];
401
402 int browser__show_help(const char *format, va_list ap)
403 {
404         int ret;
405         static int backlog;
406
407         ret = vsnprintf(browser__last_msg + backlog,
408                         sizeof(browser__last_msg) - backlog, format, ap);
409         backlog += ret;
410
411         if (browser__last_msg[backlog - 1] == '\n') {
412                 newtPopHelpLine();
413                 newtPushHelpLine(browser__last_msg);
414                 newtRefresh();
415                 backlog = 0;
416         }
417
418         return ret;
419 }
420
421 void setup_browser(void)
422 {
423         if (!isatty(1))
424                 return;
425
426         use_browser = true;
427         newtInit();
428         newtCls();
429         newtPushHelpLine(" ");
430 }
431
432 void exit_browser(bool wait_for_ok)
433 {
434         if (use_browser) {
435                 if (wait_for_ok) {
436                         char title[] = "Fatal Error", ok[] = "Ok";
437                         newtWinMessage(title, ok, browser__last_msg);
438                 }
439                 newtFinished();
440         }
441 }