Implement dwarf variable/type resolving for perf script
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 retqIn 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
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
@@ -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
PATCH 2/12 perf, tools, script: Print insn/insnlen for non PT sample
In reply to Andi KleenFrom: 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
PATCH 3/12 perf, tools: Support storing additional data in strlist
In reply to Andi KleenFrom: 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
@@ -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
@@ -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
@@ -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 *confbool 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
@@ -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
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.Signed-off-by Andi Kleen <[email protected]>--- 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@@ -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@@ -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@@ -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 *confbool 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
-0const 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@@ -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
-0struct 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]>
PATCH 4/12 perf, tools: Store variable name and register for dwarf variable lists
In reply to Andi KleenFrom: 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
@@ -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
@@ -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
PATCH 5/12 perf, tools, probe: Print location for resolved variables
In reply to Andi KleenFrom: 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
@@ -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
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
Signed-off-by Andi Kleen <[email protected]>--- tools/perf/util/probe-finder.c | 5 +++++ 1 file changed, 5 insertions(+)tools/perf/util/probe-finder.c +5
-0@@ -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]>
PATCH 6/12 perf, tools, probe: Support a quiet argument for debug info open
In reply to Andi KleenFrom: 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
@@ -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
@@ -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
@@ -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
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
Signed-off-by Andi Kleen <[email protected]>--- 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@@ -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@@ -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@@ -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]>
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
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]>
PATCH 7/12 perf, tools, script: Resolve variable names for registers
In reply to Andi KleenFrom: 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
PATCH 8/12 perf, tools: Always print probe finder warnings with -v
In reply to Andi KleenFrom: 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
@@ -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
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
Signed-off-by Andi Kleen <[email protected]>--- tools/perf/util/probe-event.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)tools/perf/util/probe-event.c +2
-2@@ -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]>
PATCH 9/12 perf, tools: Downgrade register mapping message to warning
In reply to Andi KleenFrom: 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
@@ -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
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
Signed-off-by Andi Kleen <[email protected]>--- tools/perf/util/probe-finder.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)tools/perf/util/probe-finder.c +2
-2@@ -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]>
PATCH 10/12 perf, tools: Add args and gprs shortcut for registers
In reply to Andi KleenFrom: 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
@@ -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 }, #endifSMPL_REG_END };
Signature · 1 line
2.13.6
PATCH 11/12 perf, tools: Print probe warnings for binaries only once per binary
In reply to Andi KleenFrom: 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
@@ -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
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
Signed-off-by Andi Kleen <[email protected]>--- tools/perf/util/probe-event.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-)tools/perf/util/probe-event.c +10
-6@@ -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]>
PATCH 12/12 perf, tools, script: Implement dwarf resolving of instructions
In reply to Andi KleenFrom: 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 retqIn 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
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 retqNice! :)
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.
Signed-off-by Andi Kleen <[email protected]>
[..]
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@@ -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]>
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 retqIn 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-miscperf/var-resolve-2v1: Initial post
Signature · 1 line
Masami Hiramatsu <[email protected]>