// SPDX-License-Identifier: GPL-2.0#include "../browser.h"#include "../helpline.h"#include "../ui.h"#include "../../util/annotate.h"#include "../../util/debug.h"#include "../../util/debuginfo.h"#include "../../util/dso.h"#include "../../util/hashmap.h"#include "../../util/hist.h"#include "../../util/sort.h"#include "../../util/map.h"#include "../../util/mutex.h"#include "../../util/symbol.h"#include "../../util/evsel.h"#include "../../util/evlist.h"#include "../../util/thread.h"#include <inttypes.h>#include <linux/err.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/zalloc.h>#include <sys/ttydefaults.h>#include <asm/bug.h> struct arch; struct annotate_browser { struct ui_browser b; struct rb_root entries; struct rb_node *curr_hot; struct annotation_line *selection; const struct arch *arch; /* * perf top can delete hist_entry anytime. Callers should make sure * its lifetime. */ struct hist_entry *he; struct debuginfo *dbg; struct evsel *evsel; struct hashmap *type_hash; bool searching_backwards; char search_bf[128];}; /* A copy of target hist_entry for perf top. */static struct hist_entry annotate_he; static size_t type_hash(long key, void *ctx __maybe_unused){ return key;} static bool type_equal(long key1, long key2, void *ctx __maybe_unused){ return key1 == key2;} static inline struct annotation *browser__annotation(struct ui_browser *browser){ struct map_symbol *ms = browser->priv; return symbol__annotation(ms->sym);} static bool disasm_line__filter(struct ui_browser *browser __maybe_unused, void *entry){ struct annotation_line *al = list_entry(entry, struct annotation_line, node); return annotation_line__filter(al);} static int ui_browser__jumps_percent_color(struct ui_browser *browser, int nr, bool current){ struct annotation *notes = browser__annotation(browser); if (current && (!browser->use_navkeypressed || browser->navkeypressed)) return HE_COLORSET_SELECTED; if (nr == notes->src->max_jump_sources) return HE_COLORSET_TOP; if (nr > 1) return HE_COLORSET_MEDIUM; return HE_COLORSET_NORMAL;} static int ui_browser__set_jumps_percent_color(void *browser, int nr, bool current){ int color = ui_browser__jumps_percent_color(browser, nr, current); return ui_browser__set_color(browser, color);} static int annotate_browser__set_color(void *browser, int color){ return ui_browser__set_color(browser, color);} static void annotate_browser__write_graph(void *browser, int graph){ ui_browser__write_graph(browser, graph);} static void annotate_browser__set_percent_color(void *browser, double percent, bool current){ ui_browser__set_percent_color(browser, percent, current);} static void annotate_browser__printf(void *browser, const char *fmt, ...){ va_list args; va_start(args, fmt); ui_browser__vprintf(browser, fmt, args); va_end(args);} static void annotate_browser__write(struct ui_browser *browser, void *entry, int row){ struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); struct annotation *notes = browser__annotation(browser); struct annotation_line *al = list_entry(entry, struct annotation_line, node); const bool is_current_entry = ui_browser__is_current_entry(browser, row); struct annotation_write_ops ops = { .first_line = row == 0, .current_entry = is_current_entry, .change_color = (!annotate_opts.hide_src_code && (!is_current_entry || (browser->use_navkeypressed && !browser->navkeypressed))), .width = browser->width, .obj = browser, .set_color = annotate_browser__set_color, .set_percent_color = annotate_browser__set_percent_color, .set_jumps_percent_color = ui_browser__set_jumps_percent_color, .printf = annotate_browser__printf, .write_graph = annotate_browser__write_graph, }; struct annotation_print_data apd = { .he = ab->he, .arch = ab->arch, .evsel = ab->evsel, .dbg = ab->dbg, }; /* The scroll bar isn't being used */ if (!browser->navkeypressed) ops.width += 1; if (!IS_ERR_OR_NULL(ab->type_hash)) apd.type_hash = ab->type_hash; annotation_line__write(al, notes, &ops, &apd); if (ops.current_entry) ab->selection = al;} static int is_fused(struct annotate_browser *ab, struct disasm_line *cursor){ struct disasm_line *pos = list_prev_entry(cursor, al.node); const char *name; int diff = 1; while (pos && pos->al.offset == -1) { pos = list_prev_entry(pos, al.node); if (!annotate_opts.hide_src_code) diff++; } if (!pos) return 0; if (ins__is_lock(&pos->ins)) name = pos->ops.locked.ins.name; else name = pos->ins.name; if (!name || !cursor->ins.name) return 0; if (ins__is_fused(ab->arch, name, cursor->ins.name)) return diff; return 0;} static void annotate_browser__draw_current_jump(struct ui_browser *browser){ struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); struct disasm_line *cursor = disasm_line(ab->selection); struct annotation_line *target; unsigned int from, to; struct map_symbol *ms = ab->b.priv; struct symbol *sym = ms->sym; struct annotation *notes = symbol__annotation(sym); u8 pcnt_width = annotation__pcnt_width(notes); u8 cntr_width = annotation__br_cntr_width(); int width; int diff = 0; /* PLT symbols contain external offsets */ if (strstr(sym->name, "@plt")) return; if (!disasm_line__is_valid_local_jump(cursor, sym)) return; /* * This first was seen with a gcc function, _cpp_lex_token, that * has the usual jumps: * * │1159e6c: ↓ jne 115aa32 <_cpp_lex_token@@Base+0xf92> * * I.e. jumps to a label inside that function (_cpp_lex_token), and * those works, but also this kind: * * │1159e8b: ↓ jne c469be <cpp_named_operator2name@@Base+0xa72> * * I.e. jumps to another function, outside _cpp_lex_token, which * are not being correctly handled generating as a side effect references * to ab->offset[] entries that are set to NULL, so to make this code * more robust, check that here. * * A proper fix for will be put in place, looking at the function * name right after the '<' token and probably treating this like a * 'call' instruction. */ target = annotated_source__get_line(notes->src, cursor->ops.target.offset); if (target == NULL) { ui_helpline__printf("WARN: jump target inconsistency, press 'o', notes->offsets[%#x] = NULL\n", cursor->ops.target.offset); return; } if (annotate_opts.hide_src_code) { from = cursor->al.idx_asm; to = target->idx_asm; } else { from = (u64)cursor->al.idx; to = (u64)target->idx; } width = annotation__cycles_width(notes); ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS); __ui_browser__line_arrow(browser, pcnt_width + 2 + notes->src->widths.addr + width + cntr_width, from, to); diff = is_fused(ab, cursor); if (diff > 0) { ui_browser__mark_fused(browser, pcnt_width + 3 + notes->src->widths.addr + width + cntr_width, from - diff, diff, to > from); }} static unsigned int annotate_browser__refresh(struct ui_browser *browser){ struct annotation *notes = browser__annotation(browser); int ret = ui_browser__list_head_refresh(browser); int pcnt_width = annotation__pcnt_width(notes); if (annotate_opts.jump_arrows) annotate_browser__draw_current_jump(browser); ui_browser__set_color(browser, HE_COLORSET_NORMAL); __ui_browser__vline(browser, pcnt_width, 0, browser->rows - 1); return ret;} static double disasm__cmp(struct annotation_line *a, struct annotation_line *b, int percent_type){ int i; for (i = 0; i < a->data_nr; i++) { if (a->data[i].percent[percent_type] == b->data[i].percent[percent_type]) continue; return a->data[i].percent[percent_type] - b->data[i].percent[percent_type]; } return 0;} static void disasm_rb_tree__insert(struct annotate_browser *browser, struct annotation_line *al){ struct rb_root *root = &browser->entries; struct rb_node **p = &root->rb_node; struct rb_node *parent = NULL; struct annotation_line *l; while (*p != NULL) { parent = *p; l = rb_entry(parent, struct annotation_line, rb_node); if (disasm__cmp(al, l, annotate_opts.percent_type) < 0) p = &(*p)->rb_left; else p = &(*p)->rb_right; } rb_link_node(&al->rb_node, parent, p); rb_insert_color(&al->rb_node, root);} static void annotate_browser__set_top(struct annotate_browser *browser, struct annotation_line *pos, u32 idx){ unsigned back; ui_browser__refresh_dimensions(&browser->b); back = browser->b.height / 2; browser->b.top_idx = browser->b.index = idx; while (browser->b.top_idx != 0 && back != 0) { pos = list_entry(pos->node.prev, struct annotation_line, node); if (annotation_line__filter(pos)) continue; --browser->b.top_idx; --back; } browser->b.top = pos; browser->b.navkeypressed = true;} static void annotate_browser__set_rb_top(struct annotate_browser *browser, struct rb_node *nd){ struct annotation_line * pos = rb_entry(nd, struct annotation_line, rb_node); u32 idx = pos->idx; if (annotate_opts.hide_src_code) idx = pos->idx_asm; annotate_browser__set_top(browser, pos, idx); browser->curr_hot = nd;} static void annotate_browser__calc_percent(struct annotate_browser *browser, struct evsel *evsel){ struct map_symbol *ms = browser->b.priv; struct symbol *sym = ms->sym; struct annotation *notes = symbol__annotation(sym); struct disasm_line *pos; browser->entries = RB_ROOT; annotation__lock(notes); symbol__calc_percent(sym, evsel); list_for_each_entry(pos, ¬es->src->source, al.node) { double max_percent = 0.0; int i; if (pos->al.offset == -1) { RB_CLEAR_NODE(&pos->al.rb_node); continue; } for (i = 0; i < pos->al.data_nr; i++) { double percent; percent = annotation_data__percent(&pos->al.data[i], annotate_opts.percent_type); if (max_percent < percent) max_percent = percent; } if (max_percent < 0.01 && (!pos->al.cycles || pos->al.cycles->ipc == 0)) { RB_CLEAR_NODE(&pos->al.rb_node); continue; } disasm_rb_tree__insert(browser, &pos->al); } annotation__unlock(notes); browser->curr_hot = rb_last(&browser->entries);} static struct annotation_line *annotate_browser__find_new_asm_line( struct annotate_browser *browser, int idx_asm){ struct annotation_line *al; struct list_head *head = browser->b.entries; /* find an annotation line in the new list with the same idx_asm */ list_for_each_entry(al, head, node) { if (al->idx_asm == idx_asm) return al; } /* There are no asm lines */ return NULL;} static struct annotation_line *annotate_browser__find_next_asm_line( struct annotate_browser *browser, struct annotation_line *al){ struct annotation_line *it = al; /* find next asm line */ list_for_each_entry_continue(it, browser->b.entries, node) { if (it->idx_asm >= 0) return it; } /* no asm line found forwards, try backwards */ it = al; list_for_each_entry_continue_reverse(it, browser->b.entries, node) { if (it->idx_asm >= 0) return it; } /* There are no asm lines */ return NULL;} static bool annotation__has_source(struct annotation *notes){ struct annotation_line *al; bool found_asm = false; /* Let's skip the first non-asm lines which present regardless of source. */ list_for_each_entry(al, ¬es->src->source, node) { if (al->offset >= 0) { found_asm = true; break; } } if (found_asm) { /* After assembly lines, any line without offset means source. */ list_for_each_entry_continue(al, ¬es->src->source, node) { if (al->offset == -1) return true; } } return false;} static bool annotate_browser__toggle_source(struct annotate_browser *browser, struct evsel *evsel){ struct annotation *notes = browser__annotation(&browser->b); struct annotation_line *al; off_t offset = browser->b.index - browser->b.top_idx; if (browser->b.nr_entries == 0) return false; browser->b.seek(&browser->b, offset, SEEK_CUR); al = list_entry(browser->b.top, struct annotation_line, node); if (!annotate_opts.annotate_src) annotate_opts.annotate_src = true; /* * It's about to get source code annotation for the first time. * Drop the existing annotation_lines and get the new one with source. * And then move to the original line at the same asm index. */ if (annotate_opts.hide_src_code && !notes->src->tried_source) { struct map_symbol *ms = browser->b.priv; int orig_idx_asm = al->idx_asm; /* annotate again with source code info */ annotate_opts.hide_src_code = false; annotated_source__purge(notes->src); symbol__annotate2(ms, evsel, &browser->arch); annotate_opts.hide_src_code = true; /* should be after annotated_source__purge() */ notes->src->tried_source = true; if (!annotation__has_source(notes)) ui__warning("Annotation has no source code."); browser->b.entries = ¬es->src->source; al = annotate_browser__find_new_asm_line(browser, orig_idx_asm); if (unlikely(al == NULL)) { al = list_first_entry(¬es->src->source, struct annotation_line, node); } browser->b.seek(&browser->b, al->idx_asm, SEEK_SET); } if (annotate_opts.hide_src_code) { if (al->idx_asm < offset) offset = al->idx; browser->b.nr_entries = notes->src->nr_entries; annotate_opts.hide_src_code = false; browser->b.seek(&browser->b, -offset, SEEK_CUR); browser->b.top_idx = al->idx - offset; browser->b.index = al->idx; } else {Lines 1–500 of 1272. Load more
// SPDX-License-Identifier: GPL-2.0 #include "../browser.h" #include "../helpline.h" #include "../ui.h" #include "../../util/annotate.h" #include "../../util/debug.h" #include "../../util/debuginfo.h" #include "../../util/dso.h" #include "../../util/hashmap.h" #include "../../util/hist.h" #include "../../util/sort.h" #include "../../util/map.h" #include "../../util/mutex.h" #include "../../util/symbol.h" #include "../../util/evsel.h" #include "../../util/evlist.h" #include "../../util/thread.h" #include <inttypes.h> #include <linux/err.h> #include <linux/kernel.h> #include <linux/string.h> #include <linux/zalloc.h> #include <sys/ttydefaults.h> #include <asm/bug.h> struct arch; struct annotate_browser { struct ui_browser b; struct rb_root entries; struct rb_node *curr_hot; struct annotation_line *selection; const struct arch *arch; /* * perf top can delete hist_entry anytime. Callers should make sure * its lifetime. */ struct hist_entry *he; struct debuginfo *dbg; struct evsel *evsel; struct hashmap *type_hash; bool searching_backwards; char search_bf[128]; }; /* A copy of target hist_entry for perf top. */ static struct hist_entry annotate_he; static size_t type_hash(long key, void *ctx __maybe_unused) { return key; } static bool type_equal(long key1, long key2, void *ctx __maybe_unused) { return key1 == key2; } static inline struct annotation *browser__annotation(struct ui_browser *browser) { struct map_symbol *ms = browser->priv; return symbol__annotation(ms->sym); } static bool disasm_line__filter(struct ui_browser *browser __maybe_unused, void *entry) { struct annotation_line *al = list_entry(entry, struct annotation_line, node); return annotation_line__filter(al); } static int ui_browser__jumps_percent_color(struct ui_browser *browser, int nr, bool current) { struct annotation *notes = browser__annotation(browser); if (current && (!browser->use_navkeypressed || browser->navkeypressed)) return HE_COLORSET_SELECTED; if (nr == notes->src->max_jump_sources) return HE_COLORSET_TOP; if (nr > 1) return HE_COLORSET_MEDIUM; return HE_COLORSET_NORMAL; } static int ui_browser__set_jumps_percent_color(void *browser, int nr, bool current) { int color = ui_browser__jumps_percent_color(browser, nr, current); return ui_browser__set_color(browser, color); } static int annotate_browser__set_color(void *browser, int color) { return ui_browser__set_color(browser, color); } static void annotate_browser__write_graph(void *browser, int graph) { ui_browser__write_graph(browser, graph); } static void annotate_browser__set_percent_color(void *browser, double percent, bool current) { ui_browser__set_percent_color(browser, percent, current); } static void annotate_browser__printf(void *browser, const char *fmt, ...) { va_list args; va_start(args, fmt); ui_browser__vprintf(browser, fmt, args); va_end(args); } static void annotate_browser__write(struct ui_browser *browser, void *entry, int row) { struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); struct annotation *notes = browser__annotation(browser); struct annotation_line *al = list_entry(entry, struct annotation_line, node); const bool is_current_entry = ui_browser__is_current_entry(browser, row); struct annotation_write_ops ops = { .first_line = row == 0, .current_entry = is_current_entry, .change_color = (!annotate_opts.hide_src_code && (!is_current_entry || (browser->use_navkeypressed && !browser->navkeypressed))), .width = browser->width, .obj = browser, .set_color = annotate_browser__set_color, .set_percent_color = annotate_browser__set_percent_color, .set_jumps_percent_color = ui_browser__set_jumps_percent_color, .printf = annotate_browser__printf, .write_graph = annotate_browser__write_graph, }; struct annotation_print_data apd = { .he = ab->he, .arch = ab->arch, .evsel = ab->evsel, .dbg = ab->dbg, }; /* The scroll bar isn't being used */ if (!browser->navkeypressed) ops.width += 1; if (!IS_ERR_OR_NULL(ab->type_hash)) apd.type_hash = ab->type_hash; annotation_line__write(al, notes, &ops, &apd); if (ops.current_entry) ab->selection = al; } static int is_fused(struct annotate_browser *ab, struct disasm_line *cursor) { struct disasm_line *pos = list_prev_entry(cursor, al.node); const char *name; int diff = 1; while (pos && pos->al.offset == -1) { pos = list_prev_entry(pos, al.node); if (!annotate_opts.hide_src_code) diff++; } if (!pos) return 0; if (ins__is_lock(&pos->ins)) name = pos->ops.locked.ins.name; else name = pos->ins.name; if (!name || !cursor->ins.name) return 0; if (ins__is_fused(ab->arch, name, cursor->ins.name)) return diff; return 0; } static void annotate_browser__draw_current_jump(struct ui_browser *browser) { struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); struct disasm_line *cursor = disasm_line(ab->selection); struct annotation_line *target; unsigned int from, to; struct map_symbol *ms = ab->b.priv; struct symbol *sym = ms->sym; struct annotation *notes = symbol__annotation(sym); u8 pcnt_width = annotation__pcnt_width(notes); u8 cntr_width = annotation__br_cntr_width(); int width; int diff = 0; /* PLT symbols contain external offsets */ if (strstr(sym->name, "@plt")) return; if (!disasm_line__is_valid_local_jump(cursor, sym)) return; /* * This first was seen with a gcc function, _cpp_lex_token, that * has the usual jumps: * * │1159e6c: ↓ jne 115aa32 <_cpp_lex_token@@Base+0xf92> * * I.e. jumps to a label inside that function (_cpp_lex_token), and * those works, but also this kind: * * │1159e8b: ↓ jne c469be <cpp_named_operator2name@@Base+0xa72> * * I.e. jumps to another function, outside _cpp_lex_token, which * are not being correctly handled generating as a side effect references * to ab->offset[] entries that are set to NULL, so to make this code * more robust, check that here. * * A proper fix for will be put in place, looking at the function * name right after the '<' token and probably treating this like a * 'call' instruction. */ target = annotated_source__get_line(notes->src, cursor->ops.target.offset); if (target == NULL) { ui_helpline__printf("WARN: jump target inconsistency, press 'o', notes->offsets[%#x] = NULL\n", cursor->ops.target.offset); return; } if (annotate_opts.hide_src_code) { from = cursor->al.idx_asm; to = target->idx_asm; } else { from = (u64)cursor->al.idx; to = (u64)target->idx; } width = annotation__cycles_width(notes); ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS); __ui_browser__line_arrow(browser, pcnt_width + 2 + notes->src->widths.addr + width + cntr_width, from, to); diff = is_fused(ab, cursor); if (diff > 0) { ui_browser__mark_fused(browser, pcnt_width + 3 + notes->src->widths.addr + width + cntr_width, from - diff, diff, to > from); } } static unsigned int annotate_browser__refresh(struct ui_browser *browser) { struct annotation *notes = browser__annotation(browser); int ret = ui_browser__list_head_refresh(browser); int pcnt_width = annotation__pcnt_width(notes); if (annotate_opts.jump_arrows) annotate_browser__draw_current_jump(browser); ui_browser__set_color(browser, HE_COLORSET_NORMAL); __ui_browser__vline(browser, pcnt_width, 0, browser->rows - 1); return ret; } static double disasm__cmp(struct annotation_line *a, struct annotation_line *b, int percent_type) { int i; for (i = 0; i < a->data_nr; i++) { if (a->data[i].percent[percent_type] == b->data[i].percent[percent_type]) continue; return a->data[i].percent[percent_type] - b->data[i].percent[percent_type]; } return 0; } static void disasm_rb_tree__insert(struct annotate_browser *browser, struct annotation_line *al) { struct rb_root *root = &browser->entries; struct rb_node **p = &root->rb_node; struct rb_node *parent = NULL; struct annotation_line *l; while (*p != NULL) { parent = *p; l = rb_entry(parent, struct annotation_line, rb_node); if (disasm__cmp(al, l, annotate_opts.percent_type) < 0) p = &(*p)->rb_left; else p = &(*p)->rb_right; } rb_link_node(&al->rb_node, parent, p); rb_insert_color(&al->rb_node, root); } static void annotate_browser__set_top(struct annotate_browser *browser, struct annotation_line *pos, u32 idx) { unsigned back; ui_browser__refresh_dimensions(&browser->b); back = browser->b.height / 2; browser->b.top_idx = browser->b.index = idx; while (browser->b.top_idx != 0 && back != 0) { pos = list_entry(pos->node.prev, struct annotation_line, node); if (annotation_line__filter(pos)) continue; --browser->b.top_idx; --back; } browser->b.top = pos; browser->b.navkeypressed = true; } static void annotate_browser__set_rb_top(struct annotate_browser *browser, struct rb_node *nd) { struct annotation_line * pos = rb_entry(nd, struct annotation_line, rb_node); u32 idx = pos->idx; if (annotate_opts.hide_src_code) idx = pos->idx_asm; annotate_browser__set_top(browser, pos, idx); browser->curr_hot = nd; } static void annotate_browser__calc_percent(struct annotate_browser *browser, struct evsel *evsel) { struct map_symbol *ms = browser->b.priv; struct symbol *sym = ms->sym; struct annotation *notes = symbol__annotation(sym); struct disasm_line *pos; browser->entries = RB_ROOT; annotation__lock(notes); symbol__calc_percent(sym, evsel); list_for_each_entry(pos, ¬es->src->source, al.node) { double max_percent = 0.0; int i; if (pos->al.offset == -1) { RB_CLEAR_NODE(&pos->al.rb_node); continue; } for (i = 0; i < pos->al.data_nr; i++) { double percent; percent = annotation_data__percent(&pos->al.data[i], annotate_opts.percent_type); if (max_percent < percent) max_percent = percent; } if (max_percent < 0.01 && (!pos->al.cycles || pos->al.cycles->ipc == 0)) { RB_CLEAR_NODE(&pos->al.rb_node); continue; } disasm_rb_tree__insert(browser, &pos->al); } annotation__unlock(notes); browser->curr_hot = rb_last(&browser->entries); } static struct annotation_line *annotate_browser__find_new_asm_line( struct annotate_browser *browser, int idx_asm) { struct annotation_line *al; struct list_head *head = browser->b.entries; /* find an annotation line in the new list with the same idx_asm */ list_for_each_entry(al, head, node) { if (al->idx_asm == idx_asm) return al; } /* There are no asm lines */ return NULL; } static struct annotation_line *annotate_browser__find_next_asm_line( struct annotate_browser *browser, struct annotation_line *al) { struct annotation_line *it = al; /* find next asm line */ list_for_each_entry_continue(it, browser->b.entries, node) { if (it->idx_asm >= 0) return it; } /* no asm line found forwards, try backwards */ it = al; list_for_each_entry_continue_reverse(it, browser->b.entries, node) { if (it->idx_asm >= 0) return it; } /* There are no asm lines */ return NULL; } static bool annotation__has_source(struct annotation *notes) { struct annotation_line *al; bool found_asm = false; /* Let's skip the first non-asm lines which present regardless of source. */ list_for_each_entry(al, ¬es->src->source, node) { if (al->offset >= 0) { found_asm = true; break; } } if (found_asm) { /* After assembly lines, any line without offset means source. */ list_for_each_entry_continue(al, ¬es->src->source, node) { if (al->offset == -1) return true; } } return false; } static bool annotate_browser__toggle_source(struct annotate_browser *browser, struct evsel *evsel) { struct annotation *notes = browser__annotation(&browser->b); struct annotation_line *al; off_t offset = browser->b.index - browser->b.top_idx; if (browser->b.nr_entries == 0) return false; browser->b.seek(&browser->b, offset, SEEK_CUR); al = list_entry(browser->b.top, struct annotation_line, node); if (!annotate_opts.annotate_src) annotate_opts.annotate_src = true; /* * It's about to get source code annotation for the first time. * Drop the existing annotation_lines and get the new one with source. * And then move to the original line at the same asm index. */ if (annotate_opts.hide_src_code && !notes->src->tried_source) { struct map_symbol *ms = browser->b.priv; int orig_idx_asm = al->idx_asm; /* annotate again with source code info */ annotate_opts.hide_src_code = false; annotated_source__purge(notes->src); symbol__annotate2(ms, evsel, &browser->arch); annotate_opts.hide_src_code = true; /* should be after annotated_source__purge() */ notes->src->tried_source = true; if (!annotation__has_source(notes)) ui__warning("Annotation has no source code."); browser->b.entries = ¬es->src->source; al = annotate_browser__find_new_asm_line(browser, orig_idx_asm); if (unlikely(al == NULL)) { al = list_first_entry(¬es->src->source, struct annotation_line, node); } browser->b.seek(&browser->b, al->idx_asm, SEEK_SET); } if (annotate_opts.hide_src_code) { if (al->idx_asm < offset) offset = al->idx; browser->b.nr_entries = notes->src->nr_entries; annotate_opts.hide_src_code = false; browser->b.seek(&browser->b, -offset, SEEK_CUR); browser->b.top_idx = al->idx - offset; browser->b.index = al->idx; } else {