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