3228 lines · 78.7 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> * * Parts came from builtin-annotate.c, see those files for further * copyright notes. */ #include <errno.h>#include <inttypes.h>#include <stdlib.h>#include "util.h" // hex_width()#include "ui/ui.h"#include "sort.h"#include "build-id.h"#include "color.h"#include "config.h"#include "disasm.h"#include "dso.h"#include "env.h"#include "map.h"#include "maps.h"#include "symbol.h"#include "srcline.h"#include "units.h"#include "debug.h"#include "debuginfo.h"#include "annotate.h"#include "annotate-data.h"#include "evsel.h"#include "evlist.h"#include "bpf-event.h"#include "bpf-utils.h"#include "block-range.h"#include "string2.h"#include "dwarf-regs.h"#include "util/event.h"#include "util/sharded_mutex.h"#include "arch/common.h"#include "namespaces.h"#include "thread.h"#include "hashmap.h"#include "strbuf.h"#include <regex.h>#include <linux/bitops.h>#include <linux/err.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/zalloc.h>#include <subcmd/parse-options.h>#include <subcmd/run-command.h>#include <math.h> /* FIXME: For the HE_COLORSET */#include "ui/browser.h" /* * FIXME: Using the same values as slang.h, * but that header may not be available everywhere */#define LARROW_CHAR	((unsigned char)',')#define RARROW_CHAR	((unsigned char)'+')#define DARROW_CHAR	((unsigned char)'.')#define UARROW_CHAR	((unsigned char)'-') #include <linux/ctype.h> /* global annotation options */struct annotation_options annotate_opts; /* Data type collection debug statistics */struct annotated_data_stat ann_data_stat;LIST_HEAD(ann_insn_stat); /* Pseudo data types */struct annotated_data_type stackop_type = {	.self = {		.type_name = (char *)"(stack operation)",		.children = LIST_HEAD_INIT(stackop_type.self.children),	},}; struct annotated_data_type canary_type = {	.self = {		.type_name = (char *)"(stack canary)",		.children = LIST_HEAD_INIT(canary_type.self.children),	},}; #define NO_TYPE ((struct annotated_data_type *)-1UL) /* symbol histogram: key = offset << 16 | evsel->core.idx */static size_t sym_hist_hash(long key, void *ctx __maybe_unused){	return (key >> 16) + (key & 0xffff);} static bool sym_hist_equal(long key1, long key2, void *ctx __maybe_unused){	return key1 == key2;} static struct annotated_source *annotated_source__new(void){	struct annotated_source *src = zalloc(sizeof(*src)); 	if (src != NULL)		INIT_LIST_HEAD(&src->source); 	return src;} static __maybe_unused void annotated_source__delete(struct annotated_source *src){	struct hashmap_entry *cur;	size_t bkt; 	if (src == NULL)		return; 	if (src->samples) {		hashmap__for_each_entry(src->samples, cur, bkt)			zfree(&cur->pvalue);		hashmap__free(src->samples);	}	zfree(&src->histograms);	free(src);} static int annotated_source__alloc_histograms(struct annotated_source *src,					      int nr_hists){	src->nr_histograms   = nr_hists;	src->histograms	     = calloc(nr_hists, sizeof(*src->histograms)); 	if (src->histograms == NULL)		return -1; 	src->samples = hashmap__new(sym_hist_hash, sym_hist_equal, NULL);	if (IS_ERR(src->samples)) {		zfree(&src->histograms);		src->samples = NULL;	} 	return src->histograms ? 0 : -1;} void symbol__annotate_zero_histograms(struct symbol *sym){	struct annotation *notes = symbol__annotation(sym); 	annotation__lock(notes);	if (notes->src != NULL) {		memset(notes->src->histograms, 0,		       notes->src->nr_histograms * sizeof(*notes->src->histograms));		hashmap__clear(notes->src->samples);	}	if (notes->branch && notes->branch->cycles_hist) {		memset(notes->branch->cycles_hist, 0,		       symbol__size(sym) * sizeof(struct cyc_hist));	}	annotation__unlock(notes);} static int __symbol__account_cycles(struct cyc_hist *ch,				    u64 start,				    unsigned offset, unsigned cycles,				    unsigned have_start){	/*	 * For now we can only account one basic block per	 * final jump. But multiple could be overlapping.	 * Always account the longest one. So when	 * a shorter one has been already seen throw it away.	 *	 * We separately always account the full cycles.	 */	ch[offset].num_aggr++;	ch[offset].cycles_aggr += cycles; 	if (cycles > ch[offset].cycles_max)		ch[offset].cycles_max = cycles; 	if (ch[offset].cycles_min) {		if (cycles && cycles < ch[offset].cycles_min)			ch[offset].cycles_min = cycles;	} else		ch[offset].cycles_min = cycles; 	if (!have_start && ch[offset].have_start)		return 0;	if (ch[offset].num) {		if (have_start && (!ch[offset].have_start ||				   ch[offset].start > start)) {			ch[offset].have_start = 0;			ch[offset].cycles = 0;			ch[offset].num = 0;			if (ch[offset].reset < 0xffff)				ch[offset].reset++;		} else if (have_start &&			   ch[offset].start < start)			return 0;	} 	if (ch[offset].num < NUM_SPARKS)		ch[offset].cycles_spark[ch[offset].num] = cycles; 	ch[offset].have_start = have_start;	ch[offset].start = start;	ch[offset].cycles += cycles;	ch[offset].num++;	return 0;} static int __symbol__inc_addr_samples(struct map_symbol *ms,				      struct annotated_source *src, u64 addr,				      struct perf_sample *sample){	struct evsel *evsel = sample->evsel;	struct symbol *sym = ms->sym;	long hash_key;	u64 offset;	struct sym_hist *h;	struct sym_hist_entry *entry; 	pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map__unmap_ip(ms->map, addr)); 	if ((addr < sym->start || addr >= sym->end) &&	    (addr != sym->end || sym->start != sym->end)) {		pr_debug("%s(%d): ERANGE! sym->name=%s, start=%#" PRIx64 ", addr=%#" PRIx64 ", end=%#" PRIx64 "\n",		       __func__, __LINE__, sym->name, sym->start, addr, sym->end);		return -ERANGE;	} 	offset = addr - sym->start;	h = annotated_source__histogram(src, evsel);	if (h == NULL) {		pr_debug("%s(%d): ENOMEM! sym->name=%s, start=%#" PRIx64 ", addr=%#" PRIx64 ", end=%#" PRIx64 ", func: %d\n",			 __func__, __LINE__, sym->name, sym->start, addr, sym->end,			 symbol__type(sym) == STT_FUNC);		return -ENOMEM;	} 	hash_key = offset << 16 | evsel->core.idx;	if (!hashmap__find(src->samples, hash_key, &entry)) {		entry = zalloc(sizeof(*entry));		if (entry == NULL)			return -ENOMEM; 		if (hashmap__add(src->samples, hash_key, entry) < 0)			return -ENOMEM;	} 	h->nr_samples++;	h->period += sample->period;	entry->nr_samples++;	entry->period += sample->period; 	pr_debug3("%#" PRIx64 " %s: period++ [addr: %#" PRIx64 ", %#" PRIx64		  ", evidx=%d] => nr_samples: %" PRIu64 ", period: %" PRIu64 "\n",		  sym->start, sym->name, addr, addr - sym->start, evsel->core.idx,		  entry->nr_samples, entry->period);	return 0;} struct annotated_branch *annotation__get_branch(struct annotation *notes){	if (notes == NULL)		return NULL; 	if (notes->branch == NULL)		notes->branch = zalloc(sizeof(*notes->branch)); 	return notes->branch;} static struct annotated_branch *symbol__find_branch_hist(struct symbol *sym,							 unsigned int br_cntr_nr){	struct annotation *notes = symbol__annotation(sym);	struct annotated_branch *branch;	const size_t size = symbol__size(sym); 	branch = annotation__get_branch(notes);	if (branch == NULL)		return NULL; 	if (branch->cycles_hist == NULL) {		branch->cycles_hist = calloc(size, sizeof(struct cyc_hist));		if (!branch->cycles_hist)			return NULL;	} 	if (br_cntr_nr && branch->br_cntr == NULL) {		branch->br_cntr = calloc(br_cntr_nr * size, sizeof(u64));		if (!branch->br_cntr)			return NULL;	} 	return branch;} struct annotated_source *symbol__hists(struct symbol *sym, int nr_hists){	struct annotation *notes = symbol__annotation(sym); 	if (notes->src == NULL) {		notes->src = annotated_source__new();		if (notes->src == NULL)			return NULL;		goto alloc_histograms;	} 	if (notes->src->histograms == NULL) {alloc_histograms:		annotated_source__alloc_histograms(notes->src, nr_hists);	} 	return notes->src;} static int symbol__inc_addr_samples(struct map_symbol *ms,				    u64 addr,				    struct perf_sample *sample){	struct symbol *sym = ms->sym;	struct annotated_source *src; 	if (sym == NULL)		return 0;	src = symbol__hists(sym, evlist__nr_entries(sample->evsel->evlist));	return src ? __symbol__inc_addr_samples(ms, src, addr, sample) : 0;} static int symbol__account_br_cntr(struct annotated_branch *branch,				   struct evsel *evsel,				   unsigned offset,				   u64 br_cntr){	unsigned int br_cntr_nr = evsel__leader(evsel)->br_cntr_nr;	unsigned int base = evsel__leader(evsel)->br_cntr_idx;	unsigned int off = offset * evlist__nr_br_cntr(evsel->evlist);	u64 *branch_br_cntr = branch->br_cntr;	unsigned int i, mask, width; 	if (!br_cntr || !branch_br_cntr)		return 0; 	perf_env__find_br_cntr_info(evsel__env(evsel), NULL, &width);	mask = (1L << width) - 1;	for (i = 0; i < br_cntr_nr; i++) {		u64 cntr = (br_cntr >> i * width) & mask; 		branch_br_cntr[off + i + base] += cntr;		if (cntr == mask)			branch_br_cntr[off + i + base] |= ANNOTATION__BR_CNTR_SATURATED_FLAG;	} 	return 0;} static int symbol__account_cycles(u64 addr, u64 start, struct symbol *sym,				  unsigned cycles, struct evsel *evsel,				  u64 br_cntr){	struct annotated_branch *branch;	unsigned offset;	int ret; 	if (sym == NULL)		return 0;	branch = symbol__find_branch_hist(sym, evlist__nr_br_cntr(evsel->evlist));	if (!branch)		return -ENOMEM;	if (addr < sym->start || addr >= sym->end)		return -ERANGE; 	if (start) {		if (start < sym->start || start >= sym->end)			return -ERANGE;		if (start >= addr)			start = 0;	}	offset = addr - sym->start;	ret = __symbol__account_cycles(branch->cycles_hist,					start ? start - sym->start : 0,					offset, cycles,					!!start); 	if (ret)		return ret; 	return symbol__account_br_cntr(branch, evsel, offset, br_cntr);} int addr_map_symbol__account_cycles(struct addr_map_symbol *ams,				    struct addr_map_symbol *start,				    unsigned cycles,				    struct evsel *evsel,				    u64 br_cntr){	u64 saddr = 0;	int err; 	if (!cycles)		return 0; 	/*	 * Only set start when IPC can be computed. We can only	 * compute it when the basic block is completely in a single	 * function.	 * Special case the case when the jump is elsewhere, but	 * it starts on the function start.	 */	if (start &&		(start->ms.sym == ams->ms.sym ||		 (ams->ms.sym &&		  start->addr == ams->ms.sym->start + map__start(ams->ms.map))))		saddr = start->al_addr;	if (saddr == 0)		pr_debug2("BB with bad start: addr %"PRIx64" start %"PRIx64" sym %"PRIx64" saddr %"PRIx64"\n",			ams->addr,			start ? start->addr : 0,			ams->ms.sym ? ams->ms.sym->start + map__start(ams->ms.map) : 0,			saddr);	err = symbol__account_cycles(ams->al_addr, saddr, ams->ms.sym, cycles, evsel, br_cntr);	if (err)		pr_debug2("account_cycles failed %d\n", err);	return err;} struct annotation_line *annotated_source__get_line(struct annotated_source *src,						   s64 offset){	struct annotation_line *al; 	list_for_each_entry(al, &src->source, node) {		if (al->offset == offset)			return al;	}	return NULL;} static unsigned annotation__count_insn(struct annotation *notes, u64 start, u64 end){	struct annotation_line *al;	unsigned n_insn = 0; 	al = annotated_source__get_line(notes->src, start);	if (al == NULL)		return 0; 	list_for_each_entry_from(al, &notes->src->source, node) {		if (al->offset == -1)			continue;		if ((u64)al->offset > end)			break;		n_insn++;	}	return n_insn;} static void annotated_branch__delete(struct annotated_branch *branch){	if (branch) {		zfree(&branch->cycles_hist);		free(branch->br_cntr);		free(branch);	}} static void annotation__count_and_fill(struct annotation *notes, u64 start, u64 end, struct cyc_hist *ch){	unsigned n_insn;	unsigned int cover_insn = 0; 	n_insn = annotation__count_insn(notes, start, end);	if (n_insn && ch->num && ch->cycles) {		struct annotation_line *al;		struct annotated_branch *branch;		float ipc = n_insn / ((double)ch->cycles / (double)ch->num); 		/* Hide data when there are too many overlaps. */		if (ch->reset >= 0x7fff)			return; 		al = annotated_source__get_line(notes->src, start);		if (al == NULL)			return; 		list_for_each_entry_from(al, &notes->src->source, node) {			if (al->offset == -1)				continue;			if ((u64)al->offset > end)				break;			if (al->cycles && al->cycles->ipc == 0.0) {				al->cycles->ipc = ipc;				cover_insn++;			}		}

Lines 1–500 of 3228.