Then it is more clearly for us to do the probe with this variable:
$ perf probe --add 'generic_perform_write+170 a_ops'
Added new event:
probe:generic_perform_write (on generic_perform_write+170 with a_ops)
Signed-off-by: He Kuang <heku...@huawei.com>
---
tools/perf/builtin-probe.c | 6 ++-
tools/perf/util/dwarf-aux.c | 103 +++++++++++++++++++++++++++++++++++++++++
tools/perf/util/dwarf-aux.h | 2 +
tools/perf/util/probe-finder.c | 69 ++++++++++++++++++++-------
tools/perf/util/probe-finder.h | 3 +-
5 files changed, 165 insertions(+), 18 deletions(-)
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index 19ebe93..1b20004 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -52,6 +52,7 @@ static struct {
bool list_events;
bool force_add;
bool show_ext_vars;
+ bool show_location_range;
bool uprobes;
bool quiet;
bool target_used;
@@ -375,6 +376,8 @@ __cmd_probe(int argc, const char **argv, const char *prefix
__maybe_unused)
"Show accessible variables on PROBEDEF", opt_show_vars),
OPT_BOOLEAN('\0', "externs", ¶ms.show_ext_vars,
"Show external variables too (with --vars only)"),
+ OPT_BOOLEAN('\0', "range", ¶ms.show_location_range,
+ "Show variables location range in scope (with --vars only)"),
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
"file", "vmlinux pathname"),
OPT_STRING('s', "source", &symbol_conf.source_prefix,
@@ -479,7 +482,8 @@ __cmd_probe(int argc, const char **argv, const char *prefix
__maybe_unused)
params.filter = strfilter__new(DEFAULT_VAR_FILTER,
NULL);
- var_flags = params.show_ext_vars << VAR_FLAGS_EXTERN;
+ var_flags = params.show_ext_vars << VAR_FLAGS_EXTERN |
+ params.show_location_range << VAR_FLAGS_LOCATION_RANGE;
ret = show_available_vars(params.events, params.nevents,
params.max_probe_points,
diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c
index 75d264e..23053c0 100644
--- a/tools/perf/util/dwarf-aux.c
+++ b/tools/perf/util/dwarf-aux.c
@@ -897,3 +897,106 @@ int die_get_varname(Dwarf_Die *vr_die, struct strbuf *buf)
return 0;
}
+/**
+ * die_get_var_innermost_scope - Get innermost scope range of given variable
DIE
+ * @sp_die: a subprogram DIE
+ * @vr_die: a variable DIE
+ * @buf: a strbuf for variable byte offset range
+ *
+ * Get the innermost scope range of @vr_die and stores it in @buf as
+ * "[byte offset]: <NN-NN>,<NN-NN>".
+ */
+static int die_get_var_innermost_scope(Dwarf_Die *sp_die, Dwarf_Die *vr_die,
+ struct strbuf *buf)
+{
+ Dwarf_Die *scopes;
+ int count;
+ size_t offset = 0;
+ Dwarf_Addr base;
+ Dwarf_Addr start, end;
+ Dwarf_Addr entry;
+ int ret;
+ bool first = true;
+
+ ret = dwarf_entrypc(sp_die, &entry);
+ if (ret)
+ return ret;
+
+ count = dwarf_getscopes_die(vr_die, &scopes);
+
+ /* (*SCOPES)[1] is the DIE for the scope containing that scope */
+ if (count <= 1) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ while ((offset = dwarf_ranges(&scopes[1], offset, &base,
+ &start, &end)) > 0) {
+ start -= entry;
+ end -= entry;
+
+ if (first) {
+ strbuf_addf(buf, "[byte offset]: <%ld-%ld>",
+ start, end);
+ first = false;
+ } else {
+ strbuf_addf(buf, ",<%ld-%ld>",
+ start, end);
+ }
+ }
+end:
+ free(scopes);
+ return ret;
+}
+
+/**
+ * die_get_var_range - Get byte offset range of given variable DIE
+ * @sp_die: a subprogram DIE
+ * @vr_die: a variable DIE
+ * @buf: a strbuf for type and variable name and byte offset range
+ *
+ * Get the byte offset range of @vr_die and stores it in @buf as
+ * "[byte offset]: <NN-NN>,<NN-NN>".
+ */
+int die_get_var_range(Dwarf_Die *sp_die, Dwarf_Die *vr_die, struct strbuf *buf)
+{
+ int ret = 0;
+ Dwarf_Addr base;
+ Dwarf_Addr start, end;
+ Dwarf_Addr entry;
+ Dwarf_Op *op;
+ size_t nops;
+ size_t offset = 0;
+ Dwarf_Attribute attr;
+ bool first = true;
+
+ ret = dwarf_entrypc(sp_die, &entry);
+ if (ret)
+ return ret;
+
+ if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL)
+ return -EINVAL;
+
+ while ((offset = dwarf_getlocations(
+ &attr, offset, &base,
+ &start, &end, &op, &nops)) > 0) {
+ if (start == 0) {
+ /* Single Location Descriptions */
+ ret = die_get_var_innermost_scope(sp_die, vr_die, buf);
+ } else {
+ /* Location Lists */
+ start -= entry;
+ end -= entry;
+ if (first) {
+ strbuf_addf(buf, "[byte offset]: <%ld-%ld>",
+ start, end);
+ first = false;
+ } else {
+ strbuf_addf(buf, ",<%ld-%ld>",
+ start, end);
+ }
+ }
+ }
+
+ return ret;
+}
diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h
index c0d6173..8390cde 100644
--- a/tools/perf/util/dwarf-aux.h
+++ b/tools/perf/util/dwarf-aux.h
@@ -118,4 +118,6 @@ extern int die_get_typename(Dwarf_Die *vr_die, struct
strbuf *buf);
/* Get the name and type of given variable DIE, stored as "type\tname" */
extern int die_get_varname(Dwarf_Die *vr_die, struct strbuf *buf);
+extern int die_get_var_range(Dwarf_Die *sp_die, Dwarf_Die *vr_die,
+ struct strbuf *buf);
#endif
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 6bbdf3f..af0a7ea 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -43,6 +43,9 @@
/* Kprobe tracer basic type is up to u64 */
#define MAX_BASIC_TYPE_BITS 64
+/* Variable location invalid at addr but valid in scope */
+#define VARIABLE_LOCATION_INVALID_AT_ADDR -10000
+
/* Dwarf FL wrappers */
static char *debuginfo_path; /* Currently dummy */
@@ -167,7 +170,8 @@ static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs)
*/
static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr,
Dwarf_Op *fb_ops, Dwarf_Die *sp_die,
- struct probe_trace_arg *tvar)
+ struct probe_trace_arg *tvar,
+ unsigned long *var_flags)
{
Dwarf_Attribute attr;
Dwarf_Addr tmp = 0;
@@ -177,7 +181,7 @@ static int convert_variable_location(Dwarf_Die *vr_die,
Dwarf_Addr addr,
Dwarf_Word offs = 0;
bool ref = false;
const char *regs;
- int ret;
+ int ret, ret2 = 0;
if (dwarf_attr(vr_die, DW_AT_external, &attr) != NULL)
goto static_var;
@@ -187,9 +191,20 @@ static int convert_variable_location(Dwarf_Die *vr_die,
Dwarf_Addr addr,
return -EINVAL; /* Broken DIE ? */
if (dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0) {
ret = dwarf_entrypc(sp_die, &tmp);
- if (ret || addr != tmp ||
- dwarf_tag(vr_die) != DW_TAG_formal_parameter ||
- dwarf_highpc(sp_die, &tmp))
+ if (ret)
+ return -ENOENT;
+
+ if (var_flags &&
+ test_bit(VAR_FLAGS_LOCATION_RANGE, var_flags) &&
+ (dwarf_tag(vr_die) == DW_TAG_variable)) {
+ ret2 = VARIABLE_LOCATION_INVALID_AT_ADDR;
+ } else if (addr != tmp ||
+ dwarf_tag(vr_die) != DW_TAG_formal_parameter) {
+ return -ENOENT;
+ }
+
+ ret = dwarf_highpc(sp_die, &tmp);
+ if (ret)
return -ENOENT;
/*
* This is fuzzed by fentry mcount. We try to find the
@@ -210,7 +225,7 @@ found:
if (op->atom == DW_OP_addr) {
static_var:
if (!tvar)
- return 0;
+ return ret2;
/* Static variables on memory (not stack), make @varname */
ret = strlen(dwarf_diename(vr_die));
tvar->value = zalloc(ret + 2);
@@ -220,7 +235,7 @@ static_var:
tvar->ref = alloc_trace_arg_ref((long)offs);
if (tvar->ref == NULL)
return -ENOMEM;
- return 0;
+ return ret2;
}
/* If this is based on frame buffer, set the offset */
@@ -250,7 +265,7 @@ static_var:
}
if (!tvar)
- return 0;
+ return ret2;
regs = get_arch_regstr(regn);
if (!regs) {
@@ -269,7 +284,7 @@ static_var:
if (tvar->ref == NULL)
return -ENOMEM;
}
- return 0;
+ return ret2;
}
#define BYTES_TO_BITS(nb) ((nb) * BITS_PER_LONG / sizeof(long))
@@ -516,7 +531,7 @@ static int convert_variable(Dwarf_Die *vr_die, struct
probe_finder *pf)
dwarf_diename(vr_die));
ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops,
- &pf->sp_die, pf->tvar);
+ &pf->sp_die, pf->tvar, NULL);
if (ret == -ENOENT || ret == -EINVAL)
pr_err("Failed to find the location of %s at this address.\n"
" Perhaps, it has been optimized out.\n", pf->pvar->var);
@@ -1105,7 +1120,7 @@ static int copy_variables_cb(Dwarf_Die *die_mem, void
*data)
(tag == DW_TAG_variable && vf->vars)) {
if (convert_variable_location(die_mem, vf->pf->addr,
vf->pf->fb_ops, &pf->sp_die,
- NULL) == 0) {
+ NULL, NULL) == 0) {
vf->args[vf->nargs].var = (char
*)dwarf_diename(die_mem);
if (vf->args[vf->nargs].var == NULL) {
vf->ret = -ENOMEM;
@@ -1254,11 +1269,33 @@ static int collect_variables_cb(Dwarf_Die *die_mem,
void *data)
tag == DW_TAG_variable) {
ret = convert_variable_location(die_mem, af->pf.addr,
af->pf.fb_ops, &af->pf.sp_die,
- NULL);
- if (ret == 0) {
- ret = die_get_varname(die_mem, &buf);
- pr_debug2("Add new var: %s\n", buf.buf);
- if (ret == 0) {
+ NULL, &af->var_flags);
+ if (ret == 0 || ret == VARIABLE_LOCATION_INVALID_AT_ADDR) {
+ int ret2;
+ bool show_range = test_bit(VAR_FLAGS_LOCATION_RANGE,
+ &af->var_flags);
+ bool externs = !af->child;
+
+ if (show_range) {
+ if (!externs) {
+ if (ret)
+ strbuf_addf(&buf, "[INV]\t");
+ else
+ strbuf_addf(&buf, "[VAL]\t");
+ } else
+ strbuf_addf(&buf, "[EXT]\t");
+ }
+
+ ret2 = die_get_varname(die_mem, &buf);
+
+ if (!ret2 && show_range && !externs) {
+ strbuf_addf(&buf, "\t");
+ ret2 = die_get_var_range(&af->pf.sp_die,
+ die_mem, &buf);
+ }
+
+ pr_debug("Add new var: %s\n", buf.buf);
+ if (ret2 == 0) {
strlist__add(vl->vars,
strbuf_detach(&buf, NULL));
}
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
index 95bb79d..4b30a9b 100644
--- a/tools/perf/util/probe-finder.h
+++ b/tools/perf/util/probe-finder.h
@@ -11,7 +11,8 @@
#define MAX_PROBE_ARGS 128
enum var_flags_bits {
- VAR_FLAGS_EXTERN
+ VAR_FLAGS_EXTERN,
+ VAR_FLAGS_LOCATION_RANGE
};
#define PROBE_ARG_VARS "$vars"