Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf script: Support insn output for normal samples

perf script -F +insn was only working for PT traces because the PT
instruction decoder was filling in the insn/insn_len sample attributes.
Support it for non PT samples too on x86 using the existing x86
instruction decoder.

This adds some extra checking to ensure that we don't try to decode
instructions when using perf.data from a different architecture.

% perf record -a sleep 1
% perf script -F ip,sym,insn --xed
ffffffff811704c9 remote_function movl %eax, 0x18(%rbx)
ffffffff8100bb50 intel_bts_enable_local retq
ffffffff81048612 native_apic_mem_write movl %esi, -0xa04000(%rdi)
ffffffff81048612 native_apic_mem_write movl %esi, -0xa04000(%rdi)
ffffffff81048612 native_apic_mem_write movl %esi, -0xa04000(%rdi)
ffffffff810f1f79 generic_exec_single xor %eax, %eax
ffffffff811704c9 remote_function movl %eax, 0x18(%rbx)
ffffffff8100bb34 intel_bts_enable_local movl 0x2000(%rax), %edx
ffffffff81048610 native_apic_mem_write mov %edi, %edi
...

Committer testing:

Before:

# perf script -F ip,sym,insn --xed | head -5
ffffffffa4068804 native_write_msr addb %al, (%rax)
ffffffffa4068804 native_write_msr addb %al, (%rax)
ffffffffa4068804 native_write_msr addb %al, (%rax)
ffffffffa4068806 native_write_msr addb %al, (%rax)
ffffffffa4068806 native_write_msr addb %al, (%rax)
# perf script -F ip,sym,insn --xed | grep -v "addb %al, (%rax)"
#

After:

# perf script -F ip,sym,insn --xed | head -5
ffffffffa4068804 native_write_msr wrmsr
ffffffffa4068804 native_write_msr wrmsr
ffffffffa4068804 native_write_msr wrmsr
ffffffffa4068806 native_write_msr nopl %eax, (%rax,%rax,1)
ffffffffa4068806 native_write_msr nopl %eax, (%rax,%rax,1)
# perf script -F ip,sym,insn --xed | grep -v "addb %al, (%rax)" | head -5
ffffffffa4068804 native_write_msr wrmsr
ffffffffa4068804 native_write_msr wrmsr
ffffffffa4068804 native_write_msr wrmsr
ffffffffa4068806 native_write_msr nopl %eax, (%rax,%rax,1)
ffffffffa4068806 native_write_msr nopl %eax, (%rax,%rax,1)
#

More examples:

# perf script -F ip,sym,insn --xed | grep -v native_write_msr | head
ffffffffa416b90e tick_check_broadcast_expired btq %rax, 0x1a5f42a(%rip)
ffffffffa4956bd0 nmi_cpu_backtrace pushq %r13
ffffffffa415b95e __hrtimer_next_event_base movq 0x18(%rax), %rdx
ffffffffa4956bf3 nmi_cpu_backtrace popq %r12
ffffffffa4171d5c smp_call_function_single pause
ffffffffa4956bdd nmi_cpu_backtrace mov %ebp, %r12d
ffffffffa4797e4d menu_select cmp $0x190, %rax
ffffffffa4171d5c smp_call_function_single pause
ffffffffa405a7d8 nmi_cpu_backtrace_handler callq 0xffffffffa4956bd0
ffffffffa4797f7a menu_select shr $0x3, %rax
#

Which matches the annotate output modulo resolving callqs:

# perf annotate --stdio2 nmi_cpu_backtrace_handler
Samples: 4 of event 'cycles:ppp', 4000 Hz, Event count (approx.): 35908, [percent: local period]
nmi_cpu_backtrace_handler() /lib/modules/5.0.0+/build/vmlinux
Percent
Disassembly of section .text:

ffffffff8105a7d0 <nmi_cpu_backtrace_handler>:
nmi_cpu_backtrace_handler():
nmi_trigger_cpumask_backtrace(mask, exclude_self,
nmi_raise_cpu_backtrace);
}

static int nmi_cpu_backtrace_handler(unsigned int cmd, struct pt_regs *regs)
{
24.45 → callq __fentry__
if (nmi_cpu_backtrace(regs))
mov %rsi,%rdi
75.55 → callq nmi_cpu_backtrace
return NMI_HANDLED;
movzbl %al,%eax

return NMI_DONE;
}
← retq
#

# perf annotate --stdio2 __hrtimer_next_event_base
Samples: 4 of event 'cycles:ppp', 4000 Hz, Event count (approx.): 767977, [percent: local period]
__hrtimer_next_event_base() /lib/modules/5.0.0+/build/vmlinux
Percent
Disassembly of section .text:

ffffffff8115b910 <__hrtimer_next_event_base>:
__hrtimer_next_event_base():

