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