1272 lines · 34.3 KB · C
RawDownload
// 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, &notes->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, &notes->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, &notes->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 = &notes->src->source;		al = annotate_browser__find_new_asm_line(browser, orig_idx_asm);		if (unlikely(al == NULL)) {			al = list_first_entry(&notes->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.