953 lines · 25.1 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * builtin-annotate.c * * Builtin annotate command: Analyze the perf.data input file, * look up and read DSOs and symbol information and display * a histogram of results, along various sorting keys. */#include "builtin.h"#include "perf.h" #include "util/color.h"#include <linux/list.h>#include "util/cache.h"#include <linux/rbtree.h>#include "util/symbol.h" #include "util/debug.h" #include "util/evlist.h"#include "util/evsel.h"#include "util/annotate.h"#include "util/annotate-data.h"#include "util/event.h"#include <subcmd/parse-options.h>#include "util/parse-events.h"#include "util/sort.h"#include "util/hist.h"#include "util/dso.h"#include "util/machine.h"#include "util/map.h"#include "util/session.h"#include "util/tool.h"#include "util/data.h"#include "arch/common.h"#include "util/block-range.h"#include "util/map_symbol.h"#include "util/branch.h"#include "util/util.h"#include "ui/progress.h" #include <dlfcn.h>#include <errno.h>#include <linux/bitmap.h>#include <linux/err.h>#include <inttypes.h> struct perf_annotate {	struct perf_tool tool;	struct perf_session *session;#ifdef HAVE_SLANG_SUPPORT	bool	   use_tui;#endif	bool	   use_stdio, use_stdio2;#ifdef HAVE_GTK2_SUPPORT	bool	   use_gtk;#endif	bool	   skip_missing;	bool	   has_br_stack;	bool	   group_set;	bool	   data_type;	bool	   type_stat;	bool	   insn_stat;	float	   min_percent;	const char *sym_hist_filter;	const char *cpu_list;	const char *target_data_type;	DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);}; /* * Given one basic block: * *	from	to		branch_i *	* ----> * *		| *		| block *		v *		* ----> * *		from	to	branch_i+1 * * where the horizontal are the branches and the vertical is the executed * block of instructions. * * We count, for each 'instruction', the number of blocks that covered it as * well as count the ratio each branch is taken. * * We can do this without knowing the actual instruction stream by keeping * track of the address ranges. We break down ranges such that there is no * overlap and iterate from the start until the end. * * @acme: once we parse the objdump output _before_ processing the samples, * we can easily fold the branch.cycles IPC bits in. */static void process_basic_block(struct addr_map_symbol *start,				struct addr_map_symbol *end,				struct branch_flags *flags){	struct symbol *sym = start->ms.sym;	struct annotation *notes = sym ? symbol__annotation(sym) : NULL;	struct block_range_iter iter;	struct block_range *entry;	struct annotated_branch *branch; 	/*	 * Sanity; NULL isn't executable and the CPU cannot execute backwards	 */	if (!start->addr || start->addr > end->addr)		return; 	iter = block_range__create(start->addr, end->addr);	if (!block_range_iter__valid(&iter))		return; 	branch = annotation__get_branch(notes); 	/*	 * First block in range is a branch target.	 */	entry = block_range_iter(&iter);	assert(entry->is_target);	entry->entry++; 	do {		entry = block_range_iter(&iter); 		entry->coverage++;		entry->sym = sym; 		if (branch)			branch->max_coverage = max(branch->max_coverage, entry->coverage); 	} while (block_range_iter__next(&iter)); 	/*	 * Last block in rage is a branch.	 */	entry = block_range_iter(&iter);	assert(entry->is_branch);	entry->taken++;	if (flags->predicted)		entry->pred++;} static void process_branch_stack(struct branch_stack *bs, struct addr_location *al,				 struct perf_sample *sample){	struct addr_map_symbol *prev = NULL;	struct branch_info *bi;	int i; 	if (!bs || !bs->nr)		return; 	bi = sample__resolve_bstack(sample, al);	if (!bi)		return; 	for (i = bs->nr - 1; i >= 0; i--) {		/*		 * XXX filter against symbol		 */		if (prev)			process_basic_block(prev, &bi[i].from, &bi[i].flags);		prev = &bi[i].to;	} 	free(bi);} static int hist_iter__branch_callback(struct hist_entry_iter *iter,				      struct addr_location *al __maybe_unused,				      bool single __maybe_unused,				      void *arg __maybe_unused){	struct hist_entry *he = iter->he;	struct branch_info *bi;	struct perf_sample *sample = iter->sample;	int err; 	bi = he->branch_info;	err = addr_map_symbol__inc_samples(&bi->from, sample); 	if (err)		goto out; 	err = addr_map_symbol__inc_samples(&bi->to, sample); out:	return err;} static int process_branch_callback(struct perf_sample *sample,				   struct addr_location *al,				   struct perf_annotate *ann,				   struct machine *machine){	struct hist_entry_iter iter = {		.sample		= sample,		.add_entry_cb	= hist_iter__branch_callback,		.hide_unresolved	= symbol_conf.hide_unresolved,		.ops		= &hist_iter_branch,	};	struct addr_location a;	int ret; 	addr_location__init(&a);	if (machine__resolve(machine, &a, sample) < 0) {		ret = -1;		goto out;	} 	if (a.sym == NULL) {		ret = 0;		goto out;	} 	if (a.map != NULL)		dso__set_hit(map__dso(a.map)); 	hist__account_cycles(sample->branch_stack, al, sample, /*nonany_branch_mode=*/false,			     /*total_cycles=*/NULL); 	ret = hist_entry_iter__add(&iter, &a, PERF_MAX_STACK_DEPTH, ann);out:	addr_location__exit(&a);	return ret;} static bool has_annotation(struct perf_annotate *ann){	return ui__has_annotation() || ann->use_stdio2;} static int add_sample(struct perf_sample *sample,		      struct addr_location *al, struct perf_annotate *ann,		      struct machine *machine){	struct hists *hists = evsel__hists(sample->evsel);	struct hist_entry *he;	int ret; 	if ((!ann->has_br_stack || !has_annotation(ann)) &&	    ann->sym_hist_filter != NULL &&	    (al->sym == NULL ||	     strcmp(ann->sym_hist_filter, al->sym->name) != 0)) {		/* We're only interested in a symbol named sym_hist_filter */		/*		 * FIXME: why isn't this done in the symbol_filter when loading		 * the DSO?		 */		if (al->sym != NULL) {			struct dso *dso = map__dso(al->map); 			rb_erase_cached(&al->sym->rb_node, dso__symbols(dso));			symbol__delete(al->sym);			dso__reset_find_symbol_cache(dso);		}		return 0;	} 	/*	 * XXX filtered samples can still have branch entries pointing into our	 * symbol and are missed.	 */	process_branch_stack(sample->branch_stack, al, sample); 	if (ann->has_br_stack && has_annotation(ann))		return process_branch_callback(sample, al, ann, machine); 	he = hists__add_entry(hists, al, NULL, NULL, NULL, NULL, sample, true);	if (he == NULL)		return -ENOMEM; 	ret = hist_entry__inc_addr_samples(he, sample, al->addr);	hists__inc_nr_samples(hists, true);	return ret;} static int process_sample_event(const struct perf_tool *tool,				union perf_event *event,				struct perf_sample *sample,				struct machine *machine){	struct perf_annotate *ann = container_of(tool, struct perf_annotate, tool);	struct addr_location al;	int ret = 0; 	addr_location__init(&al);	if (machine__resolve(machine, &al, sample) < 0) {		pr_warning("problem processing %s (%u) event at offset %#" PRIx64 ", skipping it.\n",			   perf_event__name(event->header.type), event->header.type,			   sample->file_offset);		ret = -1;		goto out_put;	} 	if (ann->cpu_list && (sample->cpu >= MAX_NR_CPUS ||			     !test_bit(sample->cpu, ann->cpu_bitmap)))		goto out_put; 	if (!al.filtered &&	    add_sample(sample, &al, ann, machine)) {		pr_warning("problem incrementing symbol count, "			   "skipping event\n");		ret = -1;	}out_put:	addr_location__exit(&al);	return ret;} static int hist_entry__stdio_annotate(struct hist_entry *he,				    struct evsel *evsel,				    struct perf_annotate *ann){	if (ann->use_stdio2)		return hist_entry__tty_annotate2(he, evsel); 	return hist_entry__tty_annotate(he, evsel);} static void print_annotate_data_stat(struct annotated_data_stat *s){#define PRINT_STAT(fld) if (s->fld) printf("%10d : %s\n", s->fld, #fld) 	int bad = s->no_sym +			s->no_insn +			s->no_insn_ops +			s->no_mem_ops +			s->no_reg +			s->no_dbginfo +			s->no_cuinfo +			s->no_var +			s->no_typeinfo +			s->invalid_size +			s->bad_offset;	int ok = s->total - bad; 	printf("Annotate data type stats:\n");	printf("total %d, ok %d (%.1f%%), bad %d (%.1f%%)\n",		s->total, ok, 100.0 * ok / (s->total ?: 1), bad, 100.0 * bad / (s->total ?: 1));	printf("-----------------------------------------------------------\n");	PRINT_STAT(no_sym);	PRINT_STAT(no_insn);	PRINT_STAT(no_insn_ops);	PRINT_STAT(no_mem_ops);	PRINT_STAT(no_reg);	PRINT_STAT(no_dbginfo);	PRINT_STAT(no_cuinfo);	PRINT_STAT(no_var);	PRINT_STAT(no_typeinfo);	PRINT_STAT(invalid_size);	PRINT_STAT(bad_offset);	PRINT_STAT(insn_track);	printf("\n"); #undef PRINT_STAT} static void print_annotate_item_stat(struct list_head *head, const char *title){	struct annotated_item_stat *istat, *pos, *iter;	int total_good, total_bad, total;	int sum1, sum2;	LIST_HEAD(tmp); 	/* sort the list by count */	list_splice_init(head, &tmp);	total_good = total_bad = 0; 	list_for_each_entry_safe(istat, pos, &tmp, list) {		total_good += istat->good;		total_bad += istat->bad;		sum1 = istat->good + istat->bad; 		list_for_each_entry(iter, head, list) {			sum2 = iter->good + iter->bad;			if (sum1 > sum2)				break;		}		list_move_tail(&istat->list, &iter->list);	}	total = total_good + total_bad; 	printf("Annotate %s stats\n", title);	printf("total %d, ok %d (%.1f%%), bad %d (%.1f%%)\n\n", total,	       total_good, 100.0 * total_good / (total ?: 1),	       total_bad, 100.0 * total_bad / (total ?: 1));	printf("  %-20s: %5s %5s\n", "Name/opcode", "Good", "Bad");	printf("-----------------------------------------------------------\n");	list_for_each_entry(istat, head, list)		printf("  %-20s: %5d %5d\n", istat->name, istat->good, istat->bad);	printf("\n");} static void hists__find_annotations(struct hists *hists,				    struct evsel *evsel,				    struct perf_annotate *ann){	struct rb_node *nd = rb_first_cached(&hists->entries), *next;	int key = K_RIGHT; 	if (ann->type_stat)		print_annotate_data_stat(&ann_data_stat);	if (ann->insn_stat)		print_annotate_item_stat(&ann_insn_stat, "Instruction"); 	while (nd) {		struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);		struct annotation *notes; 		if (he->ms.sym == NULL || dso__annotate_warned(map__dso(he->ms.map)))			goto find_next; 		if (ann->sym_hist_filter &&		    (strcmp(he->ms.sym->name, ann->sym_hist_filter) != 0))			goto find_next; 		if (ann->min_percent) {			float percent = 0;			u64 total = hists__total_period(hists); 			if (total)				percent = 100.0 * he->stat.period / total; 			if (percent < ann->min_percent)				goto find_next;		} 		notes = symbol__annotation(he->ms.sym);		if (notes->src == NULL) {find_next:			if (key == K_LEFT || key == '<')				nd = rb_prev(nd);			else				nd = rb_next(nd);			continue;		} 		if (ann->data_type) {			/* skip unknown type */			if (he->mem_type->histograms == NULL)				goto find_next; 			if (ann->target_data_type) {				const char *type_name = he->mem_type->self.type_name; 				/* skip 'struct ' prefix in the type name */				if (strncmp(ann->target_data_type, "struct ", 7) &&				    !strncmp(type_name, "struct ", 7))					type_name += 7; 				/* skip 'union ' prefix in the type name */				if (strncmp(ann->target_data_type, "union ", 6) &&				    !strncmp(type_name, "union ", 6))					type_name += 6; 				if (strcmp(ann->target_data_type, type_name))					goto find_next;			} 			if (use_browser == 1)				key = hist_entry__annotate_data_tui(he, evsel, NULL);			else				key = hist_entry__annotate_data_tty(he, evsel); 			switch (key) {			case -1:				if (!ann->skip_missing)					return;				/* fall through */			case K_RIGHT:			case '>':				next = rb_next(nd);				break;			case K_LEFT:			case '<':				next = rb_prev(nd);				break;			default:				return;			} 			if (use_browser == 0 || next != NULL)				nd = next; 			continue;		} 		if (use_browser == 2) {			int ret;			int (*annotate)(struct hist_entry *he,					struct evsel *evsel,					struct hist_browser_timer *hbt); 			annotate = dlsym(perf_gtk_handle,					 "hist_entry__gtk_annotate");			if (annotate == NULL) {				ui__error("GTK browser not found!\n");

Lines 1–500 of 953.