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

Commit dd2e18e9 authored by Andi Kleen's avatar Andi Kleen Committed by Arnaldo Carvalho de Melo
Browse files

perf tools: Support 'srccode' output



When looking at PT or brstackinsn traces with 'perf script' it can be
very useful to see the source code. This adds a simple facility to print
them with 'perf script', if the information is available through dwarf

  % perf record ...
  % perf script -F insn,ip,sym,srccode
  ...

            4004c6 main
  5               for (i = 0; i < 10000000; i++)
             4004cd main
  5               for (i = 0; i < 10000000; i++)
             4004c6 main
  5               for (i = 0; i < 10000000; i++)
             4004cd main
  5               for (i = 0; i < 10000000; i++)
             4004cd main
  5               for (i = 0; i < 10000000; i++)
             4004cd main
  5               for (i = 0; i < 10000000; i++)
             4004cd main
  5               for (i = 0; i < 10000000; i++)
             4004cd main
  5               for (i = 0; i < 10000000; i++)
             4004b3 main
  6                       v++;

  % perf record -b ...
  % perf script -F insn,ip,sym,srccode,brstackinsn

  ...
         main+22:
          0000000000400543        insn: e8 ca ff ff ff            # PRED
  |18                     f1();
          f1:
          0000000000400512        insn: 55
  |10       {
          0000000000400513        insn: 48 89 e5
          0000000000400516        insn: b8 00 00 00 00
  |11             f2();
          000000000040051b        insn: e8 d6 ff ff ff            # PRED
          f2:
          00000000004004f6        insn: 55
  |5        {
          00000000004004f7        insn: 48 89 e5
          00000000004004fa        insn: 8b 05 2c 0b 20 00
  |6              c = a / b;
          0000000000400500        insn: 8b 0d 2a 0b 20 00
          0000000000400506        insn: 99
          0000000000400507        insn: f7 f9
          0000000000400509        insn: 89 05 29 0b 20 00
          000000000040050f        insn: 90
  |7        }
          0000000000400510        insn: 5d
          0000000000400511        insn: c3                        # PRED
          f1+14:
          0000000000400520        insn: b8 00 00 00 00
  |12             f2();
          0000000000400525        insn: e8 cc ff ff ff            # PRED
          f2:
          00000000004004f6        insn: 55
  |5        {
          00000000004004f7        insn: 48 89 e5
          00000000004004fa        insn: 8b 05 2c 0b 20 00
  |6              c = a / b;

Not supported for callchains currently, would need some layout changes
there.

Committer notes:

Fixed the build on Alpine Linux (3.4 .. 3.8) by addressing this
warning:

  In file included from util/srccode.c:19:0:
  /usr/include/sys/fcntl.h:1:2: error: #warning redirecting incorrect #include <sys/fcntl.h> to <fcntl.h> [-Werror=cpp]
   #warning redirecting incorrect #include <sys/fcntl.h> to <fcntl.h>
    ^~~~~~~
  cc1: all warnings being treated as errors

Signed-off-by: default avatarAndi Kleen <ak@linux.intel.com>
Tested-by: default avatarJiri Olsa <jolsa@kernel.org>
Link: http://lkml.kernel.org/r/20181204001848.24769-1-andi@firstfloor.org


Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 42da438c
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -117,7 +117,7 @@ OPTIONS
        Comma separated list of fields to print. Options are:
        comm, tid, pid, time, cpu, event, trace, ip, sym, dso, addr, symoff,
        srcline, period, iregs, uregs, brstack, brstacksym, flags, bpf-output, brstackinsn,
        brstackoff, callindent, insn, insnlen, synth, phys_addr, metric, misc.
        brstackoff, callindent, insn, insnlen, synth, phys_addr, metric, misc, srccode.
        Field list can be prepended with the type, trace, sw or hw,
        to indicate to which event type the field list applies.
        e.g., -F sw:comm,tid,time,ip,sym  and -F trace:time,cpu,trace
+45 −2
Original line number Diff line number Diff line
@@ -96,6 +96,7 @@ enum perf_output_field {
	PERF_OUTPUT_UREGS	    = 1U << 27,
	PERF_OUTPUT_METRIC	    = 1U << 28,
	PERF_OUTPUT_MISC            = 1U << 29,
	PERF_OUTPUT_SRCCODE	    = 1U << 30,
};

struct output_option {
@@ -132,6 +133,7 @@ struct output_option {
	{.str = "phys_addr", .field = PERF_OUTPUT_PHYS_ADDR},
	{.str = "metric", .field = PERF_OUTPUT_METRIC},
	{.str = "misc", .field = PERF_OUTPUT_MISC},
	{.str = "srccode", .field = PERF_OUTPUT_SRCCODE},
};

enum {
@@ -424,7 +426,7 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,
		pr_err("Display of DSO requested but no address to convert.\n");
		return -EINVAL;
	}
	if (PRINT_FIELD(SRCLINE) && !PRINT_FIELD(IP)) {
	if ((PRINT_FIELD(SRCLINE) || PRINT_FIELD(SRCCODE)) && !PRINT_FIELD(IP)) {
		pr_err("Display of source line number requested but sample IP is not\n"
		       "selected. Hence, no address to lookup the source line number.\n");
		return -EINVAL;
@@ -907,6 +909,22 @@ static int grab_bb(u8 *buffer, u64 start, u64 end,
	return len;
}

static int print_srccode(struct thread *thread, u8 cpumode, uint64_t addr)
{
	struct addr_location al;
	int ret = 0;

	memset(&al, 0, sizeof(al));
	thread__find_map(thread, cpumode, addr, &al);
	if (!al.map)
		return 0;
	ret = map__fprintf_srccode(al.map, al.addr, stdout,
		    &thread->srccode_state);
	if (ret)
		ret += printf("\n");
	return ret;
}

static int ip__fprintf_jump(uint64_t ip, struct branch_entry *en,
			    struct perf_insn *x, u8 *inbuf, int len,
			    int insn, FILE *fp, int *total_cycles)
@@ -998,6 +1016,8 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample,
					   x.cpumode, x.cpu, &lastsym, attr, fp);
		printed += ip__fprintf_jump(br->entries[nr - 1].from, &br->entries[nr - 1],
					    &x, buffer, len, 0, fp, &total_cycles);
		if (PRINT_FIELD(SRCCODE))
			printed += print_srccode(thread, x.cpumode, br->entries[nr - 1].from);
	}

	/* Print all blocks */
@@ -1027,12 +1047,16 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample,
			if (ip == end) {
				printed += ip__fprintf_jump(ip, &br->entries[i], &x, buffer + off, len - off, insn, fp,
							    &total_cycles);
				if (PRINT_FIELD(SRCCODE))
					printed += print_srccode(thread, x.cpumode, ip);
				break;
			} else {
				printed += fprintf(fp, "\t%016" PRIx64 "\t%s\n", ip,
						   dump_insn(&x, ip, buffer + off, len - off, &ilen));
				if (ilen == 0)
					break;
				if (PRINT_FIELD(SRCCODE))
					print_srccode(thread, x.cpumode, ip);
				insn++;
			}
		}
@@ -1063,6 +1087,8 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample,

		printed += fprintf(fp, "\t%016" PRIx64 "\t%s\n", sample->ip,
			dump_insn(&x, sample->ip, buffer, len, NULL));
		if (PRINT_FIELD(SRCCODE))
			print_srccode(thread, x.cpumode, sample->ip);
		goto out;
	}
	for (off = 0; off <= end - start; off += ilen) {
@@ -1070,6 +1096,8 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample,
				   dump_insn(&x, start + off, buffer + off, len - off, &ilen));
		if (ilen == 0)
			break;
		if (PRINT_FIELD(SRCCODE))
			print_srccode(thread, x.cpumode, start + off);
	}
