Skip to contentSkip to conversation

perf, tools, script: Add brstackinsn for branch stacks

PATCH In review Andi Kleen 4 participants 5 messages Feb 24, 2017 – Mar 16, 2017 perf events

lore
Share & tools
lore

Andi Kleen

author
#0 lore raw

PATCH 1/1 perf, tools, script: Add brstackinsn for branch stacks

From: Andi Kleen <[email protected]>

Implement printing instruction sequences as hex dump for branch stacks. This relies on the x86 instruction decoder used by the PT decoder to find the lengths of instructions to dump them individually.

This is good enough for pattern matching.

This allows to study hot paths for individual samples, together with branch misprediction and cycle count / IPC information if available (on Skylake systems).

% perf record -b ...
% perf script -F brstackinsn
...
read_hpet+67:

        ffffffff9905b843        insn: 74 ea                     # PRED
        ffffffff9905b82f        insn: 85 c9
        ffffffff9905b831        insn: 74 12
        ffffffff9905b833        insn: f3 90
        ffffffff9905b835        insn: 48 8b 0f
        ffffffff9905b838        insn: 48 89 ca
        ffffffff9905b83b        insn: 48 c1 ea 20
        ffffffff9905b83f        insn: 39 f2
        ffffffff9905b841        insn: 89 d0
        ffffffff9905b843        insn: 74 ea                     # PRED

Only works when no special branch filters are specified.

Occasionally the path does not reach up to the sample IP, as the LBRs may be frozen before executing a final jump. In this case we print a special message.

The instruction dumper piggy backs on the existing infrastructure from the IP PT decoder.

An earlier iteration of this patch relied on a disassembler, but this version only uses the existing instruction decoder.

---
 tools/perf/Documentation/perf-script.txt           |  13 +-
 tools/perf/builtin-script.c                        | 289 ++++++++++++++++++++-
 tools/perf/util/Build                              |   1 +
 tools/perf/util/dump-insn.c                        |  16 ++
 tools/perf/util/dump-insn.h                        |  19 ++
 .../util/intel-pt-decoder/intel-pt-insn-decoder.c  |  24 ++
 6 files changed, 351 insertions(+), 11 deletions(-)
 create mode 100644 tools/perf/util/dump-insn.c
 create mode 100644 tools/perf/util/dump-insn.h
tools/perf/Documentation/perf-script.txt +11 -2
index 4ed5f239ba7d..2ad57201ea02 100644
@@ -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,
+        srcline, period, iregs, brstack, brstacksym, flags, bpf-output, brstackinsn,
         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
@@ -189,15 +189,20 @@ OPTIONS
 	i.e., -F "" is not allowed.
 
 	The brstack output includes branch related information with raw addresses using the
-	/v/v/v/v/ syntax in the following order:
+	/v/v/v/v/cycles syntax in the following order:
 	FROM: branch source instruction
 	TO  : branch target instruction
         M/P/-: M=branch target mispredicted or branch direction was mispredicted, P=target predicted or direction predicted, -=not supported
 	X/- : X=branch inside a transactional region, -=not in transaction region or not supported
 	A/- : A=TSX abort entry, -=not aborted region or not supported
+	cycles
 
 	The brstacksym is identical to brstack, except that the FROM and TO addresses are printed in a symbolic form if possible.
 
+	When brstackinsn is specified the full assembler sequences of branch sequences for each sample
+	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.
+
 -k::
 --vmlinux=<file>::
         vmlinux pathname
@@ -299,6 +304,10 @@ include::itrace.txt[]
 	stop time is not given (i.e, time string is 'x.y,') then analysis goes
 	to end of file.
 
+--max-blocks::
+	Set the maximum number of program blocks to print with brstackasm for
+	each sample.
+
 SEE ALSO
 --------
 linkperf:perf-record[1], linkperf:perf-script-perl[1],
tools/perf/builtin-script.c +280 -9

384 lines not on this page · raw message

