Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
annotate.c
Go to the documentation of this file.
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 
15  struct rb_node rb_node;
16  double percent;
18  int idx_asm;
20 };
21 
22 static struct annotate_browser_opt {
23  bool hide_src_code,
24  use_offset,
25  jump_arrows,
26  show_nr_jumps;
27 } annotate_browser__opts = {
28  .use_offset = true,
29  .jump_arrows = true,
30 };
31 
33  struct ui_browser b;
34  struct rb_root entries;
35  struct rb_node *curr_hot;
37  struct disasm_line **offsets;
42  int nr_jumps;
49  char search_bf[128];
50 };
51 
52 static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl)
53 {
54  return (struct browser_disasm_line *)(dl + 1);
55 }
56 
57 static bool disasm_line__filter(struct ui_browser *browser __maybe_unused,
58  void *entry)
59 {
60  if (annotate_browser__opts.hide_src_code) {
61  struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
62  return dl->offset == -1;
63  }
64 
65  return false;
66 }
67 
68 static int annotate_browser__jumps_percent_color(struct annotate_browser *browser,
69  int nr, bool current)
70 {
71  if (current && (!browser->b.use_navkeypressed || browser->b.navkeypressed))
72  return HE_COLORSET_SELECTED;
73  if (nr == browser->max_jump_sources)
74  return HE_COLORSET_TOP;
75  if (nr > 1)
76  return HE_COLORSET_MEDIUM;
77  return HE_COLORSET_NORMAL;
78 }
79 
80 static int annotate_browser__set_jumps_percent_color(struct annotate_browser *browser,
81  int nr, bool current)
82 {
83  int color = annotate_browser__jumps_percent_color(browser, nr, current);
84  return ui_browser__set_color(&browser->b, color);
85 }
86 
87 static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
88 {
89  struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
90  struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
91  struct browser_disasm_line *bdl = disasm_line__browser(dl);
92  bool current_entry = ui_browser__is_current_entry(browser, row);
93  bool change_color = (!annotate_browser__opts.hide_src_code &&
94  (!current_entry || (browser->use_navkeypressed &&
95  !browser->navkeypressed)));
96  int width = browser->width, printed;
97  char bf[256];
98 
99  if (dl->offset != -1 && bdl->percent != 0.0) {
100  ui_browser__set_percent_color(browser, bdl->percent, current_entry);
101  slsmg_printf("%6.2f ", bdl->percent);
102  } else {
103  ui_browser__set_percent_color(browser, 0, current_entry);
104  slsmg_write_nstring(" ", 7);
105  }
106 
107  SLsmg_write_char(' ');
108 
109  /* The scroll bar isn't being used */
110  if (!browser->navkeypressed)
111  width += 1;
112 
113  if (!*dl->line)
114  slsmg_write_nstring(" ", width - 7);
115  else if (dl->offset == -1) {
116  printed = scnprintf(bf, sizeof(bf), "%*s ",
117  ab->addr_width, " ");
118  slsmg_write_nstring(bf, printed);
119  slsmg_write_nstring(dl->line, width - printed - 6);
120  } else {
121  u64 addr = dl->offset;
122  int color = -1;
123 
124  if (!annotate_browser__opts.use_offset)
125  addr += ab->start;
126 
127  if (!annotate_browser__opts.use_offset) {
128  printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
129  } else {
130  if (bdl->jump_sources) {
131  if (annotate_browser__opts.show_nr_jumps) {
132  int prev;
133  printed = scnprintf(bf, sizeof(bf), "%*d ",
134  ab->jumps_width,
135  bdl->jump_sources);
136  prev = annotate_browser__set_jumps_percent_color(ab, bdl->jump_sources,
137  current_entry);
138  slsmg_write_nstring(bf, printed);
139  ui_browser__set_color(browser, prev);
140  }
141 
142  printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
143  ab->target_width, addr);
144  } else {
145  printed = scnprintf(bf, sizeof(bf), "%*s ",
146  ab->addr_width, " ");
147  }
148  }
149 
150  if (change_color)
151  color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
152  slsmg_write_nstring(bf, printed);
153  if (change_color)
154  ui_browser__set_color(browser, color);
155  if (dl->ins && dl->ins->ops->scnprintf) {
156  if (ins__is_jump(dl->ins)) {
157  bool fwd = dl->ops.target.offset > (u64)dl->offset;
158 
159  ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
160  SLSMG_UARROW_CHAR);
161  SLsmg_write_char(' ');
162  } else if (ins__is_call(dl->ins)) {
163  ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);
164  SLsmg_write_char(' ');
165  } else {
166  slsmg_write_nstring(" ", 2);
167  }
168  } else {
169  if (strcmp(dl->name, "retq")) {
170  slsmg_write_nstring(" ", 2);
171  } else {
172  ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
173  SLsmg_write_char(' ');
174  }
175  }
176 
177  disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset);
178  slsmg_write_nstring(bf, width - 10 - printed);
179  }
180 
181  if (current_entry)
182  ab->selection = dl;
183 }
184 
185 static void annotate_browser__draw_current_jump(struct ui_browser *browser)
186 {
187  struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
188  struct disasm_line *cursor = ab->selection, *target;
189  struct browser_disasm_line *btarget, *bcursor;
190  unsigned int from, to;
191 
192  if (!cursor || !cursor->ins || !ins__is_jump(cursor->ins) ||
193  !disasm_line__has_offset(cursor))
194  return;
195 
196  target = ab->offsets[cursor->ops.target.offset];
197  if (!target)
198  return;
199 
200  bcursor = disasm_line__browser(cursor);
201  btarget = disasm_line__browser(target);
202 
203  if (annotate_browser__opts.hide_src_code) {
204  from = bcursor->idx_asm;
205  to = btarget->idx_asm;
206  } else {
207  from = (u64)bcursor->idx;
208  to = (u64)btarget->idx;
209  }
210 
212  __ui_browser__line_arrow(browser, 9 + ab->addr_width, from, to);
213 }
214 
215 static unsigned int annotate_browser__refresh(struct ui_browser *browser)
216 {
217  int ret = ui_browser__list_head_refresh(browser);
218 
219  if (annotate_browser__opts.jump_arrows)
220  annotate_browser__draw_current_jump(browser);
221 
223  __ui_browser__vline(browser, 7, 0, browser->height - 1);
224  return ret;
225 }
226 
227 static double disasm_line__calc_percent(struct disasm_line *dl, struct symbol *sym, int evidx)
228 {
229  double percent = 0.0;
230 
231  if (dl->offset != -1) {
232  int len = sym->end - sym->start;
233  unsigned int hits = 0;
234  struct annotation *notes = symbol__annotation(sym);
235  struct source_line *src_line = notes->src->lines;
236  struct sym_hist *h = annotation__histogram(notes, evidx);
237  s64 offset = dl->offset;
238  struct disasm_line *next;
239 
240  next = disasm__get_next_ip_line(&notes->src->source, dl);
241  while (offset < (s64)len &&
242  (next == NULL || offset < next->offset)) {
243  if (src_line) {
244  percent += src_line[offset].percent;
245  } else
246  hits += h->addr[offset];
247 
248  ++offset;
249  }
250  /*
251  * If the percentage wasn't already calculated in
252  * symbol__get_source_line, do it now:
253  */
254  if (src_line == NULL && h->sum)
255  percent = 100.0 * hits / h->sum;
256  }
257 
258  return percent;
259 }
260 
261 static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl)
262 {
263  struct rb_node **p = &root->rb_node;
264  struct rb_node *parent = NULL;
265  struct browser_disasm_line *l;
266 
267  while (*p != NULL) {
268  parent = *p;
269  l = rb_entry(parent, struct browser_disasm_line, rb_node);
270  if (bdl->percent < l->percent)
271  p = &(*p)->rb_left;
272  else
273  p = &(*p)->rb_right;
274  }
275  rb_link_node(&bdl->rb_node, parent, p);
276  rb_insert_color(&bdl->rb_node, root);
277 }
278 
279 static void annotate_browser__set_top(struct annotate_browser *browser,
280  struct disasm_line *pos, u32 idx)
281 {
282  unsigned back;
283 
285  back = browser->b.height / 2;
286  browser->b.top_idx = browser->b.index = idx;
287 
288  while (browser->b.top_idx != 0 && back != 0) {
289  pos = list_entry(pos->node.prev, struct disasm_line, node);
290 
291  if (disasm_line__filter(&browser->b, &pos->node))
292  continue;
293 
294  --browser->b.top_idx;
295  --back;
296  }
297 
298  browser->b.top = pos;
299  browser->b.navkeypressed = true;
300 }
301 
302 static void annotate_browser__set_rb_top(struct annotate_browser *browser,
303  struct rb_node *nd)
304 {
305  struct browser_disasm_line *bpos;
306  struct disasm_line *pos;
307  u32 idx;
308 
309  bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
310  pos = ((struct disasm_line *)bpos) - 1;
311  idx = bpos->idx;
312  if (annotate_browser__opts.hide_src_code)
313  idx = bpos->idx_asm;
314  annotate_browser__set_top(browser, pos, idx);
315  browser->curr_hot = nd;
316 }
317 
318 static void annotate_browser__calc_percent(struct annotate_browser *browser,
319  int evidx)
320 {
321  struct map_symbol *ms = browser->b.priv;
322  struct symbol *sym = ms->sym;
323  struct annotation *notes = symbol__annotation(sym);
324  struct disasm_line *pos;
325 
326  browser->entries = RB_ROOT;
327 
328  pthread_mutex_lock(&notes->lock);
329 
330  list_for_each_entry(pos, &notes->src->source, node) {
331  struct browser_disasm_line *bpos = disasm_line__browser(pos);
332  bpos->percent = disasm_line__calc_percent(pos, sym, evidx);
333  if (bpos->percent < 0.01) {
334  RB_CLEAR_NODE(&bpos->rb_node);
335  continue;
336  }
337  disasm_rb_tree__insert(&browser->entries, bpos);
338  }
339  pthread_mutex_unlock(&notes->lock);
340 
341  browser->curr_hot = rb_last(&browser->entries);
342 }
343 
344 static bool annotate_browser__toggle_source(struct annotate_browser *browser)
345 {
346  struct disasm_line *dl;
347  struct browser_disasm_line *bdl;
348  off_t offset = browser->b.index - browser->b.top_idx;
349 
350  browser->b.seek(&browser->b, offset, SEEK_CUR);
351  dl = list_entry(browser->b.top, struct disasm_line, node);
352  bdl = disasm_line__browser(dl);
353 
354  if (annotate_browser__opts.hide_src_code) {
355  if (bdl->idx_asm < offset)
356  offset = bdl->idx;
357 
358  browser->b.nr_entries = browser->nr_entries;
359  annotate_browser__opts.hide_src_code = false;
360  browser->b.seek(&browser->b, -offset, SEEK_CUR);
361  browser->b.top_idx = bdl->idx - offset;
362  browser->b.index = bdl->idx;
363  } else {
364  if (bdl->idx_asm < 0) {
365  ui_helpline__puts("Only available for assembly lines.");
366  browser->b.seek(&browser->b, -offset, SEEK_CUR);
367  return false;
368  }
369 
370  if (bdl->idx_asm < offset)
371  offset = bdl->idx_asm;
372 
373  browser->b.nr_entries = browser->nr_asm_entries;
374  annotate_browser__opts.hide_src_code = true;
375  browser->b.seek(&browser->b, -offset, SEEK_CUR);
376  browser->b.top_idx = bdl->idx_asm - offset;
377  browser->b.index = bdl->idx_asm;
378  }
379 
380  return true;
381 }
382 
383 static void annotate_browser__init_asm_mode(struct annotate_browser *browser)
384 {
385  ui_browser__reset_index(&browser->b);
386  browser->b.nr_entries = browser->nr_asm_entries;
387 }
388 
389 static bool annotate_browser__callq(struct annotate_browser *browser,
390  int evidx, void (*timer)(void *arg),
391  void *arg, int delay_secs)
392 {
393  struct map_symbol *ms = browser->b.priv;
394  struct disasm_line *dl = browser->selection;
395  struct symbol *sym = ms->sym;
396  struct annotation *notes;
397  struct symbol *target;
398  u64 ip;
399 
400  if (!ins__is_call(dl->ins))
401  return false;
402 
403  ip = ms->map->map_ip(ms->map, dl->ops.target.addr);
404  target = map__find_symbol(ms->map, ip, NULL);
405  if (target == NULL) {
406  ui_helpline__puts("The called function was not found.");
407  return true;
408  }
409 
410  notes = symbol__annotation(target);
411  pthread_mutex_lock(&notes->lock);
412 
413  if (notes->src == NULL && symbol__alloc_hist(target) < 0) {
414  pthread_mutex_unlock(&notes->lock);
415  ui__warning("Not enough memory for annotating '%s' symbol!\n",
416  target->name);
417  return true;
418  }
419 
420  pthread_mutex_unlock(&notes->lock);
421  symbol__tui_annotate(target, ms->map, evidx, timer, arg, delay_secs);
422  ui_browser__show_title(&browser->b, sym->name);
423  return true;
424 }
425 
426 static
427 struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
428  s64 offset, s64 *idx)
429 {
430  struct map_symbol *ms = browser->b.priv;
431  struct symbol *sym = ms->sym;
432  struct annotation *notes = symbol__annotation(sym);
433  struct disasm_line *pos;
434 
435  *idx = 0;
436  list_for_each_entry(pos, &notes->src->source, node) {
437  if (pos->offset == offset)
438  return pos;
439  if (!disasm_line__filter(&browser->b, &pos->node))
440  ++*idx;
441  }
442 
443  return NULL;
444 }
445 
446 static bool annotate_browser__jump(struct annotate_browser *browser)
447 {
448  struct disasm_line *dl = browser->selection;
449  s64 idx;
450 
451  if (!ins__is_jump(dl->ins))
452  return false;
453 
454  dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
455  if (dl == NULL) {
456  ui_helpline__puts("Invallid jump offset");
457  return true;
458  }
459 
460  annotate_browser__set_top(browser, dl, idx);
461 
462  return true;
463 }
464 
465 static
466 struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
467  char *s, s64 *idx)
468 {
469  struct map_symbol *ms = browser->b.priv;
470  struct symbol *sym = ms->sym;
471  struct annotation *notes = symbol__annotation(sym);
472  struct disasm_line *pos = browser->selection;
473 
474  *idx = browser->b.index;
475  list_for_each_entry_continue(pos, &notes->src->source, node) {
476  if (disasm_line__filter(&browser->b, &pos->node))
477  continue;
478 
479  ++*idx;
480 
481  if (pos->line && strstr(pos->line, s) != NULL)
482  return pos;
483  }
484 
485  return NULL;
486 }
487 
488 static bool __annotate_browser__search(struct annotate_browser *browser)
489 {
490  struct disasm_line *dl;
491  s64 idx;
492 
493  dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
494  if (dl == NULL) {
495  ui_helpline__puts("String not found!");
496  return false;
497  }
498 
499  annotate_browser__set_top(browser, dl, idx);
500  browser->searching_backwards = false;
501  return true;
502 }
503 
504 static
505 struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
506  char *s, s64 *idx)
507 {
508  struct map_symbol *ms = browser->b.priv;
509  struct symbol *sym = ms->sym;
510  struct annotation *notes = symbol__annotation(sym);
511  struct disasm_line *pos = browser->selection;
512 
513  *idx = browser->b.index;
514  list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
515  if (disasm_line__filter(&browser->b, &pos->node))
516  continue;
517 
518  --*idx;
519 
520  if (pos->line && strstr(pos->line, s) != NULL)
521  return pos;
522  }
523 
524  return NULL;
525 }
526 
527 static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
528 {
529  struct disasm_line *dl;
530  s64 idx;
531 
532  dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
533  if (dl == NULL) {
534  ui_helpline__puts("String not found!");
535  return false;
536  }
537 
538  annotate_browser__set_top(browser, dl, idx);
539  browser->searching_backwards = true;
540  return true;
541 }
542 
543 static bool annotate_browser__search_window(struct annotate_browser *browser,
544  int delay_secs)
545 {
546  if (ui_browser__input_window("Search", "String: ", browser->search_bf,
547  "ENTER: OK, ESC: Cancel",
548  delay_secs * 2) != K_ENTER ||
549  !*browser->search_bf)
550  return false;
551 
552  return true;
553 }
554 
555 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
556 {
557  if (annotate_browser__search_window(browser, delay_secs))
558  return __annotate_browser__search(browser);
559 
560  return false;
561 }
562 
563 static bool annotate_browser__continue_search(struct annotate_browser *browser,
564  int delay_secs)
565 {
566  if (!*browser->search_bf)
567  return annotate_browser__search(browser, delay_secs);
568 
569  return __annotate_browser__search(browser);
570 }
571 
572 static bool annotate_browser__search_reverse(struct annotate_browser *browser,
573  int delay_secs)
574 {
575  if (annotate_browser__search_window(browser, delay_secs))
576  return __annotate_browser__search_reverse(browser);
577 
578  return false;
579 }
580 
581 static
582 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
583  int delay_secs)
584 {
585  if (!*browser->search_bf)
586  return annotate_browser__search_reverse(browser, delay_secs);
587 
588  return __annotate_browser__search_reverse(browser);
589 }
590 
591 static void annotate_browser__update_addr_width(struct annotate_browser *browser)
592 {
593  if (annotate_browser__opts.use_offset)
594  browser->target_width = browser->min_addr_width;
595  else
596  browser->target_width = browser->max_addr_width;
597 
598  browser->addr_width = browser->target_width;
599 
600  if (annotate_browser__opts.show_nr_jumps)
601  browser->addr_width += browser->jumps_width + 1;
602 }
603 
604 static int annotate_browser__run(struct annotate_browser *browser, int evidx,
605  void(*timer)(void *arg),
606  void *arg, int delay_secs)
607 {
608  struct rb_node *nd = NULL;
609  struct map_symbol *ms = browser->b.priv;
610  struct symbol *sym = ms->sym;
611  const char *help = "Press 'h' for help on key bindings";
612  int key;
613 
614  if (ui_browser__show(&browser->b, sym->name, help) < 0)
615  return -1;
616 
617  annotate_browser__calc_percent(browser, evidx);
618 
619  if (browser->curr_hot) {
620  annotate_browser__set_rb_top(browser, browser->curr_hot);
621  browser->b.navkeypressed = false;
622  }
623 
624  nd = browser->curr_hot;
625 
626  while (1) {
627  key = ui_browser__run(&browser->b, delay_secs);
628 
629  if (delay_secs != 0) {
630  annotate_browser__calc_percent(browser, evidx);
631  /*
632  * Current line focus got out of the list of most active
633  * lines, NULL it so that if TAB|UNTAB is pressed, we
634  * move to curr_hot (current hottest line).
635  */
636  if (nd != NULL && RB_EMPTY_NODE(nd))
637  nd = NULL;
638  }
639 
640  switch (key) {
641  case K_TIMER:
642  if (timer != NULL)
643  timer(arg);
644 
645  if (delay_secs != 0)
647  continue;
648  case K_TAB:
649  if (nd != NULL) {
650  nd = rb_prev(nd);
651  if (nd == NULL)
652  nd = rb_last(&browser->entries);
653  } else
654  nd = browser->curr_hot;
655  break;
656  case K_UNTAB:
657  if (nd != NULL)
658  nd = rb_next(nd);
659  if (nd == NULL)
660  nd = rb_first(&browser->entries);
661  else
662  nd = browser->curr_hot;
663  break;
664  case K_F1:
665  case 'h':
666  ui_browser__help_window(&browser->b,
667  "UP/DOWN/PGUP\n"
668  "PGDN/SPACE Navigate\n"
669  "q/ESC/CTRL+C Exit\n\n"
670  "-> Go to target\n"
671  "<- Exit\n"
672  "H Cycle thru hottest instructions\n"
673  "j Toggle showing jump to target arrows\n"
674  "J Toggle showing number of jump sources on targets\n"
675  "n Search next string\n"
676  "o Toggle disassembler output/simplified view\n"
677  "s Toggle source code view\n"
678  "/ Search string\n"
679  "? Search previous string\n");
680  continue;
681  case 'H':
682  nd = browser->curr_hot;
683  break;
684  case 's':
685  if (annotate_browser__toggle_source(browser))
686  ui_helpline__puts(help);
687  continue;
688  case 'o':
689  annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
690  annotate_browser__update_addr_width(browser);
691  continue;
692  case 'j':
693  annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
694  continue;
695  case 'J':
696  annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
697  annotate_browser__update_addr_width(browser);
698  continue;
699  case '/':
700  if (annotate_browser__search(browser, delay_secs)) {
701 show_help:
702  ui_helpline__puts(help);
703  }
704  continue;
705  case 'n':
706  if (browser->searching_backwards ?
707  annotate_browser__continue_search_reverse(browser, delay_secs) :
708  annotate_browser__continue_search(browser, delay_secs))
709  goto show_help;
710  continue;
711  case '?':
712  if (annotate_browser__search_reverse(browser, delay_secs))
713  goto show_help;
714  continue;
715  case 'D': {
716  static int seq;
718  ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
719  seq++, browser->b.nr_entries,
720  browser->b.height,
721  browser->b.index,
722  browser->b.top_idx,
723  browser->nr_asm_entries);
724  }
725  continue;
726  case K_ENTER:
727  case K_RIGHT:
728  if (browser->selection == NULL)
729  ui_helpline__puts("Huh? No selection. Report to [email protected]");
730  else if (browser->selection->offset == -1)
731  ui_helpline__puts("Actions are only available for assembly lines.");
732  else if (!browser->selection->ins) {
733  if (strcmp(browser->selection->name, "retq"))
734  goto show_sup_ins;
735  goto out;
736  } else if (!(annotate_browser__jump(browser) ||
737  annotate_browser__callq(browser, evidx, timer, arg, delay_secs))) {
738 show_sup_ins:
739  ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions.");
740  }
741  continue;
742  case K_LEFT:
743  case K_ESC:
744  case 'q':
745  case CTRL('c'):
746  goto out;
747  default:
748  continue;
749  }
750 
751  if (nd != NULL)
752  annotate_browser__set_rb_top(browser, nd);
753  }
754 out:
755  ui_browser__hide(&browser->b);
756  return key;
757 }
758 
759 int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
760  void(*timer)(void *arg), void *arg, int delay_secs)
761 {
762  return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx,
763  timer, arg, delay_secs);
764 }
765 
766 static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
767  size_t size)
768 {
769  u64 offset;
770 
771  for (offset = 0; offset < size; ++offset) {
772  struct disasm_line *dl = browser->offsets[offset], *dlt;
773  struct browser_disasm_line *bdlt;
774 
775  if (!dl || !dl->ins || !ins__is_jump(dl->ins) ||
776  !disasm_line__has_offset(dl))
777  continue;
778 
779  if (dl->ops.target.offset >= size) {
780  ui__error("jump to after symbol!\n"
781  "size: %zx, jump target: %" PRIx64,
782  size, dl->ops.target.offset);
783  continue;
784  }
785 
786  dlt = browser->offsets[dl->ops.target.offset];
787  /*
788  * FIXME: Oops, no jump target? Buggy disassembler? Or do we
789  * have to adjust to the previous offset?
790  */
791  if (dlt == NULL)
792  continue;
793 
794  bdlt = disasm_line__browser(dlt);
795  if (++bdlt->jump_sources > browser->max_jump_sources)
796  browser->max_jump_sources = bdlt->jump_sources;
797 
798  ++browser->nr_jumps;
799  }
800 
801 }
802 
803 static inline int width_jumps(int n)
804 {
805  if (n >= 100)
806  return 5;
807  if (n / 10)
808  return 2;
809  return 1;
810 }
811 
812 int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
813  void(*timer)(void *arg), void *arg,
814  int delay_secs)
815 {
816  struct disasm_line *pos, *n;
817  struct annotation *notes;
818  size_t size;
819  struct map_symbol ms = {
820  .map = map,
821  .sym = sym,
822  };
823  struct annotate_browser browser = {
824  .b = {
825  .refresh = annotate_browser__refresh,
827  .write = annotate_browser__write,
828  .filter = disasm_line__filter,
829  .priv = &ms,
830  .use_navkeypressed = true,
831  },
832  };
833  int ret = -1;
834 
835  if (sym == NULL)
836  return -1;
837 
838  size = symbol__size(sym);
839 
840  if (map->dso->annotate_warned)
841  return -1;
842 
843  browser.offsets = zalloc(size * sizeof(struct disasm_line *));
844  if (browser.offsets == NULL) {
845  ui__error("Not enough memory!");
846  return -1;
847  }
848 
849  if (symbol__annotate(sym, map, sizeof(struct browser_disasm_line)) < 0) {
851  goto out_free_offsets;
852  }
853 
854  ui_helpline__push("Press <- or ESC to exit");
855 
856  notes = symbol__annotation(sym);
857  browser.start = map__rip_2objdump(map, sym->start);
858 
859  list_for_each_entry(pos, &notes->src->source, node) {
860  struct browser_disasm_line *bpos;
861  size_t line_len = strlen(pos->line);
862 
863  if (browser.b.width < line_len)
864  browser.b.width = line_len;
865  bpos = disasm_line__browser(pos);
866  bpos->idx = browser.nr_entries++;
867  if (pos->offset != -1) {
868  bpos->idx_asm = browser.nr_asm_entries++;
869  /*
870  * FIXME: short term bandaid to cope with assembly
871  * routines that comes with labels in the same column
872  * as the address in objdump, sigh.
873  *
874  * E.g. copy_user_generic_unrolled
875  */
876  if (pos->offset < (s64)size)
877  browser.offsets[pos->offset] = pos;
878  } else
879  bpos->idx_asm = -1;
880  }
881 
882  annotate_browser__mark_jump_targets(&browser, size);
883 
884  browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
885  browser.max_addr_width = hex_width(sym->end);
886  browser.jumps_width = width_jumps(browser.max_jump_sources);
887  browser.b.nr_entries = browser.nr_entries;
888  browser.b.entries = &notes->src->source,
889  browser.b.width += 18; /* Percentage */
890 
891  if (annotate_browser__opts.hide_src_code)
892  annotate_browser__init_asm_mode(&browser);
893 
894  annotate_browser__update_addr_width(&browser);
895 
896  ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs);
897  list_for_each_entry_safe(pos, n, &notes->src->source, node) {
898  list_del(&pos->node);
899  disasm_line__free(pos);
900  }
901 
902 out_free_offsets:
903  free(browser.offsets);
904  return ret;
905 }
906 
907 #define ANNOTATE_CFG(n) \
908  { .name = #n, .value = &annotate_browser__opts.n, }
909 
910 /*
911  * Keep the entries sorted, they are bsearch'ed
912  */
913 static struct annotate__config {
914  const char *name;
915  bool *value;
916 } annotate__configs[] = {
917  ANNOTATE_CFG(hide_src_code),
918  ANNOTATE_CFG(jump_arrows),
919  ANNOTATE_CFG(show_nr_jumps),
920  ANNOTATE_CFG(use_offset),
921 };
922 
923 #undef ANNOTATE_CFG
924 
925 static int annotate_config__cmp(const void *name, const void *cfgp)
926 {
927  const struct annotate__config *cfg = cfgp;
928 
929  return strcmp(name, cfg->name);
930 }
931 
932 static int annotate__config(const char *var, const char *value,
933  void *data __maybe_unused)
934 {
935  struct annotate__config *cfg;
936  const char *name;
937 
938  if (prefixcmp(var, "annotate.") != 0)
939  return 0;
940 
941  name = var + 9;
942  cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
943  sizeof(struct annotate__config), annotate_config__cmp);
944 
945  if (cfg == NULL)
946  return -1;
947 
948  *cfg->value = perf_config_bool(name, value);
949  return 0;
950 }
951 
953 {
954  perf_config(annotate__config, NULL);
955 }