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: Mark Santaniello <marksan@fb.com>
Tested-by: Arnaldo 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: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Mark Santaniello 2017-06-19 09:38:25 -07:00 committed by Arnaldo Carvalho de Melo
parent 55b9b50811
commit 106dacd86f
2 changed files with 55 additions and 5 deletions

View File

@ -116,7 +116,7 @@ OPTIONS
--fields:: --fields::
Comma separated list of fields to print. Options are: Comma separated list of fields to print. Options are:
comm, tid, pid, time, cpu, event, trace, ip, sym, dso, addr, symoff, 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, 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. 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 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 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. sample was recorded with perf record -b or -j any.
The brstackoff field will print an offset into a specific dso/binary.
-k:: -k::
--vmlinux=<file>:: --vmlinux=<file>::
vmlinux pathname vmlinux pathname

View File

@ -85,6 +85,7 @@ enum perf_output_field {
PERF_OUTPUT_INSN = 1U << 21, PERF_OUTPUT_INSN = 1U << 21,
PERF_OUTPUT_INSNLEN = 1U << 22, PERF_OUTPUT_INSNLEN = 1U << 22,
PERF_OUTPUT_BRSTACKINSN = 1U << 23, PERF_OUTPUT_BRSTACKINSN = 1U << 23,
PERF_OUTPUT_BRSTACKOFF = 1U << 24,
}; };
struct output_option { struct output_option {
@ -115,6 +116,7 @@ struct output_option {
{.str = "insn", .field = PERF_OUTPUT_INSN}, {.str = "insn", .field = PERF_OUTPUT_INSN},
{.str = "insnlen", .field = PERF_OUTPUT_INSNLEN}, {.str = "insnlen", .field = PERF_OUTPUT_INSNLEN},
{.str = "brstackinsn", .field = PERF_OUTPUT_BRSTACKINSN}, {.str = "brstackinsn", .field = PERF_OUTPUT_BRSTACKINSN},
{.str = "brstackoff", .field = PERF_OUTPUT_BRSTACKOFF},
}; };
/* default set to maintain compatibility with current format */ /* default set to maintain compatibility with current format */
@ -299,10 +301,9 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,
return -EINVAL; return -EINVAL;
} }
if (PRINT_FIELD(DSO) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR) && if (PRINT_FIELD(DSO) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR) &&
!PRINT_FIELD(BRSTACK) && !PRINT_FIELD(BRSTACKSYM)) { !PRINT_FIELD(BRSTACK) && !PRINT_FIELD(BRSTACKSYM) && !PRINT_FIELD(BRSTACKOFF)) {
pr_err("Display of DSO requested but none of sample IP, sample address, " pr_err("Display of DSO requested but no address to convert. Select\n"
"brstack\nor brstacksym are selected. Hence, no addresses to " "sample IP, sample address, brstack, brstacksym, or brstackoff.\n");
"convert to DSO.\n");
return -EINVAL; return -EINVAL;
} }
if (PRINT_FIELD(SRCLINE) && !PRINT_FIELD(IP)) { 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 #define MAXBB 16384UL
static int grab_bb(u8 *buffer, u64 start, u64 end, 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); print_sample_brstack(sample, thread, attr);
else if (PRINT_FIELD(BRSTACKSYM)) else if (PRINT_FIELD(BRSTACKSYM))
print_sample_brstacksym(sample, thread, attr); 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)) if (perf_evsel__is_bpf_output(evsel) && PRINT_FIELD(BPF_OUTPUT))
print_sample_bpf_output(sample); print_sample_bpf_output(sample);