static ktime_t __hrtimer_next_event_base(struct hrtimer_cpu_base *cpu_base,
const struct hrtimer *exclude,
unsigned int active,
ktime_t expires_next)
{
→ callq __fentry__
<SNIP>
4a: add $0x1,%r14
77.31 mov 0x18(%rax),%rdx
shl $0x6,%r14
sub 0x38(%rbx,%r14,1),%rdx
if (expires < expires_next) {
cmp %r12,%rdx
↓ jge 68
<SNIP>

Signed-off-by: Andi Kleen <ak@linux.intel.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Link: http://lkml.kernel.org/r/20190305144758.12397-3-andi@firstfloor.org
[ Converted fetch_exe() to use the name it ended up having when merged: thread__memcpy() ]
[ archinsn.c needs the instruction decoder that is only build when CONFIG_AUXTRACE=y, fix that ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Andi Kleen and committed by
Arnaldo Carvalho de Melo
3ab481a1 d9c1bb2f

+59 -1
+1
tools/perf/arch/x86/util/Build
··· 14 14 perf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o 15 15 16 16 perf-$(CONFIG_AUXTRACE) += auxtrace.o 17 + perf-$(CONFIG_AUXTRACE) += archinsn.o 17 18 perf-$(CONFIG_AUXTRACE) += intel-pt.o 18 19 perf-$(CONFIG_AUXTRACE) += intel-bts.o
+26
tools/perf/arch/x86/util/archinsn.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #include "perf.h" 3 + #include "archinsn.h" 4 + #include "util/intel-pt-decoder/insn.h" 5 + #include "machine.h" 6 + #include "thread.h" 7 + #include "symbol.h" 8 + 9 + void arch_fetch_insn(struct perf_sample *sample, 10 + struct thread *thread, 11 + struct machine *machine) 12 + { 13 + struct insn insn; 14 + int len; 15 + bool is64bit = false; 16 + 17 + if (!sample->ip) 18 + return; 19 + len = thread__memcpy(thread, machine, sample->insn, sample->ip, sizeof(sample->insn), &is64bit); 20 + if (len <= 0) 21 + return; 22 + insn_init(&insn, sample->insn, len, is64bit); 23 + insn_get_length(&insn); 24 + if (insn_complete(&insn) && insn.length <= len) 25 + sample->insn_len = insn.length; 26 + }
+20 -1
tools/perf/builtin-script.c
··· 29 29 #include "util/time-utils.h" 30 30 #include "util/path.h" 31 31 #include "print_binary.h" 32 + #include "archinsn.h" 32 33 #include <linux/bitmap.h> 33 34 #include <linux/kernel.h> 34 35 #include <linux/stringify.h> 35 36 #include <linux/time64.h> 37 + #include <sys/utsname.h> 36 38 #include "asm/bug.h" 37 39 #include "util/mem-events.h" 38 40 #include "util/dump-insn.h" ··· 65 63 static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); 66 64 static struct perf_stat_config stat_config; 67 65 static int max_blocks; 66 + static bool native_arch; 68 67 69 68 unsigned int scripting_max_stack = PERF_MAX_STACK_DEPTH; 70 69 ··· 1230 1227 return len + dlen; 1231 1228 } 1232 1229 1230 + __weak void arch_fetch_insn(struct perf_sample *sample __maybe_unused, 1231 + struct thread *thread __maybe_unused, 1232 + struct machine *machine __maybe_unused) 1233 + { 1234 + } 1235 + 1233 1236 static int perf_sample__fprintf_insn(struct perf_sample *sample, 1234 1237 struct perf_event_attr *attr, 1235 1238 struct thread *thread, ··· 1243 1234 { 1244 1235 int printed = 0; 1245 1236 1237 + if (sample->insn_len == 0 && native_arch) 1238 + arch_fetch_insn(sample, thread, machine); 1239 + 1246 1240 if (PRINT_FIELD(INSNLEN)) 1247 1241 printed += fprintf(fp, " ilen: %d", sample->insn_len); 1248 - if (PRINT_FIELD(INSN)) { 1242 + if (PRINT_FIELD(INSN) && sample->insn_len) { 1249 1243 int i; 1250 1244 1251 1245 printed += fprintf(fp, " insn:"); ··· 3289 3277 .set = false, 3290 3278 .default_no_sample = true, 3291 3279 }; 3280 + struct utsname uts; 3292 3281 char *script_path = NULL; 3293 3282 const char **__argv; 3294 3283 int i, j, err = 0; ··· 3627 3614 3628 3615 if (symbol__init(&session->header.env) < 0) 3629 3616 goto out_delete; 3617 + 3618 + uname(&uts); 3619 + if (!strcmp(uts.machine, session->header.env.arch) || 3620 + (!strcmp(uts.machine, "x86_64") && 3621 + !strcmp(session->header.env.arch, "i386"))) 3622 + native_arch = true; 3630 3623 3631 3624 script.session = session; 3632 3625 script__setup_sample_type(&script);
+12
tools/perf/util/archinsn.h
··· 1 + #ifndef INSN_H 2 + #define INSN_H 1 3 + 4 + struct perf_sample; 5 + struct machine; 6 + struct thread; 7 + 8 + void arch_fetch_insn(struct perf_sample *sample, 9 + struct thread *thread, 10 + struct machine *machine); 11 + 12 + #endif