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