out:
	return printed;
@@ -1252,7 +1280,16 @@ static int perf_sample__fprintf_bts(struct perf_sample *sample,
		printed += map__fprintf_srcline(al->map, al->addr, "\n  ", fp);

	printed += perf_sample__fprintf_insn(sample, attr, thread, machine, fp);
	return printed + fprintf(fp, "\n");
	printed += fprintf(fp, "\n");
	if (PRINT_FIELD(SRCCODE)) {
		int ret = map__fprintf_srccode(al->map, al->addr, stdout,
					 &thread->srccode_state);
		if (ret) {
			printed += ret;
			printed += printf("\n");
		}
	}
	return printed;
}

static struct {
@@ -1792,6 +1829,12 @@ static void process_event(struct perf_script *script,
		fprintf(fp, "%16" PRIx64, sample->phys_addr);
	fprintf(fp, "\n");

	if (PRINT_FIELD(SRCCODE)) {
		if (map__fprintf_srccode(al->map, al->addr, stdout,
					 &thread->srccode_state))
			printf("\n");
	}

	if (PRINT_FIELD(METRIC))
		perf_sample__fprint_metric(script, thread, evsel, sample, fp);

+1 −0
Original line number Diff line number Diff line
@@ -77,6 +77,7 @@ libperf-y += stat-shadow.o
libperf-y += stat-display.o
libperf-y += record.o
libperf-y += srcline.o
libperf-y += srccode.o
libperf-y += data.o
libperf-y += tsc.o
libperf-y += cloexec.o
+1 −0
Original line number Diff line number Diff line
@@ -173,6 +173,7 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment,
			if (!print_oneline)
				printed += fprintf(fp, "\n");

			/* Add srccode here too? */
			if (symbol_conf.bt_stop_list &&
			    node->sym &&
			    strlist__has_entry(symbol_conf.bt_stop_list,
+49 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include "srcline.h"
#include "namespaces.h"
#include "unwind.h"
#include "srccode.h"

static void __maps__insert(struct maps *maps, struct map *map);
static void __maps__insert_name(struct maps *maps, struct map *map);
@@ -421,6 +422,54 @@ int map__fprintf_srcline(struct map *map, u64 addr, const char *prefix,
	return ret;
}

int map__fprintf_srccode(struct map *map, u64 addr,
			 FILE *fp,
			 struct srccode_state *state)
{
	char *srcfile;
	int ret = 0;
	unsigned line;
	int len;
	char *srccode;

	if (!map || !map->dso)
		return 0;
	srcfile = get_srcline_split(map->dso,
				    map__rip_2objdump(map, addr),
				    &line);
	if (!srcfile)
		return 0;

	/* Avoid redundant printing */
	if (state &&
	    state->srcfile &&
	    !strcmp(state->srcfile, srcfile) &&
	    state->line == line) {
		free(srcfile);
		return 0;
	}

	srccode = find_sourceline(srcfile, line, &len);
	if (!srccode)
		goto out_free_line;

	ret = fprintf(fp, "|%-8d %.*s", line, len, srccode);
	state->srcfile = srcfile;
	state->line = line;
	return ret;

out_free_line:
	free(srcfile);
	return ret;
}


void srccode_state_free(struct srccode_state *state)
{
	zfree(&state->srcfile);
	state->line = 0;
}

/**
 * map__rip_2objdump - convert symbol start address to objdump address.
 * @map: memory map
Loading