tools/perf/util/Build +1 -0
index 5da376bc1afc..47ff85d393bb 100644
@@ -81,6 +81,7 @@ libperf-$(CONFIG_AUXTRACE) += intel-pt-decoder/
 libperf-$(CONFIG_AUXTRACE) += intel-pt.o
 libperf-$(CONFIG_AUXTRACE) += intel-bts.o
 libperf-y += parse-branch-options.o
+libperf-y += dump-insn.o
 libperf-y += parse-regs-options.o
 libperf-y += term.o
 libperf-y += help-unknown-cmd.o
tools/perf/util/dump-insn.c new file +16 -0
new file mode 100644
index 000000000000..400d767c5dbb
@@ -0,0 +1,16 @@
+#include "perf.h"
+#include "dump-insn.h"
+
+/* Fallback code */
+
+__weak
+const char *dump_insn(struct perf_insn *x __maybe_unused,
+		      uint64_t ip __maybe_unused,
+		      u8 *inbuf __maybe_unused,
+		      int inlen __maybe_unused,
+		      int *lenp __maybe_unused)
+{
+	if (lenp)
+		*lenp = 0;
+	return "?";
+}
tools/perf/util/dump-insn.h new file +19 -0
new file mode 100644
index 000000000000..39485cdd43e9
@@ -0,0 +1,19 @@
+#ifndef DUMP_INSN_H
+#define DUMP_INSN_H 1
+
+#define MAXINSN 15
+
+struct perf_insn {
+	/* Initialized by callers: */
+	struct thread *thread;
+	u8 cpumode;
+	int cpu;
+	bool is64bit;
+	/* Temporary */
+	char out[256];
+};
+
+const char *dump_insn(struct perf_insn *x, uint64_t ip,
+		      u8 *inbuf, int inlen, int *lenp);
+
+#endif
tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c +24 -0
index 7913363bde5c..6b102d3117f7 100644
@@ -26,6 +26,7 @@
 #include "insn.c"
 
 #include "intel-pt-insn-decoder.h"
+#include "dump-insn.h"
 
 #if INTEL_PT_INSN_BUF_SZ < MAX_INSN_SIZE || INTEL_PT_INSN_BUF_SZ > MAX_INSN
 #error Instruction buffer size too small
@@ -177,6 +178,29 @@ int intel_pt_get_insn(const unsigned char *buf, size_t len, int x86_64,
 	return 0;
 }
 
+const char *dump_insn(struct perf_insn *x, uint64_t ip __maybe_unused,
+		      u8 *inbuf, int inlen, int *lenp)
+{
+	struct insn insn;
+	int n, i;
+	int left;
+
+	insn_init(&insn, inbuf, inlen, x->is64bit);
+	insn_get_length(&insn);
+	if (!insn_complete(&insn) || insn.length > inlen)
+		return "<bad>";
+	if (lenp)
+		*lenp = insn.length;
+	left = sizeof(x->out);
+	n = snprintf(x->out, left, "insn: ");
+	left -= n;
+	for (i = 0; i < insn.length; i++) {
+		n += snprintf(x->out + n, left, "%02x ", inbuf[i]);
+		left -= n;
+	}
+	return x->out;
+}
+
 const char *branch_name[] = {
 	[INTEL_PT_OP_OTHER]	= "Other",
 	[INTEL_PT_OP_CALL]	= "Call",
Signature · 1 line
2.9.3
#1 lore raw

Hi acme,

Quoted 37 lines · Andi Kleen
tools/perf/builtin-script.c +29 -0
index c0783b4f7b6c..b0fdb6c032a5 100644
@@ -546,6 +555,259 @@ static void print_sample_brstacksym(struct perf_sample *sample,
 	}
 }
 
