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

Commit 106dacd8 authored by Mark Santaniello's avatar Mark Santaniello Committed by Arnaldo Carvalho de Melo
Browse files

perf script: Support -F brstackoff,dso



The idea here is to make AutoFDO easier in cloud environment with ASLR.
It's easiest to show how this is useful by example. I built a small test
akin to "while(1) { do_nothing(); }" where the do_nothing function is
loaded from a dso:

  $ cat burncpu.cpp
  #include <dlfcn.h>

  int main() {
    void* handle = dlopen("./dso.so", RTLD_LAZY);
    if (!handle) return -1;

    typedef void (*fp)();
    fp do_nothing = (fp) dlsym(handle, "do_nothing");

    while(1) {
      do_nothing();
    }
  }

  $ cat dso.cpp
  extern "C" void do_nothing() {}

  $ cat build.sh
  #!/bin/bash
  g++ -shared dso.cpp -o dso.so
  g++ burncpu.cpp -o burncpu -ldl

I sampled the execution of this program with perf record -b.

Using the existing "brstack,dso", we get absolute addresses that are
affected by ASLR, and could be different on different hosts. The address
does not uniquely identify a branch/target in the binary:

  $ perf script -F brstack,dso | sed 's/\/0 /\/0\n/g' | grep burncpu | grep dso.so | head -n 1
  0x7f967139b6aa(/tmp/burncpu/dso.so)/0x4006b1(/tmp/burncpu/exe)/P/-/-/0

Using the existing "brstacksym,dso" is a little better, because the
symbol plus offset and dso name *does* uniquely identify a branch/target
in the binary.  Ultimately, however, AutoFDO wants a simple offset into
the binary, so we'd have to undo all the work perf did to symbolize in
the first place:

  $ perf script -F brstacksym,dso | sed 's/\/0 /\/0\n/g' | grep burncpu | grep dso.so | head -n 1
  do_nothing+0x5(/tmp/burncpu/dso.so)/main+0x44(/tmp/burncpu/exe)/P/-/-/0

With the new "brstackoff,dso" we get what we need: a simple offset into a
specific dso/binary that uniquely identifies a branch/target:
  $ perf script -F brstackoff,dso | sed 's/\/0 /\/0\n/g' | grep burncpu | grep dso.so | head -n 1
  0x6aa(/tmp/burncpu/dso.so)/0x4006b1(/tmp/burncpu/exe)/P/-/-/0

Signed-off-by: default avatarMark Santaniello <marksan@fb.com>
Tested-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/20170619163825.2012979-2-marksan@fb.com


[ Updated documentation about 'brstackoff' using text from above ]
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 55b9b508
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -116,7 +116,7 @@ OPTIONS
--fields::
        Comma separated list of fields to print. Options are:
        comm, tid, pid, time, cpu, event, trace, ip, sym, dso, addr, symoff,
        srcline, period, iregs, brstack, brstacksym, flags, bpf-output, brstackinsn,
        srcline, period, iregs, brstack, brstacksym, flags, bpf-output, brstackinsn, brstackoff,
        callindent, insn, insnlen. 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
@@ -211,6 +211,8 @@ OPTIONS
	is printed. This is the full execution path leading to the sample. This is only supported when the
	sample was recorded with perf record -b or -j any.

	The brstackoff field will print an offset into a specific dso/binary.

-k::
--vmlinux=<file>::
        vmlinux pathname
+52 −4
Original line number Diff line number Diff line
@@ -85,6 +85,7 @@ enum perf_output_field {
	PERF_OUTPUT_INSN	    = 1U << 21,
	PERF_OUTPUT_INSNLEN	    = 1U << 22,
	PERF_OUTPUT_BRSTACKINSN	    = 1U << 23,
	PERF_OUTPUT_BRSTACKOFF	    = 1U << 24,
};

struct output_option {
@@ -115,6 +116,7 @@ struct output_option {
	{.str = "insn", .field = PERF_OUTPUT_INSN},
	{.str = "insnlen", .field = PERF_OUTPUT_INSNLEN},
	{.str = "brstackinsn", .field = PERF_OUTPUT_BRSTACKINSN},
	{.str = "brstackoff", .field = PERF_OUTPUT_BRSTACKOFF},
};

/* default set to maintain compatibility with current format */
@@ -299,10 +301,9 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,
		return -EINVAL;
	}
	if (PRINT_FIELD(DSO) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR) &&
	    !PRINT_FIELD(BRSTACK) && !PRINT_FIELD(BRSTACKSYM)) {
		pr_err("Display of DSO requested but none of sample IP, sample address, "
		       "brstack\nor brstacksym are selected. Hence, no addresses to "
		       "convert to DSO.\n");
	    !PRINT_FIELD(BRSTACK) && !PRINT_FIELD(BRSTACKSYM) && !PRINT_FIELD(BRSTACKOFF)) {
		pr_err("Display of DSO requested but no address to convert.  Select\n"
		       "sample IP, sample address, brstack, brstacksym, or brstackoff.\n");
		return -EINVAL;
	}
	if (PRINT_FIELD(SRCLINE) && !PRINT_FIELD(IP)) {
@@ -606,6 +607,51 @@ static void print_sample_brstacksym(struct perf_sample *sample,
	}
}

static void print_sample_brstackoff(struct perf_sample *sample,
				    struct thread *thread,
				    struct perf_event_attr *attr)
{
	struct branch_stack *br = sample->branch_stack;
	struct addr_location alf, alt;
	u64 i, from, to;

	if (!(br && br->nr))
		return;

	for (i = 0; i < br->nr; i++) {

		memset(&alf, 0, sizeof(alf));
		memset(&alt, 0, sizeof(alt));
		from = br->entries[i].from;
		to   = br->entries[i].to;

		thread__find_addr_map(thread, sample->cpumode, MAP__FUNCTION, from, &alf);
		if (alf.map && !alf.map->dso->adjust_symbols)
			from = map__map_ip(alf.map, from);

		thread__find_addr_map(thread, sample->cpumode, MAP__FUNCTION, to, &alt);
		if (alt.map && !alt.map->dso->adjust_symbols)
			to = map__map_ip(alt.map, to);

		printf("0x%"PRIx64, from);
		if (PRINT_FIELD(DSO)) {
			printf("(");
			map__fprintf_dsoname(alf.map, stdout);
			printf(")");
		}
		printf("/0x%"PRIx64, to);
		if (PRINT_FIELD(DSO)) {
			printf("(");
			map__fprintf_dsoname(alt.map, stdout);
			printf(")");
		}
		printf("/%c/%c/%c/%d ",
			mispred_str(br->entries + i),
			br->entries[i].flags.in_tx ? 'X' : '-',
			br->entries[i].flags.abort ? 'A' : '-',
			br->entries[i].flags.cycles);
	}
}
#define MAXBB 16384UL

static int grab_bb(u8 *buffer, u64 start, u64 end,
@@ -1227,6 +1273,8 @@ static void process_event(struct perf_script *script,
		print_sample_brstack(sample, thread, attr);
	else if (PRINT_FIELD(BRSTACKSYM))
		print_sample_brstacksym(sample, thread, attr);
	else if (PRINT_FIELD(BRSTACKOFF))
		print_sample_brstackoff(sample, thread, attr);

	if (perf_evsel__is_bpf_output(evsel) && PRINT_FIELD(BPF_OUTPUT))
		print_sample_bpf_output(sample);