aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorArnaldo Carvalho de Melo <acme@redhat.com>2010-03-22 16:52:49 -0400
committerIngo Molnar <mingo@elte.hu>2010-03-22 16:58:08 -0400
commit4ded2b250f1fbba4e414d17dc55ee513485c0aa1 (patch)
treea6f3f21bfaaa54b0637acf3d6caea97e8653df6e /tools
parent478b0973bf8c90db3677fbb8d812e2bdefc43d9b (diff)
perf report: Implement Newt callgraphs
Starts collapsed, allows annotating by pressing 'A' or 'a' on the symbol, be it the top level one or any of the symbols in the chains. It (ab)uses the only tree widget in newt, that is actually a checkbox tree that we use with just one option ('.'), end result is usable but we really need to create a custom widget tree so that we can use the data structures we have (hist_entry rb_tree + callchain rb_tree + lists), so that we reduce the memory footprint by not creating a mirror set of data structures in the newtCheckboxTree widget. Thanks to Frédéric Weisbacker for fixing the orphanage problem in 301fde2, without that we were tripping a newt bug (fix already sent to newt's maintainer). 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: <1269291169-29820-1-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'tools')
-rw-r--r--tools/perf/util/newt.c291
1 files changed, 259 insertions, 32 deletions
diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c
index 3c2ef95d940a..12b229bb9dc1 100644
--- a/tools/perf/util/newt.c
+++ b/tools/perf/util/newt.c
@@ -28,11 +28,197 @@ static newtComponent newt_form__new(void)
28 return self; 28 return self;
29} 29}
30 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
40static 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
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
31static size_t hist_entry__append_browser(struct hist_entry *self, 218static size_t hist_entry__append_browser(struct hist_entry *self,
32 newtComponent listbox, u64 total) 219 newtComponent tree, u64 total)
33{ 220{
34 char bf[1024]; 221 char bf[1024], *s;
35 size_t len;
36 FILE *fp; 222 FILE *fp;
37 223
38 if (symbol_conf.exclude_other && !self->parent) 224 if (symbol_conf.exclude_other && !self->parent)
@@ -42,28 +228,46 @@ static size_t hist_entry__append_browser(struct hist_entry *self,
42 if (fp == NULL) 228 if (fp == NULL)
43 return 0; 229 return 0;
44 230
45 len = hist_entry__fprintf(self, NULL, false, 0, fp, total); 231 hist_entry__fprintf(self, NULL, false, 0, fp, total);
46
47 fclose(fp); 232 fclose(fp);
48 newtListboxAppendEntry(listbox, bf, self); 233
49 return len; 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);
50} 254}
51 255
52static void hist_entry__annotate_browser(struct hist_entry *self) 256static void symbol__annotate_browser(const struct symbol *self)
53{ 257{
54 FILE *fp; 258 FILE *fp;
55 int cols, rows; 259 int cols, rows;
56 newtComponent form, listbox; 260 newtComponent form, tree;
57 struct newtExitStruct es; 261 struct newtExitStruct es;
58 char *str; 262 char *str;
59 size_t line_len, max_line_len = 0; 263 size_t line_len, max_line_len = 0;
60 size_t max_usable_width; 264 size_t max_usable_width;
61 char *line = NULL; 265 char *line = NULL;
62 266
63 if (self->sym == NULL) 267 if (self == NULL)
64 return; 268 return;
65 269
66 if (asprintf(&str, "perf annotate %s 2>&1 | expand", self->sym->name) < 0) 270 if (asprintf(&str, "perf annotate %s 2>&1 | expand", self->name) < 0)
67 return; 271 return;
68 272
69 fp = popen(str, "r"); 273 fp = popen(str, "r");
@@ -72,7 +276,7 @@ static void hist_entry__annotate_browser(struct hist_entry *self)
72 276
73 newtPushHelpLine("Press ESC to exit"); 277 newtPushHelpLine("Press ESC to exit");
74 newtGetScreenSize(&cols, &rows); 278 newtGetScreenSize(&cols, &rows);
75 listbox = newtListbox(0, 0, rows - 5, NEWT_FLAG_SCROLL); 279 tree = newtListbox(0, 0, rows - 5, NEWT_FLAG_SCROLL);
76 280
77 while (!feof(fp)) { 281 while (!feof(fp)) {
78 if (getline(&line, &line_len, fp) < 0 || !line_len) 282 if (getline(&line, &line_len, fp) < 0 || !line_len)
@@ -82,7 +286,7 @@ static void hist_entry__annotate_browser(struct hist_entry *self)
82 286
83 if (line_len > max_line_len) 287 if (line_len > max_line_len)
84 max_line_len = line_len; 288 max_line_len = line_len;
85 newtListboxAppendEntry(listbox, line, NULL); 289 newtListboxAppendEntry(tree, line, NULL);
86 } 290 }
87 fclose(fp); 291 fclose(fp);
88 free(line); 292 free(line);
@@ -91,11 +295,11 @@ static void hist_entry__annotate_browser(struct hist_entry *self)
91 if (max_line_len > max_usable_width) 295 if (max_line_len > max_usable_width)
92 max_line_len = max_usable_width; 296 max_line_len = max_usable_width;
93 297
94 newtListboxSetWidth(listbox, max_line_len); 298 newtListboxSetWidth(tree, max_line_len);
95 299
96 newtCenteredWindow(max_line_len + 2, rows - 5, self->sym->name); 300 newtCenteredWindow(max_line_len + 2, rows - 5, self->name);
97 form = newt_form__new(); 301 form = newt_form__new();
98 newtFormAddComponents(form, listbox, NULL); 302 newtFormAddComponents(form, tree, NULL);
99 303
100 newtFormRun(form, &es); 304 newtFormRun(form, &es);
101 newtFormDestroy(form); 305 newtFormDestroy(form);
@@ -110,25 +314,27 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total,
110{ 314{
111 struct sort_entry *se; 315 struct sort_entry *se;
112 struct rb_node *nd; 316 struct rb_node *nd;
317 char seq[] = ".";
113 unsigned int width; 318 unsigned int width;
114 char *col_width = symbol_conf.col_width_list_str; 319 char *col_width = symbol_conf.col_width_list_str;
115 int rows; 320 int rows, cols, idx;
116 size_t max_len = 0; 321 int max_len = 0;
117 char str[1024]; 322 char str[1024];
118 newtComponent form, listbox; 323 newtComponent form, tree;
119 struct newtExitStruct es; 324 struct newtExitStruct es;
120 325
121 snprintf(str, sizeof(str), "Samples: %Ld", session_total); 326 snprintf(str, sizeof(str), "Samples: %Ld", session_total);
122 newtDrawRootText(0, 0, str); 327 newtDrawRootText(0, 0, str);
123 newtPushHelpLine(helpline); 328 newtPushHelpLine(helpline);
124 329
125 newtGetScreenSize(NULL, &rows); 330 newtGetScreenSize(&cols, &rows);
126
127 form = newt_form__new();
128 331
129 listbox = newtListbox(1, 1, rows - 2, (NEWT_FLAG_SCROLL | 332 if (symbol_conf.use_callchain)
130 NEWT_FLAG_BORDER | 333 tree = newtCheckboxTreeMulti(0, 0, rows - 5, seq,
131 NEWT_FLAG_RETURNEXIT)); 334 NEWT_FLAG_SCROLL);
335 else
336 tree = newtListbox(0, 0, rows - 5, (NEWT_FLAG_SCROLL |
337 NEWT_FLAG_RETURNEXIT));
132 338
133 list_for_each_entry(se, &hist_entry__sort_list, list) { 339 list_for_each_entry(se, &hist_entry__sort_list, list) {
134 if (se->elide) 340 if (se->elide)
@@ -147,27 +353,48 @@ void perf_session__browse_hists(struct rb_root *hists, u64 session_total,
147 } 353 }
148 } 354 }
149 355
356 idx = 0;
150 for (nd = rb_first(hists); nd; nd = rb_next(nd)) { 357 for (nd = rb_first(hists); nd; nd = rb_next(nd)) {
151 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 358 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
152 size_t len = hist_entry__append_browser(h, listbox, session_total); 359 int len = hist_entry__append_browser(h, tree, session_total);
153 if (len > max_len) 360 if (len > max_len)
154 max_len = 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 }
155 } 367 }
156 368
157 newtListboxSetWidth(listbox, max_len); 369 if (max_len > cols)
158 newtFormAddComponents(form, listbox, NULL); 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);
159 381
160 while (1) { 382 while (1) {
161 struct hist_entry *selection; 383 const struct symbol *selection;
162 384
163 newtFormRun(form, &es); 385 newtFormRun(form, &es);
164 if (es.reason == NEWT_EXIT_HOTKEY) 386 if (es.reason == NEWT_EXIT_HOTKEY &&
387 toupper(es.u.key) != 'A')
165 break; 388 break;
166 selection = newtListboxGetCurrent(listbox); 389 if (!symbol_conf.use_callchain)
167 hist_entry__annotate_browser(selection); 390 selection = newtListboxGetCurrent(tree);
391 else
392 selection = newtCheckboxTreeGetCurrent(tree);
393 symbol__annotate_browser(selection);
168 } 394 }
169 395
170 newtFormDestroy(form); 396 newtFormDestroy(form);
397 newtPopWindow();
171} 398}
172 399
173static char browser__last_msg[1024]; 400static char browser__last_msg[1024];