1 #include "../../util/util.h"
2 #include "../browser.h"
3 #include "../helpline.h"
4 #include "../libslang.h"
7 #include "../../util/annotate.h"
8 #include "../../util/hist.h"
9 #include "../../util/sort.h"
10 #include "../../util/symbol.h"
11 #include "../../util/evsel.h"
15 struct browser_disasm_line {
16 struct rb_node rb_node;
23 static struct annotate_browser_opt {
28 } annotate_browser__opts = {
33 struct annotate_browser {
35 struct rb_root entries;
36 struct rb_node *curr_hot;
37 struct disasm_line *selection;
38 struct disasm_line **offsets;
44 bool searching_backwards;
53 static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl)
55 return (struct browser_disasm_line *)(dl + 1);
58 static bool disasm_line__filter(struct ui_browser *browser __maybe_unused,
61 if (annotate_browser__opts.hide_src_code) {
62 struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
63 return dl->offset == -1;
69 static int annotate_browser__jumps_percent_color(struct annotate_browser *browser,
72 if (current && (!browser->b.use_navkeypressed || browser->b.navkeypressed))
73 return HE_COLORSET_SELECTED;
74 if (nr == browser->max_jump_sources)
75 return HE_COLORSET_TOP;
77 return HE_COLORSET_MEDIUM;
78 return HE_COLORSET_NORMAL;
81 static int annotate_browser__set_jumps_percent_color(struct annotate_browser *browser,
84 int color = annotate_browser__jumps_percent_color(browser, nr, current);
85 return ui_browser__set_color(&browser->b, color);
88 static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
90 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
91 struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
92 struct browser_disasm_line *bdl = disasm_line__browser(dl);
93 bool current_entry = ui_browser__is_current_entry(browser, row);
94 bool change_color = (!annotate_browser__opts.hide_src_code &&
95 (!current_entry || (browser->use_navkeypressed &&
96 !browser->navkeypressed)));
97 int width = browser->width, printed;
100 if (dl->offset != -1 && bdl->percent[0] != 0.0) {
101 ui_browser__set_percent_color(browser, bdl->percent[0], current_entry);
102 slsmg_printf("%6.2f ", bdl->percent[0]);
104 ui_browser__set_percent_color(browser, 0, current_entry);
105 slsmg_write_nstring(" ", 7);
108 SLsmg_write_char(' ');
110 /* The scroll bar isn't being used */
111 if (!browser->navkeypressed)
115 slsmg_write_nstring(" ", width - 7);
116 else if (dl->offset == -1) {
117 printed = scnprintf(bf, sizeof(bf), "%*s ",
118 ab->addr_width, " ");
119 slsmg_write_nstring(bf, printed);
120 slsmg_write_nstring(dl->line, width - printed - 6);
122 u64 addr = dl->offset;
125 if (!annotate_browser__opts.use_offset)
128 if (!annotate_browser__opts.use_offset) {
129 printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
131 if (bdl->jump_sources) {
132 if (annotate_browser__opts.show_nr_jumps) {
134 printed = scnprintf(bf, sizeof(bf), "%*d ",
137 prev = annotate_browser__set_jumps_percent_color(ab, bdl->jump_sources,
139 slsmg_write_nstring(bf, printed);
140 ui_browser__set_color(browser, prev);
143 printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
144 ab->target_width, addr);
146 printed = scnprintf(bf, sizeof(bf), "%*s ",
147 ab->addr_width, " ");
152 color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
153 slsmg_write_nstring(bf, printed);
155 ui_browser__set_color(browser, color);
156 if (dl->ins && dl->ins->ops->scnprintf) {
157 if (ins__is_jump(dl->ins)) {
158 bool fwd = dl->ops.target.offset > (u64)dl->offset;
160 ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
162 SLsmg_write_char(' ');
163 } else if (ins__is_call(dl->ins)) {
164 ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);
165 SLsmg_write_char(' ');
167 slsmg_write_nstring(" ", 2);
170 if (strcmp(dl->name, "retq")) {
171 slsmg_write_nstring(" ", 2);
173 ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
174 SLsmg_write_char(' ');
178 disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset);
179 slsmg_write_nstring(bf, width - 10 - printed);
186 static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym)
188 if (!dl || !dl->ins || !ins__is_jump(dl->ins)
189 || !disasm_line__has_offset(dl)
190 || dl->ops.target.offset >= symbol__size(sym))
196 static void annotate_browser__draw_current_jump(struct ui_browser *browser)
198 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
199 struct disasm_line *cursor = ab->selection, *target;
200 struct browser_disasm_line *btarget, *bcursor;
201 unsigned int from, to;
202 struct map_symbol *ms = ab->b.priv;
203 struct symbol *sym = ms->sym;
205 /* PLT symbols contain external offsets */
206 if (strstr(sym->name, "@plt"))
209 if (!disasm_line__is_valid_jump(cursor, sym))
212 target = ab->offsets[cursor->ops.target.offset];
216 bcursor = disasm_line__browser(cursor);
217 btarget = disasm_line__browser(target);
219 if (annotate_browser__opts.hide_src_code) {
220 from = bcursor->idx_asm;
221 to = btarget->idx_asm;
223 from = (u64)bcursor->idx;
224 to = (u64)btarget->idx;
227 ui_browser__set_color(browser, HE_COLORSET_CODE);
228 __ui_browser__line_arrow(browser, 9 + ab->addr_width, from, to);
231 static unsigned int annotate_browser__refresh(struct ui_browser *browser)
233 int ret = ui_browser__list_head_refresh(browser);
235 if (annotate_browser__opts.jump_arrows)
236 annotate_browser__draw_current_jump(browser);
238 ui_browser__set_color(browser, HE_COLORSET_NORMAL);
239 __ui_browser__vline(browser, 7, 0, browser->height - 1);
243 static double disasm_line__calc_percent(struct disasm_line *dl, struct symbol *sym, int evidx)
245 double percent = 0.0;
247 if (dl->offset != -1) {
248 int len = sym->end - sym->start;
249 unsigned int hits = 0;
250 struct annotation *notes = symbol__annotation(sym);
251 struct source_line *src_line = notes->src->lines;
252 struct sym_hist *h = annotation__histogram(notes, evidx);
253 s64 offset = dl->offset;
254 struct disasm_line *next;
256 next = disasm__get_next_ip_line(¬es->src->source, dl);
257 while (offset < (s64)len &&
258 (next == NULL || offset < next->offset)) {
260 percent += src_line[offset].p[0].percent;
262 hits += h->addr[offset];
267 * If the percentage wasn't already calculated in
268 * symbol__get_source_line, do it now:
270 if (src_line == NULL && h->sum)
271 percent = 100.0 * hits / h->sum;
277 static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl)
279 struct rb_node **p = &root->rb_node;
280 struct rb_node *parent = NULL;
281 struct browser_disasm_line *l;
285 l = rb_entry(parent, struct browser_disasm_line, rb_node);
286 if (bdl->percent[0] < l->percent[0])
291 rb_link_node(&bdl->rb_node, parent, p);
292 rb_insert_color(&bdl->rb_node, root);
295 static void annotate_browser__set_top(struct annotate_browser *browser,
296 struct disasm_line *pos, u32 idx)
300 ui_browser__refresh_dimensions(&browser->b);
301 back = browser->b.height / 2;
302 browser->b.top_idx = browser->b.index = idx;
304 while (browser->b.top_idx != 0 && back != 0) {
305 pos = list_entry(pos->node.prev, struct disasm_line, node);
307 if (disasm_line__filter(&browser->b, &pos->node))
310 --browser->b.top_idx;
314 browser->b.top = pos;
315 browser->b.navkeypressed = true;
318 static void annotate_browser__set_rb_top(struct annotate_browser *browser,
321 struct browser_disasm_line *bpos;
322 struct disasm_line *pos;
325 bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
326 pos = ((struct disasm_line *)bpos) - 1;
328 if (annotate_browser__opts.hide_src_code)
330 annotate_browser__set_top(browser, pos, idx);
331 browser->curr_hot = nd;
334 static void annotate_browser__calc_percent(struct annotate_browser *browser,
335 struct perf_evsel *evsel)
337 struct map_symbol *ms = browser->b.priv;
338 struct symbol *sym = ms->sym;
339 struct annotation *notes = symbol__annotation(sym);
340 struct disasm_line *pos;
342 browser->entries = RB_ROOT;
344 pthread_mutex_lock(¬es->lock);
346 list_for_each_entry(pos, ¬es->src->source, node) {
347 struct browser_disasm_line *bpos = disasm_line__browser(pos);
348 bpos->percent[0] = disasm_line__calc_percent(pos, sym, evsel->idx);
349 if (bpos->percent[0] < 0.01) {
350 RB_CLEAR_NODE(&bpos->rb_node);
353 disasm_rb_tree__insert(&browser->entries, bpos);
355 pthread_mutex_unlock(¬es->lock);
357 browser->curr_hot = rb_last(&browser->entries);
360 static bool annotate_browser__toggle_source(struct annotate_browser *browser)
362 struct disasm_line *dl;
363 struct browser_disasm_line *bdl;
364 off_t offset = browser->b.index - browser->b.top_idx;
366 browser->b.seek(&browser->b, offset, SEEK_CUR);
367 dl = list_entry(browser->b.top, struct disasm_line, node);
368 bdl = disasm_line__browser(dl);
370 if (annotate_browser__opts.hide_src_code) {
371 if (bdl->idx_asm < offset)
374 browser->b.nr_entries = browser->nr_entries;
375 annotate_browser__opts.hide_src_code = false;
376 browser->b.seek(&browser->b, -offset, SEEK_CUR);
377 browser->b.top_idx = bdl->idx - offset;
378 browser->b.index = bdl->idx;
380 if (bdl->idx_asm < 0) {
381 ui_helpline__puts("Only available for assembly lines.");
382 browser->b.seek(&browser->b, -offset, SEEK_CUR);
386 if (bdl->idx_asm < offset)
387 offset = bdl->idx_asm;
389 browser->b.nr_entries = browser->nr_asm_entries;
390 annotate_browser__opts.hide_src_code = true;
391 browser->b.seek(&browser->b, -offset, SEEK_CUR);
392 browser->b.top_idx = bdl->idx_asm - offset;
393 browser->b.index = bdl->idx_asm;
399 static void annotate_browser__init_asm_mode(struct annotate_browser *browser)
401 ui_browser__reset_index(&browser->b);
402 browser->b.nr_entries = browser->nr_asm_entries;
405 static bool annotate_browser__callq(struct annotate_browser *browser,
406 struct perf_evsel *evsel,
407 struct hist_browser_timer *hbt)
409 struct map_symbol *ms = browser->b.priv;
410 struct disasm_line *dl = browser->selection;
411 struct symbol *sym = ms->sym;
412 struct annotation *notes;
413 struct symbol *target;
416 if (!ins__is_call(dl->ins))
419 ip = ms->map->map_ip(ms->map, dl->ops.target.addr);
420 target = map__find_symbol(ms->map, ip, NULL);
421 if (target == NULL) {
422 ui_helpline__puts("The called function was not found.");
426 notes = symbol__annotation(target);
427 pthread_mutex_lock(¬es->lock);
429 if (notes->src == NULL && symbol__alloc_hist(target) < 0) {
430 pthread_mutex_unlock(¬es->lock);
431 ui__warning("Not enough memory for annotating '%s' symbol!\n",
436 pthread_mutex_unlock(¬es->lock);
437 symbol__tui_annotate(target, ms->map, evsel, hbt);
438 ui_browser__show_title(&browser->b, sym->name);
443 struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
444 s64 offset, s64 *idx)
446 struct map_symbol *ms = browser->b.priv;
447 struct symbol *sym = ms->sym;
448 struct annotation *notes = symbol__annotation(sym);
449 struct disasm_line *pos;
452 list_for_each_entry(pos, ¬es->src->source, node) {
453 if (pos->offset == offset)
455 if (!disasm_line__filter(&browser->b, &pos->node))
462 static bool annotate_browser__jump(struct annotate_browser *browser)
464 struct disasm_line *dl = browser->selection;
467 if (!ins__is_jump(dl->ins))
470 dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
472 ui_helpline__puts("Invallid jump offset");
476 annotate_browser__set_top(browser, dl, idx);
482 struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
485 struct map_symbol *ms = browser->b.priv;
486 struct symbol *sym = ms->sym;
487 struct annotation *notes = symbol__annotation(sym);
488 struct disasm_line *pos = browser->selection;
490 *idx = browser->b.index;
491 list_for_each_entry_continue(pos, ¬es->src->source, node) {
492 if (disasm_line__filter(&browser->b, &pos->node))
497 if (pos->line && strstr(pos->line, s) != NULL)
504 static bool __annotate_browser__search(struct annotate_browser *browser)
506 struct disasm_line *dl;
509 dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
511 ui_helpline__puts("String not found!");
515 annotate_browser__set_top(browser, dl, idx);
516 browser->searching_backwards = false;
521 struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
524 struct map_symbol *ms = browser->b.priv;
525 struct symbol *sym = ms->sym;
526 struct annotation *notes = symbol__annotation(sym);
527 struct disasm_line *pos = browser->selection;
529 *idx = browser->b.index;
530 list_for_each_entry_continue_reverse(pos, ¬es->src->source, node) {
531 if (disasm_line__filter(&browser->b, &pos->node))
536 if (pos->line && strstr(pos->line, s) != NULL)
543 static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
545 struct disasm_line *dl;
548 dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
550 ui_helpline__puts("String not found!");
554 annotate_browser__set_top(browser, dl, idx);
555 browser->searching_backwards = true;
559 static bool annotate_browser__search_window(struct annotate_browser *browser,
562 if (ui_browser__input_window("Search", "String: ", browser->search_bf,
563 "ENTER: OK, ESC: Cancel",
564 delay_secs * 2) != K_ENTER ||
565 !*browser->search_bf)
571 static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs)
573 if (annotate_browser__search_window(browser, delay_secs))
574 return __annotate_browser__search(browser);
579 static bool annotate_browser__continue_search(struct annotate_browser *browser,
582 if (!*browser->search_bf)
583 return annotate_browser__search(browser, delay_secs);
585 return __annotate_browser__search(browser);
588 static bool annotate_browser__search_reverse(struct annotate_browser *browser,
591 if (annotate_browser__search_window(browser, delay_secs))
592 return __annotate_browser__search_reverse(browser);
598 bool annotate_browser__continue_search_reverse(struct annotate_browser *browser,
601 if (!*browser->search_bf)
602 return annotate_browser__search_reverse(browser, delay_secs);
604 return __annotate_browser__search_reverse(browser);
607 static void annotate_browser__update_addr_width(struct annotate_browser *browser)
609 if (annotate_browser__opts.use_offset)
610 browser->target_width = browser->min_addr_width;
612 browser->target_width = browser->max_addr_width;
614 browser->addr_width = browser->target_width;
616 if (annotate_browser__opts.show_nr_jumps)
617 browser->addr_width += browser->jumps_width + 1;
620 static int annotate_browser__run(struct annotate_browser *browser,
621 struct perf_evsel *evsel,
622 struct hist_browser_timer *hbt)
624 struct rb_node *nd = NULL;
625 struct map_symbol *ms = browser->b.priv;
626 struct symbol *sym = ms->sym;
627 const char *help = "Press 'h' for help on key bindings";
628 int delay_secs = hbt ? hbt->refresh : 0;
631 if (ui_browser__show(&browser->b, sym->name, help) < 0)
634 annotate_browser__calc_percent(browser, evsel);
636 if (browser->curr_hot) {
637 annotate_browser__set_rb_top(browser, browser->curr_hot);
638 browser->b.navkeypressed = false;
641 nd = browser->curr_hot;
644 key = ui_browser__run(&browser->b, delay_secs);
646 if (delay_secs != 0) {
647 annotate_browser__calc_percent(browser, evsel);
649 * Current line focus got out of the list of most active
650 * lines, NULL it so that if TAB|UNTAB is pressed, we
651 * move to curr_hot (current hottest line).
653 if (nd != NULL && RB_EMPTY_NODE(nd))
660 hbt->timer(hbt->arg);
663 symbol__annotate_decay_histogram(sym, evsel->idx);
669 nd = rb_last(&browser->entries);
671 nd = browser->curr_hot;
677 nd = rb_first(&browser->entries);
679 nd = browser->curr_hot;
683 ui_browser__help_window(&browser->b,
685 "PGDN/SPACE Navigate\n"
686 "q/ESC/CTRL+C Exit\n\n"
689 "H Cycle thru hottest instructions\n"
690 "j Toggle showing jump to target arrows\n"
691 "J Toggle showing number of jump sources on targets\n"
692 "n Search next string\n"
693 "o Toggle disassembler output/simplified view\n"
694 "s Toggle source code view\n"
696 "r Run available scripts\n"
697 "? Search previous string\n");
705 nd = browser->curr_hot;
708 if (annotate_browser__toggle_source(browser))
709 ui_helpline__puts(help);
712 annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
713 annotate_browser__update_addr_width(browser);
716 annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
719 annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
720 annotate_browser__update_addr_width(browser);
723 if (annotate_browser__search(browser, delay_secs)) {
725 ui_helpline__puts(help);
729 if (browser->searching_backwards ?
730 annotate_browser__continue_search_reverse(browser, delay_secs) :
731 annotate_browser__continue_search(browser, delay_secs))
735 if (annotate_browser__search_reverse(browser, delay_secs))
741 ui_helpline__fpush("%d: nr_ent=%d, height=%d, idx=%d, top_idx=%d, nr_asm_entries=%d",
742 seq++, browser->b.nr_entries,
746 browser->nr_asm_entries);
751 if (browser->selection == NULL)
752 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
753 else if (browser->selection->offset == -1)
754 ui_helpline__puts("Actions are only available for assembly lines.");
755 else if (!browser->selection->ins) {
756 if (strcmp(browser->selection->name, "retq"))
759 } else if (!(annotate_browser__jump(browser) ||
760 annotate_browser__callq(browser, evsel, hbt))) {
762 ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions.");
775 annotate_browser__set_rb_top(browser, nd);
778 ui_browser__hide(&browser->b);
782 int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
783 struct hist_browser_timer *hbt)
785 return symbol__tui_annotate(he->ms.sym, he->ms.map, evsel, hbt);
788 static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
792 struct map_symbol *ms = browser->b.priv;
793 struct symbol *sym = ms->sym;
795 /* PLT symbols contain external offsets */
796 if (strstr(sym->name, "@plt"))
799 for (offset = 0; offset < size; ++offset) {
800 struct disasm_line *dl = browser->offsets[offset], *dlt;
801 struct browser_disasm_line *bdlt;
803 if (!disasm_line__is_valid_jump(dl, sym))
806 dlt = browser->offsets[dl->ops.target.offset];
808 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
809 * have to adjust to the previous offset?
814 bdlt = disasm_line__browser(dlt);
815 if (++bdlt->jump_sources > browser->max_jump_sources)
816 browser->max_jump_sources = bdlt->jump_sources;
823 static inline int width_jumps(int n)
832 int symbol__tui_annotate(struct symbol *sym, struct map *map,
833 struct perf_evsel *evsel,
834 struct hist_browser_timer *hbt)
836 struct disasm_line *pos, *n;
837 struct annotation *notes;
839 struct map_symbol ms = {
843 struct annotate_browser browser = {
845 .refresh = annotate_browser__refresh,
846 .seek = ui_browser__list_head_seek,
847 .write = annotate_browser__write,
848 .filter = disasm_line__filter,
850 .use_navkeypressed = true,
858 size = symbol__size(sym);
860 if (map->dso->annotate_warned)
863 browser.offsets = zalloc(size * sizeof(struct disasm_line *));
864 if (browser.offsets == NULL) {
865 ui__error("Not enough memory!");
869 if (symbol__annotate(sym, map, sizeof(struct browser_disasm_line)) < 0) {
870 ui__error("%s", ui_helpline__last_msg);
871 goto out_free_offsets;
874 ui_helpline__push("Press <- or ESC to exit");
876 notes = symbol__annotation(sym);
877 browser.start = map__rip_2objdump(map, sym->start);
879 list_for_each_entry(pos, ¬es->src->source, node) {
880 struct browser_disasm_line *bpos;
881 size_t line_len = strlen(pos->line);
883 if (browser.b.width < line_len)
884 browser.b.width = line_len;
885 bpos = disasm_line__browser(pos);
886 bpos->idx = browser.nr_entries++;
887 if (pos->offset != -1) {
888 bpos->idx_asm = browser.nr_asm_entries++;
890 * FIXME: short term bandaid to cope with assembly
891 * routines that comes with labels in the same column
892 * as the address in objdump, sigh.
894 * E.g. copy_user_generic_unrolled
896 if (pos->offset < (s64)size)
897 browser.offsets[pos->offset] = pos;
902 annotate_browser__mark_jump_targets(&browser, size);
904 browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
905 browser.max_addr_width = hex_width(sym->end);
906 browser.jumps_width = width_jumps(browser.max_jump_sources);
907 browser.b.nr_entries = browser.nr_entries;
908 browser.b.entries = ¬es->src->source,
909 browser.b.width += 18; /* Percentage */
911 if (annotate_browser__opts.hide_src_code)
912 annotate_browser__init_asm_mode(&browser);
914 annotate_browser__update_addr_width(&browser);
916 ret = annotate_browser__run(&browser, evsel, hbt);
917 list_for_each_entry_safe(pos, n, ¬es->src->source, node) {
918 list_del(&pos->node);
919 disasm_line__free(pos);
923 free(browser.offsets);
927 #define ANNOTATE_CFG(n) \
928 { .name = #n, .value = &annotate_browser__opts.n, }
931 * Keep the entries sorted, they are bsearch'ed
933 static struct annotate_config {
936 } annotate__configs[] = {
937 ANNOTATE_CFG(hide_src_code),
938 ANNOTATE_CFG(jump_arrows),
939 ANNOTATE_CFG(show_nr_jumps),
940 ANNOTATE_CFG(use_offset),
945 static int annotate_config__cmp(const void *name, const void *cfgp)
947 const struct annotate_config *cfg = cfgp;
949 return strcmp(name, cfg->name);
952 static int annotate__config(const char *var, const char *value,
953 void *data __maybe_unused)
955 struct annotate_config *cfg;
958 if (prefixcmp(var, "annotate.") != 0)
962 cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
963 sizeof(struct annotate_config), annotate_config__cmp);
968 *cfg->value = perf_config_bool(name, value);
972 void annotate_browser__init(void)
974 perf_config(annotate__config, NULL);