Hi,
Here is a series of bugfix patches and improvements for perf-probe.
First 5 patches are new for fixing some minor issues on perf probe,
and last 3 are for wildcard support which is just updates of the
patches which I sent before ( https://lkml.org/lkml/2014/10/31/207 ).
I've dropped --output option since it will be done by perf-cache,
but this series doesn't involve perf-cache feature yet. I found some
issues when working on that and decided to send it beforehand.
Thank you,
---
Masami Hiramatsu (8):
[BUGFIX] perf probe: Fix to close probe_events file in error
[BUGFIX] perf probe: Fix a typo for the flags of open
[BUGFIX] perf probe: Fix to return 0 when positive value returned
[BUGFIX] perf probe: --line checks valid C-style function name
perf probe: Skip kernel symbols which is out of .text
perf-probe: Add --no-inlines option to avoid searching inline functions
perf-probe: Support $params special probe argument
perf-probe: Support glob wildcards for function name
tools/perf/Documentation/perf-probe.txt | 6 +-
tools/perf/builtin-probe.c | 8 ++
tools/perf/util/dwarf-aux.c | 16 ++++
tools/perf/util/dwarf-aux.h | 3 +
tools/perf/util/probe-event.c | 116 +++++++++++++++++++++----------
tools/perf/util/probe-event.h | 4 +
tools/perf/util/probe-finder.c | 62 +++++++++++------
tools/perf/util/probe-finder.h | 6 +-
tools/perf/util/util.h | 4 +
9 files changed, 161 insertions(+), 64 deletions(-)
--
Masami HIRAMATSU
Linux Technology Research Center, System Productivity Research Dept.
Center for Technology Innovation - Systems Engineering
Hitachi, Ltd., Research & Development Group
E-mail: [email protected]
Fix perf-probe to close probe_events file if it failed to
get existing probe's name. This also fix the return error
code to -ENOMEM.
Signed-off-by: Masami Hiramatsu <[email protected]>
---
tools/perf/util/probe-event.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index abf5845..230353f 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -2384,7 +2384,8 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
namelist = get_probe_trace_event_names(fd, false);
if (!namelist) {
pr_debug("Failed to get current event list.\n");
- return -EIO;
+ ret = -ENOMEM;
+ goto close_out;
}
/* Get kprobe blacklist if exists */
if (!pev->uprobes) {
@@ -2467,6 +2468,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
kprobe_blacklist__delete(&blacklist);
strlist__delete(namelist);
+close_out:
close(fd);
return ret;
}
Fix to pass O_APPEND by using bit-or with other flags, instead of
passing it as mode.
Signed-off-by: Masami Hiramatsu <[email protected]>
---
tools/perf/util/probe-event.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 230353f..63cb7c5 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -1969,7 +1969,7 @@ static int open_probe_events(const char *trace_file, bool readwrite)
if (ret >= 0) {
pr_debug("Opening %s write=%d\n", buf, readwrite);
if (readwrite && !probe_event_dry_run)
- ret = open(buf, O_RDWR, O_APPEND);
+ ret = open(buf, O_RDWR | O_APPEND, 0);
else
ret = open(buf, O_RDONLY, 0);
Fix to return 0 when positive value returned from probe command.
At least --vars can returns a positive value if it found a point.
----
# perf probe --vars vfs_read && echo succeeded! || echo failed!
Available variables at vfs_read
@<vfs_read+0>
char* buf
loff_t* pos
size_t count
struct file* file
failed!
----
This fixes above problem.
----
# perf probe --vars vfs_read && echo succeeded! || echo failed!
Available variables at vfs_read
@<vfs_read+0>
char* buf
loff_t* pos
size_t count
struct file* file
succeeded!
----
Signed-off-by: Masami Hiramatsu <[email protected]>
---
tools/perf/builtin-probe.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index 53d475b..9c4cf5e 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -523,5 +523,5 @@ int cmd_probe(int argc, const char **argv, const char *prefix)
cleanup_params();
}
- return ret;
+ return ret < 0 ? ret : 0;
}
Fix --line to check valid C-style function name and returns
a semantic error if it is not.
For example, previously, --line doesn't support lazy pattern
but it doesn't recognized as a semantic error.
----
# perf probe -L 'func;return*:0-10'
Specified source line is not found.
Error: Failed to show lines.
----
With this patch, it is correctly handled as a semantic error.
----
# perf probe -L 'func;return*:0-10'
Semantic error :'func;return*' is not a valid function name.
...
----
Signed-off-by: Masami Hiramatsu <[email protected]>
---
tools/perf/util/probe-event.c | 35 ++++++++++++++++++++---------------
1 file changed, 20 insertions(+), 15 deletions(-)
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 63cb7c5..4265f2e 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -980,6 +980,18 @@ static int parse_line_num(char **ptr, int *val, const char *what)
return 0;
}
+/* Check the name is good for event, group or function */
+static bool is_c_func_name(const char *name)
+{
+ if (!isalpha(*name) && *name != '_')
+ return false;
+ while (*++name != '\0') {
+ if (!isalpha(*name) && !isdigit(*name) && *name != '_')
+ return false;
+ }
+ return true;
+}
+
/*
* Stuff 'lr' according to the line range described by 'arg'.
* The line range syntax is described by:
@@ -1048,10 +1060,15 @@ int parse_line_range_desc(const char *arg, struct line_range *lr)
goto err;
}
lr->function = name;
- } else if (strchr(name, '.'))
+ } else if (strchr(name, '/') || strchr(name, '.'))
lr->file = name;
- else
+ else if (is_c_func_name(name))/* We reuse it for checking funcname */
lr->function = name;
+ else { /* Invalid name */
+ semantic_error("'%s' is not a valid function name.\n", name);
+ err = -EINVAL;
+ goto err;
+ }
return 0;
err:
@@ -1059,18 +1076,6 @@ err:
return err;
}
-/* Check the name is good for event/group */
-static bool check_event_name(const char *name)
-{
- if (!isalpha(*name) && *name != '_')
- return false;
- while (*++name != '\0') {
- if (!isalpha(*name) && !isdigit(*name) && *name != '_')
- return false;
- }
- return true;
-}
-
/* Parse probepoint definition. */
static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
{
@@ -1094,7 +1099,7 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
semantic_error("Group name is not supported yet.\n");
return -ENOTSUP;
}
- if (!check_event_name(arg)) {
+ if (!is_c_func_name(arg)) {
semantic_error("%s is bad for event name -it must "
"follow C symbol-naming rule.\n", arg);
return -EINVAL;
Skip the kernel symbols which is out of .text, e.g. the functions
in .inittext. Those are found in debuginfo/kallsyms, but already
freed from memory.
e.g.
----
# perf probe vfs_caches_init
vfs_caches_init+0 is out of .text, skip it.
Probe point 'vfs_caches_init' not found.
Error: Failed to add events.
----
Signed-off-by: Masami Hiramatsu <[email protected]>
---
tools/perf/util/probe-event.c | 31 ++++++++++++++++++++++++-------
1 file changed, 24 insertions(+), 7 deletions(-)
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 4265f2e..37a3a8b 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -557,8 +557,9 @@ static int post_process_probe_trace_events(struct probe_trace_event *tevs,
bool uprobe)
{
struct ref_reloc_sym *reloc_sym;
+ u64 etext_addr;
char *tmp;
- int i;
+ int i, skipped = 0;
if (uprobe)
return add_exec_to_probe_trace_events(tevs, ntevs, module);
@@ -572,19 +573,29 @@ static int post_process_probe_trace_events(struct probe_trace_event *tevs,
pr_warning("Relocated base symbol is not found!\n");
return -EINVAL;
}
+ /* Get the address of _etext for checking non-probable text symbol */
+ etext_addr = kernel_get_symbol_address_by_name("_etext", false);
for (i = 0; i < ntevs; i++) {
if (tevs[i].point.address && !tevs[i].point.retprobe) {
- tmp = strdup(reloc_sym->name);
- if (!tmp)
- return -ENOMEM;
+ /* If we found a wrong one, mark it by NULL symbol */
+ if (etext_addr < tevs[i].point.address) {
+ pr_warning("%s+%lu is out of .text, skip it.\n",
+ tevs[i].point.symbol, tevs[i].point.offset);
+ tmp = NULL;
+ skipped++;
+ } else {
+ tmp = strdup(reloc_sym->name);
+ if (!tmp)
+ return -ENOMEM;
+ }
free(tevs[i].point.symbol);
tevs[i].point.symbol = tmp;
tevs[i].point.offset = tevs[i].point.address -
reloc_sym->unrelocated_addr;
}
}
- return 0;
+ return skipped;
}
/* Try to find perf_probe_event with debuginfo */
@@ -630,11 +641,14 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
pr_debug("Found %d probe_trace_events.\n", ntevs);
ret = post_process_probe_trace_events(*tevs, ntevs,
target, pev->uprobes);
- if (ret < 0) {
+ if (ret < 0 || ret == ntevs) {
clear_probe_trace_events(*tevs, ntevs);
zfree(tevs);
}
- return ret < 0 ? ret : ntevs;
+ if (ret != ntevs)
+ return ret < 0 ? ret : ntevs;
+ ntevs = 0;
+ /* Fall through */
}
if (ntevs == 0) { /* No error but failed to find probe point. */
@@ -2403,6 +2417,9 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
pr_info("Added new event%s\n", (ntevs > 1) ? "s:" : ":");
for (i = 0; i < ntevs; i++) {
tev = &tevs[i];
+ /* Skip if the symbol is out of .text (marked previously) */
+ if (!tev->point.symbol)
+ continue;
/* Ensure that the address is NOT blacklisted */
node = kprobe_blacklist__find_by_address(&blacklist,
tev->point.address);
Add --no-inlines(--inlines) option to avoid searching inline
functions.
Searching all functions which matches glob pattern can take a
long time and find a lot of inline functions. With this option
perf-probe searches target on the non-inlined functions.
Signed-off-by: Masami Hiramatsu <[email protected]>
---
tools/perf/Documentation/perf-probe.txt | 4 ++++
tools/perf/builtin-probe.c | 6 +++++-
tools/perf/util/probe-event.c | 25 ++++++++++++++++---------
tools/perf/util/probe-event.h | 3 ++-
tools/perf/util/probe-finder.c | 8 +++++---
tools/perf/util/probe-finder.h | 3 ++-
6 files changed, 34 insertions(+), 15 deletions(-)
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index a272f2e..a1637ce 100644
--- a/tools/perf/Documentation/perf-probe.txt
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -83,6 +83,10 @@ OPTIONS
(Only for --vars) Show external defined variables in addition to local
variables.
+--no-inlines::
+ (Only for --add) Search only for non-inlined functions. The functions
+ which do not have instances are ignored.
+
-F::
--funcs[=FILTER]::
Show available functions in given module or kernel. With -x/--exec,
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index 9c4cf5e..be7f25f 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -53,6 +53,7 @@ static struct {
bool force_add;
bool show_ext_vars;
bool uprobes;
+ bool no_inlines;
bool quiet;
bool target_used;
int nevents;
@@ -382,6 +383,8 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
OPT_CALLBACK('m', "module", NULL, "modname|path",
"target module name (for online) or path (for offline)",
opt_set_target),
+ OPT_BOOLEAN('\0', "no-inlines", ¶ms.no_inlines,
+ "Don't search inlined functions"),
#endif
OPT__DRY_RUN(&probe_event_dry_run),
OPT_INTEGER('\0', "max-probes", ¶ms.max_probe_points,
@@ -501,7 +504,8 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
ret = add_perf_probe_events(params.events, params.nevents,
params.max_probe_points,
- params.force_add);
+ params.force_add,
+ params.no_inlines);
if (ret < 0) {
pr_err_with_code(" Error: Failed to add events.", ret);
return ret;
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 37a3a8b..c9805fd 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -601,7 +601,8 @@ static int post_process_probe_trace_events(struct probe_trace_event *tevs,
/* 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,
- int max_tevs, const char *target)
+ int max_tevs, const char *target,
+ bool no_inline)
{
bool need_dwarf = perf_probe_event_need_dwarf(pev);
struct perf_probe_point tmp;
@@ -619,13 +620,15 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
pr_debug("Try to find probe point from debuginfo.\n");
/* Searching trace events corresponding to a probe event */
- ntevs = debuginfo__find_trace_events(dinfo, pev, tevs, max_tevs);
+ ntevs = debuginfo__find_trace_events(dinfo, pev, tevs, max_tevs,
+ no_inline);
if (ntevs == 0) { /* Not found, retry with an alternative */
ret = get_alternative_probe_event(dinfo, pev, &tmp, target);
if (!ret) {
ntevs = debuginfo__find_trace_events(dinfo, pev,
- tevs, max_tevs);
+ tevs, max_tevs,
+ no_inline);
/*
* Write back to the original probe_event for
* setting appropriate (user given) event name
@@ -932,7 +935,8 @@ find_perf_probe_point_from_dwarf(struct probe_trace_point *tp __maybe_unused,
static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
struct probe_trace_event **tevs __maybe_unused,
int max_tevs __maybe_unused,
- const char *target __maybe_unused)
+ const char *target __maybe_unused,
+ bool no_inline __maybe_unused)
{
if (perf_probe_event_need_dwarf(pev)) {
pr_warning("Debuginfo-analysis is not supported.\n");
@@ -2638,8 +2642,9 @@ err_out:
bool __weak arch__prefers_symtab(void) { return false; }
static int convert_to_probe_trace_events(struct perf_probe_event *pev,
- struct probe_trace_event **tevs,
- int max_tevs, const char *target)
+ struct probe_trace_event **tevs,
+ int max_tevs, const char *target,
+ bool no_inline)
{
int ret;
@@ -2659,7 +2664,8 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
}
/* Convert perf_probe_event with debuginfo */
- ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target);
+ ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target,
+ no_inline);
if (ret != 0)
return ret; /* Found in debuginfo or got an error */
@@ -2673,7 +2679,7 @@ struct __event_package {
};
int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
- int max_tevs, bool force_add)
+ int max_tevs, bool force_add, bool no_inline)
{
int i, j, ret;
struct __event_package *pkgs;
@@ -2697,7 +2703,8 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
ret = convert_to_probe_trace_events(pkgs[i].pev,
&pkgs[i].tevs,
max_tevs,
- pkgs[i].pev->target);
+ pkgs[i].pev->target,
+ no_inline);
if (ret < 0)
goto end;
pkgs[i].ntevs = ret;
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index e10aedc..f883670 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -125,7 +125,8 @@ extern int line_range__init(struct line_range *lr);
extern const char *kernel_get_module_path(const char *module);
extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
- int max_probe_points, bool force_add);
+ int max_probe_points, bool force_add,
+ bool no_inline);
extern int del_perf_probe_events(struct strfilter *filter);
extern int show_perf_probe_events(struct strfilter *filter);
extern int show_line_range(struct line_range *lr, const char *module,
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 44554c3..a37b439 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -943,7 +943,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
/* TODO: Check the address in this function */
param->retval = call_probe_finder(sp_die, pf);
}
- } else
+ } else if (!pf->no_inline)
/* Inlined function: search instances */
param->retval = die_walk_instances(sp_die,
probe_point_inline_cb, (void *)pf);
@@ -1211,10 +1211,12 @@ end:
/* Find probe_trace_events specified by perf_probe_event from debuginfo */
int debuginfo__find_trace_events(struct debuginfo *dbg,
struct perf_probe_event *pev,
- struct probe_trace_event **tevs, int max_tevs)
+ struct probe_trace_event **tevs,
+ int max_tevs, bool no_inline)
{
struct trace_event_finder tf = {
- .pf = {.pev = pev, .callback = add_probe_trace_event},
+ .pf = {.pev = pev, .callback = add_probe_trace_event,
+ .no_inline = no_inline},
.mod = dbg->mod, .max_tevs = max_tevs};
int ret;
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
index ebf8c8c..996f007 100644
--- a/tools/perf/util/probe-finder.h
+++ b/tools/perf/util/probe-finder.h
@@ -38,7 +38,7 @@ extern void debuginfo__delete(struct debuginfo *dbg);
extern int debuginfo__find_trace_events(struct debuginfo *dbg,
struct perf_probe_event *pev,
struct probe_trace_event **tevs,
- int max_tevs);
+ int max_tevs, bool no_inline);
/* Find a perf_probe_point from debuginfo */
extern int debuginfo__find_probe_point(struct debuginfo *dbg,
@@ -80,6 +80,7 @@ struct probe_finder {
Dwarf_Op *fb_ops; /* Frame base attribute */
struct perf_probe_arg *pvar; /* Current target variable */
struct probe_trace_arg *tvar; /* Current result variable */
+ bool no_inline; /* Do not find inlines */
};
struct trace_event_finder {
$params is similar to $vars but matches only function parameters
not local variables.
Thus, this is useful for tracing function parameter changing
or tracing function call with parameters.
Signed-off-by: Masami Hiramatsu <[email protected]>
---
tools/perf/Documentation/perf-probe.txt | 2 +-
tools/perf/util/probe-finder.c | 29 ++++++++++++++++-------------
tools/perf/util/probe-finder.h | 3 +++
3 files changed, 20 insertions(+), 14 deletions(-)
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index a1637ce..3a8a9ba 100644
--- a/tools/perf/Documentation/perf-probe.txt
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -155,7 +155,7 @@ Each probe argument follows below syntax.
[NAME=]LOCALVAR|$retval|%REG|@SYMBOL[:TYPE]
'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), local array with fixed index (e.g. array[1], var->array[0], var->pointer[2]), or kprobe-tracer argument format (e.g. $retval, %ax, etc). Note that the name of this argument will be set as the last member name if you specify a local data structure member (e.g. field2 for 'var->field1.field2'.)
-'$vars' special argument is also available for NAME, it is expanded to the local variables which can access at given probe point.
+'$vars' and '$params' special arguments are also available for NAME, '$vars' is expanded to the local variables (including function parameters) which can access at given probe point. '$params' is expanded to only the function parameters.
'TYPE' casts the type of this argument (optional). If omitted, perf probe automatically set the type based on debuginfo. You can specify 'string' type only for the local variable or structure member which is an array of or a pointer to 'char' or 'unsigned char' type.
On x86 systems %REG is always the short form of the register: for example %AX. %RAX or %EAX is not valid.
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index a37b439..ce48719 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -1087,6 +1087,7 @@ found:
struct local_vars_finder {
struct probe_finder *pf;
struct perf_probe_arg *args;
+ bool vars;
int max_args;
int nargs;
int ret;
@@ -1101,7 +1102,7 @@ static int copy_variables_cb(Dwarf_Die *die_mem, void *data)
tag = dwarf_tag(die_mem);
if (tag == DW_TAG_formal_parameter ||
- tag == DW_TAG_variable) {
+ (tag == DW_TAG_variable && vf->vars)) {
if (convert_variable_location(die_mem, vf->pf->addr,
vf->pf->fb_ops, &pf->sp_die,
NULL) == 0) {
@@ -1127,26 +1128,28 @@ static int expand_probe_args(Dwarf_Die *sc_die, struct probe_finder *pf,
Dwarf_Die die_mem;
int i;
int n = 0;
- struct local_vars_finder vf = {.pf = pf, .args = args,
+ struct local_vars_finder vf = {.pf = pf, .args = args, .vars = false,
.max_args = MAX_PROBE_ARGS, .ret = 0};
for (i = 0; i < pf->pev->nargs; i++) {
/* var never be NULL */
- if (strcmp(pf->pev->args[i].var, "$vars") == 0) {
- pr_debug("Expanding $vars into:");
- vf.nargs = n;
- /* Special local variables */
- die_find_child(sc_die, copy_variables_cb, (void *)&vf,
- &die_mem);
- pr_debug(" (%d)\n", vf.nargs - n);
- if (vf.ret < 0)
- return vf.ret;
- n = vf.nargs;
- } else {
+ if (strcmp(pf->pev->args[i].var, PROBE_ARG_VARS) == 0)
+ vf.vars = true;
+ else if (strcmp(pf->pev->args[i].var, PROBE_ARG_PARAMS) != 0) {
/* Copy normal argument */
args[n] = pf->pev->args[i];
n++;
+ continue;
}
+ pr_debug("Expanding %s into:", pf->pev->args[i].var);
+ vf.nargs = n;
+ /* Special local variables */
+ die_find_child(sc_die, copy_variables_cb, (void *)&vf,
+ &die_mem);
+ pr_debug(" (%d)\n", vf.nargs - n);
+ if (vf.ret < 0)
+ return vf.ret;
+ n = vf.nargs;
}
return n;
}
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
index 996f007..13edda6 100644
--- a/tools/perf/util/probe-finder.h
+++ b/tools/perf/util/probe-finder.h
@@ -10,6 +10,9 @@
#define MAX_PROBES 128
#define MAX_PROBE_ARGS 128
+#define PROBE_ARG_VARS "$vars"
+#define PROBE_ARG_PARAMS "$params"
+
static inline int is_c_varname(const char *name)
{
/* TODO */
Support glob wildcards for function name when adding
new probes. This will allow us to build caches of
function-entry level information with $params.
e.g.
----
# perf probe --no-inlines --add 'kmalloc* $params'
Added new events:
probe:kmalloc_slab (on kmalloc* with $params)
probe:kmalloc_large_node (on kmalloc* with $params)
probe:kmalloc_order_trace (on kmalloc* with $params)
You can now use it in all perf tools, such as:
perf record -e probe:kmalloc_order_trace -aR sleep 1
# perf probe --list
probe:kmalloc_large_node (on kmalloc_large_node@mm/slub.c with size flags node)
probe:kmalloc_order_trace (on kmalloc_order_trace@mm/slub.c with size flags order)
probe:kmalloc_slab (on kmalloc_slab@mm/slab_common.c with size flags)
----
Signed-off-by: Masami Hiramatsu <[email protected]>
---
tools/perf/util/dwarf-aux.c | 16 ++++++++++++++++
tools/perf/util/dwarf-aux.h | 3 +++
tools/perf/util/probe-event.c | 19 ++++++++++++++-----
tools/perf/util/probe-event.h | 1 +
tools/perf/util/probe-finder.c | 27 +++++++++++++++++++++------
tools/perf/util/util.h | 4 ++++
6 files changed, 59 insertions(+), 11 deletions(-)
diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c
index c34e024..16d46e2 100644
--- a/tools/perf/util/dwarf-aux.c
+++ b/tools/perf/util/dwarf-aux.c
@@ -139,11 +139,27 @@ int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr,
bool die_compare_name(Dwarf_Die *dw_die, const char *tname)
{
const char *name;
+
name = dwarf_diename(dw_die);
return name ? (strcmp(tname, name) == 0) : false;
}
/**
+ * die_match_name - Match diename and glob
+ * @dw_die: a DIE
+ * @glob: a string of target glob pattern
+ *
+ * Glob matching the name of @dw_die and @glob. Return false if matching fail.
+ */
+bool die_match_name(Dwarf_Die *dw_die, const char *glob)
+{
+ const char *name;
+
+ name = dwarf_diename(dw_die);
+ return name ? strglobmatch(name, glob) : false;
+}
+
+/**
* die_get_call_lineno - Get callsite line number of inline-function instance
* @in_die: a DIE of an inlined function instance
*
diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h
index af7dbcd..50a3cdc 100644
--- a/tools/perf/util/dwarf-aux.h
+++ b/tools/perf/util/dwarf-aux.h
@@ -47,6 +47,9 @@ extern bool die_is_func_instance(Dwarf_Die *dw_die);
/* Compare diename and tname */
extern bool die_compare_name(Dwarf_Die *dw_die, const char *tname);
+/* Matching diename with glob pattern */
+extern bool die_match_name(Dwarf_Die *dw_die, const char *glob);
+
/* Get callsite line number of inline-function instance */
extern int die_get_call_lineno(Dwarf_Die *in_die);
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index c9805fd..2c3b79f 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -589,7 +589,11 @@ static int post_process_probe_trace_events(struct probe_trace_event *tevs,
if (!tmp)
return -ENOMEM;
}
- free(tevs[i].point.symbol);
+ /* If we have no realname, use symbol for it */
+ if (!tevs[i].point.realname)
+ tevs[i].point.realname = tevs[i].point.symbol;
+ else
+ free(tevs[i].point.symbol);
tevs[i].point.symbol = tmp;
tevs[i].point.offset = tevs[i].point.address -
reloc_sym->unrelocated_addr;
@@ -1915,6 +1919,7 @@ static void clear_probe_trace_event(struct probe_trace_event *tev)
free(tev->event);
free(tev->group);
free(tev->point.symbol);
+ free(tev->point.realname);
free(tev->point.module);
for (i = 0; i < tev->nargs; i++) {
free(tev->args[i].name);
@@ -2392,6 +2397,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
struct strlist *namelist;
LIST_HEAD(blacklist);
struct kprobe_blacklist_node *node;
+ bool safename;
if (pev->uprobes)
fd = open_uprobe_events(true);
@@ -2417,6 +2423,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
pr_debug("No kprobe blacklist support, ignored\n");
}
+ safename = (pev->point.function && !strisglob(pev->point.function));
ret = 0;
pr_info("Added new event%s\n", (ntevs > 1) ? "s:" : ":");
for (i = 0; i < ntevs; i++) {
@@ -2435,10 +2442,10 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
if (pev->event)
event = pev->event;
else
- if (pev->point.function)
+ if (safename)
event = pev->point.function;
else
- event = tev->point.symbol;
+ event = tev->point.realname;
if (pev->group)
group = pev->group;
else
@@ -2503,9 +2510,11 @@ static int find_probe_functions(struct map *map, char *name)
{
int found = 0;
struct symbol *sym;
+ struct rb_node *tmp;
- map__for_each_symbol_by_name(map, name, sym) {
- found++;
+ map__for_each_symbol(map, sym, tmp) {
+ if (strglobmatch(sym->name, name))
+ found++;
}
return found;
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index f883670..a5ea1fe 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -10,6 +10,7 @@ extern bool probe_event_dry_run;
/* kprobe-tracer and uprobe-tracer tracing point */
struct probe_trace_point {
+ char *realname; /* function real name (if needed) */
char *symbol; /* Base symbol */
char *module; /* Module name */
unsigned long offset; /* Offset from symbol */
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index ce48719..6d61411 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -717,7 +717,7 @@ static int find_best_scope_cb(Dwarf_Die *fn_die, void *data)
}
/* If the function name is given, that's what user expects */
if (fsp->function) {
- if (die_compare_name(fn_die, fsp->function)) {
+ if (die_match_name(fn_die, fsp->function)) {
memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die));
fsp->found = true;
return 1;
@@ -920,13 +920,14 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
/* Check tag and diename */
if (!die_is_func_def(sp_die) ||
- !die_compare_name(sp_die, pp->function))
+ !die_match_name(sp_die, pp->function))
return DWARF_CB_OK;
/* Check declared file */
if (pp->file && strtailcmp(pp->file, dwarf_decl_file(sp_die)))
return DWARF_CB_OK;
+ pr_debug("Matched function: %s\n", dwarf_diename(sp_die));
pf->fname = dwarf_decl_file(sp_die);
if (pp->line) { /* Function relative line */
dwarf_decl_line(sp_die, &pf->lno);
@@ -943,10 +944,20 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
/* TODO: Check the address in this function */
param->retval = call_probe_finder(sp_die, pf);
}
- } else if (!pf->no_inline)
+ } else if (!pf->no_inline) {
/* Inlined function: search instances */
param->retval = die_walk_instances(sp_die,
probe_point_inline_cb, (void *)pf);
+ /* This could be a non-existed inline definition */
+ if (param->retval == -ENOENT && strisglob(pp->function))
+ param->retval = 0;
+ }
+
+ /* We need to find other candidates */
+ if (strisglob(pp->function) && param->retval >= 0) {
+ param->retval = 0; /* We have to clear the result */
+ return DWARF_CB_OK;
+ }
return DWARF_CB_ABORT; /* Exit; no same symbol in this CU. */
}
@@ -975,7 +986,7 @@ static int pubname_search_cb(Dwarf *dbg, Dwarf_Global *gl, void *data)
if (dwarf_tag(param->sp_die) != DW_TAG_subprogram)
return DWARF_CB_OK;
- if (die_compare_name(param->sp_die, param->function)) {
+ if (die_match_name(param->sp_die, param->function)) {
if (!dwarf_offdie(dbg, gl->cu_offset, param->cu_die))
return DWARF_CB_OK;
@@ -1028,7 +1039,7 @@ static int debuginfo__find_probes(struct debuginfo *dbg,
return -ENOMEM;
/* Fastpath: lookup by function name from .debug_pubnames section */
- if (pp->function) {
+ if (pp->function && !strisglob(pp->function)) {
struct pubname_callback_param pubname_param = {
.function = pp->function,
.file = pp->file,
@@ -1177,6 +1188,10 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf)
if (ret < 0)
return ret;
+ tev->point.realname = strdup(dwarf_diename(sc_die));
+ if (!tev->point.realname)
+ return -ENOMEM;
+
pr_debug("Probe point found: %s+%lu\n", tev->point.symbol,
tev->point.offset);
@@ -1538,7 +1553,7 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
return DWARF_CB_OK;
if (die_is_func_def(sp_die) &&
- die_compare_name(sp_die, lr->function)) {
+ die_match_name(sp_die, lr->function)) {
lf->fname = dwarf_decl_file(sp_die);
dwarf_decl_line(sp_die, &lr->offset);
pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset);
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index 1ff23e0..3601ffd 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -257,6 +257,10 @@ char **argv_split(const char *str, int *argcp);
void argv_free(char **argv);
bool strglobmatch(const char *str, const char *pat);
bool strlazymatch(const char *str, const char *pat);
+static inline bool strisglob(const char *str)
+{
+ return strpbrk(str, "*?[") != NULL;
+}
int strtailcmp(const char *s1, const char *s2);
char *strxfrchar(char *s, char from, char to);
unsigned long convert_unit(unsigned long value, char *unit);
Em Wed, May 06, 2015 at 09:46:51PM +0900, Masami Hiramatsu escreveu:
> Add --no-inlines(--inlines) option to avoid searching inline
> functions.
> Searching all functions which matches glob pattern can take a
> long time and find a lot of inline functions. With this option
> perf-probe searches target on the non-inlined functions.
>
>
> ret = add_perf_probe_events(params.events, params.nevents,
> params.max_probe_points,
> - params.force_add);
> + params.force_add,
> + params.no_inlines);
So, there is just one call to this function, and all the parameters come
from the 'param' struct, why not just pass it?
- Arnaldo
Em Wed, May 06, 2015 at 09:46:38PM +0900, Masami Hiramatsu escreveu:
> Hi,
>
> Here is a series of bugfix patches and improvements for perf-probe.
> First 5 patches are new for fixing some minor issues on perf probe,
> and last 3 are for wildcard support which is just updates of the
> patches which I sent before ( https://lkml.org/lkml/2014/10/31/207 ).
>
> I've dropped --output option since it will be done by perf-cache,
> but this series doesn't involve perf-cache feature yet. I found some
> issues when working on that and decided to send it beforehand.
Applied all but [6,8]/8, will do after you reply to those.
Tested most of them, added some comments with examples on using some.
Thanks,
- Arnaldo
On 2015/05/07 0:52, Arnaldo Carvalho de Melo wrote:
> Em Wed, May 06, 2015 at 09:46:51PM +0900, Masami Hiramatsu escreveu:
>> Add --no-inlines(--inlines) option to avoid searching inline
>> functions.
>> Searching all functions which matches glob pattern can take a
>> long time and find a lot of inline functions. With this option
>> perf-probe searches target on the non-inlined functions.
>>
>>
>> ret = add_perf_probe_events(params.events, params.nevents,
>> params.max_probe_points,
>> - params.force_add);
>> + params.force_add,
>> + params.no_inlines);
>
> So, there is just one call to this function, and all the parameters come
> from the 'param' struct, why not just pass it?
Hm, the reason why I don't do that is for readability in
util/probe-event.c side. Passing params to the callee involves
- export params struct (not be static)
- callee has to extract required parameters from params.
- it makes obscure that what parameters will be used in the method.
However, it seems that some of them would be better global configs
like symbol_conf. I'd like to suggest to introduce probe_conf global
config. Is it OK ?
Thank you,
--
Masami HIRAMATSU
Linux Technology Research Center, System Productivity Research Dept.
Center for Technology Innovation - Systems Engineering
Hitachi, Ltd., Research & Development Group
E-mail: [email protected]
Commit-ID: ae2cb1ac60758e99cec15e9edd68e0d22bfd310e
Gitweb: http://git.kernel.org/tip/ae2cb1ac60758e99cec15e9edd68e0d22bfd310e
Author: Masami Hiramatsu <[email protected]>
AuthorDate: Wed, 6 May 2015 21:46:40 +0900
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Fri, 8 May 2015 16:05:01 -0300
perf probe: Fix to close probe_events file in error
Fix perf-probe to close probe_events file if it failed to get existing
probe's name. This also fix the return error code to -ENOMEM.
Signed-off-by: Masami Hiramatsu <[email protected]>
Cc: Ananth N Mavinakayanahalli <[email protected]>
Cc: David Ahern <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/util/probe-event.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index abf5845..230353f 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -2384,7 +2384,8 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
namelist = get_probe_trace_event_names(fd, false);
if (!namelist) {
pr_debug("Failed to get current event list.\n");
- return -EIO;
+ ret = -ENOMEM;
+ goto close_out;
}
/* Get kprobe blacklist if exists */
if (!pev->uprobes) {
@@ -2467,6 +2468,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
kprobe_blacklist__delete(&blacklist);
strlist__delete(namelist);
+close_out:
close(fd);
return ret;
}
Commit-ID: b8dc3984c1fce87a36d3247c9f722229692bec72
Gitweb: http://git.kernel.org/tip/b8dc3984c1fce87a36d3247c9f722229692bec72
Author: Masami Hiramatsu <[email protected]>
AuthorDate: Wed, 6 May 2015 21:46:42 +0900
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Fri, 8 May 2015 16:05:01 -0300
perf probe: Fix a typo for the flags of open
Fix to pass O_APPEND by using bit-or with other flags, instead of
passing it as mode.
Signed-off-by: Masami Hiramatsu <[email protected]>
Cc: Ananth N Mavinakayanahalli <[email protected]>
Cc: David Ahern <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/util/probe-event.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 230353f..63cb7c5 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -1969,7 +1969,7 @@ static int open_probe_events(const char *trace_file, bool readwrite)
if (ret >= 0) {
pr_debug("Opening %s write=%d\n", buf, readwrite);
if (readwrite && !probe_event_dry_run)
- ret = open(buf, O_RDWR, O_APPEND);
+ ret = open(buf, O_RDWR | O_APPEND, 0);
else
ret = open(buf, O_RDONLY, 0);
Commit-ID: 9bc9f3b6800e8de16f40a2da1d6ded3a391ea01a
Gitweb: http://git.kernel.org/tip/9bc9f3b6800e8de16f40a2da1d6ded3a391ea01a
Author: Masami Hiramatsu <[email protected]>
AuthorDate: Wed, 6 May 2015 21:46:45 +0900
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Fri, 8 May 2015 16:05:01 -0300
perf probe: Fix to return 0 when positive value returned
Fix to return 0 when positive value returned from probe command.
At least --vars can returns a positive value if it found a point.
----
# perf probe --vars vfs_read && echo succeeded! || echo failed!
Available variables at vfs_read
@<vfs_read+0>
char* buf
loff_t* pos
size_t count
struct file* file
failed!
----
This fixes above problem.
----
# perf probe --vars vfs_read && echo succeeded! || echo failed!
Available variables at vfs_read
@<vfs_read+0>
char* buf
loff_t* pos
size_t count
struct file* file
succeeded!
----
Signed-off-by: Masami Hiramatsu <[email protected]>
Tested-by: Arnaldo Carvalho de Melo <[email protected]>
Cc: Ananth N Mavinakayanahalli <[email protected]>
Cc: David Ahern <[email protected]>
Cc: Hemant Kumar <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/builtin-probe.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index 53d475b..9c4cf5e 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -523,5 +523,5 @@ int cmd_probe(int argc, const char **argv, const char *prefix)
cleanup_params();
}
- return ret;
+ return ret < 0 ? ret : 0;
}
Commit-ID: 573709fdfd668423ba4202c4f1016e3cd7bdd134
Gitweb: http://git.kernel.org/tip/573709fdfd668423ba4202c4f1016e3cd7bdd134
Author: Masami Hiramatsu <[email protected]>
AuthorDate: Wed, 6 May 2015 21:46:47 +0900
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Fri, 8 May 2015 16:05:02 -0300
perf probe: Make --line checks validate C-style function name
Fix --line to check valid C-style function name and returns
a semantic error if it is not.
For example, previously, --line doesn't support lazy pattern
but it doesn't recognized as a semantic error.
----
# perf probe -L 'func;return*:0-10'
Specified source line is not found.
Error: Failed to show lines.
----
With this patch, it is correctly handled as a semantic error.
----
# perf probe -L 'func;return*:0-10'
Semantic error :'func;return*' is not a valid function name.
...
----
Signed-off-by: Masami Hiramatsu <[email protected]>
Tested-by: Arnaldo Carvalho de Melo <[email protected]>
Cc: Ananth N Mavinakayanahalli <[email protected]>
Cc: David Ahern <[email protected]>
Cc: Hemant Kumar <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/util/probe-event.c | 35 ++++++++++++++++++++---------------
1 file changed, 20 insertions(+), 15 deletions(-)
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 63cb7c5..4265f2e 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -980,6 +980,18 @@ static int parse_line_num(char **ptr, int *val, const char *what)
return 0;
}
+/* Check the name is good for event, group or function */
+static bool is_c_func_name(const char *name)
+{
+ if (!isalpha(*name) && *name != '_')
+ return false;
+ while (*++name != '\0') {
+ if (!isalpha(*name) && !isdigit(*name) && *name != '_')
+ return false;
+ }
+ return true;
+}
+
/*
* Stuff 'lr' according to the line range described by 'arg'.
* The line range syntax is described by:
@@ -1048,10 +1060,15 @@ int parse_line_range_desc(const char *arg, struct line_range *lr)
goto err;
}
lr->function = name;
- } else if (strchr(name, '.'))
+ } else if (strchr(name, '/') || strchr(name, '.'))
lr->file = name;
- else
+ else if (is_c_func_name(name))/* We reuse it for checking funcname */
lr->function = name;
+ else { /* Invalid name */
+ semantic_error("'%s' is not a valid function name.\n", name);
+ err = -EINVAL;
+ goto err;
+ }
return 0;
err:
@@ -1059,18 +1076,6 @@ err:
return err;
}
-/* Check the name is good for event/group */
-static bool check_event_name(const char *name)
-{
- if (!isalpha(*name) && *name != '_')
- return false;
- while (*++name != '\0') {
- if (!isalpha(*name) && !isdigit(*name) && *name != '_')
- return false;
- }
- return true;
-}
-
/* Parse probepoint definition. */
static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
{
@@ -1094,7 +1099,7 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
semantic_error("Group name is not supported yet.\n");
return -ENOTSUP;
}
- if (!check_event_name(arg)) {
+ if (!is_c_func_name(arg)) {
semantic_error("%s is bad for event name -it must "
"follow C symbol-naming rule.\n", arg);
return -EINVAL;
Commit-ID: 5a51fcd1f30c0f93bb54cec7201a3690032470cb
Gitweb: http://git.kernel.org/tip/5a51fcd1f30c0f93bb54cec7201a3690032470cb
Author: Masami Hiramatsu <[email protected]>
AuthorDate: Wed, 6 May 2015 21:46:49 +0900
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Fri, 8 May 2015 16:05:02 -0300
perf probe: Skip kernel symbols which is out of .text
Skip the kernel symbols which is out of .text, e.g. the functions
in .inittext. Those are found in debuginfo/kallsyms, but already
freed from memory.
e.g.
----
# perf probe vfs_caches_init
vfs_caches_init+0 is out of .text, skip it.
Probe point 'vfs_caches_init' not found.
Error: Failed to add events.
----
Signed-off-by: Masami Hiramatsu <[email protected]>
Tested-by: Arnaldo Carvalho de Melo <[email protected]>
Cc: Ananth N Mavinakayanahalli <[email protected]>
Cc: David Ahern <[email protected]>
Cc: Hemant Kumar <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/util/probe-event.c | 31 ++++++++++++++++++++++++-------
1 file changed, 24 insertions(+), 7 deletions(-)
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 4265f2e..37a3a8b 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -557,8 +557,9 @@ static int post_process_probe_trace_events(struct probe_trace_event *tevs,
bool uprobe)
{
struct ref_reloc_sym *reloc_sym;
+ u64 etext_addr;
char *tmp;
- int i;
+ int i, skipped = 0;
if (uprobe)
return add_exec_to_probe_trace_events(tevs, ntevs, module);
@@ -572,19 +573,29 @@ static int post_process_probe_trace_events(struct probe_trace_event *tevs,
pr_warning("Relocated base symbol is not found!\n");
return -EINVAL;
}
+ /* Get the address of _etext for checking non-probable text symbol */
+ etext_addr = kernel_get_symbol_address_by_name("_etext", false);
for (i = 0; i < ntevs; i++) {
if (tevs[i].point.address && !tevs[i].point.retprobe) {
- tmp = strdup(reloc_sym->name);
- if (!tmp)
- return -ENOMEM;
+ /* If we found a wrong one, mark it by NULL symbol */
+ if (etext_addr < tevs[i].point.address) {
+ pr_warning("%s+%lu is out of .text, skip it.\n",
+ tevs[i].point.symbol, tevs[i].point.offset);
+ tmp = NULL;
+ skipped++;
+ } else {
+ tmp = strdup(reloc_sym->name);
+ if (!tmp)
+ return -ENOMEM;
+ }
free(tevs[i].point.symbol);
tevs[i].point.symbol = tmp;
tevs[i].point.offset = tevs[i].point.address -
reloc_sym->unrelocated_addr;
}
}
- return 0;
+ return skipped;
}
/* Try to find perf_probe_event with debuginfo */
@@ -630,11 +641,14 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
pr_debug("Found %d probe_trace_events.\n", ntevs);
ret = post_process_probe_trace_events(*tevs, ntevs,
target, pev->uprobes);
- if (ret < 0) {
+ if (ret < 0 || ret == ntevs) {
clear_probe_trace_events(*tevs, ntevs);
zfree(tevs);
}
- return ret < 0 ? ret : ntevs;
+ if (ret != ntevs)
+ return ret < 0 ? ret : ntevs;
+ ntevs = 0;
+ /* Fall through */
}
if (ntevs == 0) { /* No error but failed to find probe point. */
@@ -2403,6 +2417,9 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
pr_info("Added new event%s\n", (ntevs > 1) ? "s:" : ":");
for (i = 0; i < ntevs; i++) {
tev = &tevs[i];
+ /* Skip if the symbol is out of .text (marked previously) */
+ if (!tev->point.symbol)
+ continue;
/* Ensure that the address is NOT blacklisted */
node = kprobe_blacklist__find_by_address(&blacklist,
tev->point.address);
Commit-ID: f8bffbf1222a64336a81974fc25fe846656ac53e
Gitweb: http://git.kernel.org/tip/f8bffbf1222a64336a81974fc25fe846656ac53e
Author: Masami Hiramatsu <[email protected]>
AuthorDate: Wed, 6 May 2015 21:46:53 +0900
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Fri, 8 May 2015 16:05:03 -0300
perf probe: Support $params special probe argument
$params is similar to $vars but matches only function parameters not
local variables.
Thus, this is useful for tracing function parameter changing or tracing
function call with parameters.
Testing it:
# perf probe tcp_sendmsg '$params'
Added new event:
probe:tcp_sendmsg (on tcp_sendmsg with $params)
You can now use it in all perf tools, such as:
perf record -e probe:tcp_sendmsg -aR sleep 1
# perf probe -l
probe:tcp_sendmsg (on tcp_sendmsg@acme/git/linux/net/ipv4/tcp.c with iocb sk msg size)
# perf record -a -e probe:*
press some random letters to generate TCP (sshd) traffic...
^C[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.223 MB perf.data (6 samples) ]
# perf script
sshd 6385 [2] 3.907529: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 4.138973: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 4.378966: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 4.603681: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 4.818455: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 5.043603: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
# cat /sys/kernel/debug/tracing/events/probe/tcp_sendmsg/format
name: tcp_sendmsg
ID: 1927
format:
field:unsigned short common_type; offset:0; size:2; signed:0;
field:unsigned char common_flags; offset:2; size:1; signed:0;
field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
field:int common_pid; offset:4; size:4; signed:1;
field:unsigned long __probe_ip; offset:8; size:8; signed:0;
field:u64 iocb; offset:16; size:8; signed:0;
field:u64 sk; offset:24; size:8; signed:0;
field:u64 msg; offset:32; size:8; signed:0;
field:u64 size; offset:40; size:8; signed:0;
print fmt: "(%lx) iocb=0x%Lx sk=0x%Lx msg=0x%Lx size=0x%Lx", REC->__probe_ip, REC->iocb, REC->sk, REC->msg, REC->size
#
Do some system wide tracing of this probe + write syscalls:
# perf trace -e write --ev probe:* --filter-pids 6385
462.612 (0.010 ms): bash/19153 write(fd: 1</dev/pts/1>, buf: 0x7f7556c78000, count: 29 ) = 29
462.701 (0.027 ms): sshd/19152 write(fd: 3<socket:[63117]>, buf: 0x7f78dd12e160, count: 68 ) ...
462.701 ( ): probe:tcp_sendmsg:(ffffffff8163db30) iocb=0xffff8803ebec7e70 sk=0xffff88042196ab80 msg=0xffff8803ebec7da8 size=0x44)
462.710 (0.035 ms): sshd/19152 ... [continued]: write()) = 68
462.787 (0.009 ms): bash/19153 write(fd: 2</dev/pts/1>, buf: 0x7f7556c77000, count: 22 ) = 22
462.865 (0.002 ms): sshd/19152 write(fd: 3<socket:[63117]>, buf: 0x7f78dd12e160, count: 68 ) ...
462.865 ( ): probe:tcp_sendmsg:(ffffffff8163db30) iocb=0xffff8803ebec7e70 sk=0xffff88042196ab80 msg=0xffff8803ebec7da8 size=0x44)
462.873 (0.010 ms): sshd/19152 ... [continued]: write()) = 68
Signed-off-by: Masami Hiramatsu <[email protected]>
Tested-by: Arnaldo Carvalho de Melo <[email protected]>
Cc: Ananth N Mavinakayanahalli <[email protected]>
Cc: David Ahern <[email protected]>
Cc: Hemant Kumar <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
[ Add some examples to the changelog message showing how to use it ]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/Documentation/perf-probe.txt | 2 +-
tools/perf/util/probe-finder.c | 29 ++++++++++++++++-------------
tools/perf/util/probe-finder.h | 3 +++
3 files changed, 20 insertions(+), 14 deletions(-)
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index a272f2e..ad3e355 100644
--- a/tools/perf/Documentation/perf-probe.txt
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -151,7 +151,7 @@ Each probe argument follows below syntax.
[NAME=]LOCALVAR|$retval|%REG|@SYMBOL[:TYPE]
'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), local array with fixed index (e.g. array[1], var->array[0], var->pointer[2]), or kprobe-tracer argument format (e.g. $retval, %ax, etc). Note that the name of this argument will be set as the last member name if you specify a local data structure member (e.g. field2 for 'var->field1.field2'.)
-'$vars' special argument is also available for NAME, it is expanded to the local variables which can access at given probe point.
+'$vars' and '$params' special arguments are also available for NAME, '$vars' is expanded to the local variables (including function parameters) which can access at given probe point. '$params' is expanded to only the function parameters.
'TYPE' casts the type of this argument (optional). If omitted, perf probe automatically set the type based on debuginfo. You can specify 'string' type only for the local variable or structure member which is an array of or a pointer to 'char' or 'unsigned char' type.
On x86 systems %REG is always the short form of the register: for example %AX. %RAX or %EAX is not valid.
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index b5bf9d5..63d3389 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -1087,6 +1087,7 @@ found:
struct local_vars_finder {
struct probe_finder *pf;
struct perf_probe_arg *args;
+ bool vars;
int max_args;
int nargs;
int ret;
@@ -1101,7 +1102,7 @@ static int copy_variables_cb(Dwarf_Die *die_mem, void *data)
tag = dwarf_tag(die_mem);
if (tag == DW_TAG_formal_parameter ||
- tag == DW_TAG_variable) {
+ (tag == DW_TAG_variable && vf->vars)) {
if (convert_variable_location(die_mem, vf->pf->addr,
vf->pf->fb_ops, &pf->sp_die,
NULL) == 0) {
@@ -1127,26 +1128,28 @@ static int expand_probe_args(Dwarf_Die *sc_die, struct probe_finder *pf,
Dwarf_Die die_mem;
int i;
int n = 0;
- struct local_vars_finder vf = {.pf = pf, .args = args,
+ struct local_vars_finder vf = {.pf = pf, .args = args, .vars = false,
.max_args = MAX_PROBE_ARGS, .ret = 0};
for (i = 0; i < pf->pev->nargs; i++) {
/* var never be NULL */
- if (strcmp(pf->pev->args[i].var, "$vars") == 0) {
- pr_debug("Expanding $vars into:");
- vf.nargs = n;
- /* Special local variables */
- die_find_child(sc_die, copy_variables_cb, (void *)&vf,
- &die_mem);
- pr_debug(" (%d)\n", vf.nargs - n);
- if (vf.ret < 0)
- return vf.ret;
- n = vf.nargs;
- } else {
+ if (strcmp(pf->pev->args[i].var, PROBE_ARG_VARS) == 0)
+ vf.vars = true;
+ else if (strcmp(pf->pev->args[i].var, PROBE_ARG_PARAMS) != 0) {
/* Copy normal argument */
args[n] = pf->pev->args[i];
n++;
+ continue;
}
+ pr_debug("Expanding %s into:", pf->pev->args[i].var);
+ vf.nargs = n;
+ /* Special local variables */
+ die_find_child(sc_die, copy_variables_cb, (void *)&vf,
+ &die_mem);
+ pr_debug(" (%d)\n", vf.nargs - n);
+ if (vf.ret < 0)
+ return vf.ret;
+ n = vf.nargs;
}
return n;
}
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
index ebf8c8c..f53553d 100644
--- a/tools/perf/util/probe-finder.h
+++ b/tools/perf/util/probe-finder.h
@@ -10,6 +10,9 @@
#define MAX_PROBES 128
#define MAX_PROBE_ARGS 128
+#define PROBE_ARG_VARS "$vars"
+#define PROBE_ARG_PARAMS "$params"
+
static inline int is_c_varname(const char *name)
{
/* TODO */