Skip to contentSkip to conversation

Implement dwarf variable/type resolving for perf script

PATCH In review Andi Kleen 2 participants 23 messages Nov 28, 2017 – Dec 1, 2017 perf events

lore
Share & tools
lore

Andi Kleen

author
#0 lore raw

This patchkit extends perf script to query dwarf information for variable names or types/structure fields accessed from code.

The dwarf resolution is all on top of Masami's perf probe dwarf code.

It supports multiple use cases:
- When we sample registers it can use the dwarf information to resolve the registers to names.
- When we sample any instruction the instruction can be decoded and we can determine the type/struct field to make an estimate of the memory access patterns in data structures.
- When we sample the new PTWRITE instruction the logged value from the PT log can be associated with a variable.
- Various cleanups and fixes to make the one above all possible.

It is all implemented with new output formats in perf script:
iregval (map register values to names) and insnvar (decode instruction and map back memory operand to dwarf operation)

There are some limitations, it cannot decode everything, and is somewhat slow, but it's already quite useful for typical code

    % perf record -Idi,si ./targ
    % perf script -F +iregvals
    ...
        targ  8584 169763.761843:    2091795 cycles:ppp:            40041a main (targ)
        targ  8584 169763.762520:    1913932 cycles:ppp:            400534 f1 (targ) { b = 0x2, int }  { a = 0x1, int }
        targ  8584 169763.763141:    1638913 cycles:ppp:            400523 f2 (targ) { b = 0x1, int }  { a = 0x2, int }
        targ  8584 169763.763672:    1516522 cycles:ppp:            400522 f2 (targ) { b = 0x1, int }  { a = 0x2, int }
        targ  8584 169763.764165:    1335501 cycles:ppp:            400523 f2 (targ) { b = 0x1, int }  { a = 0x2, int }
        targ  8584 169763.764598:    1253289 cycles:ppp:            400522 f2 (targ) { b = 0x2, int }  { a = 0x1, int }
        targ  8584 169763.765005:    1135131 cycles:ppp:            400534 f1 (targ) { b = 0x2, int }  { a = 0x1, int }
        targ  8584 169763.765373:    1080325 cycles:ppp:            400522 f2 (targ) { b = 0x2, int }  { a = 0x1, int }
        targ  8584 169763.765724:    1036999 cycles:ppp:            400522 f2 (targ) { b = 0x1, int }  { a = 0x2, int }
        targ  8584 169763.766061:     971213 cycles:ppp:            400534 f1 (targ) { b = 0x2, int }  { a = 0x1, int }


    % perf record -e intel_pt//u  -a sleep 1
    % perf script --itrace=i0ns -F insnvar,insn,ip,sym  -f 2>&1 | xed -F insn: -A -64 | less
    ...
               4f7e61 xyarray__max_y                pushq  %rbp
               4f7e62 xyarray__max_y                mov %rsp, %rbp
               4f7e65 xyarray__max_y                sub $0x20, %rsp
               4f7e69 xyarray__max_y                movq  %rdi, -0x18(%rbp) { -24(xy), struct xyarray* }
               4f7e6d xyarray__max_y                movq  %fs:0x28, %rax
               4f7e76 xyarray__max_y                movq  %rax, -0x8(%rbp) { -8(xy), struct xyarray* }
               4f7e7a xyarray__max_y                xor %eax, %eax
               4f7e7c xyarray__max_y                movq  -0x18(%rbp), %rax { -24(xy), struct xyarray* }
               4f7e80 xyarray__max_y                movq  0x20(%rax), %rax
               4f7e84 xyarray__max_y                movq  -0x8(%rbp), %rdx { -8(xy), struct xyarray* }
               4f7e88 xyarray__max_y                xorq  %fs:0x28, %rdx
               4f7e91 xyarray__max_y                jz 0x7
               4f7e98 xyarray__max_y                leaveq
               4f7e99 xyarray__max_y                retq

In this example we now know that this function accesses two fields in struct xyarray *

Available from

git://git.kernel.org/pub/scm/linux/kernel/git/ak/linux-misc perf/var-resolve-2

v1: Initial post

author
#1 lore raw

PATCH 1/12 perf, tools, pt: Clear instruction for ptwrite samples

From: Andi Kleen <[email protected]>

When a PTWRITE sample is synthesized the PT decoder already ran ahead and sample->insn contains the next branch instruction, not the PTWRITE.

Clear it for PTWRITE samples to avoid confusion.

---
 tools/perf/util/intel-pt.c | 6 ++++++
 1 file changed, 6 insertions(+)
tools/perf/util/intel-pt.c +6 -0
index 23f9ba676df0..485c8040484e 100644
@@ -1262,6 +1262,12 @@ static void intel_pt_prep_p_sample(struct intel_pt *pt,
 	 */
 	if (!sample->ip)
 		sample->flags = 0;
+
+	/*
+	 * Don't have valid instructions because decoder already ran ahead.
+	 */
+	sample->insn_len = 0;
+	memset(sample->insn, 0, INTEL_PT_INSN_BUF_SZ);
 }
 
 static int intel_pt_synth_ptwrite_sample(struct intel_pt_queue *ptq)
Signature · 1 line
2.13.6
author
#2 lore raw

PATCH 2/12 perf, tools, script: Print insn/insnlen for non PT sample

In reply to Andi Kleen

From: Andi Kleen <[email protected]>

Dumping insn/insnlen only works for PT samples where the PT decoder fills in these fields. Add a fallback for other samples where we grab the instructions manually and then call an architecture specific function to determine the instruction length.

The architecture specific function is currently only implemented for x86, and uses the standard Linux instruction decoder.

---
 tools/perf/arch/x86/util/Build     |  1 +
 tools/perf/arch/x86/util/insnlen.c | 12 ++++++++++++
 tools/perf/builtin-script.c        | 13 +++++++++++++
 tools/perf/util/Build              |  1 +
 tools/perf/util/insnlen.c          | 10 ++++++++++
 tools/perf/util/insnlen.h          |  6 ++++++
 6 files changed, 43 insertions(+)
 create mode 100644 tools/perf/arch/x86/util/insnlen.c
 create mode 100644 tools/perf/util/insnlen.c
 create mode 100644 tools/perf/util/insnlen.h
tools/perf/arch/x86/util/Build +1 -0
index f95e6f46ef0d..139f9f1a56f9 100644
@@ -1,6 +1,7 @@
 libperf-y += header.o
 libperf-y += tsc.o
 libperf-y += pmu.o
+libperf-y += insnlen.o
 libperf-y += kvm-stat.o
 libperf-y += perf_regs.o
 libperf-y += group.o
tools/perf/arch/x86/util/insnlen.c new file +12 -0
new file mode 100644
index 000000000000..8e2e50bd5201
@@ -0,0 +1,12 @@
+#include "intel-pt-decoder/insn.h"
+#include "intel-pt-decoder/inat.h"
+#include "insnlen.h"
+
+int arch_insn_len(char *insnbytes, int insnlen, int is64bit)
+{
+	struct insn insn;
+
+	insn_init(&insn, insnbytes, insnlen, is64bit);
+	insn_get_length(&insn);
+	return insn.length;
+}
tools/perf/builtin-script.c +13 -0
index ee7c7aaaae72..cae4b13fc715 100644
@@ -20,6 +20,7 @@
 #include "util/data.h"
 #include "util/auxtrace.h"
 #include "util/cpumap.h"
+#include "util/insnlen.h"
 #include "util/thread_map.h"
 #include "util/stat.h"
 #include "util/string2.h"