+#define MAXBB 16384UL
+
+static int grab_bb(u8 *buffer, u64 start, u64 end,
+		    struct machine *machine, struct thread *thread,
+		    bool *is64bit, u8 *cpumode, bool last)
+{
+	long offset, len;
+	struct addr_location al;
+	bool kernel;
+
+	if (!start || !end)
+		return 0;
+
+	kernel = machine__kernel_ip(machine, start);
+	if (kernel)
+		*cpumode = PERF_RECORD_MISC_KERNEL;
+	else
+		*cpumode = PERF_RECORD_MISC_USER;
+
+	/*
+	 * Block overlaps between kernel and user.
+	 * This can happen due to ring filtering
+	 * On Intel CPUs the entry into the kernel is filtered,
+	 * but the exit is not. Let the caller patch it up.
+	 */
+	if (kernel != machine__kernel_ip(machine, end)) {
+		printf("\tblock %" PRIx64 "-%" PRIx64 " transfers between kernel and user\n",
+				start, end);
+		return -ENXIO;

tools/perf/builtin-script.c static void print_sample_brstacksym(struct perf_sample *sample, · line 596

Quoted 10 lines
tools/perf/builtin-script.c +10 -0
+	}
+
+	memset(&al, 0, sizeof(al));
+	if (end - start > MAXBB - MAXINSN) {
+		if (last)
+			printf("\tbrstack does not reach to final jump (%" PRIx64 "-%" PRIx64 ")\n",
+					start, end);
+		else
+			printf("\tblock %" PRIx64 "-%" PRIx64 " (%ld) too long to dump\n",
+					start, end, end - start);

This looks to be breaking the build on ppc64:

builtin-script.c: In function ‘grab_bb’:
builtin-script.c:595:11: error: format ‘%ld’ expects argument of type ‘long int’, but argument 4 has type ‘u64 {aka long long unsigned int}’ [-Werror=format=]

      printf("\tblock %" PRIx64 "-%" PRIx64 " (%ld) too long to dump\n", start, end, end - start);
             ^

cheers

author
#2 lore raw
Quoted 7 lines

tools/perf/builtin-script.c static void print_sample_brstacksym(struct perf_sample *sample, · line 595

Quoted 7 lines
tools/perf/builtin-script.c +7 -0
+	memset(&al, 0, sizeof(al));
+	if (end - start > MAXBB - MAXINSN) {
+		if (last)
+			printf("\tbrstack does not reach to final jump (%" PRIx64 "-%" PRIx64 ")\n",
+					start, end);
+		else
+			printf("\tblock %" PRIx64 "-%" PRIx64 " (%ld) too long to dump\n",

Thanks. Should be %" PRIu64 " of course.

Arnaldo, can you do the change or should I send a patch?

-Andi

Quoted 11 lines

tools/perf/builtin-script.c static void print_sample_brstacksym(struct perf_sample *sample, · line 596

Quoted 1 line
tools/perf/builtin-script.c +1 -0
+					start, end, end - start);

This looks to be breaking the build on ppc64:

builtin-script.c: In function ‘grab_bb’:
builtin-script.c:595:11: error: format ‘%ld’ expects argument of type ‘long int’, but argument 4 has type ‘u64 {aka long long unsigned int}’ [-Werror=format=]

      printf("\tblock %" PRIx64 "-%" PRIx64 " (%ld) too long to dump\n", start, end, end - start);
             ^

cheers

#3 lore raw

Em Wed, Mar 15, 2017 at 07:22:18PM -0700, Andi Kleen escreveu:

Quoted 11 lines
Quoted 7 lines

tools/perf/builtin-script.c static void print_sample_brstacksym(struct perf_sample *sample, · line 595

Quoted 7 lines
tools/perf/builtin-script.c +7 -0
+	memset(&al, 0, sizeof(al));
+	if (end - start > MAXBB - MAXINSN) {
+		if (last)
+			printf("\tbrstack does not reach to final jump (%" PRIx64 "-%" PRIx64 ")\n",
+					start, end);
+		else
+			printf("\tblock %" PRIx64 "-%" PRIx64 " (%ld) too long to dump\n",

Thanks. Should be %" PRIu64 " of course.

Arnaldo, can you do the change

Done.

Quoted 1 line

or should I send a patch?

- Arnaldo

#4 lore raw

[tip:perf/core] perf script: Add 'brstackinsn' for branch stacks

In reply to Andi Kleen
perf script: Add 'brstackinsn' for branch stacks
Commit-ID:  48d02a1d5c137d362defd11a5d57d0af4a75a983
Gitweb:     http://git.kernel.org/tip/48d02a1d5c137d362defd11a5d57d0af4a75a983
Author:     Andi Kleen <[email protected]>
AuthorDate: Thu, 23 Feb 2017 15:46:34 -0800
Committer:  Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Thu, 16 Mar 2017 09:24:35 -0300

perf script: Add 'brstackinsn' for branch stacks

Implement printing instruction sequences as hex dump for branch stacks.

This relies on the x86 instruction decoder used by the PT decoder to find the lengths of instructions to dump them individually.

This is good enough for pattern matching.

This allows to study hot paths for individual samples, together with branch misprediction and cycle count / IPC information if available (on Skylake systems).

% perf record -b ...
% perf script -F brstackinsn
...

    read_hpet+67:
          ffffffff9905b843        insn: 74 ea                     # PRED
          ffffffff9905b82f        insn: 85 c9
          ffffffff9905b831        insn: 74 12
          ffffffff9905b833        insn: f3 90
          ffffffff9905b835        insn: 48 8b 0f
          ffffffff9905b838        insn: 48 89 ca
          ffffffff9905b83b        insn: 48 c1 ea 20
          ffffffff9905b83f        insn: 39 f2
          ffffffff9905b841        insn: 89 d0
          ffffffff9905b843        insn: 74 ea                     # PRED

Only works when no special branch filters are specified.

Occasionally the path does not reach up to the sample IP, as the LBRs may be frozen before executing a final jump. In this case we print a special message.

The instruction dumper piggy backs on the existing infrastructure from the IP PT decoder.

An earlier iteration of this patch relied on a disassembler, but this version only uses the existing instruction decoder.

Committer note:

Added hint about how to get suitable perf.data files for use with '-F brstackinsm':

  $ perf record usleep 1
  [ perf record: Woken up 1 times to write data ]
  [ perf record: Captured and wrote 0.018 MB perf.data (8 samples) ]
  $
  $ perf script -F brstackinsn
  Display of branch stack assembler requested, but non all-branch filter set
  Hint: run 'perf record -b ...'
  $
---
 tools/perf/Documentation/perf-script.txt           |  13 +-
 tools/perf/builtin-script.c                        | 264 ++++++++++++++++++++-
 tools/perf/util/Build                              |   1 +
 tools/perf/util/dump-insn.c                        |  14 ++
 tools/perf/util/dump-insn.h                        |  22 ++
 .../util/intel-pt-decoder/intel-pt-insn-decoder.c  |  24 ++
 6 files changed, 327 insertions(+), 11 deletions(-)
tools/perf/Documentation/perf-script.txt +11 -2
index 62c9b0c..cb0eda3 100644
@@ -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,
+        srcline, period, iregs, brstack, brstacksym, flags, bpf-output, brstackinsn,
         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
@@ -189,15 +189,20 @@ OPTIONS
 	i.e., -F "" is not allowed.
 
 	The brstack output includes branch related information with raw addresses using the
-	/v/v/v/v/ syntax in the following order:
+	/v/v/v/v/cycles syntax in the following order:
 	FROM: branch source instruction
 	TO  : branch target instruction
         M/P/-: M=branch target mispredicted or branch direction was mispredicted, P=target predicted or direction predicted, -=not supported
 	X/- : X=branch inside a transactional region, -=not in transaction region or not supported
 	A/- : A=TSX abort entry, -=not aborted region or not supported
+	cycles
 
 	The brstacksym is identical to brstack, except that the FROM and TO addresses are printed in a symbolic form if possible.
 
+	When brstackinsn is specified the full assembler sequences of branch sequences for each sample
+	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.
+
 -k::
 --vmlinux=<file>::
         vmlinux pathname
@@ -302,6 +307,10 @@ include::itrace.txt[]
 	stop time is not given (i.e, time string is 'x.y,') then analysis goes
 	to end of file.
 
+--max-blocks::
+	Set the maximum number of program blocks to print with brstackasm for
+	each sample.
+
 SEE ALSO
 --------
 linkperf:perf-record[1], linkperf:perf-script-perl[1],
tools/perf/builtin-script.c +255 -9

359 lines not on this page · raw message

tools/perf/util/Build +1 -0
index 2ea5ee1..fb4f42f 100644
@@ -82,6 +82,7 @@ libperf-$(CONFIG_AUXTRACE) += intel-pt-decoder/
 libperf-$(CONFIG_AUXTRACE) += intel-pt.o
 libperf-$(CONFIG_AUXTRACE) += intel-bts.o
 libperf-y += parse-branch-options.o
+libperf-y += dump-insn.o
 libperf-y += parse-regs-options.o
 libperf-y += term.o
 libperf-y += help-unknown-cmd.o
tools/perf/util/dump-insn.c new file +14 -0
new file mode 100644
index 0000000..ffbdb19
@@ -0,0 +1,14 @@
+#include <linux/compiler.h>
+#include "dump-insn.h"
+
+/* Fallback code */
+
+__weak
+const char *dump_insn(struct perf_insn *x __maybe_unused,
+		      u64 ip __maybe_unused, u8 *inbuf __maybe_unused,
+		      int inlen __maybe_unused, int *lenp)
+{
+	if (lenp)
+		*lenp = 0;
+	return "?";
+}
tools/perf/util/dump-insn.h new file +22 -0
new file mode 100644
index 0000000..90fb115
@@ -0,0 +1,22 @@
+#ifndef __PERF_DUMP_INSN_H
+#define __PERF_DUMP_INSN_H 1
+
+#define MAXINSN 15
+
+#include <linux/types.h>
+
+struct thread;
+
+struct perf_insn {
+	/* Initialized by callers: */
+	struct thread *thread;
+	u8	      cpumode;
+	bool	      is64bit;
+	int	      cpu;
+	/* Temporary */
+	char	      out[256];
+};
+
+const char *dump_insn(struct perf_insn *x, u64 ip,
+		      u8 *inbuf, int inlen, int *lenp);
+#endif
tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c +24 -0
index 55b6250..a5f35b2 100644
@@ -26,6 +26,7 @@
 #include "insn.c"
 
 #include "intel-pt-insn-decoder.h"
+#include "dump-insn.h"
 
 #if INTEL_PT_INSN_BUF_SZ < MAX_INSN_SIZE || INTEL_PT_INSN_BUF_SZ > MAX_INSN
 #error Instruction buffer size too small
@@ -179,6 +180,29 @@ int intel_pt_get_insn(const unsigned char *buf, size_t len, int x86_64,
 	return 0;
 }
 
+const char *dump_insn(struct perf_insn *x, uint64_t ip __maybe_unused,
+		      u8 *inbuf, int inlen, int *lenp)
+{
+	struct insn insn;
+	int n, i;
+	int left;
+
+	insn_init(&insn, inbuf, inlen, x->is64bit);
+	insn_get_length(&insn);
+	if (!insn_complete(&insn) || insn.length > inlen)
+		return "<bad>";
+	if (lenp)
+		*lenp = insn.length;
+	left = sizeof(x->out);
+	n = snprintf(x->out, left, "insn: ");
+	left -= n;
+	for (i = 0; i < insn.length; i++) {
+		n += snprintf(x->out + n, left, "%02x ", inbuf[i]);
+		left -= n;
+	}
+	return x->out;
+}
+
 const char *branch_name[] = {
 	[INTEL_PT_OP_OTHER]	= "Other",
 	[INTEL_PT_OP_CALL]	= "Call",