Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 301406b9 authored by Frederic Weisbecker's avatar Frederic Weisbecker Committed by Ingo Molnar
Browse files

perf annotate: Print the filename:line for annotated colored lines



When we have a colored line in perf annotate, ie a middle/high
overhead one, it's sometimes useful to get the matching line
and filename from the source file, especially this path prepares
to another subsequent one which will print a sorted summary of
midle/high overhead lines in the beginning of the output.

Filename:Lines have the same color than the concerned ip lines.

It can be slow because it relies on addr2line. We could also
use objdump with -l but that implies we would have to bufferize
objdump output and parse it to filter the relevant lines since
we want to print a sorted summary in the beginning.

Signed-off-by: default avatarFrederic Weisbecker <fweisbec@gmail.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
LKML-Reference: <1244844682-12928-1-git-send-email-fweisbec@gmail.com>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent f3ad1165
Loading
Loading
Loading
Loading
+97 −1
Original line number Diff line number Diff line
@@ -39,6 +39,8 @@ static int dump_trace = 0;

static int		verbose;

static int		print_line;

static unsigned long	page_size;
static unsigned long	mmap_window = 32;

@@ -84,6 +86,12 @@ typedef union event_union {
	struct period_event		period;
} event_t;


struct sym_ext {
	double		percent;
	char		*path;
};

static LIST_HEAD(dsos);
static struct dso *kernel_dso;
static struct dso *vdso;
@@ -1034,6 +1042,8 @@ static int
parse_line(FILE *file, struct symbol *sym, __u64 start, __u64 len)
{
	char *line = NULL, *tmp, *tmp2;
	static const char *prev_line;
	static const char *prev_color;
	unsigned int offset;
	size_t line_len;
	__u64 line_ip;
@@ -1073,15 +1083,20 @@ parse_line(FILE *file, struct symbol *sym, __u64 start, __u64 len)
	}

	if (line_ip != -1) {
		const char *path = NULL;
		unsigned int hits = 0;
		double percent = 0.0;
		char *color = PERF_COLOR_NORMAL;
		struct sym_ext *sym_ext = sym->priv;

		offset = line_ip - start;
		if (offset < len)
			hits = sym->hist[offset];

		if (sym->hist_sum)
		if (sym_ext) {
			path = sym_ext[offset].path;
			percent = sym_ext[offset].percent;
		} else if (sym->hist_sum)
			percent = 100.0 * hits / sym->hist_sum;

		/*
@@ -1096,6 +1111,20 @@ parse_line(FILE *file, struct symbol *sym, __u64 start, __u64 len)
				color = PERF_COLOR_GREEN;
		}

		/*
		 * Also color the filename and line if needed, with
		 * the same color than the percentage. Don't print it
		 * twice for close colored ip with the same filename:line
		 */
		if (path) {
			if (!prev_line || strcmp(prev_line, path)
				       || color != prev_color) {
				color_fprintf(stdout, color, " %s", path);
				prev_line = path;
				prev_color = color;
			}
		}

		color_fprintf(stdout, color, " %7.2f", percent);
		printf(" :	");
		color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", line);
@@ -1109,6 +1138,67 @@ parse_line(FILE *file, struct symbol *sym, __u64 start, __u64 len)
	return 0;
}

static void free_source_line(struct symbol *sym, int len)
{
	struct sym_ext *sym_ext = sym->priv;
	int i;

	if (!sym_ext)
		return;

	for (i = 0; i < len; i++)
		free(sym_ext[i].path);
	free(sym_ext);

	sym->priv = NULL;
}

/* Get the filename:line for the colored entries */
static void get_source_line(struct symbol *sym, __u64 start, int len)
{
	int i;
	char cmd[PATH_MAX * 2];
	struct sym_ext *sym_ext;

	if (!sym->hist_sum)
		return;

	sym->priv = calloc(len, sizeof(struct sym_ext));
	if (!sym->priv)
		return;

	sym_ext = sym->priv;

	for (i = 0; i < len; i++) {
		char *path = NULL;
		size_t line_len;
		__u64 offset;
		FILE *fp;

		sym_ext[i].percent = 100.0 * sym->hist[i] / sym->hist_sum;
		if (sym_ext[i].percent <= 0.5)
			continue;

		offset = start + i;
		sprintf(cmd, "addr2line -e %s %016llx", vmlinux, offset);
		fp = popen(cmd, "r");
		if (!fp)
			continue;

		if (getline(&path, &line_len, fp) < 0 || !line_len)
			goto next;

		sym_ext[i].path = malloc(sizeof(char) * line_len);
		if (!sym_ext[i].path)
			goto next;

		strcpy(sym_ext[i].path, path);

	next:
		pclose(fp);
	}
}

static void annotate_sym(struct dso *dso, struct symbol *sym)
{
	char *filename = dso->name;
@@ -1135,6 +1225,9 @@ static void annotate_sym(struct dso *dso, struct symbol *sym)
	end = start + sym->end - sym->start + 1;
	len = sym->end - sym->start;

	if (print_line)
		get_source_line(sym, start, len);

	sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s", (__u64)start, (__u64)end, filename);

	if (verbose >= 3)
@@ -1150,6 +1243,7 @@ static void annotate_sym(struct dso *dso, struct symbol *sym)
	}

	pclose(file);
	free_source_line(sym, len);
}

static void find_annotations(void)
@@ -1308,6 +1402,8 @@ static const struct option options[] = {
	OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
		    "dump raw trace in ASCII"),
	OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"),
	OPT_BOOLEAN('l', "print-line", &print_line,
		    "print matching source lines (may be slow)"),
	OPT_END()
};

+1 −0
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@ struct symbol {
	__u64		obj_start;
	__u64		hist_sum;
	__u64		*hist;
	void		*priv;
	char		name[0];
};