@@ -1108,6 +1109,18 @@ static int perf_sample__fprintf_insn(struct perf_sample *sample,
 {
 	int printed = 0;
 
+	if ((PRINT_FIELD(INSNLEN) || PRINT_FIELD(INSN)) && !sample->insn_len) {
+		u8 ibuf[64];
+		bool is64bit;
+		u8 cpumode;
+
+		if (grab_bb(ibuf, sample->ip, sample->ip + 16,
+			    machine, thread, &is64bit, &cpumode, false) > 0) {
+			sample->insn_len = arch_insn_len((char *)ibuf, 16, is64bit);
+			memcpy(sample->insn, ibuf, sample->insn_len);
+		}
+	}
+
 	if (PRINT_FIELD(INSNLEN))
 		printed += fprintf(fp, " ilen: %d", sample->insn_len);
 	if (PRINT_FIELD(INSN)) {
tools/perf/util/Build +1 -0
index a3de7916fe63..80c05329835a 100644
@@ -14,6 +14,7 @@ libperf-y += kallsyms.o
 libperf-y += levenshtein.o
 libperf-y += llvm-utils.o
 libperf-y += mmap.o
+libperf-y += insnlen.o
 libperf-y += memswap.o
 libperf-y += parse-events.o
 libperf-y += perf_regs.o
tools/perf/util/insnlen.c new file +10 -0
new file mode 100644
index 000000000000..6c126960a7e6
@@ -0,0 +1,10 @@
+#include "perf.h"
+#include "insnlen.h"
+
+/* Fallback for architectures not supporting this */
+__weak int arch_insn_len(char *buf __maybe_unused,
+			 int len __maybe_unused,
+			 int is64bit __maybe_unused)
+{
+	return 0;
+}
tools/perf/util/insnlen.h new file +6 -0
new file mode 100644
index 000000000000..289877dff89d
@@ -0,0 +1,6 @@
+#ifndef INSNLEN_H
+#define INSNLEN_H 1
+
+int arch_insn_len(char *buf, int len, int is64bit);
+
+#endif
Signature · 1 line
2.13.6
author
#3 lore raw

PATCH 3/12 perf, tools: Support storing additional data in strlist

In reply to Andi Kleen

From: Andi Kleen <[email protected]>

Add a configurable node size to strlist, which allows users to store additional data in a str_node. Also add a new interface to add a new strlist node, and return the node, so additional data can be added.

---
 tools/perf/util/rblist.c  | 16 ++++++++++++++--
 tools/perf/util/rblist.h  |  2 ++
 tools/perf/util/strlist.c | 15 ++++++++++++++-
 tools/perf/util/strlist.h |  8 ++++++++
 4 files changed, 38 insertions(+), 3 deletions(-)
tools/perf/util/rblist.c +14 -2
index 0dfe27d99458..fa221e6b0932 100644
@@ -11,11 +11,13 @@
 
 #include "rblist.h"
 
-int rblist__add_node(struct rblist *rblist, const void *new_entry)
+int rblist__add_node_ptr(struct rblist *rblist, const void *new_entry,
+		       struct rb_node **nodep)
 {
 	struct rb_node **p = &rblist->entries.rb_node;
 	struct rb_node *parent = NULL, *new_node;
 
+	*nodep = NULL;
 	while (*p != NULL) {
 		int rc;
 
@@ -26,13 +28,16 @@ int rblist__add_node(struct rblist *rblist, const void *new_entry)
 			p = &(*p)->rb_left;
 		else if (rc < 0)
 			p = &(*p)->rb_right;
-		else
+		else {
+			*nodep = parent;
 			return -EEXIST;
+		}
 	}
 
 	new_node = rblist->node_new(rblist, new_entry);
 	if (new_node == NULL)
 		return -ENOMEM;
+	*nodep = new_node;
 
 	rb_link_node(new_node, parent, p);
 	rb_insert_color(new_node, &rblist->entries);
@@ -41,6 +46,13 @@ int rblist__add_node(struct rblist *rblist, const void *new_entry)
 	return 0;
 }
 
+int rblist__add_node(struct rblist *rblist, const void *new_entry)
+{
+	struct rb_node *nd;
+
+	return rblist__add_node_ptr(rblist, new_entry, &nd);
+}
+
 void rblist__remove_node(struct rblist *rblist, struct rb_node *rb_node)
 {
 	rb_erase(rb_node, &rblist->entries);
tools/perf/util/rblist.h +2 -0
index 4c8638a22571..2941e4295f63 100644
@@ -31,6 +31,8 @@ struct rblist {
 void rblist__init(struct rblist *rblist);
 void rblist__delete(struct rblist *rblist);
 int rblist__add_node(struct rblist *rblist, const void *new_entry);
+int rblist__add_node_ptr(struct rblist *rblist, const void *new_entry,
+			 struct rb_node **nodep);
 void rblist__remove_node(struct rblist *rblist, struct rb_node *rb_node);
 struct rb_node *rblist__find(struct rblist *rblist, const void *entry);
 struct rb_node *rblist__findnew(struct rblist *rblist, const void *entry);
tools/perf/util/strlist.c +14 -1
index 9de5434bb49e..68ef21c3797c 100644
@@ -18,7 +18,7 @@ struct rb_node *strlist__node_new(struct rblist *rblist, const void *entry)
 	const char *s = entry;
 	struct rb_node *rc = NULL;
 	struct strlist *strlist = container_of(rblist, struct strlist, rblist);
-	struct str_node *snode = malloc(sizeof(*snode));
+	struct str_node *snode = malloc(strlist->node_size);
 
 	if (snode != NULL) {
 		if (strlist->dupstr) {
@@ -66,6 +66,14 @@ int strlist__add(struct strlist *slist, const char *new_entry)
 	return rblist__add_node(&slist->rblist, new_entry);
 }
 
+struct str_node *strlist__add_node(struct strlist *slist, const char *new_entry)
+{
+	struct rb_node *nd;
+
+	rblist__add_node_ptr(&slist->rblist, new_entry, &nd);
+	return container_of(nd, struct str_node, rb_node);
+}
+
 int strlist__load(struct strlist *slist, const char *filename)
 {
 	char entry[1024];
@@ -165,11 +173,15 @@ struct strlist *strlist__new(const char *list, const struct strlist_config *conf
 		bool dupstr = true;
 		bool file_only = false;
 		const char *dirname = NULL;
+		size_t node_size = sizeof(struct str_node);
 
 		if (config) {
 			dupstr = !config->dont_dupstr;
 			dirname = config->dirname;
 			file_only = config->file_only;
+			node_size = config->node_size;
+			if (!node_size)
+				node_size = sizeof(struct str_node);
 		}
 
 		rblist__init(&slist->rblist);
@@ -179,6 +191,7 @@ struct strlist *strlist__new(const char *list, const struct strlist_config *conf
 
 		slist->dupstr	 = dupstr;
 		slist->file_only = file_only;
+		slist->node_size = node_size;
 
 		if (list && strlist__parse_list(slist, list, dirname) != 0)
 			goto out_error;
tools/perf/util/strlist.h +8 -0
index d58f1e08b170..fd407e11e124 100644
@@ -16,25 +16,33 @@ struct strlist {
 	struct rblist rblist;
 	bool	      dupstr;
 	bool	      file_only;
+	size_t	      node_size;
 };
 
 /*
  * @file_only: When dirname is present, only consider entries as filenames,
  *             that should not be added to the list if dirname/entry is not
  *             found
+ * @node_size: Allocate extra space after str_node which can be used for other
+ *	       data. This is the complete size including str_node
  */
 struct strlist_config {
 	bool dont_dupstr;
 	bool file_only;
 	const char *dirname;
+	size_t node_size;
 };
 
+#define STRLIST_CONFIG_DEFAULT \
+	{ false, false, NULL, sizeof(struct str_node) }
+
 struct strlist *strlist__new(const char *slist, const struct strlist_config *config);
 void strlist__delete(struct strlist *slist);
 
 void strlist__remove(struct strlist *slist, struct str_node *sn);
 int strlist__load(struct strlist *slist, const char *filename);
 int strlist__add(struct strlist *slist, const char *str);
+struct str_node *strlist__add_node(struct strlist *slist, const char *str);
 
 struct str_node *strlist__entry(const struct strlist *slist, unsigned int idx);
 struct str_node *strlist__find(struct strlist *slist, const char *entry);
Signature · 1 line
2.13.6
#4 lore raw

Re: [PATCH 03/12] perf, tools: Support storing additional data in strlist

Quoted 93 lines · On Mon, 27 Nov 2017 16:23:12 -0800 Andi Kleen <[email protected]> wrote:

From: Andi Kleen <[email protected]>

Add a configurable node size to strlist, which allows users to store additional data in a str_node. Also add a new interface to add a new strlist node, and return the node, so additional data can be added.

---
 tools/perf/util/rblist.c  | 16 ++++++++++++++--
 tools/perf/util/rblist.h  |  2 ++
 tools/perf/util/strlist.c | 15 ++++++++++++++-
 tools/perf/util/strlist.h |  8 ++++++++
 4 files changed, 38 insertions(+), 3 deletions(-)
tools/perf/util/rblist.c +14 -2
index 0dfe27d99458..fa221e6b0932 100644
@@ -11,11 +11,13 @@
 
 #include "rblist.h"
 
-int rblist__add_node(struct rblist *rblist, const void *new_entry)
+int rblist__add_node_ptr(struct rblist *rblist, const void *new_entry,
+		       struct rb_node **nodep)
 {
 	struct rb_node **p = &rblist->entries.rb_node;
 	struct rb_node *parent = NULL, *new_node;
 
+	*nodep = NULL;
 	while (*p != NULL) {
 		int rc;
 
@@ -26,13 +28,16 @@ int rblist__add_node(struct rblist *rblist, const void *new_entry)
 			p = &(*p)->rb_left;
 		else if (rc < 0)
 			p = &(*p)->rb_right;
-		else
+		else {
+			*nodep = parent;
 			return -EEXIST;
+		}
 	}
 
 	new_node = rblist->node_new(rblist, new_entry);
 	if (new_node == NULL)
 		return -ENOMEM;
+	*nodep = new_node;
 
 	rb_link_node(new_node, parent, p);
 	rb_insert_color(new_node, &rblist->entries);
@@ -41,6 +46,13 @@ int rblist__add_node(struct rblist *rblist, const void *new_entry)
 	return 0;
 }
 
+int rblist__add_node(struct rblist *rblist, const void *new_entry)
+{
+	struct rb_node *nd;
+
+	return rblist__add_node_ptr(rblist, new_entry, &nd);
+}
+
 void rblist__remove_node(struct rblist *rblist, struct rb_node *rb_node)
 {
 	rb_erase(rb_node, &rblist->entries);
tools/perf/util/rblist.h +2 -0
index 4c8638a22571..2941e4295f63 100644
@@ -31,6 +31,8 @@ struct rblist {
 void rblist__init(struct rblist *rblist);
 void rblist__delete(struct rblist *rblist);
 int rblist__add_node(struct rblist *rblist, const void *new_entry);
+int rblist__add_node_ptr(struct rblist *rblist, const void *new_entry,
+			 struct rb_node **nodep);
 void rblist__remove_node(struct rblist *rblist, struct rb_node *rb_node);
 struct rb_node *rblist__find(struct rblist *rblist, const void *entry);
 struct rb_node *rblist__findnew(struct rblist *rblist, const void *entry);
tools/perf/util/strlist.c +1 -1
index 9de5434bb49e..68ef21c3797c 100644
@@ -18,7 +18,7 @@ struct rb_node *strlist__node_new(struct rblist *rblist, const void *entry)
 	const char *s = entry;
 	struct rb_node *rc = NULL;
 	struct strlist *strlist = container_of(rblist, struct strlist, rblist);
-	struct str_node *snode = malloc(sizeof(*snode));
+	struct str_node *snode = malloc(strlist->node_size);
 
 	if (snode != NULL) {
 		if (strlist->dupstr) {

tools/perf/util/strlist.c int strlist__add(struct strlist *slist, const char *new_entry) · line 75

Quoted 10 lines
tools/perf/util/strlist.c +7 -0
@@ -66,6 +66,14 @@ int strlist__add(struct strlist *slist, const char *new_entry)
 	return rblist__add_node(&slist->rblist, new_entry);
 }
 
+struct str_node *strlist__add_node(struct strlist *slist, const char *new_entry)
+{
+	struct rb_node *nd;
+
+	rblist__add_node_ptr(&slist->rblist, new_entry, &nd);
+	return container_of(nd, struct str_node, rb_node);
+}

It should check the result of rblist__add_node_ptr() and return NULL if an error.

Quoted 7 lines
tools/perf/util/strlist.c +1 -0
+
 int strlist__load(struct strlist *slist, const char *filename)
 {
 	char entry[1024];
@@ -165,11 +173,15 @@ struct strlist *strlist__new(const char *list, const struct strlist_config *conf
 		bool dupstr = true;
 		bool file_only = false;

tools/perf/util/strlist.c struct strlist *strlist__new(const char *list, const struct strlist_config *conf · line 184

Quoted 10 lines
tools/perf/util/strlist.c +4 -0
 		const char *dirname = NULL;
+		size_t node_size = sizeof(struct str_node);
 
 		if (config) {
 			dupstr = !config->dont_dupstr;
 			dirname = config->dirname;
 			file_only = config->file_only;
+			node_size = config->node_size;
+			if (!node_size)
+				node_size = sizeof(struct str_node);

This is dangerous, node_size can be smaller than sizeof(struct str_node). Instead, why don't we use "data_size" and set additional data size?

Also, we can add "u8 data[0]" at the end of struct str_node, so that user can easily access the appended data.

Quoted 36 lines
tools/perf/util/strlist.c +1 -0
 		}
 
 		rblist__init(&slist->rblist);
@@ -179,6 +191,7 @@ struct strlist *strlist__new(const char *list, const struct strlist_config *conf
 
 		slist->dupstr	 = dupstr;
 		slist->file_only = file_only;
+		slist->node_size = node_size;
 
 		if (list && strlist__parse_list(slist, list, dirname) != 0)
 			goto out_error;
tools/perf/util/strlist.h +4 -0
index d58f1e08b170..fd407e11e124 100644
@@ -16,25 +16,33 @@ struct strlist {
 	struct rblist rblist;
 	bool	      dupstr;
 	bool	      file_only;
+	size_t	      node_size;
 };
 
 /*
  * @file_only: When dirname is present, only consider entries as filenames,
  *             that should not be added to the list if dirname/entry is not
  *             found
+ * @node_size: Allocate extra space after str_node which can be used for other
+ *	       data. This is the complete size including str_node
  */
 struct strlist_config {
 	bool dont_dupstr;
 	bool file_only;
 	const char *dirname;
+	size_t node_size;
 };
 

tools/perf/util/strlist.h struct strlist { · line 45

Quoted 10 lines
tools/perf/util/strlist.h +4 -0
+#define STRLIST_CONFIG_DEFAULT \
+	{ false, false, NULL, sizeof(struct str_node) }
+
 struct strlist *strlist__new(const char *slist, const struct strlist_config *config);
 void strlist__delete(struct strlist *slist);
 
 void strlist__remove(struct strlist *slist, struct str_node *sn);
 int strlist__load(struct strlist *slist, const char *filename);
 int strlist__add(struct strlist *slist, const char *str);
+struct str_node *strlist__add_node(struct strlist *slist, const char *str);

Hmm, I see this is much more efficient, but from the programming point of view, we can also use the combination of strlist__add() and strlist__find() for that purpose.

BTW, what happen if we already have same string on strlist?

Thank you,

Quoted 6 lines
tools/perf/util/strlist.h +0 -0
 struct str_node *strlist__entry(const struct strlist *slist, unsigned int idx);
 struct str_node *strlist__find(struct strlist *slist, const char *entry);
Signature · 1 line
2.13.6
Signature · 1 line
Masami Hiramatsu <[email protected]>
author
#5 lore raw

PATCH 4/12 perf, tools: Store variable name and register for dwarf variable lists

In reply to Andi Kleen

From: Andi Kleen <[email protected]>

Extend the strlist returned by debuginfo__find_available_vars_at to also directly include the variable name and the location of the resolved variables in each node. This makes it easier to use for callers that parse the output instead of just printing it.

---
 tools/perf/util/probe-finder.c | 29 +++++++++++++++++++++++++----
 tools/perf/util/probe-finder.h |  7 +++++++
 2 files changed, 32 insertions(+), 4 deletions(-)
tools/perf/util/probe-finder.c +25 -4
index a5731de0e5eb..0149428d453e 100644
@@ -1368,9 +1368,12 @@ static int collect_variables_cb(Dwarf_Die *die_mem, void *data)
 	tag = dwarf_tag(die_mem);
 	if (tag == DW_TAG_formal_parameter ||
 	    tag == DW_TAG_variable) {
+		struct probe_trace_arg ta;
+
+		memset(&ta, 0, sizeof(struct probe_trace_arg));
 		ret = convert_variable_location(die_mem, af->pf.addr,
 						af->pf.fb_ops, &af->pf.sp_die,
-						af->pf.machine, NULL);
+						af->pf.machine, &ta);
 		if (ret == 0 || ret == -ERANGE) {
 			int ret2;
 			bool externs = !af->child;
@@ -1400,8 +1403,23 @@ static int collect_variables_cb(Dwarf_Die *die_mem, void *data)
 
 			pr_debug("Add new var: %s\n", buf.buf);
 			if (ret2 == 0) {
-				strlist__add(vl->vars,
-					strbuf_detach(&buf, NULL));
+				struct str_node *sn;
+
+				/* Will get confused with shadowed variables */
+				sn = strlist__add_node(vl->vars,
+						       strbuf_detach(&buf, NULL));
+				if (sn) {
+					struct variable_node *vn =
+						container_of(sn, struct variable_node, snode);
+					if (dwarf_diename(die_mem))
+						strlcpy(vn->name, dwarf_diename(die_mem), sizeof(vn->name));
+					else
+						vn->name[0] = 0;
+					if (ta.value)
+						strlcpy(vn->value, ta.value, sizeof(vn->value));
+					else
+						vn->value[0] = 0;
+				}
 			}
 			strbuf_release(&buf);
 		}
@@ -1420,6 +1438,7 @@ static int collect_variables_cb(Dwarf_Die *die_mem, void *data)
 /* Add a found vars into available variables list */
 static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf)
 {
+	static struct strlist_config sconfig = STRLIST_CONFIG_DEFAULT;
 	struct available_var_finder *af =
 			container_of(pf, struct available_var_finder, pf);
 	struct perf_probe_point *pp = &pf->pev->point;
@@ -1427,6 +1446,8 @@ static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf)
 	Dwarf_Die die_mem;
 	int ret;
 
+	sconfig.node_size = sizeof(struct variable_node);
+
 	/* Check number of tevs */
 	if (af->nvls == af->max_vls) {
 		pr_warning("Too many( > %d) probe point found.\n", af->max_vls);
@@ -1444,7 +1465,7 @@ static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf)
 		 vl->point.offset);
 
 	/* Find local variables */
-	vl->vars = strlist__new(NULL, NULL);
+	vl->vars = strlist__new(NULL, &sconfig);
 	if (vl->vars == NULL)
 		return -ENOMEM;
 	af->child = true;
tools/perf/util/probe-finder.h +7 -0
index 16252980ff00..6368e95a5d16 100644
@@ -4,6 +4,7 @@
 
 #include <stdbool.h>
 #include "intlist.h"
+#include "strlist.h"
 #include "probe-event.h"
 #include "sane_ctype.h"
 
@@ -117,6 +118,12 @@ struct line_finder {
 	int			found;
 };
 
+struct variable_node {
+	struct str_node		snode;
+	char			value[64];
+	char			name[256];
+};
+
 #endif /* HAVE_DWARF_SUPPORT */
 
 #endif /*_PROBE_FINDER_H */
Signature · 1 line
2.13.6
author
#6 lore raw

PATCH 5/12 perf, tools, probe: Print location for resolved variables

In reply to Andi Kleen

From: Andi Kleen <[email protected]>

Print the location, e.g. the register, for resolved variables with perf probe -V. This is useful for debugging, and manually making sense of disassembly. I also have some scripts which can make use of this information.

Before:

% perf probe -x ./tsrc/tstruct -V main+20
Available variables at main+20
@<main+20>
struct str* xp

After:

% perf probe -x ./tsrc/tstruct -V main+20
Available variables at main+20
@<main+20>
struct str* xp %ax

---
 tools/perf/util/probe-finder.c | 5 +++++
 1 file changed, 5 insertions(+)
tools/perf/util/probe-finder.c +5 -0
index 0149428d453e..699f29d8a28e 100644
@@ -1369,6 +1369,7 @@ static int collect_variables_cb(Dwarf_Die *die_mem, void *data)
 	if (tag == DW_TAG_formal_parameter ||
 	    tag == DW_TAG_variable) {
 		struct probe_trace_arg ta;
+		struct probe_trace_arg_ref *ref;
 
 		memset(&ta, 0, sizeof(struct probe_trace_arg));
 		ret = convert_variable_location(die_mem, af->pf.addr,
@@ -1401,6 +1402,10 @@ static int collect_variables_cb(Dwarf_Die *die_mem, void *data)
 							die_mem, &buf);
 			}
 
+			strbuf_addf(&buf, "\t%s", ta.value);
+			for (ref = ta.ref; ref; ref = ref->next)
+				strbuf_addf(&buf, " off %ld", ref->offset);
+
 			pr_debug("Add new var: %s\n", buf.buf);
 			if (ret2 == 0) {
 				struct str_node *sn;
Signature · 1 line
2.13.6
#7 lore raw

Re: [PATCH 05/12] perf, tools, probe: Print location for resolved variables

Quoted 14 lines · On Mon, 27 Nov 2017 16:23:14 -0800 Andi Kleen <[email protected]> wrote:

From: Andi Kleen <[email protected]>

Print the location, e.g. the register, for resolved variables with perf probe -V. This is useful for debugging, and manually making sense of disassembly. I also have some scripts which can make use of this information.

Before:

% perf probe -x ./tsrc/tstruct -V main+20
Available variables at main+20
@<main+20>
struct str* xp

Quoted 7 lines

After:

% perf probe -x ./tsrc/tstruct -V main+20
Available variables at main+20
@<main+20>
struct str* xp %ax

Sounds good :)
For clearly separating it from variable name,
I would like to see as below;

% perf probe -x ./tsrc/tstruct -V main+20
Available variables at main+20
@<main+20>
struct str* xp // %ax

Thank you,

Quoted 31 lines
---
 tools/perf/util/probe-finder.c | 5 +++++
 1 file changed, 5 insertions(+)
tools/perf/util/probe-finder.c +5 -0
index 0149428d453e..699f29d8a28e 100644
@@ -1369,6 +1369,7 @@ static int collect_variables_cb(Dwarf_Die *die_mem, void *data)
 	if (tag == DW_TAG_formal_parameter ||
 	    tag == DW_TAG_variable) {
 		struct probe_trace_arg ta;
+		struct probe_trace_arg_ref *ref;
 
 		memset(&ta, 0, sizeof(struct probe_trace_arg));
 		ret = convert_variable_location(die_mem, af->pf.addr,
@@ -1401,6 +1402,10 @@ static int collect_variables_cb(Dwarf_Die *die_mem, void *data)
 							die_mem, &buf);
 			}
 
+			strbuf_addf(&buf, "\t%s", ta.value);
+			for (ref = ta.ref; ref; ref = ref->next)
+				strbuf_addf(&buf, " off %ld", ref->offset);
+
 			pr_debug("Add new var: %s\n", buf.buf);
 			if (ret2 == 0) {
 				struct str_node *sn;
Signature · 1 line
2.13.6
Signature · 1 line
Masami Hiramatsu <[email protected]>
author
#8 lore raw

PATCH 6/12 perf, tools, probe: Support a quiet argument for debug info open

In reply to Andi Kleen

From: Andi Kleen <[email protected]>

Add a extra quiet argument to the debug info open / probe finder code that allows perf script to make them quieter. Otherwise we may end up with too many error messages when lots of instructions fail debug info parsing.

---
 tools/perf/util/probe-event.c  |  4 ++--
 tools/perf/util/probe-finder.c | 19 ++++++++++++-------
 tools/perf/util/probe-finder.h |  5 ++++-
 3 files changed, 18 insertions(+), 10 deletions(-)
tools/perf/util/probe-event.c +2 -2
index b7aaf9b2294d..2f9469e862fb 100644
@@ -1071,12 +1071,12 @@ static int show_available_vars_at(struct debuginfo *dinfo,
 		return -EINVAL;
 	pr_debug("Searching variables at %s\n", buf);
 
-	ret = debuginfo__find_available_vars_at(dinfo, pev, &vls);
+	ret = debuginfo__find_available_vars_at(dinfo, pev, &vls, false);
 	if (!ret) {  /* Not found, retry with an alternative */
 		ret = get_alternative_probe_event(dinfo, pev, &tmp);
 		if (!ret) {
 			ret = debuginfo__find_available_vars_at(dinfo, pev,
-								&vls);
+								&vls, false);
 			/* Release the old probe_point */
 			clear_perf_probe_point(&tmp);
 		}
tools/perf/util/probe-finder.c +12 -7
index 699f29d8a28e..137b2fe71838 100644
@@ -685,12 +685,14 @@ static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf)
 	if (!die_is_func_def(sc_die)) {
 		if (!die_find_realfunc(&pf->cu_die, pf->addr, &pf->sp_die)) {
 			if (die_find_tailfunc(&pf->cu_die, pf->addr, &pf->sp_die)) {
-				pr_warning("Ignoring tail call from %s\n",
+				if (!pf->quiet)
+					pr_warning("Ignoring tail call from %s\n",
 						dwarf_diename(&pf->sp_die));
 				return 0;
 			} else {
-				pr_warning("Failed to find probe point in any "
-					   "functions.\n");
+				if (!pf->quiet)
+					pr_warning("Failed to find probe point in any "
+						   "functions.\n");
 				return -ENOENT;
 			}
 		}
@@ -708,8 +710,9 @@ static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf)
 		if ((dwarf_cfi_addrframe(pf->cfi_eh, pf->addr, &frame) != 0 &&
 		     (dwarf_cfi_addrframe(pf->cfi_dbg, pf->addr, &frame) != 0)) ||
 		    dwarf_frame_cfa(frame, &pf->fb_ops, &nops) != 0) {
-			pr_warning("Failed to get call frame on 0x%jx\n",
-				   (uintmax_t)pf->addr);
+			if (!pf->quiet)
+				pr_warning("Failed to get call frame on 0x%jx\n",
+					   (uintmax_t)pf->addr);
 			free(frame);
 			return -ENOENT;
 		}
@@ -1499,10 +1502,12 @@ static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf)
  */
 int debuginfo__find_available_vars_at(struct debuginfo *dbg,
 				      struct perf_probe_event *pev,
-				      struct variable_list **vls)
+				      struct variable_list **vls,
+				      bool be_quiet)
 {
 	struct available_var_finder af = {
-			.pf = {.pev = pev, .callback = add_available_vars},
+			.pf = {.pev = pev, .callback = add_available_vars,
+			       .quiet = be_quiet},
 			.mod = dbg->mod,
 			.max_vls = probe_conf.max_probes};
 	int ret;
tools/perf/util/probe-finder.h +4 -1
index 6368e95a5d16..abcb2262ea72 100644
@@ -57,7 +57,8 @@ int debuginfo__find_line_range(struct debuginfo *dbg, struct line_range *lr);
 /* Find available variables */
 int debuginfo__find_available_vars_at(struct debuginfo *dbg,
 				      struct perf_probe_event *pev,
-				      struct variable_list **vls);
+				      struct variable_list **vls,
+				      bool quiet);
 
 /* Find a src file from a DWARF tag path */
 int get_real_path(const char *raw_path, const char *comp_dir,
@@ -88,6 +89,8 @@ struct probe_finder {
 	unsigned int		machine;	/* Target machine arch */
 	struct perf_probe_arg	*pvar;		/* Current target variable */
 	struct probe_trace_arg	*tvar;		/* Current result variable */
+
+	bool			quiet;		/* Avoid warnings */
 };
 
 struct trace_event_finder {
Signature · 1 line
2.13.6
#9 lore raw

Re: [PATCH 06/12] perf, tools, probe: Support a quiet argument for debug info open

Quoted 6 lines · On Mon, 27 Nov 2017 16:23:15 -0800 Andi Kleen <[email protected]> wrote:

From: Andi Kleen <[email protected]>

Add a extra quiet argument to the debug info open / probe finder code that allows perf script to make them quieter. Otherwise we may end up with too many error messages when lots of instructions fail debug info parsing.

IMHO, this kind of simple suppress warning message would better be done with pr_* implementation (or its macro) since there maybe new message added or other function which has warning messages can be called in the future.

Thank you,

Quoted 102 lines
---
 tools/perf/util/probe-event.c  |  4 ++--
 tools/perf/util/probe-finder.c | 19 ++++++++++++-------
 tools/perf/util/probe-finder.h |  5 ++++-
 3 files changed, 18 insertions(+), 10 deletions(-)
tools/perf/util/probe-event.c +2 -2
index b7aaf9b2294d..2f9469e862fb 100644
@@ -1071,12 +1071,12 @@ static int show_available_vars_at(struct debuginfo *dinfo,
 		return -EINVAL;
 	pr_debug("Searching variables at %s\n", buf);
 
-	ret = debuginfo__find_available_vars_at(dinfo, pev, &vls);
+	ret = debuginfo__find_available_vars_at(dinfo, pev, &vls, false);
 	if (!ret) {  /* Not found, retry with an alternative */
 		ret = get_alternative_probe_event(dinfo, pev, &tmp);
 		if (!ret) {
 			ret = debuginfo__find_available_vars_at(dinfo, pev,
-								&vls);
+								&vls, false);
 			/* Release the old probe_point */
 			clear_perf_probe_point(&tmp);
 		}
tools/perf/util/probe-finder.c +12 -7
index 699f29d8a28e..137b2fe71838 100644
@@ -685,12 +685,14 @@ static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf)
 	if (!die_is_func_def(sc_die)) {
 		if (!die_find_realfunc(&pf->cu_die, pf->addr, &pf->sp_die)) {
 			if (die_find_tailfunc(&pf->cu_die, pf->addr, &pf->sp_die)) {
-				pr_warning("Ignoring tail call from %s\n",
+				if (!pf->quiet)
+					pr_warning("Ignoring tail call from %s\n",
 						dwarf_diename(&pf->sp_die));
 				return 0;
 			} else {
-				pr_warning("Failed to find probe point in any "
-					   "functions.\n");
+				if (!pf->quiet)
+					pr_warning("Failed to find probe point in any "
+						   "functions.\n");
 				return -ENOENT;
 			}
 		}
@@ -708,8 +710,9 @@ static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf)
 		if ((dwarf_cfi_addrframe(pf->cfi_eh, pf->addr, &frame) != 0 &&
 		     (dwarf_cfi_addrframe(pf->cfi_dbg, pf->addr, &frame) != 0)) ||
 		    dwarf_frame_cfa(frame, &pf->fb_ops, &nops) != 0) {
-			pr_warning("Failed to get call frame on 0x%jx\n",
-				   (uintmax_t)pf->addr);
+			if (!pf->quiet)
+				pr_warning("Failed to get call frame on 0x%jx\n",
+					   (uintmax_t)pf->addr);
 			free(frame);
 			return -ENOENT;
 		}
@@ -1499,10 +1502,12 @@ static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf)
  */
 int debuginfo__find_available_vars_at(struct debuginfo *dbg,
 				      struct perf_probe_event *pev,
-				      struct variable_list **vls)
+				      struct variable_list **vls,
+				      bool be_quiet)
 {
 	struct available_var_finder af = {
-			.pf = {.pev = pev, .callback = add_available_vars},
+			.pf = {.pev = pev, .callback = add_available_vars,
+			       .quiet = be_quiet},
 			.mod = dbg->mod,
 			.max_vls = probe_conf.max_probes};
 	int ret;
tools/perf/util/probe-finder.h +4 -1
index 6368e95a5d16..abcb2262ea72 100644
@@ -57,7 +57,8 @@ int debuginfo__find_line_range(struct debuginfo *dbg, struct line_range *lr);
 /* Find available variables */
 int debuginfo__find_available_vars_at(struct debuginfo *dbg,
 				      struct perf_probe_event *pev,
-				      struct variable_list **vls);
+				      struct variable_list **vls,
+				      bool quiet);
 
 /* Find a src file from a DWARF tag path */
 int get_real_path(const char *raw_path, const char *comp_dir,
@@ -88,6 +89,8 @@ struct probe_finder {
 	unsigned int		machine;	/* Target machine arch */
 	struct perf_probe_arg	*pvar;		/* Current target variable */
 	struct probe_trace_arg	*tvar;		/* Current result variable */
+
+	bool			quiet;		/* Avoid warnings */
 };
 
 struct trace_event_finder {
Signature · 1 line
2.13.6
Signature · 1 line
Masami Hiramatsu <[email protected]>
author
#10 lore raw

Re: [PATCH 06/12] perf, tools, probe: Support a quiet argument for debug info open

Quoted 10 lines · Masami Hiramatsu
Quoted 6 lines · On Mon, 27 Nov 2017 16:23:15 -0800 Andi Kleen <[email protected]> wrote:

From: Andi Kleen <[email protected]>

Add a extra quiet argument to the debug info open / probe finder code that allows perf script to make them quieter. Otherwise we may end up with too many error messages when lots of instructions fail debug info parsing.

Quoted 4 lines

IMHO, this kind of simple suppress warning message would better be done with pr_* implementation (or its macro) since there maybe new message added or other function which has warning messages can be called in the future.

Do you really mean adding a special pr_ / global just for this case?

Seems less clean to me, but I can do it.

-Andi

#11 lore raw

Re: [PATCH 06/12] perf, tools, probe: Support a quiet argument for debug info open

Quoted 11 lines · On Tue, 28 Nov 2017 19:39:43 -0800 Andi Kleen <[email protected]> wrote:
Quoted 10 lines · Masami Hiramatsu
Quoted 6 lines · On Mon, 27 Nov 2017 16:23:15 -0800 Andi Kleen <[email protected]> wrote:

From: Andi Kleen <[email protected]>

Add a extra quiet argument to the debug info open / probe finder code that allows perf script to make them quieter. Otherwise we may end up with too many error messages when lots of instructions fail debug info parsing.

Quoted 6 lines
Quoted 4 lines

IMHO, this kind of simple suppress warning message would better be done with pr_* implementation (or its macro) since there maybe new message added or other function which has warning messages can be called in the future.

Do you really mean adding a special pr_ / global just for this case?

I thought that we could control it by changing "verbose" global variables. Something like inc_verbose() & dec_verbose() macros will help us to suppress warning/debug messages temporary.

Thank you,

Quoted 4 lines

Seems less clean to me, but I can do it.

-Andi

Signature · 1 line
Masami Hiramatsu <[email protected]>
author
#12 lore raw

PATCH 7/12 perf, tools, script: Resolve variable names for registers

In reply to Andi Kleen

From: Andi Kleen <[email protected]>

Add a new iregvals output that uses the dwarf decoding code in perf probe to resolve registers to names based on dwarf debuginfo.

This allows to trace the values of variables which are stored in registers, and sampled by perf.

We also print the type.

This builds on top of Masami Hiramatsu's perf probe code, which implements all the difficult dwarf magic.
But we use it for a 'reverse lookup' to figure out the variable names

We use the perf probe code to generate a list of variables with their registers at the sample point, and then print any variable for which we have a valid register.

Note when testing please compile the program with -O2 -g. Unoptimized code stores variables generally on the stack, which we cannot decode with this. -g is needed for the debug information

For example to track the first two arguments passed in registers on x86-64:

% cat targ.c
% gcc -O2 -g -o targ targ.c
volatile c;

__attribute__((noinline)) f2(int a, int b)

{
	c = a / b;
}

__attribute__((noinline)) f1(int a, int b)

{
	f2(a, b);
	f2(b, a);
}
main()
{
	int i;
	for (i = 0; i < 5000000; i++)
		f1(1, 2);
}

% perf record -Idi,si ./targ
% perf script -F +iregvals
...

    targ  8584 169763.761843:    2091795 cycles:ppp:            40041a main (targ)
    targ  8584 169763.762520:    1913932 cycles:ppp:            400534 f1 (targ) { b = 0x2, int }  { a = 0x1, int }
    targ  8584 169763.763141:    1638913 cycles:ppp:            400523 f2 (targ) { b = 0x1, int }  { a = 0x2, int }
    targ  8584 169763.763672:    1516522 cycles:ppp:            400522 f2 (targ) { b = 0x1, int }  { a = 0x2, int }
    targ  8584 169763.764165:    1335501 cycles:ppp:            400523 f2 (targ) { b = 0x1, int }  { a = 0x2, int }
    targ  8584 169763.764598:    1253289 cycles:ppp:            400522 f2 (targ) { b = 0x2, int }  { a = 0x1, int }
    targ  8584 169763.765005:    1135131 cycles:ppp:            400534 f1 (targ) { b = 0x2, int }  { a = 0x1, int }
    targ  8584 169763.765373:    1080325 cycles:ppp:            400522 f2 (targ) { b = 0x2, int }  { a = 0x1, int }
    targ  8584 169763.765724:    1036999 cycles:ppp:            400522 f2 (targ) { b = 0x1, int }  { a = 0x2, int }
    targ  8584 169763.766061:     971213 cycles:ppp:            400534 f1 (targ) { b = 0x2, int }  { a = 0x1, int }

It works for other variables too, as long as they are in integer registers and the compiler generated correct dwarf debug information.

---
 tools/perf/Documentation/perf-script.txt |   6 +-
 tools/perf/builtin-script.c              |  63 ++++++++++++++++-
 tools/perf/util/Build                    |   1 +
 tools/perf/util/dwarf-sample.c           | 113 +++++++++++++++++++++++++++++++
 tools/perf/util/dwarf-sample.h           |  13 ++++
 tools/perf/util/probe-event.c            |   2 +-
 tools/perf/util/probe-event.h            |   3 +
 7 files changed, 196 insertions(+), 5 deletions(-)
 create mode 100644 tools/perf/util/dwarf-sample.c
 create mode 100644 tools/perf/util/dwarf-sample.h
tools/perf/Documentation/perf-script.txt +5 -1
index 2811fcf684cb..e296944cc03f 100644
@@ -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.
+        brstackoff, callindent, insn, insnlen, synth, phys_addr, iregvals.
         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
@@ -217,6 +217,10 @@ OPTIONS
 
 	The brstackoff field will print an offset into a specific dso/binary.
 
+	With iregvals perf script uses dwarf debug information to map sampled register values
+	(with perf record -I ...) to their symbolic names in the program. This requires availability
+	of debug information in the binaries.
+
 -k::
 --vmlinux=<file>::
         vmlinux pathname
tools/perf/builtin-script.c +60 -3

117 lines not on this page · raw message

tools/perf/util/Build +1 -0
index 80c05329835a..361db92a4bfd 100644
@@ -42,6 +42,7 @@ libperf-y += callchain.o
 libperf-y += values.o
 libperf-y += debug.o
 libperf-y += machine.o
+libperf-y += dwarf-sample.o
 libperf-y += map.o
 libperf-y += pstack.o
 libperf-y += session.o
tools/perf/util/dwarf-sample.c new file +113 -0

113 lines not on this page · raw message

tools/perf/util/dwarf-sample.h new file +13 -0
new file mode 100644
index 000000000000..39ec1a6c45d3
@@ -0,0 +1,13 @@
+#ifndef DWARF_SAMPLE_H
+#define DWARF_SAMPLE_H 1
+
+#include "probe-finder.h"
+
+int dwarf_resolve_sample(struct perf_sample *sample,
+			 struct thread *thread,
+			 struct variable_list **vls);
+void dwarf_free_varlist(struct variable_list *vls, int dret);
+int dwarf_varlist_find_reg(struct variable_list *vls, int dret, int r,
+			   char **name, char **type);
+
+#endif
tools/perf/util/probe-event.c +1 -1
index 2f9469e862fb..4ef6ee967468 100644
@@ -507,7 +507,7 @@ static struct debuginfo *open_debuginfo(const char *module, struct nsinfo *nsi,
 static struct debuginfo *debuginfo_cache;
 static char *debuginfo_cache_path;
 
-static struct debuginfo *debuginfo_cache__open(const char *module, bool silent)
+struct debuginfo *debuginfo_cache__open(const char *module, bool silent)
 {
 	const char *path = module;
 
tools/perf/util/probe-event.h +3 -0
index 45b14f020558..e694a3680e1b 100644
@@ -190,4 +190,7 @@ struct map *get_target_map(const char *target, struct nsinfo *nsi, bool user);
 void arch__post_process_probe_trace_events(struct perf_probe_event *pev,
 					   int ntevs);
 
+struct debuginfo;
+struct debuginfo *debuginfo_cache__open(const char *module, bool silent);
+
 #endif /*_PROBE_EVENT_H */
Signature · 1 line
2.13.6
author
#13 lore raw

PATCH 8/12 perf, tools: Always print probe finder warnings with -v

In reply to Andi Kleen

From: Andi Kleen <[email protected]>

Normally perf script debug info resolution doesn't print warnings, but allow -v to override that. Useful for finding out why things don't work.

---
 tools/perf/util/probe-event.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
tools/perf/util/probe-event.c +2 -2
index 4ef6ee967468..fb5031ac24a2 100644
@@ -482,7 +482,7 @@ static struct debuginfo *open_debuginfo(const char *module, struct nsinfo *nsi,
 					strcpy(reason, "(unknown)");
 			} else
 				dso__strerror_load(dso, reason, STRERR_BUFSIZE);
-			if (!silent)
+			if (!silent || verbose)
 				pr_err("Failed to find the path for %s: %s\n",
 					module ?: "kernel", reason);
 			return NULL;
@@ -491,7 +491,7 @@ static struct debuginfo *open_debuginfo(const char *module, struct nsinfo *nsi,
 	}
 	nsinfo__mountns_enter(nsi, &nsc);
 	ret = debuginfo__new(path);
-	if (!ret && !silent) {
+	if (!ret && (!silent || verbose)) {
 		pr_warning("The %s file has no debug information.\n", path);
 		if (!module || !strtailcmp(path, ".ko"))
 			pr_warning("Rebuild with CONFIG_DEBUG_INFO=y, ");
Signature · 1 line
2.13.6
#14 lore raw

Re: [PATCH 08/12] perf, tools: Always print probe finder warnings with -v

Quoted 5 lines · On Mon, 27 Nov 2017 16:23:17 -0800 Andi Kleen <[email protected]> wrote:

From: Andi Kleen <[email protected]>

Normally perf script debug info resolution doesn't print warnings, but allow -v to override that. Useful for finding out why things don't work.

This must be done in call-site, since we don't need it clearly in below case.

----
/* Try to find perf_probe_event with debuginfo */
static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
struct probe_trace_event **tevs)

{
        bool need_dwarf = perf_probe_event_need_dwarf(pev);
        struct perf_probe_point tmp;
        struct debuginfo *dinfo;
        int ntevs, ret = 0;

        dinfo = open_debuginfo(pev->target, pev->nsi, !need_dwarf);

----

Thanks,

Quoted 31 lines
---
 tools/perf/util/probe-event.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
tools/perf/util/probe-event.c +2 -2
index 4ef6ee967468..fb5031ac24a2 100644
@@ -482,7 +482,7 @@ static struct debuginfo *open_debuginfo(const char *module, struct nsinfo *nsi,
 					strcpy(reason, "(unknown)");
 			} else
 				dso__strerror_load(dso, reason, STRERR_BUFSIZE);
-			if (!silent)
+			if (!silent || verbose)
 				pr_err("Failed to find the path for %s: %s\n",
 					module ?: "kernel", reason);
 			return NULL;
@@ -491,7 +491,7 @@ static struct debuginfo *open_debuginfo(const char *module, struct nsinfo *nsi,
 	}
 	nsinfo__mountns_enter(nsi, &nsc);
 	ret = debuginfo__new(path);
-	if (!ret && !silent) {
+	if (!ret && (!silent || verbose)) {
 		pr_warning("The %s file has no debug information.\n", path);
 		if (!module || !strtailcmp(path, ".ko"))
 			pr_warning("Rebuild with CONFIG_DEBUG_INFO=y, ");
Signature · 1 line
2.13.6
Signature · 1 line
Masami Hiramatsu <[email protected]>
author
#15 lore raw

PATCH 9/12 perf, tools: Downgrade register mapping message to warning

In reply to Andi Kleen

From: Andi Kleen <[email protected]>

When tracing floating point code it's quite possible that perf doesn't recognize the register number. Downgrade the warning for unknown registers to a debug message.

---
 tools/perf/util/probe-finder.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
tools/perf/util/probe-finder.c +2 -2
index 137b2fe71838..5fe6466254f9 100644
@@ -272,8 +272,8 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr,
 
 	regs = get_dwarf_regstr(regn, machine);
 	if (!regs) {
-		/* This should be a bug in DWARF or this tool */
-		pr_warning("Mapping for the register number %u "
+		/* This can happen with floating point */
+		pr_debug("Mapping for the register number %u "
 			   "missing on this architecture.\n", regn);
 		return -ENOTSUP;
 	}
Signature · 1 line
2.13.6
#16 lore raw

Re: [PATCH 09/12] perf, tools: Downgrade register mapping message to warning

Quoted 5 lines · On Mon, 27 Nov 2017 16:23:18 -0800 Andi Kleen <[email protected]> wrote:

From: Andi Kleen <[email protected]>

When tracing floating point code it's quite possible that perf doesn't recognize the register number. Downgrade the warning for unknown registers to a debug message.

Hmm, but without this message, user will just see ENOTSUP error. I'm considering to introduce storage of error string for probe-finder so that user can choose to show it or not.

Thank you,

Quoted 24 lines
---
 tools/perf/util/probe-finder.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
tools/perf/util/probe-finder.c +2 -2
index 137b2fe71838..5fe6466254f9 100644
@@ -272,8 +272,8 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr,
 
 	regs = get_dwarf_regstr(regn, machine);
 	if (!regs) {
-		/* This should be a bug in DWARF or this tool */
-		pr_warning("Mapping for the register number %u "
+		/* This can happen with floating point */
+		pr_debug("Mapping for the register number %u "
 			   "missing on this architecture.\n", regn);
 		return -ENOTSUP;
 	}
Signature · 1 line
2.13.6
Signature · 1 line
Masami Hiramatsu <[email protected]>
author
#17 lore raw

PATCH 10/12 perf, tools: Add args and gprs shortcut for registers

In reply to Andi Kleen

From: Andi Kleen <[email protected]>

Writing all registers to sample with -I can give very long command lines. Add short hands for "args" and "gprs"

---
 tools/perf/arch/x86/util/perf_regs.c | 9 +++++++++
 1 file changed, 9 insertions(+)
tools/perf/arch/x86/util/perf_regs.c +9 -0
index 4b2caf6d48e7..7d877c49bae5 100644
@@ -30,6 +30,15 @@ const struct sample_reg sample_reg_masks[] = {
 	SMPL_REG(R13, PERF_REG_X86_R13),
 	SMPL_REG(R14, PERF_REG_X86_R14),
 	SMPL_REG(R15, PERF_REG_X86_R15),
+	{ "args", PERF_REG_X86_DI | PERF_REG_X86_SI | PERF_REG_X86_DX |
+		  PERF_REG_X86_CX |
+		  PERF_REG_X86_R8 | PERF_REG_X86_R9 },
+	{ "gpr",  PERF_REG_X86_AX | PERF_REG_X86_BX | PERF_REG_X86_CX |
+		  PERF_REG_X86_DX | PERF_REG_X86_SI | PERF_REG_X86_DI |
+		  PERF_REG_X86_BP | PERF_REG_X86_SP | PERF_REG_X86_R8 |
+		  PERF_REG_X86_R9 | PERF_REG_X86_R10| PERF_REG_X86_R11|
+		  PERF_REG_X86_R12| PERF_REG_X86_R13| PERF_REG_X86_R14|
+		  PERF_REG_X86_R15 },
 #endif
 	SMPL_REG_END
 };
Signature · 1 line
2.13.6
author
#18 lore raw

PATCH 11/12 perf, tools: Print probe warnings for binaries only once per binary

In reply to Andi Kleen

From: Andi Kleen <[email protected]>

When the perf probe code is called from perf script we may end up with a flood of bad binary errors with -v. Only print the error message once in this case.

---
 tools/perf/util/probe-event.c | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)
tools/perf/util/probe-event.c +10 -6
index fb5031ac24a2..85fbeeb364bf 100644
@@ -492,12 +492,16 @@ static struct debuginfo *open_debuginfo(const char *module, struct nsinfo *nsi,
 	nsinfo__mountns_enter(nsi, &nsc);
 	ret = debuginfo__new(path);
 	if (!ret && (!silent || verbose)) {
-		pr_warning("The %s file has no debug information.\n", path);
-		if (!module || !strtailcmp(path, ".ko"))
-			pr_warning("Rebuild with CONFIG_DEBUG_INFO=y, ");
-		else
-			pr_warning("Rebuild with -g, ");
-		pr_warning("or install an appropriate debuginfo package.\n");
+		static char printed[1024];
+		if (strcmp(path, printed)) {
+			snprintf(printed, sizeof printed, "%s", path);
+			pr_warning("The %s file has no debug information.\n", path);
+			if (!module || !strtailcmp(path, ".ko"))
+				pr_warning("Rebuild with CONFIG_DEBUG_INFO=y, ");
+			else
+				pr_warning("Rebuild with -g, ");
+			pr_warning("or install an appropriate debuginfo package.\n");
+		}
 	}
 	nsinfo__mountns_exit(&nsc);
 	return ret;
Signature · 1 line
2.13.6
#19 lore raw

Re: [PATCH 11/12] perf, tools: Print probe warnings for binaries only once per binary

Quoted 5 lines · On Mon, 27 Nov 2017 16:23:20 -0800 Andi Kleen <[email protected]> wrote:

From: Andi Kleen <[email protected]>

When the perf probe code is called from perf script we may end up with a flood of bad binary errors with -v. Only print the error message once in this case.

Indeed, but this looks like a hack. You may need to store the path in blacklist and skip it next time.

Thank you,

Quoted 36 lines
---
 tools/perf/util/probe-event.c | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)
tools/perf/util/probe-event.c +10 -6
index fb5031ac24a2..85fbeeb364bf 100644
@@ -492,12 +492,16 @@ static struct debuginfo *open_debuginfo(const char *module, struct nsinfo *nsi,
 	nsinfo__mountns_enter(nsi, &nsc);
 	ret = debuginfo__new(path);
 	if (!ret && (!silent || verbose)) {
-		pr_warning("The %s file has no debug information.\n", path);
-		if (!module || !strtailcmp(path, ".ko"))
-			pr_warning("Rebuild with CONFIG_DEBUG_INFO=y, ");
-		else
-			pr_warning("Rebuild with -g, ");
-		pr_warning("or install an appropriate debuginfo package.\n");
+		static char printed[1024];
+		if (strcmp(path, printed)) {
+			snprintf(printed, sizeof printed, "%s", path);
+			pr_warning("The %s file has no debug information.\n", path);
+			if (!module || !strtailcmp(path, ".ko"))
+				pr_warning("Rebuild with CONFIG_DEBUG_INFO=y, ");
+			else
+				pr_warning("Rebuild with -g, ");
+			pr_warning("or install an appropriate debuginfo package.\n");
+		}
 	}
 	nsinfo__mountns_exit(&nsc);
 	return ret;
Signature · 1 line
2.13.6
Signature · 1 line
Masami Hiramatsu <[email protected]>
author
#20 lore raw

PATCH 12/12 perf, tools, script: Implement dwarf resolving of instructions

In reply to Andi Kleen

From: Andi Kleen <[email protected]>

Implement resolving arguments of instructions to dwarf variable names.

When we sample an instruction, decode the instruction and try to symbolize the register or destination it is using. Also print the type. It builds on the perf probe debugging information reverse lookup infrastructure added earlier.

The dwarf decoding magic is all done using Masami Hiramatsu's perf probe code.

This is useful for

- The PTWRITE instruction: when the compiler generates debugging information for PTWRITE arguments. The value logged by PTWRITE is available to the PT decoder, so it can print the value.

- It also works for other samples with an IP, so it's possible to follow their memory access patterns (but not the values)

For the sample we use the instruction decoder to decode the instruction at the sample point, and then map the arguments to dwarf information.

For structure reference we only print the numeric offset, but do not resolve the field name.

Absolute memory references are not supported

It doesn't distinguish SSE (but AVX) registers from GPRs (this would require extending the instruction decoder to detect SSE instructions)

Example:

Quoted 1 line

From perf itself

% perf record -e intel_pt//u -a sleep 1
% perf script --itrace=i0ns -F insnvar,insn,ip,sym -f 2>&1 | xed -F insn: -A -64 | less
...

           4f7e61 xyarray__max_y                pushq  %rbp
           4f7e62 xyarray__max_y                mov %rsp, %rbp
           4f7e65 xyarray__max_y                sub $0x20, %rsp
           4f7e69 xyarray__max_y                movq  %rdi, -0x18(%rbp) { -24(xy), struct xyarray* }
           4f7e6d xyarray__max_y                movq  %fs:0x28, %rax
           4f7e76 xyarray__max_y                movq  %rax, -0x8(%rbp) { -8(xy), struct xyarray* }
           4f7e7a xyarray__max_y                xor %eax, %eax
           4f7e7c xyarray__max_y                movq  -0x18(%rbp), %rax { -24(xy), struct xyarray* }
           4f7e80 xyarray__max_y                movq  0x20(%rax), %rax
           4f7e84 xyarray__max_y                movq  -0x8(%rbp), %rdx { -8(xy), struct xyarray* }
           4f7e88 xyarray__max_y                xorq  %fs:0x28, %rdx
           4f7e91 xyarray__max_y                jz 0x7
           4f7e98 xyarray__max_y                leaveq
           4f7e99 xyarray__max_y                retq

In this example we now know that this function accesses two fields in struct xyarray *

Open Issues:
- It is fairly slow. Some caching would likely help.
- Frame pointer references are usually not correctly resolved, which are common in unoptimized code. That's usually fine because memory access on the stack is not very interesting.
- It cannot resolve some references.

But I find it already quite useful.

---
 tools/perf/Documentation/perf-script.txt |   8 +-
 tools/perf/arch/x86/util/Build           |   1 +
 tools/perf/arch/x86/util/operand.c       | 131 +++++++++++++++++++++++++
 tools/perf/builtin-script.c              | 162 ++++++++++++++++++++++++++++++-
 tools/perf/util/Build                    |   1 +
 tools/perf/util/operand.c                |  16 +++
 tools/perf/util/operand.h                |  16 +++
 tools/perf/util/probe-event.c            |   3 +
 8 files changed, 335 insertions(+), 3 deletions(-)
 create mode 100644 tools/perf/arch/x86/util/operand.c
 create mode 100644 tools/perf/util/operand.c
 create mode 100644 tools/perf/util/operand.h
tools/perf/Documentation/perf-script.txt +7 -1
index e296944cc03f..d3b93b7f804b 100644
@@ -117,7 +117,9 @@ 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, iregvals.
+        brstackoff, callindent, insn, insnlen, synth, phys_addr, iregvals,
+	insnvar
+
         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
@@ -221,6 +223,10 @@ OPTIONS
 	(with perf record -I ...) to their symbolic names in the program. This requires availability
 	of debug information in the binaries.
 
+	With insnvar try to decode and symbolize operands of sampled or traced instructions
+	using debug information.  When PTWRITEs are synthesized with Intel PT the values of the
+	PTWRITEs are automatically symbolized.
+
 -k::
 --vmlinux=<file>::
         vmlinux pathname
tools/perf/arch/x86/util/Build +1 -0
index 139f9f1a56f9..8e9a2140e72b 100644
@@ -5,6 +5,7 @@ libperf-y += insnlen.o
 libperf-y += kvm-stat.o
 libperf-y += perf_regs.o
 libperf-y += group.o
+libperf-y += operand.o
 
 libperf-$(CONFIG_DWARF) += dwarf-regs.o
 libperf-$(CONFIG_BPF_PROLOGUE) += dwarf-regs.o
tools/perf/arch/x86/util/operand.c new file +131 -0

131 lines not on this page · raw message

tools/perf/builtin-script.c +160 -2

204 lines not on this page · raw message

tools/perf/util/Build +1 -0
index 361db92a4bfd..4f42b2fad398 100644
@@ -42,6 +42,7 @@ libperf-y += callchain.o
 libperf-y += values.o
 libperf-y += debug.o
 libperf-y += machine.o
+libperf-y += operand.o
 libperf-y += dwarf-sample.o
 libperf-y += map.o
 libperf-y += pstack.o
tools/perf/util/operand.c new file +16 -0
new file mode 100644
index 000000000000..88d9284a7049
@@ -0,0 +1,16 @@
+#include <errno.h>
+#include "perf.h"
+#include "operand.h"
+
+/* Fall back, can be overriden per architecture */
+__weak
+int arch_resolve_operand(char *insn __maybe_unused,
+			 int insnlen __maybe_unused,
+			 bool is64bit __maybe_unused,
+			 u64 ip __maybe_unused,
+			 u64 val __maybe_unused,
+			 struct operand_print_ops *ops __maybe_unused,
+			 void *ctx __maybe_unused)
+{
+	return -EINVAL;
+}
tools/perf/util/operand.h new file +16 -0
new file mode 100644
index 000000000000..63a7602727a1
@@ -0,0 +1,16 @@
+#ifndef OPERAND_H
+#define OPERAND_H 1
+
+struct operand_print_ops {
+	void (*print_reg)(void *ctx, int reg, bool has_val, u64 val);
+	void (*print_symbol)(void *ctx, u64 addr, bool has_val, u64 val);
+	void (*print_indirect_reg)(void *ctx, int reg, s32 off, bool has_val, u64 val);
+	void (*print_unknown)(void *ctx);
+};
+
+int arch_resolve_operand(char *insn, int insnlen, bool is64bit, u64 ip,
+			 u64 val,
+			 struct operand_print_ops *ops,
+			 void *ctx);
+
+#endif
tools/perf/util/probe-event.c +3 -0
index 85fbeeb364bf..2a65ebed0998 100644
@@ -78,6 +78,9 @@ int init_probe_symbol_maps(bool user_only)
 {
 	int ret;
 
+	if (host_machine)
+		return 0;
+
 	symbol_conf.sort_by_name = true;
 	symbol_conf.allow_aliases = true;
 	ret = symbol__init(NULL);
Signature · 1 line
2.13.6
#21 lore raw

Re: [PATCH 12/12] perf, tools, script: Implement dwarf resolving of instructions

Quoted 20 lines · On Mon, 27 Nov 2017 16:23:21 -0800 Andi Kleen <[email protected]> wrote:

From: Andi Kleen <[email protected]>

Implement resolving arguments of instructions to dwarf variable names.

When we sample an instruction, decode the instruction and try to symbolize the register or destination it is using. Also print the type. It builds on the perf probe debugging information reverse lookup infrastructure added earlier.

The dwarf decoding magic is all done using Masami Hiramatsu's perf probe code.

This is useful for

- The PTWRITE instruction: when the compiler generates debugging information for PTWRITE arguments. The value logged by PTWRITE is available to the PT decoder, so it can print the value.

- It also works for other samples with an IP, so it's possible to follow their memory access patterns (but not the values)

Quoted 7 lines

For the sample we use the instruction decoder to decode the instruction at the sample point, and then map the arguments to dwarf information.

For structure reference we only print the numeric offset, but do not resolve the field name.

Absolute memory references are not supported

Hmm, I think perf-probe also have same limitation.

Quoted 12 lines

It doesn't distinguish SSE (but AVX) registers from GPRs (this would require extending the instruction decoder to detect SSE instructions)

Example:

From perf itself

% perf record -e intel_pt//u -a sleep 1
% perf script --itrace=i0ns -F insnvar,insn,ip,sym -f 2>&1 | xed -F insn: -A -64 | less
...

Quoted 14 lines
           4f7e61 xyarray__max_y                pushq  %rbp
           4f7e62 xyarray__max_y                mov %rsp, %rbp
           4f7e65 xyarray__max_y                sub $0x20, %rsp
           4f7e69 xyarray__max_y                movq  %rdi, -0x18(%rbp) { -24(xy), struct xyarray* }
           4f7e6d xyarray__max_y                movq  %fs:0x28, %rax
           4f7e76 xyarray__max_y                movq  %rax, -0x8(%rbp) { -8(xy), struct xyarray* }
           4f7e7a xyarray__max_y                xor %eax, %eax
           4f7e7c xyarray__max_y                movq  -0x18(%rbp), %rax { -24(xy), struct xyarray* }
           4f7e80 xyarray__max_y                movq  0x20(%rax), %rax
           4f7e84 xyarray__max_y                movq  -0x8(%rbp), %rdx { -8(xy), struct xyarray* }
           4f7e88 xyarray__max_y                xorq  %fs:0x28, %rdx
           4f7e91 xyarray__max_y                jz 0x7
           4f7e98 xyarray__max_y                leaveq
           4f7e99 xyarray__max_y                retq

Nice! :)

Quoted 5 lines

In this example we now know that this function accesses two fields in struct xyarray *

Open Issues:
- It is fairly slow. Some caching would likely help.

OK, we can keep debuginfo open, but it may consume lot of memory.

Quoted 4 lines

- Frame pointer references are usually not correctly resolved, which are common in unoptimized code. That's usually fine because memory access on the stack is not very interesting.
- It cannot resolve some references.

OK, let's find what kind of references can not be solved.

Quoted 4 lines

But I find it already quite useful.

[..]

tools/perf/util/probe-event.c int init_probe_symbol_maps(bool user_only) · line 82

Quoted 10 lines
tools/perf/util/probe-event.c +2 -0
index 85fbeeb364bf..2a65ebed0998 100644
@@ -78,6 +78,9 @@ int init_probe_symbol_maps(bool user_only)
 {
 	int ret;
 
+	if (host_machine)
+		return 0;

What is this code for? Please put comment or make it separated patch.

Thanks!

Quoted 7 lines
tools/perf/util/probe-event.c +1 -0
+
 	symbol_conf.sort_by_name = true;
 	symbol_conf.allow_aliases = true;
 	ret = symbol__init(NULL);
Signature · 1 line
2.13.6
Signature · 1 line
Masami Hiramatsu <[email protected]>
#22 lore raw
In reply to Andi Kleen

Hi Andi,

Quoted 2 lines · On Mon, 27 Nov 2017 16:23:09 -0800 Andi Kleen <[email protected]> wrote:

This patchkit extends perf script to query dwarf information for variable names or types/structure fields accessed from code.

Very interesting! so it is a kind of revert query interface.

Quoted 12 lines

The dwarf resolution is all on top of Masami's perf probe dwarf code.

It supports multiple use cases:
- When we sample registers it can use the dwarf information to resolve the registers to names.
- When we sample any instruction the instruction can be decoded and we can determine the type/struct field to make an estimate of the memory access patterns in data structures.
- When we sample the new PTWRITE instruction the logged value from the PT log can be associated with a variable.
- Various cleanups and fixes to make the one above all possible.

OK, I'll review that.

Thanks!

Quoted 49 lines

It is all implemented with new output formats in perf script:
iregval (map register values to names) and insnvar (decode instruction and map back memory operand to dwarf operation)

There are some limitations, it cannot decode everything, and is somewhat slow, but it's already quite useful for typical code

    % perf record -Idi,si ./targ
    % perf script -F +iregvals
    ...
        targ  8584 169763.761843:    2091795 cycles:ppp:            40041a main (targ)
        targ  8584 169763.762520:    1913932 cycles:ppp:            400534 f1 (targ) { b = 0x2, int }  { a = 0x1, int }
        targ  8584 169763.763141:    1638913 cycles:ppp:            400523 f2 (targ) { b = 0x1, int }  { a = 0x2, int }
        targ  8584 169763.763672:    1516522 cycles:ppp:            400522 f2 (targ) { b = 0x1, int }  { a = 0x2, int }
        targ  8584 169763.764165:    1335501 cycles:ppp:            400523 f2 (targ) { b = 0x1, int }  { a = 0x2, int }
        targ  8584 169763.764598:    1253289 cycles:ppp:            400522 f2 (targ) { b = 0x2, int }  { a = 0x1, int }
        targ  8584 169763.765005:    1135131 cycles:ppp:            400534 f1 (targ) { b = 0x2, int }  { a = 0x1, int }
        targ  8584 169763.765373:    1080325 cycles:ppp:            400522 f2 (targ) { b = 0x2, int }  { a = 0x1, int }
        targ  8584 169763.765724:    1036999 cycles:ppp:            400522 f2 (targ) { b = 0x1, int }  { a = 0x2, int }
        targ  8584 169763.766061:     971213 cycles:ppp:            400534 f1 (targ) { b = 0x2, int }  { a = 0x1, int }


    % perf record -e intel_pt//u  -a sleep 1
    % perf script --itrace=i0ns -F insnvar,insn,ip,sym  -f 2>&1 | xed -F insn: -A -64 | less
    ...
               4f7e61 xyarray__max_y                pushq  %rbp
               4f7e62 xyarray__max_y                mov %rsp, %rbp
               4f7e65 xyarray__max_y                sub $0x20, %rsp
               4f7e69 xyarray__max_y                movq  %rdi, -0x18(%rbp) { -24(xy), struct xyarray* }
               4f7e6d xyarray__max_y                movq  %fs:0x28, %rax
               4f7e76 xyarray__max_y                movq  %rax, -0x8(%rbp) { -8(xy), struct xyarray* }
               4f7e7a xyarray__max_y                xor %eax, %eax
               4f7e7c xyarray__max_y                movq  -0x18(%rbp), %rax { -24(xy), struct xyarray* }
               4f7e80 xyarray__max_y                movq  0x20(%rax), %rax
               4f7e84 xyarray__max_y                movq  -0x8(%rbp), %rdx { -8(xy), struct xyarray* }
               4f7e88 xyarray__max_y                xorq  %fs:0x28, %rdx
               4f7e91 xyarray__max_y                jz 0x7
               4f7e98 xyarray__max_y                leaveq
               4f7e99 xyarray__max_y                retq

In this example we now know that this function accesses two fields in struct xyarray *

Available from

git://git.kernel.org/pub/scm/linux/kernel/git/ak/linux-misc perf/var-resolve-2

v1: Initial post

Signature · 1 line
Masami Hiramatsu <[email protected]>