perf annotate: Group operands members
[pandora-kernel.git] / tools / perf / ui / browsers / annotate.c
1 #include "../../util/util.h"
2 #include "../browser.h"
3 #include "../helpline.h"
4 #include "../libslang.h"
5 #include "../ui.h"
6 #include "../util.h"
7 #include "../../util/annotate.h"
8 #include "../../util/hist.h"
9 #include "../../util/sort.h"
10 #include "../../util/symbol.h"
11 #include <pthread.h>
12 #include <newt.h>
13
14 struct browser_disasm_line {
15         struct rb_node  rb_node;
16         double          percent;
17         u32             idx;
18         int             idx_asm;
19         bool            jump_target;
20 };
21
22 struct annotate_browser {
23         struct ui_browser b;
24         struct rb_root    entries;
25         struct rb_node    *curr_hot;
26         struct disasm_line        *selection;
27         struct disasm_line  **offsets;
28         u64                 start;
29         int                 nr_asm_entries;
30         int                 nr_entries;
31         bool                hide_src_code;
32         bool                use_offset;
33         bool                searching_backwards;
34         u8                  offset_width;
35         char                search_bf[128];
36 };
37
38 static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl)
39 {
40         return (struct browser_disasm_line *)(dl + 1);
41 }
42
43 static bool disasm_line__filter(struct ui_browser *browser, void *entry)
44 {
45         struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
46
47         if (ab->hide_src_code) {
48                 struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
49                 return dl->offset == -1;
50         }
51
52         return false;
53 }
54
55 static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
56 {
57         struct annotate_browser *ab = container_of(self, struct annotate_browser, b);
58         struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
59         struct browser_disasm_line *bdl = disasm_line__browser(dl);
60         bool current_entry = ui_browser__is_current_entry(self, row);
61         bool change_color = (!ab->hide_src_code &&
62                              (!current_entry || (self->use_navkeypressed &&
63                                                  !self->navkeypressed)));
64         int width = self->width;
65
66         if (dl->offset != -1) {
67                 ui_browser__set_percent_color(self, bdl->percent, current_entry);
68                 slsmg_printf(" %7.2f ", bdl->percent);
69         } else {
70                 ui_browser__set_percent_color(self, 0, current_entry);
71                 slsmg_write_nstring(" ", 9);
72         }
73
74         SLsmg_set_char_set(1);
75         SLsmg_write_char(SLSMG_VLINE_CHAR);
76         SLsmg_set_char_set(0);
77         SLsmg_write_char(' ');
78
79         /* The scroll bar isn't being used */
80         if (!self->navkeypressed)
81                 width += 1;
82
83         if (dl->offset != -1 && change_color)
84                 ui_browser__set_color(self, HE_COLORSET_CODE);
85
86         if (!*dl->line)
87                 slsmg_write_nstring(" ", width - 10);
88         else if (dl->offset == -1)
89                 slsmg_write_nstring(dl->line, width - 10);
90         else {
91                 char bf[256];
92                 u64 addr = dl->offset;
93                 int printed, color = -1;
94
95                 if (!ab->use_offset)
96                         addr += ab->start;
97
98                 if (!ab->use_offset) {
99                         printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ":", addr);
100                 } else {
101                         if (bdl->jump_target) {
102                                 printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ":",
103                                                     ab->offset_width, addr);
104                         } else {
105                                 printed = scnprintf(bf, sizeof(bf), "%*s ",
106                                                     ab->offset_width, " ");
107                         }
108                 }
109
110                 if (change_color)
111                         color = ui_browser__set_color(self, HE_COLORSET_ADDR);
112                 slsmg_write_nstring(bf, printed);
113                 if (change_color)
114                         ui_browser__set_color(self, color);
115                 if (dl->ins && dl->ins->ops->scnprintf) {
116                         dl->ins->ops->scnprintf(dl->ins, bf, sizeof(bf), &dl->ops,
117                                                 !ab->use_offset);
118                         slsmg_write_nstring(" ", 2);
119                         printed += 2;
120                 } else
121                         scnprintf(bf, sizeof(bf), "  %-6.6s %s", dl->name, dl->ops.raw);
122
123                 slsmg_write_nstring(bf, width - 10 - printed);
124         }
125
126         if (current_entry)
127                 ab->selection = dl;
128 }
129
130 static double disasm_line__calc_percent(struct disasm_line *dl, struct symbol *sym, int evidx)
131 {
132         double percent = 0.0;
133
134         if (dl->offset != -1) {
135                 int len = sym->end - sym->start;
136                 unsigned int hits = 0;
137                 struct annotation *notes = symbol__annotation(sym);
138                 struct source_line *src_line = notes->src->lines;
139                 struct sym_hist *h = annotation__histogram(notes, evidx);
140                 s64 offset = dl->offset;
141                 struct disasm_line *next;
142
143                 next = disasm__get_next_ip_line(&notes->src->source, dl);
144                 while (offset < (s64)len &&
145                        (next == NULL || offset < next->offset)) {
146                         if (src_line) {
147                                 percent += src_line[offset].percent;
148                         } else
149                                 hits += h->addr[offset];
150
151                         ++offset;
152                 }
153                 /*
154                  * If the percentage wasn't already calculated in
155                  * symbol__get_source_line, do it now:
156                  */
157                 if (src_line == NULL && h->sum)
158                         percent = 100.0 * hits / h->sum;
159         }
160
161         return percent;
162 }
163
164 static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl)
165 {
166         struct rb_node **p = &root->rb_node;
167         struct rb_node *parent = NULL;
168         struct browser_disasm_line *l;
169
170         while (*p != NULL) {
171                 parent = *p;
172                 l = rb_entry(parent, struct browser_disasm_line, rb_node);
173                 if (bdl->percent < l->percent)
174                         p = &(*p)->rb_left;
175                 else
176                         p = &(*p)->rb_right;
177         }
178         rb_link_node(&bdl->rb_node, parent, p);
179         rb_insert_color(&bdl->rb_node, root);
180 }
181
182 static void annotate_browser__set_top(struct annotate_browser *self,
183                                       struct disasm_line *pos, u32 idx)
184 {
185         unsigned back;
186
187         ui_browser__refresh_dimensions(&self->b);
188         back = self->b.height / 2;
189         self->b.top_idx = self->b.index = idx;
190
191         while (self->b.top_idx != 0 && back != 0) {
192                 pos = list_entry(pos->node.prev, struct disasm_line, node);
193
194                 if (disasm_line__filter(&self->b, &pos->node))
195                         continue;
196
197                 --self->b.top_idx;
198                 --back;
199         }
200
201         self->b.top = pos;
202         self->b.navkeypressed = true;
203 }
204
205 static void annotate_browser__set_rb_top(struct annotate_browser *browser,
206                                          struct rb_node *nd)
207 {
208         struct browser_disasm_line *bpos;
209         struct disasm_line *pos;
210
211         bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
212         pos = ((struct disasm_line *)bpos) - 1;
213         annotate_browser__set_top(browser, pos, bpos->idx);
214         browser->curr_hot = nd;
215 }
216
217 static void annotate_browser__calc_percent(struct annotate_browser *browser,
218                                            int evidx)
219 {
220         struct map_symbol *ms = browser->b.priv;
221         struct symbol *sym = ms->sym;
222         struct annotation *notes = symbol__annotation(sym);
223         struct disasm_line *pos;
224
225         browser->entries = RB_ROOT;
226
227         pthread_mutex_lock(&notes->lock);
228
229         list_for_each_entry(pos, &notes->src->source, node) {
230                 struct browser_disasm_line *bpos = disasm_line__browser(pos);
231                 bpos->percent = disasm_line__calc_percent(pos, sym, evidx);
232                 if (bpos->percent < 0.01) {
233                         RB_CLEAR_NODE(&bpos->rb_node);
234                         continue;
235                 }
236                 disasm_rb_tree__insert(&browser->entries, bpos);
237         }
238         pthread_mutex_unlock(&notes->lock);
239
240         browser->curr_hot = rb_last(&browser->entries);
241 }
242
243 static bool annotate_browser__toggle_source(struct annotate_browser *browser)
244 {
245         struct disasm_line *dl;
246         struct browser_disasm_line *bdl;
247         off_t offset = browser->b.index - browser->b.top_idx;
248
249         browser->b.seek(&browser->b, offset, SEEK_CUR);
250         dl = list_entry(browser->b.top, struct disasm_line, node);
251         bdl = disasm_line__browser(dl);
252
253         if (browser->hide_src_code) {
254                 if (bdl->idx_asm < offset)
255                         offset = bdl->idx;
256
257                 browser->b.nr_entries = browser->nr_entries;
258                 browser->hide_src_code = false;
259                 browser->b.seek(&browser->b, -offset, SEEK_CUR);
260                 browser->b.top_idx = bdl->idx - offset;
261                 browser->b.index = bdl->idx;
262         } else {
263                 if (bdl->idx_asm < 0) {
264                         ui_helpline__puts("Only available for assembly lines.");
265                         browser->b.seek(&browser->b, -offset, SEEK_CUR);
266                         return false;
267                 }
268
269                 if (bdl->idx_asm < offset)
270                         offset = bdl->idx_asm;
271
272                 browser->b.nr_entries = browser->nr_asm_entries;
273                 browser->hide_src_code = true;
274                 browser->b.seek(&browser->b, -offset, SEEK_CUR);
275                 browser->b.top_idx = bdl->idx_asm - offset;
276                 browser->b.index = bdl->idx_asm;
277         }
278
279         return true;
280 }
281
282 static bool annotate_browser__callq(struct annotate_browser *browser,
283                                     int evidx, void (*timer)(void *arg),
284                                     void *arg, int delay_secs)
285 {
286         struct map_symbol *ms = browser->b.priv;
287         struct disasm_line *dl = browser->selection;
288         struct symbol *sym = ms->sym;
289         struct annotation *notes;
290         struct symbol *target;
291         u64 ip;
292
293         if (!ins__is_call(dl->ins))
294                 return false;
295
296         ip = ms->map->map_ip(ms->map, dl->ops.target);
297         target = map__find_symbol(ms->map, ip, NULL);
298         if (target == NULL) {
299                 ui_helpline__puts("The called function was not found.");
300                 return true;
301         }
302
303         notes = symbol__annotation(target);
304         pthread_mutex_lock(&notes->lock);
305
306         if (notes->src == NULL && symbol__alloc_hist(target) < 0) {
307                 pthread_mutex_unlock(&notes->lock);
308                 ui__warning("Not enough memory for annotating '%s' symbol!\n",
309                             target->name);
310                 return true;
311         }
312
313         pthread_mutex_unlock(&notes->lock);
314         symbol__tui_annotate(target, ms->map, evidx, timer, arg, delay_secs);
315         ui_browser__show_title(&browser->b, sym->name);
316         return true;
317 }
318
319 static
320 struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
321                                           s64 offset, s64 *idx)
322 {
323         struct map_symbol *ms = browser->b.priv;
324         struct symbol *sym = ms->sym;
325         struct annotation *notes = symbol__annotation(sym);
326         struct disasm_line *pos;
327
328         *idx = 0;
329         list_for_each_entry(pos, &notes->src->source, node) {
330                 if (pos->offset == offset)
331                         return pos;
332                 if (!disasm_line__filter(&browser->b, &pos->node))
333                         ++*idx;
334         }
335
336         return NULL;
337 }
338
339 static bool annotate_browser__jump(struct annotate_browser *browser)
340 {
341         struct disasm_line *dl = browser->selection;
342         s64 idx;
343
344         if (!ins__is_jump(dl->ins))
345                 return false;
346
347         dl = annotate_browser__find_offset(browser, dl->ops.target, &idx);
348         if (dl == NULL) {
349                 ui_helpline__puts("Invallid jump offset");
350                 return true;
351         }
352
353         annotate_browser__set_top(browser, dl, idx);
354         
355         return true;
356 }
357
358 static
359 struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
360                                           char *s, s64 *idx)
361 {
362         struct map_symbol *ms = browser->b.priv;
363         struct symbol *sym = ms->sym;
364         struct annotation *notes = symbol__annotation(sym);
365         struct disasm_line *pos = browser->selection;
366
367         *idx = browser->b.index;
368         list_for_each_entry_continue(pos, &notes->src->source, node) {
369                 if (disasm_line__filter(&browser->b, &pos->node))
370                         continue;
371
372                 ++*idx;
373
374                 if (pos->line && strstr(pos->line, s) != NULL)
375                         return pos;
376         }
377
378         return NULL;
379 }
380
381 static bool __annotate_browser__search(struct annotate_browser *browser)
382 {
383         struct disasm_line *dl;
384         s64 idx;
385
386         dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
387         if (dl == NULL) {
388                 ui_helpline__puts("String not found!");
389                 return false;
390         }
391
392         annotate_browser__set_top(browser, dl, idx);
393         browser->searching_backwards = false;
394         return true;
395 }
396
397 static
398 struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
399                                                   char *s, s64 *idx)
400 {
401         struct map_symbol *ms = browser->b.priv;
402         struct symbol *sym = ms->sym;
403         struct annotation *notes = symbol__annotation(sym);
404         struct disasm_line *pos = browser->selection;
405
406         *idx = browser->b.index;
407         list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
408                 if (disasm_line__filter(&browser->b, &pos->node))
409                         continue;
410
411                 --*idx;
412
413                 if (pos->line && strstr(pos->line, s) != NULL)
414                         return pos;
415         }
416
417         return NULL;
418 }
419
420 static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
421 {
422         struct disasm_line *dl;
423         s64 idx;
424
425         dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
426         if (dl == NULL) {
427                 ui_helpline__puts("String not found!");
428                 return false;
429         }
430
431         annotate_browser__set_top(browser, dl, idx);
432         browser->searching_backwards = true;
433         return true;
434 }
435
436 static bool annotate_browser__search_window(struct annotate_browser *browser,
437                                             int delay_secs)
438 {
439         if (ui_browser__input_window("Search", "String: ", browser->search_bf,
440                                      "ENTER: OK, ESC: Cancel",
441                                      delay_secs * 2) != K_ENTER ||
442             !*browser->search_bf)
443                 return false;
444
445         return true;
446 }
447
448 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
449 {
450         if (annotate_browser__search_window(browser, delay_secs))
451                 return __annotate_browser__search(browser);
452
453         return false;
454 }
455
456 static bool annotate_browser__continue_search(struct annotate_browser *browser,
457                                               int delay_secs)
458 {
459         if (!*browser->search_bf)
460                 return annotate_browser__search(browser, delay_secs);
461
462         return __annotate_browser__search(browser);
463 }
464
465 static bool annotate_browser__search_reverse(struct annotate_browser *browser,
466                                            int delay_secs)
467 {
468         if (annotate_browser__search_window(browser, delay_secs))
469                 return __annotate_browser__search_reverse(browser);
470
471         return false;
472 }
473
474 static
475 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
476                                                int delay_secs)
477 {
478         if (!*browser->search_bf)
479                 return annotate_browser__search_reverse(browser, delay_secs);
480
481         return __annotate_browser__search_reverse(browser);
482 }
483
484 static int annotate_browser__run(struct annotate_browser *self, int evidx,
485                                  void(*timer)(void *arg),
486                                  void *arg, int delay_secs)
487 {
488         struct rb_node *nd = NULL;
489         struct map_symbol *ms = self->b.priv;
490         struct symbol *sym = ms->sym;
491         const char *help = "<-/ESC: Exit, TAB/shift+TAB: Cycle hot lines, "
492                            "H: Go to hottest line, ->/ENTER: Line action, "
493                            "O: Toggle offset view, "
494                            "S: Toggle source code view";
495         int key;
496
497         if (ui_browser__show(&self->b, sym->name, help) < 0)
498                 return -1;
499
500         annotate_browser__calc_percent(self, evidx);
501
502         if (self->curr_hot) {
503                 annotate_browser__set_rb_top(self, self->curr_hot);
504                 self->b.navkeypressed = false;
505         }
506
507         nd = self->curr_hot;
508
509         while (1) {
510                 key = ui_browser__run(&self->b, delay_secs);
511
512                 if (delay_secs != 0) {
513                         annotate_browser__calc_percent(self, evidx);
514                         /*
515                          * Current line focus got out of the list of most active
516                          * lines, NULL it so that if TAB|UNTAB is pressed, we
517                          * move to curr_hot (current hottest line).
518                          */
519                         if (nd != NULL && RB_EMPTY_NODE(nd))
520                                 nd = NULL;
521                 }
522
523                 switch (key) {
524                 case K_TIMER:
525                         if (timer != NULL)
526                                 timer(arg);
527
528                         if (delay_secs != 0)
529                                 symbol__annotate_decay_histogram(sym, evidx);
530                         continue;
531                 case K_TAB:
532                         if (nd != NULL) {
533                                 nd = rb_prev(nd);
534                                 if (nd == NULL)
535                                         nd = rb_last(&self->entries);
536                         } else
537                                 nd = self->curr_hot;
538                         break;
539                 case K_UNTAB:
540                         if (nd != NULL)
541                                 nd = rb_next(nd);
542                                 if (nd == NULL)
543                                         nd = rb_first(&self->entries);
544                         else
545                                 nd = self->curr_hot;
546                         break;
547                 case 'H':
548                 case 'h':
549                         nd = self->curr_hot;
550                         break;
551                 case 'S':
552                 case 's':
553                         if (annotate_browser__toggle_source(self))
554                                 ui_helpline__puts(help);
555                         continue;
556                 case 'O':
557                 case 'o':
558                         self->use_offset = !self->use_offset;
559                         continue;
560                 case '/':
561                         if (annotate_browser__search(self, delay_secs)) {
562 show_help:
563                                 ui_helpline__puts(help);
564                         }
565                         continue;
566                 case 'n':
567                         if (self->searching_backwards ?
568                             annotate_browser__continue_search_reverse(self, delay_secs) :
569                             annotate_browser__continue_search(self, delay_secs))
570                                 goto show_help;
571                         continue;
572                 case '?':
573                         if (annotate_browser__search_reverse(self, delay_secs))
574                                 goto show_help;
575                         continue;
576                 case K_ENTER:
577                 case K_RIGHT:
578                         if (self->selection == NULL)
579                                 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
580                         else if (self->selection->offset == -1)
581                                 ui_helpline__puts("Actions are only available for assembly lines.");
582                         else if (!self->selection->ins ||
583                                  !(annotate_browser__jump(self) ||
584                                    annotate_browser__callq(self, evidx, timer, arg, delay_secs)))
585                                 ui_helpline__puts("Actions are only available for the 'callq' and jump instructions.");
586                         continue;
587                 case K_LEFT:
588                 case K_ESC:
589                 case 'q':
590                 case CTRL('c'):
591                         goto out;
592                 default:
593                         continue;
594                 }
595
596                 if (nd != NULL)
597                         annotate_browser__set_rb_top(self, nd);
598         }
599 out:
600         ui_browser__hide(&self->b);
601         return key;
602 }
603
604 int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
605                              void(*timer)(void *arg), void *arg, int delay_secs)
606 {
607         return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx,
608                                     timer, arg, delay_secs);
609 }
610
611 static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
612                                                 size_t size)
613 {
614         u64 offset;
615
616         for (offset = 0; offset < size; ++offset) {
617                 struct disasm_line *dl = browser->offsets[offset], *dlt;
618                 struct browser_disasm_line *bdlt;
619
620                 if (!dl || !dl->ins || !ins__is_jump(dl->ins))
621                         continue;
622
623                 if (dl->ops.target >= size) {
624                         ui__error("jump to after symbol!\n"
625                                   "size: %zx, jump target: %" PRIx64,
626                                   size, dl->ops.target);
627                         continue;
628                 }
629
630                 dlt = browser->offsets[dl->ops.target];
631                 bdlt = disasm_line__browser(dlt);
632                 bdlt->jump_target = true;
633         }
634                 
635 }
636
637 int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
638                          void(*timer)(void *arg), void *arg,
639                          int delay_secs)
640 {
641         struct disasm_line *pos, *n;
642         struct annotation *notes;
643         const size_t size = symbol__size(sym);
644         struct map_symbol ms = {
645                 .map = map,
646                 .sym = sym,
647         };
648         struct annotate_browser browser = {
649                 .b = {
650                         .refresh = ui_browser__list_head_refresh,
651                         .seek    = ui_browser__list_head_seek,
652                         .write   = annotate_browser__write,
653                         .filter  = disasm_line__filter,
654                         .priv    = &ms,
655                         .use_navkeypressed = true,
656                 },
657                 .use_offset = true,
658         };
659         int ret = -1;
660
661         if (sym == NULL)
662                 return -1;
663
664         if (map->dso->annotate_warned)
665                 return -1;
666
667         browser.offsets = zalloc(size * sizeof(struct disasm_line *));
668         if (browser.offsets == NULL) {
669                 ui__error("Not enough memory!");
670                 return -1;
671         }
672
673         if (symbol__annotate(sym, map, sizeof(struct browser_disasm_line)) < 0) {
674                 ui__error("%s", ui_helpline__last_msg);
675                 goto out_free_offsets;
676         }
677
678         ui_helpline__push("Press <- or ESC to exit");
679
680         notes = symbol__annotation(sym);
681         browser.start = map__rip_2objdump(map, sym->start);
682
683         list_for_each_entry(pos, &notes->src->source, node) {
684                 struct browser_disasm_line *bpos;
685                 size_t line_len = strlen(pos->line);
686
687                 if (browser.b.width < line_len)
688                         browser.b.width = line_len;
689                 bpos = disasm_line__browser(pos);
690                 bpos->idx = browser.nr_entries++;
691                 if (pos->offset != -1) {
692                         bpos->idx_asm = browser.nr_asm_entries++;
693                         browser.offsets[pos->offset] = pos;
694                 } else
695                         bpos->idx_asm = -1;
696         }
697
698         annotate_browser__mark_jump_targets(&browser, size);
699
700         browser.offset_width = hex_width(size);
701         browser.b.nr_entries = browser.nr_entries;
702         browser.b.entries = &notes->src->source,
703         browser.b.width += 18; /* Percentage */
704         ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs);
705         list_for_each_entry_safe(pos, n, &notes->src->source, node) {
706                 list_del(&pos->node);
707                 disasm_line__free(pos);
708         }
709
710 out_free_offsets:
711         free(browser.offsets);
712         return ret;
713 }