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