diff options
Diffstat (limited to 'tools/perf/util/ui/browsers/annotate.c')
-rw-r--r-- | tools/perf/util/ui/browsers/annotate.c | 215 |
1 files changed, 171 insertions, 44 deletions
diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c index 0229723aceb3..0575905d1205 100644 --- a/tools/perf/util/ui/browsers/annotate.c +++ b/tools/perf/util/ui/browsers/annotate.c | |||
@@ -1,31 +1,31 @@ | |||
1 | #include "../../util.h" | ||
1 | #include "../browser.h" | 2 | #include "../browser.h" |
2 | #include "../helpline.h" | 3 | #include "../helpline.h" |
3 | #include "../libslang.h" | 4 | #include "../libslang.h" |
5 | #include "../ui.h" | ||
6 | #include "../util.h" | ||
4 | #include "../../annotate.h" | 7 | #include "../../annotate.h" |
5 | #include "../../hist.h" | 8 | #include "../../hist.h" |
6 | #include "../../sort.h" | 9 | #include "../../sort.h" |
7 | #include "../../symbol.h" | 10 | #include "../../symbol.h" |
8 | #include <pthread.h> | 11 | #include <pthread.h> |
9 | 12 | #include <newt.h> | |
10 | static void ui__error_window(const char *fmt, ...) | ||
11 | { | ||
12 | va_list ap; | ||
13 | |||
14 | va_start(ap, fmt); | ||
15 | newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap); | ||
16 | va_end(ap); | ||
17 | } | ||
18 | 13 | ||
19 | struct annotate_browser { | 14 | struct annotate_browser { |
20 | struct ui_browser b; | 15 | struct ui_browser b; |
21 | struct rb_root entries; | 16 | struct rb_root entries; |
22 | struct rb_node *curr_hot; | 17 | struct rb_node *curr_hot; |
18 | struct objdump_line *selection; | ||
19 | int nr_asm_entries; | ||
20 | int nr_entries; | ||
21 | bool hide_src_code; | ||
23 | }; | 22 | }; |
24 | 23 | ||
25 | struct objdump_line_rb_node { | 24 | struct objdump_line_rb_node { |
26 | struct rb_node rb_node; | 25 | struct rb_node rb_node; |
27 | double percent; | 26 | double percent; |
28 | u32 idx; | 27 | u32 idx; |
28 | int idx_asm; | ||
29 | }; | 29 | }; |
30 | 30 | ||
31 | static inline | 31 | static inline |
@@ -34,9 +34,22 @@ struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self) | |||
34 | return (struct objdump_line_rb_node *)(self + 1); | 34 | return (struct objdump_line_rb_node *)(self + 1); |
35 | } | 35 | } |
36 | 36 | ||
37 | static bool objdump_line__filter(struct ui_browser *browser, void *entry) | ||
38 | { | ||
39 | struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); | ||
40 | |||
41 | if (ab->hide_src_code) { | ||
42 | struct objdump_line *ol = list_entry(entry, struct objdump_line, node); | ||
43 | return ol->offset == -1; | ||
44 | } | ||
45 | |||
46 | return false; | ||
47 | } | ||
48 | |||
37 | static void annotate_browser__write(struct ui_browser *self, void *entry, int row) | 49 | static void annotate_browser__write(struct ui_browser *self, void *entry, int row) |
38 | { | 50 | { |
39 | struct objdump_line *ol = rb_entry(entry, struct objdump_line, node); | 51 | struct annotate_browser *ab = container_of(self, struct annotate_browser, b); |
52 | struct objdump_line *ol = list_entry(entry, struct objdump_line, node); | ||
40 | bool current_entry = ui_browser__is_current_entry(self, row); | 53 | bool current_entry = ui_browser__is_current_entry(self, row); |
41 | int width = self->width; | 54 | int width = self->width; |
42 | 55 | ||
@@ -51,6 +64,11 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro | |||
51 | 64 | ||
52 | SLsmg_write_char(':'); | 65 | SLsmg_write_char(':'); |
53 | slsmg_write_nstring(" ", 8); | 66 | slsmg_write_nstring(" ", 8); |
67 | |||
68 | /* The scroll bar isn't being used */ | ||
69 | if (!self->navkeypressed) | ||
70 | width += 1; | ||
71 | |||
54 | if (!*ol->line) | 72 | if (!*ol->line) |
55 | slsmg_write_nstring(" ", width - 18); | 73 | slsmg_write_nstring(" ", width - 18); |
56 | else | 74 | else |
@@ -58,6 +76,8 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro | |||
58 | 76 | ||
59 | if (!current_entry) | 77 | if (!current_entry) |
60 | ui_browser__set_color(self, HE_COLORSET_CODE); | 78 | ui_browser__set_color(self, HE_COLORSET_CODE); |
79 | else | ||
80 | ab->selection = ol; | ||
61 | } | 81 | } |
62 | 82 | ||
63 | static double objdump_line__calc_percent(struct objdump_line *self, | 83 | static double objdump_line__calc_percent(struct objdump_line *self, |
@@ -141,7 +161,8 @@ static void annotate_browser__set_top(struct annotate_browser *self, | |||
141 | static void annotate_browser__calc_percent(struct annotate_browser *browser, | 161 | static void annotate_browser__calc_percent(struct annotate_browser *browser, |
142 | int evidx) | 162 | int evidx) |
143 | { | 163 | { |
144 | struct symbol *sym = browser->b.priv; | 164 | struct map_symbol *ms = browser->b.priv; |
165 | struct symbol *sym = ms->sym; | ||
145 | struct annotation *notes = symbol__annotation(sym); | 166 | struct annotation *notes = symbol__annotation(sym); |
146 | struct objdump_line *pos; | 167 | struct objdump_line *pos; |
147 | 168 | ||
@@ -163,25 +184,60 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser, | |||
163 | browser->curr_hot = rb_last(&browser->entries); | 184 | browser->curr_hot = rb_last(&browser->entries); |
164 | } | 185 | } |
165 | 186 | ||
187 | static bool annotate_browser__toggle_source(struct annotate_browser *browser) | ||
188 | { | ||
189 | struct objdump_line *ol; | ||
190 | struct objdump_line_rb_node *olrb; | ||
191 | off_t offset = browser->b.index - browser->b.top_idx; | ||
192 | |||
193 | browser->b.seek(&browser->b, offset, SEEK_CUR); | ||
194 | ol = list_entry(browser->b.top, struct objdump_line, node); | ||
195 | olrb = objdump_line__rb(ol); | ||
196 | |||
197 | if (browser->hide_src_code) { | ||
198 | if (olrb->idx_asm < offset) | ||
199 | offset = olrb->idx; | ||
200 | |||
201 | browser->b.nr_entries = browser->nr_entries; | ||
202 | browser->hide_src_code = false; | ||
203 | browser->b.seek(&browser->b, -offset, SEEK_CUR); | ||
204 | browser->b.top_idx = olrb->idx - offset; | ||
205 | browser->b.index = olrb->idx; | ||
206 | } else { | ||
207 | if (olrb->idx_asm < 0) { | ||
208 | ui_helpline__puts("Only available for assembly lines."); | ||
209 | browser->b.seek(&browser->b, -offset, SEEK_CUR); | ||
210 | return false; | ||
211 | } | ||
212 | |||
213 | if (olrb->idx_asm < offset) | ||
214 | offset = olrb->idx_asm; | ||
215 | |||
216 | browser->b.nr_entries = browser->nr_asm_entries; | ||
217 | browser->hide_src_code = true; | ||
218 | browser->b.seek(&browser->b, -offset, SEEK_CUR); | ||
219 | browser->b.top_idx = olrb->idx_asm - offset; | ||
220 | browser->b.index = olrb->idx_asm; | ||
221 | } | ||
222 | |||
223 | return true; | ||
224 | } | ||
225 | |||
166 | static int annotate_browser__run(struct annotate_browser *self, int evidx, | 226 | static int annotate_browser__run(struct annotate_browser *self, int evidx, |
167 | int refresh) | 227 | int nr_events, void(*timer)(void *arg), |
228 | void *arg, int delay_secs) | ||
168 | { | 229 | { |
169 | struct rb_node *nd = NULL; | 230 | struct rb_node *nd = NULL; |
170 | struct symbol *sym = self->b.priv; | 231 | struct map_symbol *ms = self->b.priv; |
171 | /* | 232 | struct symbol *sym = ms->sym; |
172 | * RIGHT To allow builtin-annotate to cycle thru multiple symbols by | 233 | const char *help = "<-, ESC: exit, TAB/shift+TAB: cycle hottest lines, " |
173 | * examining the exit key for this function. | 234 | "H: Hottest, -> Line action, S -> Toggle source " |
174 | */ | 235 | "code view"; |
175 | int exit_keys[] = { 'H', NEWT_KEY_TAB, NEWT_KEY_UNTAB, | ||
176 | NEWT_KEY_RIGHT, 0 }; | ||
177 | int key; | 236 | int key; |
178 | 237 | ||
179 | if (ui_browser__show(&self->b, sym->name, | 238 | if (ui_browser__show(&self->b, sym->name, help) < 0) |
180 | "<-, -> or ESC: exit, TAB/shift+TAB: " | ||
181 | "cycle hottest lines, H: Hottest") < 0) | ||
182 | return -1; | 239 | return -1; |
183 | 240 | ||
184 | ui_browser__add_exit_keys(&self->b, exit_keys); | ||
185 | annotate_browser__calc_percent(self, evidx); | 241 | annotate_browser__calc_percent(self, evidx); |
186 | 242 | ||
187 | if (self->curr_hot) | 243 | if (self->curr_hot) |
@@ -189,13 +245,10 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, | |||
189 | 245 | ||
190 | nd = self->curr_hot; | 246 | nd = self->curr_hot; |
191 | 247 | ||
192 | if (refresh != 0) | ||
193 | newtFormSetTimer(self->b.form, refresh); | ||
194 | |||
195 | while (1) { | 248 | while (1) { |
196 | key = ui_browser__run(&self->b); | 249 | key = ui_browser__run(&self->b, delay_secs); |
197 | 250 | ||
198 | if (refresh != 0) { | 251 | if (delay_secs != 0) { |
199 | annotate_browser__calc_percent(self, evidx); | 252 | annotate_browser__calc_percent(self, evidx); |
200 | /* | 253 | /* |
201 | * Current line focus got out of the list of most active | 254 | * Current line focus got out of the list of most active |
@@ -207,15 +260,14 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, | |||
207 | } | 260 | } |
208 | 261 | ||
209 | switch (key) { | 262 | switch (key) { |
210 | case -1: | 263 | case K_TIMER: |
211 | /* | 264 | if (timer != NULL) |
212 | * FIXME we need to check if it was | 265 | timer(arg); |
213 | * es.reason == NEWT_EXIT_TIMER | 266 | |
214 | */ | 267 | if (delay_secs != 0) |
215 | if (refresh != 0) | ||
216 | symbol__annotate_decay_histogram(sym, evidx); | 268 | symbol__annotate_decay_histogram(sym, evidx); |
217 | continue; | 269 | continue; |
218 | case NEWT_KEY_TAB: | 270 | case K_TAB: |
219 | if (nd != NULL) { | 271 | if (nd != NULL) { |
220 | nd = rb_prev(nd); | 272 | nd = rb_prev(nd); |
221 | if (nd == NULL) | 273 | if (nd == NULL) |
@@ -223,7 +275,7 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, | |||
223 | } else | 275 | } else |
224 | nd = self->curr_hot; | 276 | nd = self->curr_hot; |
225 | break; | 277 | break; |
226 | case NEWT_KEY_UNTAB: | 278 | case K_UNTAB: |
227 | if (nd != NULL) | 279 | if (nd != NULL) |
228 | nd = rb_next(nd); | 280 | nd = rb_next(nd); |
229 | if (nd == NULL) | 281 | if (nd == NULL) |
@@ -234,8 +286,68 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, | |||
234 | case 'H': | 286 | case 'H': |
235 | nd = self->curr_hot; | 287 | nd = self->curr_hot; |
236 | break; | 288 | break; |
237 | default: | 289 | case 'S': |
290 | if (annotate_browser__toggle_source(self)) | ||
291 | ui_helpline__puts(help); | ||
292 | continue; | ||
293 | case K_ENTER: | ||
294 | case K_RIGHT: | ||
295 | if (self->selection == NULL) { | ||
296 | ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org"); | ||
297 | continue; | ||
298 | } | ||
299 | |||
300 | if (self->selection->offset == -1) { | ||
301 | ui_helpline__puts("Actions are only available for assembly lines."); | ||
302 | continue; | ||
303 | } else { | ||
304 | char *s = strstr(self->selection->line, "callq "); | ||
305 | struct annotation *notes; | ||
306 | struct symbol *target; | ||
307 | u64 ip; | ||
308 | |||
309 | if (s == NULL) { | ||
310 | ui_helpline__puts("Actions are only available for the 'callq' instruction."); | ||
311 | continue; | ||
312 | } | ||
313 | |||
314 | s = strchr(s, ' '); | ||
315 | if (s++ == NULL) { | ||
316 | ui_helpline__puts("Invallid callq instruction."); | ||
317 | continue; | ||
318 | } | ||
319 | |||
320 | ip = strtoull(s, NULL, 16); | ||
321 | ip = ms->map->map_ip(ms->map, ip); | ||
322 | target = map__find_symbol(ms->map, ip, NULL); | ||
323 | if (target == NULL) { | ||
324 | ui_helpline__puts("The called function was not found."); | ||
325 | continue; | ||
326 | } | ||
327 | |||
328 | notes = symbol__annotation(target); | ||
329 | pthread_mutex_lock(¬es->lock); | ||
330 | |||
331 | if (notes->src == NULL && | ||
332 | symbol__alloc_hist(target, nr_events) < 0) { | ||
333 | pthread_mutex_unlock(¬es->lock); | ||
334 | ui__warning("Not enough memory for annotating '%s' symbol!\n", | ||
335 | target->name); | ||
336 | continue; | ||
337 | } | ||
338 | |||
339 | pthread_mutex_unlock(¬es->lock); | ||
340 | symbol__tui_annotate(target, ms->map, evidx, nr_events, | ||
341 | timer, arg, delay_secs); | ||
342 | } | ||
343 | continue; | ||
344 | case K_LEFT: | ||
345 | case K_ESC: | ||
346 | case 'q': | ||
347 | case CTRL('c'): | ||
238 | goto out; | 348 | goto out; |
349 | default: | ||
350 | continue; | ||
239 | } | 351 | } |
240 | 352 | ||
241 | if (nd != NULL) | 353 | if (nd != NULL) |
@@ -246,22 +358,31 @@ out: | |||
246 | return key; | 358 | return key; |
247 | } | 359 | } |
248 | 360 | ||
249 | int hist_entry__tui_annotate(struct hist_entry *he, int evidx) | 361 | int hist_entry__tui_annotate(struct hist_entry *he, int evidx, int nr_events, |
362 | void(*timer)(void *arg), void *arg, int delay_secs) | ||
250 | { | 363 | { |
251 | return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, 0); | 364 | return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, nr_events, |
365 | timer, arg, delay_secs); | ||
252 | } | 366 | } |
253 | 367 | ||
254 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | 368 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, |
255 | int refresh) | 369 | int nr_events, void(*timer)(void *arg), void *arg, |
370 | int delay_secs) | ||
256 | { | 371 | { |
257 | struct objdump_line *pos, *n; | 372 | struct objdump_line *pos, *n; |
258 | struct annotation *notes; | 373 | struct annotation *notes; |
374 | struct map_symbol ms = { | ||
375 | .map = map, | ||
376 | .sym = sym, | ||
377 | }; | ||
259 | struct annotate_browser browser = { | 378 | struct annotate_browser browser = { |
260 | .b = { | 379 | .b = { |
261 | .refresh = ui_browser__list_head_refresh, | 380 | .refresh = ui_browser__list_head_refresh, |
262 | .seek = ui_browser__list_head_seek, | 381 | .seek = ui_browser__list_head_seek, |
263 | .write = annotate_browser__write, | 382 | .write = annotate_browser__write, |
264 | .priv = sym, | 383 | .filter = objdump_line__filter, |
384 | .priv = &ms, | ||
385 | .use_navkeypressed = true, | ||
265 | }, | 386 | }, |
266 | }; | 387 | }; |
267 | int ret; | 388 | int ret; |
@@ -273,7 +394,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | |||
273 | return -1; | 394 | return -1; |
274 | 395 | ||
275 | if (symbol__annotate(sym, map, sizeof(struct objdump_line_rb_node)) < 0) { | 396 | if (symbol__annotate(sym, map, sizeof(struct objdump_line_rb_node)) < 0) { |
276 | ui__error_window(ui_helpline__last_msg); | 397 | ui__error("%s", ui_helpline__last_msg); |
277 | return -1; | 398 | return -1; |
278 | } | 399 | } |
279 | 400 | ||
@@ -288,12 +409,18 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | |||
288 | if (browser.b.width < line_len) | 409 | if (browser.b.width < line_len) |
289 | browser.b.width = line_len; | 410 | browser.b.width = line_len; |
290 | rbpos = objdump_line__rb(pos); | 411 | rbpos = objdump_line__rb(pos); |
291 | rbpos->idx = browser.b.nr_entries++; | 412 | rbpos->idx = browser.nr_entries++; |
413 | if (pos->offset != -1) | ||
414 | rbpos->idx_asm = browser.nr_asm_entries++; | ||
415 | else | ||
416 | rbpos->idx_asm = -1; | ||
292 | } | 417 | } |
293 | 418 | ||
419 | browser.b.nr_entries = browser.nr_entries; | ||
294 | browser.b.entries = ¬es->src->source, | 420 | browser.b.entries = ¬es->src->source, |
295 | browser.b.width += 18; /* Percentage */ | 421 | browser.b.width += 18; /* Percentage */ |
296 | ret = annotate_browser__run(&browser, evidx, refresh); | 422 | ret = annotate_browser__run(&browser, evidx, nr_events, |
423 | timer, arg, delay_secs); | ||
297 | list_for_each_entry_safe(pos, n, ¬es->src->source, node) { | 424 | list_for_each_entry_safe(pos, n, ¬es->src->source, node) { |
298 | list_del(&pos->node); | 425 | list_del(&pos->node); |
299 | objdump_line__free(pos); | 426 | objdump_line__free(pos); |