You can subscribe to this list here.
| 2009 |
Jan
|
Feb
|
Mar
|
Apr
|
May
(32) |
Jun
(66) |
Jul
(102) |
Aug
(78) |
Sep
(106) |
Oct
(137) |
Nov
(147) |
Dec
(147) |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2010 |
Jan
(71) |
Feb
(139) |
Mar
(86) |
Apr
(76) |
May
(57) |
Jun
(10) |
Jul
(12) |
Aug
(6) |
Sep
(8) |
Oct
(12) |
Nov
(12) |
Dec
(18) |
| 2011 |
Jan
(16) |
Feb
(19) |
Mar
(3) |
Apr
(1) |
May
(16) |
Jun
(17) |
Jul
(74) |
Aug
(22) |
Sep
(18) |
Oct
(24) |
Nov
(21) |
Dec
(30) |
| 2012 |
Jan
(31) |
Feb
(16) |
Mar
(22) |
Apr
(25) |
May
(18) |
Jun
(13) |
Jul
(83) |
Aug
(49) |
Sep
(20) |
Oct
(60) |
Nov
(35) |
Dec
(28) |
| 2013 |
Jan
(39) |
Feb
(61) |
Mar
(35) |
Apr
(21) |
May
(45) |
Jun
(56) |
Jul
(20) |
Aug
(9) |
Sep
(10) |
Oct
(31) |
Nov
(8) |
Dec
(4) |
| 2014 |
Jan
(6) |
Feb
(7) |
Mar
(7) |
Apr
(6) |
May
(4) |
Jun
(8) |
Jul
(5) |
Aug
(2) |
Sep
(4) |
Oct
(4) |
Nov
(11) |
Dec
(5) |
| 2015 |
Jan
(4) |
Feb
(4) |
Mar
(3) |
Apr
(4) |
May
(9) |
Jun
(4) |
Jul
(15) |
Aug
(8) |
Sep
(16) |
Oct
(18) |
Nov
(15) |
Dec
(7) |
| 2016 |
Jan
(20) |
Feb
(9) |
Mar
(15) |
Apr
(24) |
May
(16) |
Jun
(28) |
Jul
(22) |
Aug
(23) |
Sep
(18) |
Oct
(30) |
Nov
(40) |
Dec
(9) |
| 2017 |
Jan
(1) |
Feb
(8) |
Mar
(37) |
Apr
(26) |
May
(25) |
Jun
(46) |
Jul
(24) |
Aug
(9) |
Sep
|
Oct
|
Nov
|
Dec
|
|
From: Masami H. <mhi...@re...> - 2010-03-29 20:31:19
|
Query the basic type information (byte-size and signed-flag) from
debuginfo and pass that to kprobe-tracer. This is especially useful
for tracing the members of data structure, because each member has
different byte-size on the memory.
Signed-off-by: Masami Hiramatsu <mhi...@re...>
Cc: Ingo Molnar <mi...@el...>
Cc: Paul Mackerras <pa...@sa...>
Cc: Arnaldo Carvalho de Melo <ac...@re...>
Cc: Peter Zijlstra <pe...@in...>
Cc: Mike Galbraith <ef...@gm...>
Cc: Frederic Weisbecker <fwe...@gm...>
---
tools/perf/util/probe-event.c | 9 +++++
tools/perf/util/probe-event.h | 1 +
tools/perf/util/probe-finder.c | 78 ++++++++++++++++++++++++++++++++++++----
3 files changed, 80 insertions(+), 8 deletions(-)
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 19de8b7..05ca4a9 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -740,6 +740,13 @@ static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg,
buf += ret;
buflen -= ret;
}
+ /* Print argument type */
+ if (arg->type) {
+ ret = e_snprintf(buf, buflen, ":%s", arg->type);
+ if (ret <= 0)
+ return ret;
+ buf += ret;
+ }
return buf - tmp;
}
@@ -848,6 +855,8 @@ void clear_kprobe_trace_event(struct kprobe_trace_event *tev)
free(tev->args[i].name);
if (tev->args[i].value)
free(tev->args[i].value);
+ if (tev->args[i].type)
+ free(tev->args[i].type);
ref = tev->args[i].ref;
while (ref) {
next = ref->next;
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index a73ede6..0759db6 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -23,6 +23,7 @@ struct kprobe_trace_arg_ref {
struct kprobe_trace_arg {
char *name; /* Argument name */
char *value; /* Base value */
+ char *type; /* Type name */
struct kprobe_trace_arg_ref *ref; /* Referencing offset */
};
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 043140f..f3c2706 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -84,6 +84,9 @@ const char *x86_64_regs_table[X86_64_MAX_REGS] = {
#define arch_regs_table x86_32_regs_table
#endif
+/* Kprobe tracer basic type is up to u64 */
+#define MAX_BASIC_TYPE_BITS 64
+
/* Return architecture dependent register string (for kprobe-tracer) */
static const char *get_arch_regstr(unsigned int n)
{
@@ -228,6 +231,31 @@ static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
return die_mem;
}
+static bool die_is_signed_type(Dwarf_Die *tp_die)
+{
+ Dwarf_Attribute attr;
+ Dwarf_Word ret;
+
+ if (dwarf_attr(tp_die, DW_AT_encoding, &attr) == NULL ||
+ dwarf_formudata(&attr, &ret) != 0)
+ return false;
+
+ return (ret == DW_ATE_signed_char || ret == DW_ATE_signed ||
+ ret == DW_ATE_signed_fixed);
+}
+
+static int die_get_byte_size(Dwarf_Die *tp_die)
+{
+ Dwarf_Attribute attr;
+ Dwarf_Word ret;
+
+ if (dwarf_attr(tp_die, DW_AT_byte_size, &attr) == NULL ||
+ dwarf_formudata(&attr, &ret) != 0)
+ return 0;
+
+ return (int)ret;
+}
+
/* Return values for die_find callbacks */
enum {
DIE_FIND_CB_FOUND = 0, /* End of Search */
@@ -404,13 +432,42 @@ static void convert_location(Dwarf_Op *op, struct probe_finder *pf)
}
}
+static void convert_variable_type(Dwarf_Die *vr_die,
+ struct kprobe_trace_arg *targ)
+{
+ Dwarf_Die type;
+ char buf[16];
+ int ret;
+
+ if (die_get_real_type(vr_die, &type) == NULL)
+ die("Failed to get a type information of %s.",
+ dwarf_diename(vr_die));
+
+ ret = die_get_byte_size(&type) * 8;
+ if (ret) {
+ /* Check the bitwidth */
+ if (ret > MAX_BASIC_TYPE_BITS) {
+ pr_warning(" Warning: %s exceeds max-bitwidth."
+ " Cut down to %d bits.\n",
+ dwarf_diename(&type), MAX_BASIC_TYPE_BITS);
+ ret = MAX_BASIC_TYPE_BITS;
+ }
+
+ ret = snprintf(buf, 16, "%c%d",
+ die_is_signed_type(&type) ? 's' : 'u', ret);
+ if (ret < 0 || ret >= 16)
+ die("Failed to convert variable type.");
+ targ->type = xstrdup(buf);
+ }
+}
+
static void convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
struct perf_probe_arg_field *field,
- struct kprobe_trace_arg_ref **ref_ptr)
+ struct kprobe_trace_arg_ref **ref_ptr,
+ Dwarf_Die *die_mem)
{
struct kprobe_trace_arg_ref *ref = *ref_ptr;
Dwarf_Attribute attr;
- Dwarf_Die member;
Dwarf_Die type;
Dwarf_Word offs;
@@ -444,26 +501,27 @@ static void convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
if (dwarf_tag(&type) != DW_TAG_structure_type)
die("%s is not a data structure.", varname);
- if (die_find_member(&type, field->name, &member) == NULL)
+ if (die_find_member(&type, field->name, die_mem) == NULL)
die("%s(tyep:%s) has no member %s.", varname,
dwarf_diename(&type), field->name);
/* Get the offset of the field */
- if (dwarf_attr(&member, DW_AT_data_member_location, &attr) == NULL ||
+ if (dwarf_attr(die_mem, DW_AT_data_member_location, &attr) == NULL ||
dwarf_formudata(&attr, &offs) != 0)
die("Failed to get the offset of %s.", field->name);
ref->offset += (long)offs;
/* Converting next field */
if (field->next)
- convert_variable_fields(&member, field->name, field->next,
- &ref);
+ convert_variable_fields(die_mem, field->name, field->next,
+ &ref, die_mem);
}
/* Show a variables in kprobe event format */
static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
{
Dwarf_Attribute attr;
+ Dwarf_Die die_mem;
Dwarf_Op *expr;
size_t nexpr;
int ret;
@@ -477,9 +535,13 @@ static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
convert_location(expr, pf);
- if (pf->pvar->field)
+ if (pf->pvar->field) {
convert_variable_fields(vr_die, pf->pvar->var,
- pf->pvar->field, &pf->tvar->ref);
+ pf->pvar->field, &pf->tvar->ref,
+ &die_mem);
+ vr_die = &die_mem;
+ }
+ convert_variable_type(vr_die, pf->tvar);
/* *expr will be cached in libdw. Don't free it. */
return ;
error:
--
Masami Hiramatsu
e-mail: mhi...@re...
|
|
From: Masami H. <mhi...@re...> - 2010-03-29 20:30:52
|
Set the last field name to the argument name when the argument
is refering a data-structure member.
e.g.
./perf probe --add 'vfs_read file->f_mode'
Add new event:
probe:vfs_read (on vfs_read with f_mode=file->f_mode)
This probe records file->f_mode, but the argument name becomes "f_mode".
This enables perf-trace command to parse trace event format correctly.
Signed-off-by: Masami Hiramatsu <mhi...@re...>
Cc: Ingo Molnar <mi...@el...>
Cc: Paul Mackerras <pa...@sa...>
Cc: Arnaldo Carvalho de Melo <ac...@re...>
Cc: Peter Zijlstra <pe...@in...>
Cc: Mike Galbraith <ef...@gm...>
Cc: Frederic Weisbecker <fwe...@gm...>
---
tools/perf/Documentation/perf-probe.txt | 2 +-
tools/perf/util/probe-event.c | 4 ++++
2 files changed, 5 insertions(+), 1 deletions(-)
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index 0657cf4..d781fc5 100644
--- a/tools/perf/Documentation/perf-probe.txt
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -87,7 +87,7 @@ Each probe argument follows below syntax.
[NAME=]LOCALVAR|$retval|%REG|@SYMBOL
-'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), or kprobe-tracer argument format (e.g. $retval, %ax, etc).
+'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), 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'.)
LINE SYNTAX
-----------
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index ab6f53d..19de8b7 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -481,6 +481,10 @@ static void parse_perf_probe_arg(const char *str, struct perf_probe_arg *arg)
} while (tmp);
(*fieldp)->name = xstrdup(str);
pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref);
+
+ /* If no name is specified, set the last field name */
+ if (!arg->name)
+ arg->name = xstrdup((*fieldp)->name);
}
/* Parse perf-probe event command */
--
Masami Hiramatsu
e-mail: mhi...@re...
|
|
From: Masami H. <mhi...@re...> - 2010-03-29 20:30:49
|
Fix to close libdw routine when failing to analyze it in
find_perf_probe_point().
Signed-off-by: Masami Hiramatsu <mhi...@re...>
Cc: Ingo Molnar <mi...@el...>
Cc: Paul Mackerras <pa...@sa...>
Cc: Arnaldo Carvalho de Melo <ac...@re...>
Cc: Peter Zijlstra <pe...@in...>
Cc: Mike Galbraith <ef...@gm...>
Cc: Frederic Weisbecker <fwe...@gm...>
---
tools/perf/util/probe-finder.c | 6 ++++--
1 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index db52ec2..5f6cecc 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -812,8 +812,10 @@ int find_perf_probe_point(int fd, unsigned long addr,
return -ENOENT;
/* Find cu die */
- if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr, &cudie))
- return -EINVAL;
+ if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr, &cudie)) {
+ ret = -EINVAL;
+ goto end;
+ }
/* Find a corresponding line */
line = dwarf_getsrc_die(&cudie, (Dwarf_Addr)addr);
--
Masami Hiramatsu
e-mail: mhi...@re...
|
|
From: Masami H. <mhi...@re...> - 2010-03-29 20:30:45
|
Set given names to event arguments. The syntax is same as kprobe-tracer,
you can add 'NAME=' right before each argument.
e.g.
./perf probe vfs_read foo=file
then, 'foo' is set to the argument name as below.
./perf probe -l
probe:vfs_read (on vfs_read@linux-2.6-tip/fs/read_write.c with foo)
Signed-off-by: Masami Hiramatsu <mhi...@re...>
Cc: Ingo Molnar <mi...@el...>
Cc: Paul Mackerras <pa...@sa...>
Cc: Arnaldo Carvalho de Melo <ac...@re...>
Cc: Peter Zijlstra <pe...@in...>
Cc: Mike Galbraith <ef...@gm...>
Cc: Frederic Weisbecker <fwe...@gm...>
---
tools/perf/Documentation/perf-probe.txt | 10 ++++++++-
tools/perf/builtin-probe.c | 4 ++--
tools/perf/util/probe-event.c | 34 ++++++++++++++++++++++---------
tools/perf/util/probe-event.h | 1 +
tools/perf/util/probe-finder.c | 27 +++++++++++++++----------
5 files changed, 52 insertions(+), 24 deletions(-)
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index 0f944b3..0657cf4 100644
--- a/tools/perf/Documentation/perf-probe.txt
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -79,7 +79,15 @@ Probe points are defined by following syntax.
'EVENT' specifies the name of new event, if omitted, it will be set the name of the probed function. Currently, event group name is set as 'probe'.
'FUNC' specifies a probed function name, and it may have one of the following options; '+OFFS' is the offset from function entry address in bytes, ':RLN' is the relative-line number from function entry line, and '%return' means that it probes function return. And ';PTN' means lazy matching pattern (see LAZY MATCHING). Note that ';PTN' must be the end of the probe point definition. In addition, '@SRC' specifies a source file which has that function.
It is also possible to specify a probe point by the source line number or lazy matching by using 'SRC:ALN' or 'SRC;PTN' syntax, where 'SRC' is the source file path, ':ALN' is the line number and ';PTN' is the lazy matching pattern.
-'ARG' specifies the arguments of this probe point. You can use the name of local variable, or kprobe-tracer argument format (e.g. $retval, %ax, etc).
+'ARG' specifies the arguments of this probe point, (see PROBE ARGUMENT).
+
+PROBE ARGUMENT
+--------------
+Each probe argument follows below syntax.
+
+ [NAME=]LOCALVAR|$retval|%REG|@SYMBOL
+
+'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), or kprobe-tracer argument format (e.g. $retval, %ax, etc).
LINE SYNTAX
-----------
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index cf2ffa5..23933c1 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -142,9 +142,9 @@ static const struct option options[] = {
OPT_CALLBACK('a', "add", NULL,
#ifdef DWARF_SUPPORT
"[EVENT=]FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT"
- " [ARG ...]",
+ " [[NAME=]ARG ...]",
#else
- "[EVENT=]FUNC[+OFF|%return] [ARG ...]",
+ "[EVENT=]FUNC[+OFF|%return] [[NAME=]ARG ...]",
#endif
"probe point definition, where\n"
"\t\tGROUP:\tGroup name (optional)\n"
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 3fc0be7..ab6f53d 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -437,22 +437,28 @@ static void parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
/* Parse perf-probe event argument */
static void parse_perf_probe_arg(const char *str, struct perf_probe_arg *arg)
{
- const char *tmp;
+ char *tmp;
struct perf_probe_arg_field **fieldp;
pr_debug("parsing arg: %s into ", str);
+ tmp = strchr(str, '=');
+ if (tmp) {
+ arg->name = xstrndup(str, tmp - str);
+ str = tmp + 1;
+ }
+
tmp = strpbrk(str, "-.");
if (!is_c_varname(str) || !tmp) {
/* A variable, register, symbol or special value */
- arg->name = xstrdup(str);
- pr_debug("%s\n", arg->name);
+ arg->var = xstrdup(str);
+ pr_debug("%s\n", arg->var);
return;
}
/* Structure fields */
- arg->name = xstrndup(str, tmp - str);
- pr_debug("%s, ", arg->name);
+ arg->var = xstrndup(str, tmp - str);
+ pr_debug("%s, ", arg->var);
fieldp = &arg->field;
do {
@@ -497,7 +503,7 @@ void parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev)
pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs);
for (i = 0; i < pev->nargs; i++) {
parse_perf_probe_arg(argv[i + 1], &pev->args[i]);
- if (is_c_varname(pev->args[i].name) && pev->point.retprobe)
+ if (is_c_varname(pev->args[i].var) && pev->point.retprobe)
semantic_error("You can't specify local variable for"
" kretprobe");
}
@@ -514,7 +520,7 @@ bool perf_probe_event_need_dwarf(struct perf_probe_event *pev)
return true;
for (i = 0; i < pev->nargs; i++)
- if (is_c_varname(pev->args[i].name))
+ if (is_c_varname(pev->args[i].var))
return true;
return false;
@@ -575,7 +581,10 @@ int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len)
int ret;
char *tmp = buf;
- ret = e_snprintf(tmp, len, "%s", pa->name);
+ if (pa->name && pa->var)
+ ret = e_snprintf(tmp, len, "%s=%s", pa->name, pa->var);
+ else
+ ret = e_snprintf(tmp, len, "%s", pa->name ? pa->name : pa->var);
if (ret <= 0)
goto error;
tmp += ret;
@@ -803,6 +812,8 @@ void clear_perf_probe_event(struct perf_probe_event *pev)
for (i = 0; i < pev->nargs; i++) {
if (pev->args[i].name)
free(pev->args[i].name);
+ if (pev->args[i].var)
+ free(pev->args[i].var);
field = pev->args[i].field;
while (field) {
next = field->next;
@@ -1117,8 +1128,11 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev,
if (tev->nargs) {
tev->args = xzalloc(sizeof(struct kprobe_trace_arg)
* tev->nargs);
- for (i = 0; i < tev->nargs; i++)
- tev->args[i].value = xstrdup(pev->args[i].name);
+ for (i = 0; i < tev->nargs; i++) {
+ if (pev->args[i].name)
+ tev->args[i].name = xstrdup(pev->args[i].name);
+ tev->args[i].value = xstrdup(pev->args[i].var);
+ }
}
/* Currently just checking function name from symbol map */
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index cd308b0..a73ede6 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -55,6 +55,7 @@ struct perf_probe_arg_field {
/* Perf probe probing argument */
struct perf_probe_arg {
char *name; /* Argument name */
+ char *var; /* Variable name */
struct perf_probe_arg_field *field; /* Structure fields */
};
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 5f6cecc..043140f 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -478,35 +478,40 @@ static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
convert_location(expr, pf);
if (pf->pvar->field)
- convert_variable_fields(vr_die, pf->pvar->name,
+ convert_variable_fields(vr_die, pf->pvar->var,
pf->pvar->field, &pf->tvar->ref);
/* *expr will be cached in libdw. Don't free it. */
return ;
error:
/* TODO: Support const_value */
die("Failed to find the location of %s at this address.\n"
- " Perhaps, it has been optimized out.", pf->pvar->name);
+ " Perhaps, it has been optimized out.", pf->pvar->var);
}
/* Find a variable in a subprogram die */
static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
{
Dwarf_Die vr_die;
- char buf[128];
+ char buf[32];
- /* TODO: Support struct members and arrays */
- if (!is_c_varname(pf->pvar->name)) {
+ /* TODO: Support arrays */
+ if (pf->pvar->name)
+ pf->tvar->name = xstrdup(pf->pvar->name);
+ else {
+ synthesize_perf_probe_arg(pf->pvar, buf, 32);
+ pf->tvar->name = xstrdup(buf);
+ }
+
+ if (!is_c_varname(pf->pvar->var)) {
/* Copy raw parameters */
- pf->tvar->value = xstrdup(pf->pvar->name);
+ pf->tvar->value = xstrdup(pf->pvar->var);
} else {
- synthesize_perf_probe_arg(pf->pvar, buf, 128);
- pf->tvar->name = xstrdup(buf);
pr_debug("Searching '%s' variable in context.\n",
- pf->pvar->name);
+ pf->pvar->var);
/* Search child die for local variables and parameters. */
- if (!die_find_variable(sp_die, pf->pvar->name, &vr_die))
+ if (!die_find_variable(sp_die, pf->pvar->var, &vr_die))
die("Failed to find '%s' in this function.",
- pf->pvar->name);
+ pf->pvar->var);
convert_variable(&vr_die, pf);
}
}
--
Masami Hiramatsu
e-mail: mhi...@re...
|
|
From: Masami H. <mhi...@re...> - 2010-03-29 20:30:23
|
Hi Ingo,
Here are several updates of perf-probe. This series improves
data structure accessing.
- Set the name of argument which traces a data structure member
as the last member of the data structure reference (e.g. f_mode
of file->f_mode). This allows us to use perf-trace for tracin
data-structure members.
- Add the basic type support. This allows us to fetch the memory
with specified bitwidth. Usually, data-structure members are
packed on the memory, this means if we want to read a member
from memory, we have to access it with type casting.
kprobe-tracer now support tracing argument with basic types
(u8,u16,u32,u64,s8,s16,s32,s64), and perf-probe decodes the type
information of the members.
- Support canonical frame address, which is used for refering
frame-base on the kernel built without CONFIG_FRAME_POINTER.
TODOs (possible features):
- Support array element (var[N])
- Support string/dynamic arrays (*var, var[N..M])
- Support tracing static variables (non global)
- Support dynamic array-indexing (var[var2])
- Support force type-casting ((type)var)
- Show what deta-structure member is assigned to each argument.
- Better support for probes on modules
- More debugger like enhancements(%next, --disasm, etc.)
I decided to drop non-root user support (for --add/--del),
because this can set probes for getting sensitive data...
Thank you,
---
Masami Hiramatsu (7):
perf probe: Support DW_OP_call_frame_cfa in debuginfo
perf probe: Support basic type casting
perf probe: Query basic types from debuginfo
trace/kprobes: Support basic types
perf probe: Use the last field name as the argument name
perf probe: Support argument name
[BUGFIX] perf probe: Fix to close dwarf when failing to analyze it
Documentation/trace/kprobetrace.txt | 4
kernel/trace/trace.h | 16 -
kernel/trace/trace_kprobe.c | 535 +++++++++++++++++++------------
tools/perf/Documentation/perf-probe.txt | 11 +
tools/perf/builtin-probe.c | 4
tools/perf/util/probe-event.c | 70 +++-
tools/perf/util/probe-event.h | 3
tools/perf/util/probe-finder.c | 131 ++++++--
tools/perf/util/probe-finder.h | 1
9 files changed, 516 insertions(+), 259 deletions(-)
--
Masami Hiramatsu
e-mail: mhi...@re...
|
|
From: <gr...@su...> - 2010-03-29 18:30:07
|
This is a note to let you know that we have just queued up the patch titled
Subject: perf probe: Fix probe_point buffer overrun
to the 2.6.33-stable tree. Its filename is
perf-probe-fix-probe_point-buffer-overrun.patch
A git repo of this tree can be found at
http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary
>From 594087a04eea544356f9c52e83c1a9bc380ce80f Mon Sep 17 00:00:00 2001
From: Masami Hiramatsu <mhi...@re...>
Date: Fri, 12 Mar 2010 18:22:17 -0500
Subject: perf probe: Fix probe_point buffer overrun
From: Masami Hiramatsu <mhi...@re...>
commit 594087a04eea544356f9c52e83c1a9bc380ce80f upstream.
Fix probe_point array-size overrun problem. In some cases (e.g.
inline function), one user-specified probe-point can be
translated to many probe address, and it overruns pre-defined
array-size. This also removes redundant MAX_PROBES macro
definition.
Signed-off-by: Masami Hiramatsu <mhi...@re...>
Cc: systemtap <sys...@so...>
Cc: DLE <dle...@li...>
LKML-Reference: <20100312232217.2017.45017.stgit@localhost6.localdomain6>
[ Note that only root can create new probes. Eventually we should remove
the MAX_PROBES limit, but that is a larger patch not eligible to
perf/urgent treatment. ]
Signed-off-by: Ingo Molnar <mi...@el...>
Signed-off-by: Greg Kroah-Hartman <gr...@su...>
---
tools/perf/builtin-probe.c | 1 -
tools/perf/util/probe-finder.c | 3 +++
2 files changed, 3 insertions(+), 1 deletion(-)
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -48,7 +48,6 @@
#include "util/probe-event.h"
#define MAX_PATH_LEN 256
-#define MAX_PROBES 128
/* Session management structure */
static struct {
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -544,6 +544,9 @@ static void show_probepoint(Dwarf_Die sp
}
free_current_frame_base(pf);
+ if (pp->found == MAX_PROBES)
+ die("Too many( > %d) probe point found.\n", MAX_PROBES);
+
pp->probes[pp->found] = strdup(tmp);
pp->found++;
}
Patches currently in stable-queue which might be from mhi...@re... are
|
|
From: Arnaldo C. de M. <ac...@in...> - 2010-03-23 17:01:13
|
Em Tue, Mar 23, 2010 at 04:55:13PM +0100, Peter Zijlstra escreveu: > On Thu, 2010-03-18 at 04:28 +0100, Frederic Weisbecker wrote: > > On Wed, Mar 17, 2010 at 03:14:43PM -0400, Masami Hiramatsu wrote: > > > Mark Wielaard wrote: > > > > If you find that a problem then you can do like SystemTap does and allow > > > > '.' in place of '->'. In the code you already use the > > > > perf_probe_arg_field ref flag only to check that the DIE gives you the > > > > same information. So you could just drop that and use any separator. > > > > Then you decide based on whether you see a DW_TAG_pointer_type. This > > > > gives the user some extra flexibility by letting them not having to care > > > > about specifying extra type information already available elsewhere. > > > Thanks, when designing this feature, I considered it too. > > > Since perf probe already support displaying source code by --line option, > > > users will read the probed code itself and try to probe it. In that case, > > > I think they naturally use '.' and '->' as they read (they might try to > > > copy & paste it). > > > So, I think that it would be good to support both of '.' and '->' as > > > they are used in the code, because it will not confuse users. > > And lets people use what is common for them: expressions that follow > > C rules in the context. > > And those who will be more familiar with perf probe will know they can > > use the simplified "." based scheme. > I'd expect a syntax error when I mix up '.' and '->'. Yup, I'd stick with following the C language rules. - Arnaldo |
|
From: Peter Z. <pe...@in...> - 2010-03-23 15:55:24
|
On Thu, 2010-03-18 at 04:28 +0100, Frederic Weisbecker wrote: > On Wed, Mar 17, 2010 at 03:14:43PM -0400, Masami Hiramatsu wrote: > > Mark Wielaard wrote: > > > On Tue, 2010-03-16 at 18:06 -0400, Masami Hiramatsu wrote: > > >> Support accessing members in the data structures. With this, > > >> perf-probe accepts data-structure members(IOW, it now accepts > > >> dot '.' and arrow '->' operators) as probe arguemnts. > > >> > > >> e.g. > > >> > > >> ./perf probe --add 'schedule:44 rq->curr' > > >> > > >> ./perf probe --add 'vfs_read file->f_op->read file->f_path.dentry' > > >> > > >> Note that '>' can be interpreted as redirection in command-line. > > > > > > If you find that a problem then you can do like SystemTap does and allow > > > '.' in place of '->'. In the code you already use the > > > perf_probe_arg_field ref flag only to check that the DIE gives you the > > > same information. So you could just drop that and use any separator. > > > Then you decide based on whether you see a DW_TAG_pointer_type. This > > > gives the user some extra flexibility by letting them not having to care > > > about specifying extra type information already available elsewhere. > > > > Thanks, when designing this feature, I considered it too. > > > > Since perf probe already support displaying source code by --line option, > > users will read the probed code itself and try to probe it. In that case, > > I think they naturally use '.' and '->' as they read (they might try to > > copy & paste it). > > > > So, I think that it would be good to support both of '.' and '->' as > > they are used in the code, because it will not confuse users. > > > > Thank you, > > > Agreed. > > And lets people use what is common for them: expressions that follow > C rules in the context. > > And those who will be more familiar with perf probe will know they can > use the simplified "." based scheme. I'd expect a syntax error when I mix up '.' and '->'. |
|
From: Masami H. <mhi...@re...> - 2010-03-20 04:20:56
|
Frederic Weisbecker wrote: > On Wed, Mar 17, 2010 at 03:14:43PM -0400, Masami Hiramatsu wrote: >> Mark Wielaard wrote: >>> On Tue, 2010-03-16 at 18:06 -0400, Masami Hiramatsu wrote: >>>> Support accessing members in the data structures. With this, >>>> perf-probe accepts data-structure members(IOW, it now accepts >>>> dot '.' and arrow '->' operators) as probe arguemnts. >>>> >>>> e.g. >>>> >>>> ./perf probe --add 'schedule:44 rq->curr' >>>> >>>> ./perf probe --add 'vfs_read file->f_op->read file->f_path.dentry' >>>> >>>> Note that '>' can be interpreted as redirection in command-line. >>> >>> If you find that a problem then you can do like SystemTap does and allow >>> '.' in place of '->'. In the code you already use the >>> perf_probe_arg_field ref flag only to check that the DIE gives you the >>> same information. So you could just drop that and use any separator. >>> Then you decide based on whether you see a DW_TAG_pointer_type. This >>> gives the user some extra flexibility by letting them not having to care >>> about specifying extra type information already available elsewhere. >> >> Thanks, when designing this feature, I considered it too. >> >> Since perf probe already support displaying source code by --line option, >> users will read the probed code itself and try to probe it. In that case, >> I think they naturally use '.' and '->' as they read (they might try to >> copy & paste it). >> >> So, I think that it would be good to support both of '.' and '->' as >> they are used in the code, because it will not confuse users. >> >> Thank you, > > > Agreed. > > And lets people use what is common for them: expressions that follow > C rules in the context. > > And those who will be more familiar with perf probe will know they can > use the simplified "." based scheme. Hi Frederic, Steven, BTW, currently perf trace can't parse these 'data structure' type field yet. e.g. execute following commands; ./perf probe vfs_read 'file->f_mode' ./perf record -e probe:vfs_read -afR ls -l ./perf trace Warning: Error: expected ';' but read '->' Warning: failed to read event format for vfs_read Because perf trace expects that the field name should be a C-variable name. Moreover, if perf probe supports array element, it becomes more problematic, e.g. 'field:u8 array[8]'! oops! what does the field contain? an array with 8 elements? or 8th element of 'array' array? :( Should I suppose that trace event format only accept C-style variable name for field? Thank you, -- Masami Hiramatsu e-mail: mhi...@re... |
|
From: Neil H. <nh...@tu...> - 2010-03-19 14:18:52
|
On Fri, Mar 19, 2010 at 10:10:19AM -0400, Masami Hiramatsu wrote:
> Oleg Nesterov wrote:
> > (add Neil)
> >
> > On 03/19, Masami Hiramatsu wrote:
> >>
> >> void do_coredump(long signr, int exit_code, struct pt_regs *regs)
> >> {
> >> struct core_state core_state;
> >> - char corename[CORENAME_MAX_SIZE + 1];
> >> + char corename[CORENAME_MAX_SIZE + 1] = "";
> >> struct mm_struct *mm = current->mm;
> >> struct linux_binfmt * binfmt;
> >> struct inode * inode;
> >> @@ -1802,6 +1803,7 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
> >> static atomic_t core_dump_count = ATOMIC_INIT(0);
> >> struct coredump_params cprm = {
> >> .signr = signr,
> >> + .file = NULL,
> >> .regs = regs,
> >> .limit = rlimit(RLIMIT_CORE),
> >> /*
> >> @@ -1815,8 +1817,10 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
> >> audit_core_dumps(signr);
> >>
> >> binfmt = mm->binfmt;
> >> - if (!binfmt || !binfmt->core_dump)
> >> + if (!binfmt || !binfmt->core_dump) {
> >> + retval = -ENOSYS;
> >> goto fail;
> >> + }
> >
> > Oh.
> >
> > Masami, may I ask you to delay these changes a bit?
> >
> > This patch conflicts very much with other changes (hopefully in -mm soon)
> > we are doing.
> >
> > If your patch comes first, we have to redo 12 patches. Besides, this patch
> > complicates do_coredump() even more while it really needs the cleanups.
>
> Ah, that's a big conflict :O
> Yes, sure, I can wait for your commits. I'll update this patch on yours.
>
> > Please see
> > http://groups.google.com/group/fa.linux.kernel/browse_thread/thread/96f01d85034ca029/3b6bcb9b2d756dbc
> > I can send you these patches privately if you wish.
>
> OK, I can pick them up from the thread:)
>
> Thank you!
>
Yes, thank you. I think you may find if you put them on top of the changes Oleg
and I have made, you'll find this one will be alot cleaner as well.
Thanks!
Neil
> --
> Masami Hiramatsu
> e-mail: mhi...@re...
>
|
|
From: Oleg N. <ol...@re...> - 2010-03-19 14:16:32
|
On 03/19, Masami Hiramatsu wrote: > > Ah, that's a big conflict :O > Yes, sure, I can wait for your commits. I'll update this patch on yours. Great, thanks ;) Oleg. |
|
From: Masami H. <mhi...@re...> - 2010-03-19 14:11:19
|
Oleg Nesterov wrote:
> (add Neil)
>
> On 03/19, Masami Hiramatsu wrote:
>>
>> void do_coredump(long signr, int exit_code, struct pt_regs *regs)
>> {
>> struct core_state core_state;
>> - char corename[CORENAME_MAX_SIZE + 1];
>> + char corename[CORENAME_MAX_SIZE + 1] = "";
>> struct mm_struct *mm = current->mm;
>> struct linux_binfmt * binfmt;
>> struct inode * inode;
>> @@ -1802,6 +1803,7 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
>> static atomic_t core_dump_count = ATOMIC_INIT(0);
>> struct coredump_params cprm = {
>> .signr = signr,
>> + .file = NULL,
>> .regs = regs,
>> .limit = rlimit(RLIMIT_CORE),
>> /*
>> @@ -1815,8 +1817,10 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
>> audit_core_dumps(signr);
>>
>> binfmt = mm->binfmt;
>> - if (!binfmt || !binfmt->core_dump)
>> + if (!binfmt || !binfmt->core_dump) {
>> + retval = -ENOSYS;
>> goto fail;
>> + }
>
> Oh.
>
> Masami, may I ask you to delay these changes a bit?
>
> This patch conflicts very much with other changes (hopefully in -mm soon)
> we are doing.
>
> If your patch comes first, we have to redo 12 patches. Besides, this patch
> complicates do_coredump() even more while it really needs the cleanups.
Ah, that's a big conflict :O
Yes, sure, I can wait for your commits. I'll update this patch on yours.
> Please see
> http://groups.google.com/group/fa.linux.kernel/browse_thread/thread/96f01d85034ca029/3b6bcb9b2d756dbc
> I can send you these patches privately if you wish.
OK, I can pick them up from the thread:)
Thank you!
--
Masami Hiramatsu
e-mail: mhi...@re...
|
|
From: Oleg N. <ol...@re...> - 2010-03-19 13:35:37
|
(add Neil)
On 03/19, Masami Hiramatsu wrote:
>
> void do_coredump(long signr, int exit_code, struct pt_regs *regs)
> {
> struct core_state core_state;
> - char corename[CORENAME_MAX_SIZE + 1];
> + char corename[CORENAME_MAX_SIZE + 1] = "";
> struct mm_struct *mm = current->mm;
> struct linux_binfmt * binfmt;
> struct inode * inode;
> @@ -1802,6 +1803,7 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
> static atomic_t core_dump_count = ATOMIC_INIT(0);
> struct coredump_params cprm = {
> .signr = signr,
> + .file = NULL,
> .regs = regs,
> .limit = rlimit(RLIMIT_CORE),
> /*
> @@ -1815,8 +1817,10 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
> audit_core_dumps(signr);
>
> binfmt = mm->binfmt;
> - if (!binfmt || !binfmt->core_dump)
> + if (!binfmt || !binfmt->core_dump) {
> + retval = -ENOSYS;
> goto fail;
> + }
Oh.
Masami, may I ask you to delay these changes a bit?
This patch conflicts very much with other changes (hopefully in -mm soon)
we are doing.
If your patch comes first, we have to redo 12 patches. Besides, this patch
complicates do_coredump() even more while it really needs the cleanups.
Please see
http://groups.google.com/group/fa.linux.kernel/browse_thread/thread/96f01d85034ca029/3b6bcb9b2d756dbc
I can send you these patches privately if you wish.
Oleg.
|
|
From: Masami H. <mhi...@re...> - 2010-03-19 13:16:29
|
Add signal coredump tracepoint which shows signal number,
mm->flags, core file size limitation, the result of
coredump, and core file name.
This tracepoint requirement comes mainly from the viewpoint of
administrators. Since now we have introduced many coredump
configurations (e.g. dumpable, coredump_filter, core_pattern,
etc) and some of them can be modified by users, it will be hard
to know what was actually dumped (or not dumped) after some
problem happened on the system. For example, a process didn't
generated core, coredump doesn't have some sections, etc.
In those cases, the coredump tracepoint can help us to know
why the core file is so big or small, or not generated, by
recording all configurations for all processes on the system.
That will reduce system-administration cost.
Changes in v5:
- Just update against the latest -tip.
Changes in v4:
- Rename limit trace-argument to core_size_limit.
Changes in v3:
- Move tracepoint at the end of do_coredump() for tracing
the result of coredump.
- Use retval to record error-code at every failure points
for passing the result of coredump to tracepoint.
- Trace retval instead of cprm->file for recording the
result of coredump.
Changes in v2:
- Fix a bug to clear file local variable when
call_usermodehelper_pipe() is failed.
Signed-off-by: Masami Hiramatsu <mhi...@re...>
Cc: Oleg Nesterov <ol...@re...>
Cc: Roland McGrath <ro...@re...>
Cc: Jason Baron <jb...@re...>
Cc: Ingo Molnar <mi...@el...>
Cc: Andrew Morton <ak...@li...>
Cc: KOSAKI Motohiro <kos...@jp...>
---
fs/exec.c | 58 ++++++++++++++++++++++++++++++-----------
include/trace/events/signal.h | 48 ++++++++++++++++++++++++++++++++++
2 files changed, 90 insertions(+), 16 deletions(-)
diff --git a/fs/exec.c b/fs/exec.c
index 49cdaa1..b6bdf00 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -55,6 +55,7 @@
#include <linux/fsnotify.h>
#include <linux/fs_struct.h>
#include <linux/pipe_fs_i.h>
+#include <trace/events/signal.h>
#include <asm/uaccess.h>
#include <asm/mmu_context.h>
@@ -1787,7 +1788,7 @@ static void wait_for_dump_helpers(struct file *file)
void do_coredump(long signr, int exit_code, struct pt_regs *regs)
{
struct core_state core_state;
- char corename[CORENAME_MAX_SIZE + 1];
+ char corename[CORENAME_MAX_SIZE + 1] = "";
struct mm_struct *mm = current->mm;
struct linux_binfmt * binfmt;
struct inode * inode;
@@ -1802,6 +1803,7 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
static atomic_t core_dump_count = ATOMIC_INIT(0);
struct coredump_params cprm = {
.signr = signr,
+ .file = NULL,
.regs = regs,
.limit = rlimit(RLIMIT_CORE),
/*
@@ -1815,8 +1817,10 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
audit_core_dumps(signr);
binfmt = mm->binfmt;
- if (!binfmt || !binfmt->core_dump)
+ if (!binfmt || !binfmt->core_dump) {
+ retval = -ENOSYS;
goto fail;
+ }
cred = prepare_creds();
if (!cred) {
@@ -1829,6 +1833,7 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
* If another thread got here first, or we are not dumpable, bail out.
*/
if (mm->core_state || !__get_dumpable(cprm.mm_flags)) {
+ /* This is not an error. retval should be 0 */
up_write(&mm->mmap_sem);
put_cred(cred);
goto fail;
@@ -1867,11 +1872,14 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
ispipe = format_corename(corename, signr);
unlock_kernel();
- if ((!ispipe) && (cprm.limit < binfmt->min_coredump))
+ if ((!ispipe) && (cprm.limit < binfmt->min_coredump)) {
+ retval = -EFBIG;
goto fail_unlock;
+ }
if (ispipe) {
if (cprm.limit == 0) {
+ retval = -EINVAL;
/*
* Normally core limits are irrelevant to pipes, since
* we're not writing to the file system, but we use
@@ -1895,6 +1903,7 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
dump_count = atomic_inc_return(&core_dump_count);
if (core_pipe_limit && (core_pipe_limit < dump_count)) {
+ retval = -EFBIG;
printk(KERN_WARNING "Pid %d(%s) over core_pipe_limit\n",
task_tgid_vnr(current), current->comm);
printk(KERN_WARNING "Skipping core dump\n");
@@ -1903,6 +1912,7 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
helper_argv = argv_split(GFP_KERNEL, corename+1, &helper_argc);
if (!helper_argv) {
+ retval = -ENOMEM;
printk(KERN_WARNING "%s failed to allocate memory\n",
__func__);
goto fail_dropcount;
@@ -1911,8 +1921,9 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
cprm.limit = RLIM_INFINITY;
/* SIGPIPE can happen, but it's just never processed */
- if (call_usermodehelper_pipe(helper_argv[0], helper_argv, NULL,
- &cprm.file)) {
+ retval = call_usermodehelper_pipe(helper_argv[0], helper_argv,
+ NULL, &cprm.file);
+ if (retval < 0) {
printk(KERN_INFO "Core dump to %s pipe failed\n",
corename);
goto fail_dropcount;
@@ -1921,32 +1932,44 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
cprm.file = filp_open(corename,
O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag,
0600);
- if (IS_ERR(cprm.file))
+ if (IS_ERR(cprm.file)) {
+ retval = (int)PTR_ERR(cprm.file);
goto fail_dropcount;
+ }
inode = cprm.file->f_path.dentry->d_inode;
- if (inode->i_nlink > 1)
+ if (inode->i_nlink > 1) {
+ retval = -EMLINK;
goto close_fail; /* multiple links - don't dump */
- if (!ispipe && d_unhashed(cprm.file->f_path.dentry))
+ }
+ if (!ispipe && d_unhashed(cprm.file->f_path.dentry)) {
+ retval = -EBADF;
goto close_fail;
+ }
/* AK: actually i see no reason to not allow this for named pipes etc.,
but keep the previous behaviour for now. */
- if (!ispipe && !S_ISREG(inode->i_mode))
+ if (!ispipe && !S_ISREG(inode->i_mode)) {
+ retval = -EBADF;
goto close_fail;
+ }
/*
* Dont allow local users get cute and trick others to coredump
* into their pre-created files:
* Note, this is not relevant for pipes
*/
- if (!ispipe && (inode->i_uid != current_fsuid()))
+ if (!ispipe && (inode->i_uid != current_fsuid())) {
+ retval = -EPERM;
goto close_fail;
- if (!cprm.file->f_op)
- goto close_fail;
- if (!cprm.file->f_op->write)
- goto close_fail;
- if (!ispipe &&
- do_truncate(cprm.file->f_path.dentry, 0, 0, cprm.file) != 0)
+ }
+ if (!cprm.file->f_op || !cprm.file->f_op->write) {
+ retval = -EINVAL;
goto close_fail;
+ }
+ if (!ispipe) {
+ retval = do_truncate(cprm.file->f_path.dentry, 0, 0, cprm.file);
+ if (retval != 0)
+ goto close_fail;
+ }
retval = binfmt->core_dump(&cprm);
@@ -1967,5 +1990,8 @@ fail_unlock:
put_cred(cred);
coredump_finish(mm);
fail:
+ /* Trace coredump parameters and return value */
+ trace_signal_coredump(&cprm, corename, retval);
+
return;
}
diff --git a/include/trace/events/signal.h b/include/trace/events/signal.h
index a510b75..6dbc856 100644
--- a/include/trace/events/signal.h
+++ b/include/trace/events/signal.h
@@ -4,8 +4,10 @@
#if !defined(_TRACE_SIGNAL_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_SIGNAL_H
+#include <linux/err.h>
#include <linux/signal.h>
#include <linux/sched.h>
+#include <linux/binfmts.h>
#include <linux/tracepoint.h>
#define TP_STORE_SIGINFO(__entry, info) \
@@ -167,6 +169,52 @@ TRACE_EVENT(signal_lose_info,
TP_printk("sig=%d group=%d errno=%d code=%d",
__entry->sig, __entry->group, __entry->errno, __entry->code)
);
+
+/**
+ * signal_coredump - called when dumping core by signal
+ * @cprm: pointer to struct coredump_params
+ * @core_name: core-name string
+ * @retval: return value of binfmt->coredump or error-code
+ *
+ * Current process dumps core file to 'core_name' file, because 'cprm->signr'
+ * signal is delivered.
+ * 'retval' is an error code or 0/1. retval == 1 means the core file was
+ * dumped successfully and retval == 0 means binfmt->coredump failed to dump.
+ * If retval < 0, this means do_coredump() failed to dump core file before
+ * calling binfmt->coredump.
+ */
+TRACE_EVENT(signal_coredump,
+
+ TP_PROTO(struct coredump_params *cprm, const char *core_name,
+ int retval),
+
+ TP_ARGS(cprm, core_name, retval),
+
+ TP_STRUCT__entry(
+ __field( int, sig )
+ __field( unsigned long, core_size_limit )
+ __field( unsigned long, flags )
+ __field( int, retval )
+ __string( name, core_name )
+ ),
+
+
+ TP_fast_assign(
+ __entry->sig = (int)cprm->signr;
+ __entry->core_size_limit = cprm->limit;
+ __entry->flags = cprm->mm_flags;
+ __entry->retval = retval;
+ __assign_str(name, core_name);
+ ),
+
+ TP_printk("sig=%d core_size_limit=%lu dumpable=0x%lx dump_filter=0x%lx"
+ " corename=\"%s\" retval=%d",
+ __entry->sig, __entry->core_size_limit,
+ __entry->flags & MMF_DUMPABLE_MASK,
+ (__entry->flags & MMF_DUMP_FILTER_MASK) >>
+ MMF_DUMP_FILTER_SHIFT,
+ __get_str(name), __entry->retval)
+);
#endif /* _TRACE_SIGNAL_H */
/* This part must be outside protection */
--
Masami Hiramatsu
Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division
e-mail: mhi...@re...
|
|
From: Frederic W. <fwe...@gm...> - 2010-03-18 03:28:09
|
On Wed, Mar 17, 2010 at 03:14:43PM -0400, Masami Hiramatsu wrote: > Mark Wielaard wrote: > > On Tue, 2010-03-16 at 18:06 -0400, Masami Hiramatsu wrote: > >> Support accessing members in the data structures. With this, > >> perf-probe accepts data-structure members(IOW, it now accepts > >> dot '.' and arrow '->' operators) as probe arguemnts. > >> > >> e.g. > >> > >> ./perf probe --add 'schedule:44 rq->curr' > >> > >> ./perf probe --add 'vfs_read file->f_op->read file->f_path.dentry' > >> > >> Note that '>' can be interpreted as redirection in command-line. > > > > If you find that a problem then you can do like SystemTap does and allow > > '.' in place of '->'. In the code you already use the > > perf_probe_arg_field ref flag only to check that the DIE gives you the > > same information. So you could just drop that and use any separator. > > Then you decide based on whether you see a DW_TAG_pointer_type. This > > gives the user some extra flexibility by letting them not having to care > > about specifying extra type information already available elsewhere. > > Thanks, when designing this feature, I considered it too. > > Since perf probe already support displaying source code by --line option, > users will read the probed code itself and try to probe it. In that case, > I think they naturally use '.' and '->' as they read (they might try to > copy & paste it). > > So, I think that it would be good to support both of '.' and '->' as > they are used in the code, because it will not confuse users. > > Thank you, Agreed. And lets people use what is common for them: expressions that follow C rules in the context. And those who will be more familiar with perf probe will know they can use the simplified "." based scheme. |
|
From: Masami H. <mhi...@re...> - 2010-03-17 22:04:14
|
Add NO_DWARF make option for testing build without libdw.
Signed-off-by: Masami Hiramatsu <mhi...@re...>
Cc: Ingo Molnar <mi...@el...>
Cc: Frederic Weisbecker <fwe...@gm...>
Cc: Arnaldo Carvalho de Melo <ac...@re...>
Cc: Paul Mackerras <pa...@sa...>
Cc: Mike Galbraith <ef...@gm...>
Cc: Peter Zijlstra <a.p...@ch...>
---
tools/perf/Makefile | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index c991818..6903645 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -150,6 +150,8 @@ all::
# Define LDFLAGS=-static to build a static binary.
#
# Define EXTRA_CFLAGS=-m64 or EXTRA_CFLAGS=-m32 as appropriate for cross-builds.
+#
+# Define NO_DWARF if you do not want debug-info analysis feature at all.
PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE
@$(SHELL_PATH) util/PERF-VERSION-GEN
@@ -507,10 +509,12 @@ endif
ifneq ($(shell sh -c "(echo '\#include <dwarf.h>'; echo '\#include <libdw.h>'; echo 'int main(void) { Dwarf *dbg; dbg = dwarf_begin(0, DWARF_C_READ); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -I/usr/include/elfutils -ldw -lelf -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y)
msg := $(warning No libdw.h found or old libdw.h found, disables dwarf support. Please install elfutils-devel/elfutils-dev);
else
+ifndef NO_DWARF
BASIC_CFLAGS += -I/usr/include/elfutils -DDWARF_SUPPORT
EXTLIBS += -lelf -ldw
LIB_OBJS += util/probe-finder.o
endif
+endif
ifneq ($(shell sh -c "(echo '\#include <newt.h>'; echo 'int main(void) { newtInit(); newtCls(); return newtFinished(); }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -lnewt -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y)
msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev);
--
Masami Hiramatsu
e-mail: mhi...@re...
|
|
From: Masami H. <mhi...@re...> - 2010-03-17 22:04:01
|
Cleanup debuginfo related code to eliminate fragile code which
pointed by Ingo (Thanks!).
1) Invert logic of NO_DWARF_SUPPORT to DWARF_SUPPORT.
2) For removing assymetric/local variable ifdefs, introduce
more helper functions.
3) Change options order to reduce the number of ifdefs.
Signed-off-by: Masami Hiramatsu <mhi...@re...>
Reported-by: Ingo Molnar <mi...@el...>
Cc: Frederic Weisbecker <fwe...@gm...>
Cc: Arnaldo Carvalho de Melo <ac...@re...>
Cc: Paul Mackerras <pa...@sa...>
Cc: Mike Galbraith <ef...@gm...>
Cc: Peter Zijlstra <a.p...@ch...>
---
tools/perf/Makefile | 3
tools/perf/builtin-probe.c | 28 ++-
tools/perf/util/probe-event.c | 343 +++++++++++++++++++++-------------------
tools/perf/util/probe-finder.h | 4
4 files changed, 200 insertions(+), 178 deletions(-)
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 0abd25e..c991818 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -506,9 +506,8 @@ endif
ifneq ($(shell sh -c "(echo '\#include <dwarf.h>'; echo '\#include <libdw.h>'; echo 'int main(void) { Dwarf *dbg; dbg = dwarf_begin(0, DWARF_C_READ); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -I/usr/include/elfutils -ldw -lelf -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y)
msg := $(warning No libdw.h found or old libdw.h found, disables dwarf support. Please install elfutils-devel/elfutils-dev);
- BASIC_CFLAGS += -DNO_DWARF_SUPPORT
else
- BASIC_CFLAGS += -I/usr/include/elfutils
+ BASIC_CFLAGS += -I/usr/include/elfutils -DDWARF_SUPPORT
EXTLIBS += -lelf -ldw
LIB_OBJS += util/probe-finder.o
endif
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index e0dafd9..cf2ffa5 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -109,7 +109,7 @@ static int opt_del_probe_event(const struct option *opt __used,
return 0;
}
-#ifndef NO_DWARF_SUPPORT
+#ifdef DWARF_SUPPORT
static int opt_show_lines(const struct option *opt __used,
const char *str, int unset __used)
{
@@ -126,7 +126,7 @@ static const char * const probe_usage[] = {
"perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]",
"perf probe [<options>] --del '[GROUP:]EVENT' ...",
"perf probe --list",
-#ifndef NO_DWARF_SUPPORT
+#ifdef DWARF_SUPPORT
"perf probe --line 'LINEDESC'",
#endif
NULL
@@ -135,20 +135,16 @@ static const char * const probe_usage[] = {
static const struct option options[] = {
OPT_BOOLEAN('v', "verbose", &verbose,
"be more verbose (show parsed arguments, etc)"),
-#ifndef NO_DWARF_SUPPORT
- OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
- "file", "vmlinux pathname"),
-#endif
OPT_BOOLEAN('l', "list", ¶ms.list_events,
"list up current probe events"),
OPT_CALLBACK('d', "del", NULL, "[GROUP:]EVENT", "delete a probe event.",
opt_del_probe_event),
OPT_CALLBACK('a', "add", NULL,
-#ifdef NO_DWARF_SUPPORT
- "[EVENT=]FUNC[+OFF|%return] [ARG ...]",
-#else
+#ifdef DWARF_SUPPORT
"[EVENT=]FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT"
" [ARG ...]",
+#else
+ "[EVENT=]FUNC[+OFF|%return] [ARG ...]",
#endif
"probe point definition, where\n"
"\t\tGROUP:\tGroup name (optional)\n"
@@ -156,23 +152,25 @@ static const struct option options[] = {
"\t\tFUNC:\tFunction name\n"
"\t\tOFF:\tOffset from function entry (in byte)\n"
"\t\t%return:\tPut the probe at function return\n"
-#ifdef NO_DWARF_SUPPORT
- "\t\tARG:\tProbe argument (only \n"
-#else
+#ifdef DWARF_SUPPORT
"\t\tSRC:\tSource code path\n"
"\t\tRL:\tRelative line number from function entry.\n"
"\t\tAL:\tAbsolute line number in file.\n"
"\t\tPT:\tLazy expression of line code.\n"
"\t\tARG:\tProbe argument (local variable name or\n"
-#endif
"\t\t\tkprobe-tracer argument format.)\n",
+#else
+ "\t\tARG:\tProbe argument (kprobe-tracer argument format.)\n",
+#endif
opt_add_probe_event),
OPT_BOOLEAN('f', "force", ¶ms.force_add, "forcibly add events"
" with existing name"),
-#ifndef NO_DWARF_SUPPORT
+#ifdef DWARF_SUPPORT
OPT_CALLBACK('L', "line", NULL,
"FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]",
"Show source code lines.", opt_show_lines),
+ OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
+ "file", "vmlinux pathname"),
#endif
OPT__DRY_RUN(&probe_event_dry_run),
OPT_END()
@@ -211,7 +209,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
return 0;
}
-#ifndef NO_DWARF_SUPPORT
+#ifdef DWARF_SUPPORT
if (params.show_lines) {
if (params.nevents != 0 || params.dellist) {
pr_warning(" Error: Don't use --line with"
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index f333269..284294d 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -42,7 +42,8 @@
#include "color.h"
#include "symbol.h"
#include "thread.h"
-#include "parse-events.h" /* For debugfs_path */
+#include "trace-event.h" /* For __unused */
+#include "parse-events.h" /* For debugfs_path */
#include "probe-event.h"
#include "probe-finder.h"
@@ -70,10 +71,11 @@ static int e_snprintf(char *str, size_t size, const char *format, ...)
return ret;
}
+static char *synthesize_perf_probe_point(struct perf_probe_point *pp);
static struct map_groups kmap_groups;
static struct map *kmaps[MAP__NR_TYPES];
-/* Initialize symbol maps for vmlinux */
+/* Initialize symbol maps and path of vmlinux */
static void init_vmlinux(void)
{
symbol_conf.sort_by_name = true;
@@ -89,7 +91,7 @@ static void init_vmlinux(void)
die("Failed to create kernel maps.");
}
-#ifndef NO_DWARF_SUPPORT
+#ifdef DWARF_SUPPORT
static int open_vmlinux(void)
{
if (map__load(kmaps[MAP__FUNCTION], NULL) < 0) {
@@ -99,6 +101,176 @@ static int open_vmlinux(void)
pr_debug("Try to open %s\n", kmaps[MAP__FUNCTION]->dso->long_name);
return open(kmaps[MAP__FUNCTION]->dso->long_name, O_RDONLY);
}
+
+static void convert_to_perf_probe_point(struct kprobe_trace_point *tp,
+ struct perf_probe_point *pp)
+{
+ struct symbol *sym;
+ int fd, ret = 0;
+
+ sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION],
+ tp->symbol, NULL);
+ if (sym) {
+ fd = open_vmlinux();
+ ret = find_perf_probe_point(fd, sym->start + tp->offset, pp);
+ close(fd);
+ }
+ if (ret <= 0) {
+ pp->function = xstrdup(tp->symbol);
+ pp->offset = tp->offset;
+ }
+ pp->retprobe = tp->retprobe;
+}
+
+/* Try to find perf_probe_event with debuginfo */
+static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev,
+ struct kprobe_trace_event **tevs)
+{
+ bool need_dwarf = perf_probe_event_need_dwarf(pev);
+ int fd, ntevs;
+
+ fd = open_vmlinux();
+ if (fd < 0) {
+ if (need_dwarf)
+ die("Could not open debuginfo file.");
+
+ pr_debug("Could not open vmlinux. Try to use symbols.\n");
+ return 0;
+ }
+
+ /* Searching trace events corresponding to probe event */
+ ntevs = find_kprobe_trace_events(fd, pev, tevs);
+ close(fd);
+
+ if (ntevs > 0) /* Succeeded to find trace events */
+ return ntevs;
+
+ if (ntevs == 0) /* No error but failed to find probe point. */
+ die("Probe point '%s' not found. - probe not added.",
+ synthesize_perf_probe_point(&pev->point));
+
+ /* Error path */
+ if (need_dwarf) {
+ if (ntevs == -ENOENT)
+ pr_warning("No dwarf info found in the vmlinux - "
+ "please rebuild with CONFIG_DEBUG_INFO=y.\n");
+ die("Could not analyze debuginfo.");
+ }
+ pr_debug("An error occurred in debuginfo analysis."
+ " Try to use symbols.\n");
+ return 0;
+
+}
+
+#define LINEBUF_SIZE 256
+#define NR_ADDITIONAL_LINES 2
+
+static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num)
+{
+ char buf[LINEBUF_SIZE];
+ const char *color = PERF_COLOR_BLUE;
+
+ if (fgets(buf, LINEBUF_SIZE, fp) == NULL)
+ goto error;
+ if (!skip) {
+ if (show_num)
+ fprintf(stdout, "%7u %s", l, buf);
+ else
+ color_fprintf(stdout, color, " %s", buf);
+ }
+
+ while (strlen(buf) == LINEBUF_SIZE - 1 &&
+ buf[LINEBUF_SIZE - 2] != '\n') {
+ if (fgets(buf, LINEBUF_SIZE, fp) == NULL)
+ goto error;
+ if (!skip) {
+ if (show_num)
+ fprintf(stdout, "%s", buf);
+ else
+ color_fprintf(stdout, color, "%s", buf);
+ }
+ }
+ return;
+error:
+ if (feof(fp))
+ die("Source file is shorter than expected.");
+ else
+ die("File read error: %s", strerror(errno));
+}
+
+/*
+ * Show line-range always requires debuginfo to find source file and
+ * line number.
+ */
+void show_line_range(struct line_range *lr)
+{
+ unsigned int l = 1;
+ struct line_node *ln;
+ FILE *fp;
+ int fd, ret;
+
+ /* Search a line range */
+ init_vmlinux();
+ fd = open_vmlinux();
+ if (fd < 0)
+ die("Could not open debuginfo file.");
+ ret = find_line_range(fd, lr);
+ if (ret <= 0)
+ die("Source line is not found.\n");
+ close(fd);
+
+ setup_pager();
+
+ if (lr->function)
+ fprintf(stdout, "<%s:%d>\n", lr->function,
+ lr->start - lr->offset);
+ else
+ fprintf(stdout, "<%s:%d>\n", lr->file, lr->start);
+
+ fp = fopen(lr->path, "r");
+ if (fp == NULL)
+ die("Failed to open %s: %s", lr->path, strerror(errno));
+ /* Skip to starting line number */
+ while (l < lr->start)
+ show_one_line(fp, l++, true, false);
+
+ list_for_each_entry(ln, &lr->line_list, list) {
+ while (ln->line > l)
+ show_one_line(fp, (l++) - lr->offset, false, false);
+ show_one_line(fp, (l++) - lr->offset, false, true);
+ }
+
+ if (lr->end == INT_MAX)
+ lr->end = l + NR_ADDITIONAL_LINES;
+ while (l < lr->end && !feof(fp))
+ show_one_line(fp, (l++) - lr->offset, false, false);
+
+ fclose(fp);
+}
+
+#else /* !DWARF_SUPPORT */
+
+static void convert_to_perf_probe_point(struct kprobe_trace_point *tp,
+ struct perf_probe_point *pp)
+{
+ pp->function = xstrdup(tp->symbol);
+ pp->offset = tp->offset;
+ pp->retprobe = tp->retprobe;
+}
+
+static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev,
+ struct kprobe_trace_event **tevs __unused)
+{
+ if (perf_probe_event_need_dwarf(pev))
+ die("Debuginfo-analysis is not supported");
+ return 0;
+}
+
+void show_line_range(struct line_range *lr __unused)
+{
+ die("Debuginfo-analysis is not supported");
+}
+
#endif
void parse_line_range_desc(const char *arg, struct line_range *lr)
@@ -592,32 +764,14 @@ void convert_to_perf_probe_event(struct kprobe_trace_event *tev,
{
char buf[64];
int i;
-#ifndef NO_DWARF_SUPPORT
- struct symbol *sym;
- int fd, ret = 0;
-
- sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION],
- tev->point.symbol, NULL);
- if (sym) {
- fd = open_vmlinux();
- ret = find_perf_probe_point(fd, sym->start + tev->point.offset,
- &pev->point);
- close(fd);
- }
- if (ret <= 0) {
- pev->point.function = xstrdup(tev->point.symbol);
- pev->point.offset = tev->point.offset;
- }
-#else
- /* Convert trace_point to probe_point */
- pev->point.function = xstrdup(tev->point.symbol);
- pev->point.offset = tev->point.offset;
-#endif
- pev->point.retprobe = tev->point.retprobe;
+ /* Convert event/group name */
pev->event = xstrdup(tev->event);
pev->group = xstrdup(tev->group);
+ /* Convert trace_point to probe_point */
+ convert_to_perf_probe_point(&tev->point, &pev->point);
+
/* Convert trace_arg to probe_arg */
pev->nargs = tev->nargs;
pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs);
@@ -944,52 +1098,13 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev,
struct kprobe_trace_event **tevs)
{
struct symbol *sym;
- bool need_dwarf;
-#ifndef NO_DWARF_SUPPORT
- int fd;
-#endif
int ntevs = 0, i;
struct kprobe_trace_event *tev;
- need_dwarf = perf_probe_event_need_dwarf(pev);
-
- if (need_dwarf)
-#ifdef NO_DWARF_SUPPORT
- die("Debuginfo-analysis is not supported");
-#else /* !NO_DWARF_SUPPORT */
- pr_debug("Some probes require debuginfo.\n");
-
- fd = open_vmlinux();
- if (fd < 0) {
- if (need_dwarf)
- die("Could not open debuginfo file.");
-
- pr_debug("Could not open vmlinux/module file."
- " Try to use symbols.\n");
- goto end_dwarf;
- }
-
- /* Searching probe points */
- ntevs = find_kprobe_trace_events(fd, pev, tevs);
-
- if (ntevs > 0) /* Found */
- goto found;
-
- if (ntevs == 0) /* No error but failed to find probe point. */
- die("Probe point '%s' not found. - probe not added.",
- synthesize_perf_probe_point(&pev->point));
-
- /* Error path */
- if (need_dwarf) {
- if (ntevs == -ENOENT)
- pr_warning("No dwarf info found in the vmlinux - please rebuild with CONFIG_DEBUG_INFO=y.\n");
- die("Could not analyze debuginfo.");
- }
- pr_debug("An error occurred in debuginfo analysis."
- " Try to use symbols.\n");
-
-end_dwarf:
-#endif /* !NO_DWARF_SUPPORT */
+ /* Convert perf_probe_event with debuginfo */
+ ntevs = try_to_find_kprobe_trace_events(pev, tevs);
+ if (ntevs > 0)
+ return ntevs;
/* Allocate trace event buffer */
ntevs = 1;
@@ -1012,10 +1127,7 @@ end_dwarf:
if (!sym)
die("Kernel symbol \'%s\' not found - probe not added.",
tev->point.symbol);
-#ifndef NO_DWARF_SUPPORT
-found:
- close(fd);
-#endif
+
return ntevs;
}
@@ -1133,90 +1245,3 @@ void del_perf_probe_events(struct strlist *dellist)
close(fd);
}
-#define LINEBUF_SIZE 256
-#define NR_ADDITIONAL_LINES 2
-
-static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num)
-{
- char buf[LINEBUF_SIZE];
- const char *color = PERF_COLOR_BLUE;
-
- if (fgets(buf, LINEBUF_SIZE, fp) == NULL)
- goto error;
- if (!skip) {
- if (show_num)
- fprintf(stdout, "%7u %s", l, buf);
- else
- color_fprintf(stdout, color, " %s", buf);
- }
-
- while (strlen(buf) == LINEBUF_SIZE - 1 &&
- buf[LINEBUF_SIZE - 2] != '\n') {
- if (fgets(buf, LINEBUF_SIZE, fp) == NULL)
- goto error;
- if (!skip) {
- if (show_num)
- fprintf(stdout, "%s", buf);
- else
- color_fprintf(stdout, color, "%s", buf);
- }
- }
- return;
-error:
- if (feof(fp))
- die("Source file is shorter than expected.");
- else
- die("File read error: %s", strerror(errno));
-}
-
-void show_line_range(struct line_range *lr)
-{
- unsigned int l = 1;
- struct line_node *ln;
- FILE *fp;
-#ifndef NO_DWARF_SUPPORT
- int fd, ret;
-#endif
-
- /* Search a line range */
- init_vmlinux();
-#ifndef NO_DWARF_SUPPORT
- fd = open_vmlinux();
- if (fd < 0)
- die("Could not open debuginfo file.");
- ret = find_line_range(fd, lr);
- if (ret <= 0)
- die("Source line is not found.\n");
- close(fd);
-#endif
-
- setup_pager();
-
- if (lr->function)
- fprintf(stdout, "<%s:%d>\n", lr->function,
- lr->start - lr->offset);
- else
- fprintf(stdout, "<%s:%d>\n", lr->file, lr->start);
-
- fp = fopen(lr->path, "r");
- if (fp == NULL)
- die("Failed to open %s: %s", lr->path, strerror(errno));
- /* Skip to starting line number */
- while (l < lr->start)
- show_one_line(fp, l++, true, false);
-
- list_for_each_entry(ln, &lr->line_list, list) {
- while (ln->line > l)
- show_one_line(fp, (l++) - lr->offset, false, false);
- show_one_line(fp, (l++) - lr->offset, false, true);
- }
-
- if (lr->end == INT_MAX)
- lr->end = l + NR_ADDITIONAL_LINES;
- while (l < lr->end && !feof(fp))
- show_one_line(fp, (l++) - lr->offset, false, false);
-
- fclose(fp);
-}
-
-
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
index 2f2307d..3564f22 100644
--- a/tools/perf/util/probe-finder.h
+++ b/tools/perf/util/probe-finder.h
@@ -15,7 +15,7 @@ static inline int is_c_varname(const char *name)
return isalpha(name[0]) || name[0] == '_';
}
-#ifndef NO_DWARF_SUPPORT
+#ifdef DWARF_SUPPORT
/* Find kprobe_trace_events specified by perf_probe_event from debuginfo */
extern int find_kprobe_trace_events(int fd, struct perf_probe_event *pev,
struct kprobe_trace_event **tevs);
@@ -57,6 +57,6 @@ struct line_finder {
int found;
};
-#endif /* NO_DWARF_SUPPORT */
+#endif /* DWARF_SUPPORT */
#endif /*_PROBE_FINDER_H */
--
Masami Hiramatsu
e-mail: mhi...@re...
|
|
From: Masami H. <mhi...@re...> - 2010-03-17 19:15:24
|
Mark Wielaard wrote: > On Tue, 2010-03-16 at 18:06 -0400, Masami Hiramatsu wrote: >> Support accessing members in the data structures. With this, >> perf-probe accepts data-structure members(IOW, it now accepts >> dot '.' and arrow '->' operators) as probe arguemnts. >> >> e.g. >> >> ./perf probe --add 'schedule:44 rq->curr' >> >> ./perf probe --add 'vfs_read file->f_op->read file->f_path.dentry' >> >> Note that '>' can be interpreted as redirection in command-line. > > If you find that a problem then you can do like SystemTap does and allow > '.' in place of '->'. In the code you already use the > perf_probe_arg_field ref flag only to check that the DIE gives you the > same information. So you could just drop that and use any separator. > Then you decide based on whether you see a DW_TAG_pointer_type. This > gives the user some extra flexibility by letting them not having to care > about specifying extra type information already available elsewhere. Thanks, when designing this feature, I considered it too. Since perf probe already support displaying source code by --line option, users will read the probed code itself and try to probe it. In that case, I think they naturally use '.' and '->' as they read (they might try to copy & paste it). So, I think that it would be good to support both of '.' and '->' as they are used in the code, because it will not confuse users. Thank you, -- Masami Hiramatsu e-mail: mhi...@re... |
|
From: tip-bot f. M. H. <mhi...@re...> - 2010-03-17 11:31:16
|
Commit-ID: fb1587d869a399554220e166d4b90b581a8ade01 Gitweb: http://git.kernel.org/tip/fb1587d869a399554220e166d4b90b581a8ade01 Author: Masami Hiramatsu <mhi...@re...> AuthorDate: Tue, 16 Mar 2010 18:06:19 -0400 Committer: Ingo Molnar <mi...@el...> CommitDate: Wed, 17 Mar 2010 11:32:32 +0100 perf probe: List probes with line number and file name Improve --list to show current exist probes with line number and file name. This enables user easily to check which line is already probed. for example: ./perf probe --list probe:vfs_read (on vfs_read:8@linux-2.6-tip/fs/read_write.c) Signed-off-by: Masami Hiramatsu <mhi...@re...> Cc: systemtap <sys...@so...> Cc: DLE <dle...@li...> Cc: Frederic Weisbecker <fwe...@gm...> Cc: Arnaldo Carvalho de Melo <ac...@re...> Cc: Paul Mackerras <pa...@sa...> Cc: Mike Galbraith <ef...@gm...> Cc: Peter Zijlstra <a.p...@ch...> LKML-Reference: <20100316220619.32050.48702.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar <mi...@el...> --- tools/perf/util/probe-event.c | 57 ++++++++++++++++++++++++-------- tools/perf/util/probe-finder.c | 70 ++++++++++++++++++++++++++++++++++++++++ tools/perf/util/probe-finder.h | 4 ++ 3 files changed, 117 insertions(+), 14 deletions(-) diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index b44ddfb..4e3c1ae 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -70,7 +70,6 @@ static int e_snprintf(char *str, size_t size, const char *format, ...) return ret; } - static struct map_groups kmap_groups; static struct map *kmaps[MAP__NR_TYPES]; @@ -357,27 +356,39 @@ void parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev) /* Compose only probe point (not argument) */ static char *synthesize_perf_probe_point(struct perf_probe_point *pp) { - char *buf; - char offs[64] = "", line[64] = ""; - int ret; + char *buf, *tmp; + char offs[32] = "", line[32] = "", file[32] = ""; + int ret, len; buf = xzalloc(MAX_CMDLEN); if (pp->offset) { - ret = e_snprintf(offs, 64, "+%lu", pp->offset); + ret = e_snprintf(offs, 32, "+%lu", pp->offset); if (ret <= 0) goto error; } if (pp->line) { - ret = e_snprintf(line, 64, ":%d", pp->line); + ret = e_snprintf(line, 32, ":%d", pp->line); + if (ret <= 0) + goto error; + } + if (pp->file) { + len = strlen(pp->file) - 32; + if (len < 0) + len = 0; + tmp = strchr(pp->file + len, '/'); + if (!tmp) + tmp = pp->file + len - 1; + ret = e_snprintf(file, 32, "@%s", tmp + 1); if (ret <= 0) goto error; } if (pp->function) - ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s", pp->function, - offs, pp->retprobe ? "%return" : "", line); + ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s%s", pp->function, + offs, pp->retprobe ? "%return" : "", line, + file); else - ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", pp->file, line); + ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", file, line); if (ret <= 0) goto error; @@ -511,15 +522,32 @@ void convert_to_perf_probe_event(struct kprobe_trace_event *tev, { char buf[64]; int i; +#ifndef NO_DWARF_SUPPORT + struct symbol *sym; + int fd, ret = 0; - pev->event = xstrdup(tev->event); - pev->group = xstrdup(tev->group); - + sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION], + tev->point.symbol, NULL); + if (sym) { + fd = open_vmlinux(); + ret = find_perf_probe_point(fd, sym->start + tev->point.offset, + &pev->point); + close(fd); + } + if (ret <= 0) { + pev->point.function = xstrdup(tev->point.symbol); + pev->point.offset = tev->point.offset; + } +#else /* Convert trace_point to probe_point */ pev->point.function = xstrdup(tev->point.symbol); pev->point.offset = tev->point.offset; +#endif pev->point.retprobe = tev->point.retprobe; + pev->event = xstrdup(tev->event); + pev->group = xstrdup(tev->group); + /* Convert trace_arg to probe_arg */ pev->nargs = tev->nargs; pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs); @@ -650,7 +678,7 @@ static void show_perf_probe_event(struct perf_probe_event *pev) ret = e_snprintf(buf, 128, "%s:%s", pev->group, pev->event); if (ret < 0) die("Failed to copy event: %s", strerror(-ret)); - printf(" %-40s (on %s", buf, place); + printf(" %-20s (on %s", buf, place); if (pev->nargs > 0) { printf(" with"); @@ -671,6 +699,7 @@ void show_perf_probe_events(void) struct str_node *ent; setup_pager(); + init_vmlinux(); memset(&tev, 0, sizeof(tev)); memset(&pev, 0, sizeof(pev)); diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 251b4c4..e02b607 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -693,6 +693,76 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, return pf.ntevs; } +/* Reverse search */ +int find_perf_probe_point(int fd, unsigned long addr, + struct perf_probe_point *ppt) +{ + Dwarf_Die cudie, spdie, indie; + Dwarf *dbg; + Dwarf_Line *line; + Dwarf_Addr laddr, eaddr; + const char *tmp; + int lineno, ret = 0; + + dbg = dwarf_begin(fd, DWARF_C_READ); + if (!dbg) + return -ENOENT; + + /* Find cu die */ + if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr, &cudie)) + return -EINVAL; + + /* Find a corresponding line */ + line = dwarf_getsrc_die(&cudie, (Dwarf_Addr)addr); + if (line) { + dwarf_lineaddr(line, &laddr); + if ((Dwarf_Addr)addr == laddr) { + dwarf_lineno(line, &lineno); + ppt->line = lineno; + + tmp = dwarf_linesrc(line, NULL, NULL); + DIE_IF(!tmp); + ppt->file = xstrdup(tmp); + ret = 1; + } + } + + /* Find a corresponding function */ + if (die_find_real_subprogram(&cudie, (Dwarf_Addr)addr, &spdie)) { + tmp = dwarf_diename(&spdie); + if (!tmp) + goto end; + + dwarf_entrypc(&spdie, &eaddr); + if (!lineno) { + /* We don't have a line number, let's use offset */ + ppt->function = xstrdup(tmp); + ppt->offset = addr - (unsigned long)eaddr; + ret = 1; + goto end; + } + if (die_find_inlinefunc(&spdie, (Dwarf_Addr)addr, &indie)) { + /* addr in an inline function */ + tmp = dwarf_diename(&indie); + if (!tmp) + goto end; + dwarf_decl_line(&indie, &lineno); + } else { + if (eaddr == addr) /* No offset: function entry */ + lineno = ppt->line; + else + dwarf_decl_line(&spdie, &lineno); + } + ppt->function = xstrdup(tmp); + ppt->line -= lineno; /* Make a relative line number */ + } + +end: + dwarf_end(dbg); + return ret; +} + + /* Find line range from its line number */ static void find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) { diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index 4949526..2f2307d 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h @@ -20,6 +20,10 @@ static inline int is_c_varname(const char *name) extern int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, struct kprobe_trace_event **tevs); +/* Find a perf_probe_point from debuginfo */ +extern int find_perf_probe_point(int fd, unsigned long addr, + struct perf_probe_point *ppt); + extern int find_line_range(int fd, struct line_range *lr); #include <dwarf.h> |
|
From: tip-bot f. M. H. <mhi...@re...> - 2010-03-17 11:31:05
|
Commit-ID: 4235b0454ebeefc2295ad8417e18a8761425b19e Gitweb: http://git.kernel.org/tip/4235b0454ebeefc2295ad8417e18a8761425b19e Author: Masami Hiramatsu <mhi...@re...> AuthorDate: Tue, 16 Mar 2010 18:06:12 -0400 Committer: Ingo Molnar <mi...@el...> CommitDate: Wed, 17 Mar 2010 11:32:31 +0100 perf probe: Introduce kprobe_trace_event and perf_probe_event Introduce kprobe_trace_event and perf_probe_event and replace old probe_point structure with it. probe_point structure is not enough flexible nor extensible. New data structures will help implementing further features. Signed-off-by: Masami Hiramatsu <mhi...@re...> Cc: systemtap <sys...@so...> Cc: DLE <dle...@li...> Cc: Frederic Weisbecker <fwe...@gm...> Cc: Arnaldo Carvalho de Melo <ac...@re...> Cc: Paul Mackerras <pa...@sa...> Cc: Mike Galbraith <ef...@gm...> Cc: Peter Zijlstra <a.p...@ch...> LKML-Reference: <20100316220612.32050.33806.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar <mi...@el...> --- tools/perf/builtin-probe.c | 30 +- tools/perf/util/probe-event.c | 628 +++++++++++++++++++++++++--------------- tools/perf/util/probe-event.h | 111 +++++++- tools/perf/util/probe-finder.c | 140 ++++----- tools/perf/util/probe-finder.h | 58 +--- 5 files changed, 590 insertions(+), 377 deletions(-) diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index a1a2891..e0dafd9 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -48,12 +48,11 @@ /* Session management structure */ static struct { - bool need_dwarf; bool list_events; bool force_add; bool show_lines; - int nr_probe; - struct probe_point probes[MAX_PROBES]; + int nevents; + struct perf_probe_event events[MAX_PROBES]; struct strlist *dellist; struct line_range line_range; } params; @@ -62,16 +61,16 @@ static struct { /* Parse an event definition. Note that any error must die. */ static void parse_probe_event(const char *str) { - struct probe_point *pp = ¶ms.probes[params.nr_probe]; + struct perf_probe_event *pev = ¶ms.events[params.nevents]; - pr_debug("probe-definition(%d): %s\n", params.nr_probe, str); - if (++params.nr_probe == MAX_PROBES) + pr_debug("probe-definition(%d): %s\n", params.nevents, str); + if (++params.nevents == MAX_PROBES) die("Too many probes (> %d) are specified.", MAX_PROBES); - /* Parse perf-probe event into probe_point */ - parse_perf_probe_event(str, pp, ¶ms.need_dwarf); + /* Parse a perf-probe command into event */ + parse_perf_probe_command(str, pev); - pr_debug("%d arguments\n", pp->nr_args); + pr_debug("%d arguments\n", pev->nargs); } static void parse_probe_event_argv(int argc, const char **argv) @@ -191,7 +190,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) parse_probe_event_argv(argc, argv); } - if ((!params.nr_probe && !params.dellist && !params.list_events && + if ((!params.nevents && !params.dellist && !params.list_events && !params.show_lines)) usage_with_options(probe_usage, options); @@ -199,7 +198,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) die("Failed to find debugfs path."); if (params.list_events) { - if (params.nr_probe != 0 || params.dellist) { + if (params.nevents != 0 || params.dellist) { pr_warning(" Error: Don't use --list with" " --add/--del.\n"); usage_with_options(probe_usage, options); @@ -214,7 +213,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) #ifndef NO_DWARF_SUPPORT if (params.show_lines) { - if (params.nr_probe != 0 || params.dellist) { + if (params.nevents != 0 || params.dellist) { pr_warning(" Error: Don't use --line with" " --add/--del.\n"); usage_with_options(probe_usage, options); @@ -226,14 +225,13 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) #endif if (params.dellist) { - del_trace_kprobe_events(params.dellist); + del_perf_probe_events(params.dellist); strlist__delete(params.dellist); - if (params.nr_probe == 0) + if (params.nevents == 0) return 0; } - add_trace_kprobe_events(params.probes, params.nr_probe, - params.force_add, params.need_dwarf); + add_perf_probe_events(params.events, params.nevents, params.force_add); return 0; } diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index ac41578..b44ddfb 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -44,6 +44,7 @@ #include "thread.h" #include "parse-events.h" /* For debugfs_path */ #include "probe-event.h" +#include "probe-finder.h" #define MAX_CMDLEN 256 #define MAX_PROBE_ARGS 128 @@ -150,8 +151,9 @@ static bool check_event_name(const char *name) } /* Parse probepoint definition. */ -static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) +static void parse_perf_probe_point(char *arg, struct perf_probe_event *pev) { + struct perf_probe_point *pp = &pev->point; char *ptr, *tmp; char c, nc = 0; /* @@ -172,7 +174,8 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) if (!check_event_name(arg)) semantic_error("%s is bad for event name -it must " "follow C symbol-naming rule.", arg); - pp->event = xstrdup(arg); + pev->event = xstrdup(arg); + pev->group = NULL; arg = tmp; } @@ -255,57 +258,65 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) semantic_error("Offset/Line/Lazy pattern can't be used with " "return probe."); - pr_debug("symbol:%s file:%s line:%d offset:%d return:%d lazy:%s\n", + pr_debug("symbol:%s file:%s line:%d offset:%lu return:%d lazy:%s\n", pp->function, pp->file, pp->line, pp->offset, pp->retprobe, pp->lazy_line); } -/* Parse perf-probe event definition */ -void parse_perf_probe_event(const char *str, struct probe_point *pp, - bool *need_dwarf) +/* Parse perf-probe event command */ +void parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev) { char **argv; int argc, i; - *need_dwarf = false; - - argv = argv_split(str, &argc); + argv = argv_split(cmd, &argc); if (!argv) die("argv_split failed."); if (argc > MAX_PROBE_ARGS + 1) semantic_error("Too many arguments"); /* Parse probe point */ - parse_perf_probe_probepoint(argv[0], pp); - if (pp->file || pp->line || pp->lazy_line) - *need_dwarf = true; + parse_perf_probe_point(argv[0], pev); /* Copy arguments and ensure return probe has no C argument */ - pp->nr_args = argc - 1; - pp->args = xzalloc(sizeof(char *) * pp->nr_args); - for (i = 0; i < pp->nr_args; i++) { - pp->args[i] = xstrdup(argv[i + 1]); - if (is_c_varname(pp->args[i])) { - if (pp->retprobe) - semantic_error("You can't specify local" - " variable for kretprobe"); - *need_dwarf = true; - } + pev->nargs = argc - 1; + pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs); + for (i = 0; i < pev->nargs; i++) { + pev->args[i].name = xstrdup(argv[i + 1]); + if (is_c_varname(pev->args[i].name) && pev->point.retprobe) + semantic_error("You can't specify local variable for" + " kretprobe"); } argv_free(argv); } +/* Return true if this perf_probe_event requires debuginfo */ +bool perf_probe_event_need_dwarf(struct perf_probe_event *pev) +{ + int i; + + if (pev->point.file || pev->point.line || pev->point.lazy_line) + return true; + + for (i = 0; i < pev->nargs; i++) + if (is_c_varname(pev->args[i].name)) + return true; + + return false; +} + /* Parse kprobe_events event into struct probe_point */ -void parse_trace_kprobe_event(const char *str, struct probe_point *pp) +void parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev) { + struct kprobe_trace_point *tp = &tev->point; char pr; char *p; int ret, i, argc; char **argv; - pr_debug("Parsing kprobe_events: %s\n", str); - argv = argv_split(str, &argc); + pr_debug("Parsing kprobe_events: %s\n", cmd); + argv = argv_split(cmd, &argc); if (!argv) die("argv_split failed."); if (argc < 2) @@ -313,47 +324,46 @@ void parse_trace_kprobe_event(const char *str, struct probe_point *pp) /* Scan event and group name. */ ret = sscanf(argv[0], "%c:%a[^/ \t]/%a[^ \t]", - &pr, (float *)(void *)&pp->group, - (float *)(void *)&pp->event); + &pr, (float *)(void *)&tev->group, + (float *)(void *)&tev->event); if (ret != 3) semantic_error("Failed to parse event name: %s", argv[0]); - pr_debug("Group:%s Event:%s probe:%c\n", pp->group, pp->event, pr); + pr_debug("Group:%s Event:%s probe:%c\n", tev->group, tev->event, pr); - pp->retprobe = (pr == 'r'); + tp->retprobe = (pr == 'r'); /* Scan function name and offset */ - ret = sscanf(argv[1], "%a[^+]+%d", (float *)(void *)&pp->function, - &pp->offset); + ret = sscanf(argv[1], "%a[^+]+%lu", (float *)(void *)&tp->symbol, + &tp->offset); if (ret == 1) - pp->offset = 0; - - /* kprobe_events doesn't have this information */ - pp->line = 0; - pp->file = NULL; + tp->offset = 0; - pp->nr_args = argc - 2; - pp->args = xzalloc(sizeof(char *) * pp->nr_args); - for (i = 0; i < pp->nr_args; i++) { + tev->nargs = argc - 2; + tev->args = xzalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); + for (i = 0; i < tev->nargs; i++) { p = strchr(argv[i + 2], '='); if (p) /* We don't need which register is assigned. */ - *p = '\0'; - pp->args[i] = xstrdup(argv[i + 2]); + *p++ = '\0'; + else + p = argv[i + 2]; + tev->args[i].name = xstrdup(argv[i + 2]); + /* TODO: parse regs and offset */ + tev->args[i].value = xstrdup(p); } argv_free(argv); } -/* Synthesize only probe point (not argument) */ -int synthesize_perf_probe_point(struct probe_point *pp) +/* Compose only probe point (not argument) */ +static char *synthesize_perf_probe_point(struct perf_probe_point *pp) { char *buf; char offs[64] = "", line[64] = ""; int ret; - pp->probes[0] = buf = xzalloc(MAX_CMDLEN); - pp->found = 1; + buf = xzalloc(MAX_CMDLEN); if (pp->offset) { - ret = e_snprintf(offs, 64, "+%d", pp->offset); + ret = e_snprintf(offs, 64, "+%lu", pp->offset); if (ret <= 0) goto error; } @@ -368,68 +378,209 @@ int synthesize_perf_probe_point(struct probe_point *pp) offs, pp->retprobe ? "%return" : "", line); else ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", pp->file, line); - if (ret <= 0) { + if (ret <= 0) + goto error; + + return buf; error: - free(pp->probes[0]); - pp->probes[0] = NULL; - pp->found = 0; - } - return ret; + die("Failed to synthesize perf probe point: %s", strerror(-ret)); } -int synthesize_perf_probe_event(struct probe_point *pp) +#if 0 +char *synthesize_perf_probe_command(struct perf_probe_event *pev) { char *buf; int i, len, ret; - len = synthesize_perf_probe_point(pp); - if (len < 0) - return 0; + buf = synthesize_perf_probe_point(&pev->point); + if (!buf) + return NULL; - buf = pp->probes[0]; - for (i = 0; i < pp->nr_args; i++) { + len = strlen(buf); + for (i = 0; i < pev->nargs; i++) { ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", - pp->args[i]); - if (ret <= 0) - goto error; + pev->args[i].name); + if (ret <= 0) { + free(buf); + return NULL; + } len += ret; } - pp->found = 1; - return pp->found; -error: - free(pp->probes[0]); - pp->probes[0] = NULL; + return buf; +} +#endif + +static int __synthesize_kprobe_trace_arg_ref(struct kprobe_trace_arg_ref *ref, + char **buf, size_t *buflen, + int depth) +{ + int ret; + if (ref->next) { + depth = __synthesize_kprobe_trace_arg_ref(ref->next, buf, + buflen, depth + 1); + if (depth < 0) + goto out; + } + + ret = e_snprintf(*buf, *buflen, "%+ld(", ref->offset); + if (ret < 0) + depth = ret; + else { + *buf += ret; + *buflen -= ret; + } +out: + return depth; - return ret; } -int synthesize_trace_kprobe_event(struct probe_point *pp) +static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg, + char *buf, size_t buflen) { + int ret, depth = 0; + char *tmp = buf; + + /* Argument name or separator */ + if (arg->name) + ret = e_snprintf(buf, buflen, " %s=", arg->name); + else + ret = e_snprintf(buf, buflen, " "); + if (ret < 0) + return ret; + buf += ret; + buflen -= ret; + + /* Dereferencing arguments */ + if (arg->ref) { + depth = __synthesize_kprobe_trace_arg_ref(arg->ref, &buf, + &buflen, 1); + if (depth < 0) + return depth; + } + + /* Print argument value */ + ret = e_snprintf(buf, buflen, "%s", arg->value); + if (ret < 0) + return ret; + buf += ret; + buflen -= ret; + + /* Closing */ + while (depth--) { + ret = e_snprintf(buf, buflen, ")"); + if (ret < 0) + return ret; + buf += ret; + buflen -= ret; + } + + return buf - tmp; +} + +char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev) +{ + struct kprobe_trace_point *tp = &tev->point; char *buf; int i, len, ret; - pp->probes[0] = buf = xzalloc(MAX_CMDLEN); - ret = e_snprintf(buf, MAX_CMDLEN, "%s+%d", pp->function, pp->offset); - if (ret <= 0) + buf = xzalloc(MAX_CMDLEN); + len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s+%lu", + tp->retprobe ? 'r' : 'p', + tev->group, tev->event, + tp->symbol, tp->offset); + if (len <= 0) goto error; - len = ret; - for (i = 0; i < pp->nr_args; i++) { - ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", - pp->args[i]); + for (i = 0; i < tev->nargs; i++) { + ret = synthesize_kprobe_trace_arg(&tev->args[i], buf + len, + MAX_CMDLEN - len); if (ret <= 0) goto error; len += ret; } - pp->found = 1; - return pp->found; + return buf; error: - free(pp->probes[0]); - pp->probes[0] = NULL; + free(buf); + return NULL; +} - return ret; +void convert_to_perf_probe_event(struct kprobe_trace_event *tev, + struct perf_probe_event *pev) +{ + char buf[64]; + int i; + + pev->event = xstrdup(tev->event); + pev->group = xstrdup(tev->group); + + /* Convert trace_point to probe_point */ + pev->point.function = xstrdup(tev->point.symbol); + pev->point.offset = tev->point.offset; + pev->point.retprobe = tev->point.retprobe; + + /* Convert trace_arg to probe_arg */ + pev->nargs = tev->nargs; + pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs); + for (i = 0; i < tev->nargs; i++) + if (tev->args[i].name) + pev->args[i].name = xstrdup(tev->args[i].name); + else { + synthesize_kprobe_trace_arg(&tev->args[i], buf, 64); + pev->args[i].name = xstrdup(buf); + } +} + +void clear_perf_probe_event(struct perf_probe_event *pev) +{ + struct perf_probe_point *pp = &pev->point; + int i; + + if (pev->event) + free(pev->event); + if (pev->group) + free(pev->group); + if (pp->file) + free(pp->file); + if (pp->function) + free(pp->function); + if (pp->lazy_line) + free(pp->lazy_line); + for (i = 0; i < pev->nargs; i++) + if (pev->args[i].name) + free(pev->args[i].name); + if (pev->args) + free(pev->args); + memset(pev, 0, sizeof(*pev)); +} + +void clear_kprobe_trace_event(struct kprobe_trace_event *tev) +{ + struct kprobe_trace_arg_ref *ref, *next; + int i; + + if (tev->event) + free(tev->event); + if (tev->group) + free(tev->group); + if (tev->point.symbol) + free(tev->point.symbol); + for (i = 0; i < tev->nargs; i++) { + if (tev->args[i].name) + free(tev->args[i].name); + if (tev->args[i].value) + free(tev->args[i].value); + ref = tev->args[i].ref; + while (ref) { + next = ref->next; + free(ref); + ref = next; + } + } + if (tev->args) + free(tev->args); + memset(tev, 0, sizeof(*tev)); } static int open_kprobe_events(bool readwrite) @@ -458,7 +609,7 @@ static int open_kprobe_events(bool readwrite) } /* Get raw string list of current kprobe_events */ -static struct strlist *get_trace_kprobe_event_rawlist(int fd) +static struct strlist *get_kprobe_trace_command_rawlist(int fd) { int ret, idx; FILE *fp; @@ -486,99 +637,82 @@ static struct strlist *get_trace_kprobe_event_rawlist(int fd) return sl; } -/* Free and zero clear probe_point */ -static void clear_probe_point(struct probe_point *pp) -{ - int i; - - if (pp->event) - free(pp->event); - if (pp->group) - free(pp->group); - if (pp->function) - free(pp->function); - if (pp->file) - free(pp->file); - if (pp->lazy_line) - free(pp->lazy_line); - for (i = 0; i < pp->nr_args; i++) - free(pp->args[i]); - if (pp->args) - free(pp->args); - for (i = 0; i < pp->found; i++) - free(pp->probes[i]); - memset(pp, 0, sizeof(*pp)); -} - /* Show an event */ -static void show_perf_probe_event(const char *event, const char *place, - struct probe_point *pp) +static void show_perf_probe_event(struct perf_probe_event *pev) { int i, ret; char buf[128]; + char *place; - ret = e_snprintf(buf, 128, "%s:%s", pp->group, event); + /* Synthesize only event probe point */ + place = synthesize_perf_probe_point(&pev->point); + + ret = e_snprintf(buf, 128, "%s:%s", pev->group, pev->event); if (ret < 0) die("Failed to copy event: %s", strerror(-ret)); printf(" %-40s (on %s", buf, place); - if (pp->nr_args > 0) { + if (pev->nargs > 0) { printf(" with"); - for (i = 0; i < pp->nr_args; i++) - printf(" %s", pp->args[i]); + for (i = 0; i < pev->nargs; i++) + printf(" %s", pev->args[i].name); } printf(")\n"); + free(place); } /* List up current perf-probe events */ void show_perf_probe_events(void) { int fd; - struct probe_point pp; + struct kprobe_trace_event tev; + struct perf_probe_event pev; struct strlist *rawlist; struct str_node *ent; setup_pager(); - memset(&pp, 0, sizeof(pp)); + + memset(&tev, 0, sizeof(tev)); + memset(&pev, 0, sizeof(pev)); fd = open_kprobe_events(false); - rawlist = get_trace_kprobe_event_rawlist(fd); + rawlist = get_kprobe_trace_command_rawlist(fd); close(fd); strlist__for_each(ent, rawlist) { - parse_trace_kprobe_event(ent->s, &pp); - /* Synthesize only event probe point */ - synthesize_perf_probe_point(&pp); + parse_kprobe_trace_command(ent->s, &tev); + convert_to_perf_probe_event(&tev, &pev); /* Show an event */ - show_perf_probe_event(pp.event, pp.probes[0], &pp); - clear_probe_point(&pp); + show_perf_probe_event(&pev); + clear_perf_probe_event(&pev); + clear_kprobe_trace_event(&tev); } strlist__delete(rawlist); } /* Get current perf-probe event names */ -static struct strlist *get_perf_event_names(int fd, bool include_group) +static struct strlist *get_kprobe_trace_event_names(int fd, bool include_group) { char buf[128]; struct strlist *sl, *rawlist; struct str_node *ent; - struct probe_point pp; + struct kprobe_trace_event tev; - memset(&pp, 0, sizeof(pp)); - rawlist = get_trace_kprobe_event_rawlist(fd); + memset(&tev, 0, sizeof(tev)); + rawlist = get_kprobe_trace_command_rawlist(fd); sl = strlist__new(true, NULL); strlist__for_each(ent, rawlist) { - parse_trace_kprobe_event(ent->s, &pp); + parse_kprobe_trace_command(ent->s, &tev); if (include_group) { - if (e_snprintf(buf, 128, "%s:%s", pp.group, - pp.event) < 0) + if (e_snprintf(buf, 128, "%s:%s", tev.group, + tev.event) < 0) die("Failed to copy group:event name."); strlist__add(sl, buf); } else - strlist__add(sl, pp.event); - clear_probe_point(&pp); + strlist__add(sl, tev.event); + clear_kprobe_trace_event(&tev); } strlist__delete(rawlist); @@ -586,9 +720,10 @@ static struct strlist *get_perf_event_names(int fd, bool include_group) return sl; } -static void write_trace_kprobe_event(int fd, const char *buf) +static void write_kprobe_trace_event(int fd, struct kprobe_trace_event *tev) { int ret; + char *buf = synthesize_kprobe_trace_command(tev); pr_debug("Writing event: %s\n", buf); if (!probe_event_dry_run) { @@ -596,6 +731,7 @@ static void write_trace_kprobe_event(int fd, const char *buf) if (ret <= 0) die("Failed to write event: %s", strerror(errno)); } + free(buf); } static void get_new_event_name(char *buf, size_t len, const char *base, @@ -628,81 +764,83 @@ static void get_new_event_name(char *buf, size_t len, const char *base, die("Too many events are on the same function."); } -static void __add_trace_kprobe_events(struct probe_point *probes, - int nr_probes, bool force_add) +static void __add_kprobe_trace_events(struct perf_probe_event *pev, + struct kprobe_trace_event *tevs, + int ntevs, bool allow_suffix) { - int i, j, fd; - struct probe_point *pp; - char buf[MAX_CMDLEN]; - char event[64]; + int i, fd; + struct kprobe_trace_event *tev; + char buf[64]; + const char *event, *group; struct strlist *namelist; - bool allow_suffix; fd = open_kprobe_events(true); /* Get current event names */ - namelist = get_perf_event_names(fd, false); - - for (j = 0; j < nr_probes; j++) { - pp = probes + j; - if (!pp->event) - pp->event = xstrdup(pp->function); - if (!pp->group) - pp->group = xstrdup(PERFPROBE_GROUP); - /* If force_add is true, suffix search is allowed */ - allow_suffix = force_add; - for (i = 0; i < pp->found; i++) { - /* Get an unused new event name */ - get_new_event_name(event, 64, pp->event, namelist, - allow_suffix); - snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s\n", - pp->retprobe ? 'r' : 'p', - pp->group, event, - pp->probes[i]); - write_trace_kprobe_event(fd, buf); - printf("Added new event:\n"); - /* Get the first parameter (probe-point) */ - sscanf(pp->probes[i], "%s", buf); - show_perf_probe_event(event, buf, pp); - /* Add added event name to namelist */ - strlist__add(namelist, event); - /* - * Probes after the first probe which comes from same - * user input are always allowed to add suffix, because - * there might be several addresses corresponding to - * one code line. - */ - allow_suffix = true; - } + namelist = get_kprobe_trace_event_names(fd, false); + + printf("Add new event%s\n", (ntevs > 1) ? "s:" : ":"); + for (i = 0; i < ntevs; i++) { + tev = &tevs[i]; + if (pev->event) + event = pev->event; + else + if (pev->point.function) + event = pev->point.function; + else + event = tev->point.symbol; + if (pev->group) + group = pev->group; + else + group = PERFPROBE_GROUP; + + /* Get an unused new event name */ + get_new_event_name(buf, 64, event, namelist, allow_suffix); + event = buf; + + tev->event = xstrdup(event); + tev->group = xstrdup(group); + write_kprobe_trace_event(fd, tev); + /* Add added event name to namelist */ + strlist__add(namelist, event); + + /* Trick here - save current event/group */ + event = pev->event; + group = pev->group; + pev->event = tev->event; + pev->group = tev->group; + show_perf_probe_event(pev); + /* Trick here - restore current event/group */ + pev->event = (char *)event; + pev->group = (char *)group; + + /* + * Probes after the first probe which comes from same + * user input are always allowed to add suffix, because + * there might be several addresses corresponding to + * one code line. + */ + allow_suffix = true; } /* Show how to use the event. */ printf("\nYou can now use it on all perf tools, such as:\n\n"); - printf("\tperf record -e %s:%s -a sleep 1\n\n", PERFPROBE_GROUP, event); + printf("\tperf record -e %s:%s -a sleep 1\n\n", tev->group, tev->event); strlist__delete(namelist); close(fd); } -/* Currently just checking function name from symbol map */ -static void evaluate_probe_point(struct probe_point *pp) +static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, + struct kprobe_trace_event **tevs) { struct symbol *sym; - sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION], - pp->function, NULL); - if (!sym) - die("Kernel symbol \'%s\' not found - probe not added.", - pp->function); -} - -void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, - bool force_add, bool need_dwarf) -{ - int i, ret; - struct probe_point *pp; + bool need_dwarf; #ifndef NO_DWARF_SUPPORT int fd; #endif - /* Add probes */ - init_vmlinux(); + int ntevs = 0, i; + struct kprobe_trace_event *tev; + + need_dwarf = perf_probe_event_need_dwarf(pev); if (need_dwarf) #ifdef NO_DWARF_SUPPORT @@ -721,57 +859,90 @@ void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, } /* Searching probe points */ - for (i = 0; i < nr_probes; i++) { - pp = &probes[i]; - if (pp->found) - continue; - - lseek(fd, SEEK_SET, 0); - ret = find_probe_point(fd, pp); - if (ret > 0) - continue; - if (ret == 0) { /* No error but failed to find probe point. */ - synthesize_perf_probe_point(pp); - die("Probe point '%s' not found. - probe not added.", - pp->probes[0]); - } - /* Error path */ - if (need_dwarf) { - if (ret == -ENOENT) - pr_warning("No dwarf info found in the vmlinux - please rebuild with CONFIG_DEBUG_INFO=y.\n"); - die("Could not analyze debuginfo."); - } - pr_debug("An error occurred in debuginfo analysis." - " Try to use symbols.\n"); - break; + ntevs = find_kprobe_trace_events(fd, pev, tevs); + + if (ntevs > 0) /* Found */ + goto found; + + if (ntevs == 0) /* No error but failed to find probe point. */ + die("Probe point '%s' not found. - probe not added.", + synthesize_perf_probe_point(&pev->point)); + + /* Error path */ + if (need_dwarf) { + if (ntevs == -ENOENT) + pr_warning("No dwarf info found in the vmlinux - please rebuild with CONFIG_DEBUG_INFO=y.\n"); + die("Could not analyze debuginfo."); } - close(fd); + pr_debug("An error occurred in debuginfo analysis." + " Try to use symbols.\n"); end_dwarf: #endif /* !NO_DWARF_SUPPORT */ - /* Synthesize probes without dwarf */ - for (i = 0; i < nr_probes; i++) { - pp = &probes[i]; - if (pp->found) /* This probe is already found. */ - continue; + /* Allocate trace event buffer */ + ntevs = 1; + tev = *tevs = xzalloc(sizeof(struct kprobe_trace_event)); + + /* Copy parameters */ + tev->point.symbol = xstrdup(pev->point.function); + tev->point.offset = pev->point.offset; + tev->nargs = pev->nargs; + if (tev->nargs) { + tev->args = xzalloc(sizeof(struct kprobe_trace_arg) + * tev->nargs); + for (i = 0; i < tev->nargs; i++) + tev->args[i].value = xstrdup(pev->args[i].name); + } + + /* Currently just checking function name from symbol map */ + sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION], + tev->point.symbol, NULL); + if (!sym) + die("Kernel symbol \'%s\' not found - probe not added.", + tev->point.symbol); +found: + close(fd); + return ntevs; +} + +struct __event_package { + struct perf_probe_event *pev; + struct kprobe_trace_event *tevs; + int ntevs; +}; - evaluate_probe_point(pp); - ret = synthesize_trace_kprobe_event(pp); - if (ret == -E2BIG) - die("probe point definition becomes too long."); - else if (ret < 0) - die("Failed to synthesize a probe point."); +void add_perf_probe_events(struct perf_probe_event *pevs, int npevs, + bool force_add) +{ + int i; + struct __event_package *pkgs; + + pkgs = xzalloc(sizeof(struct __event_package) * npevs); + + /* Init vmlinux path */ + init_vmlinux(); + + /* Loop 1: convert all events */ + for (i = 0; i < npevs; i++) { + pkgs[i].pev = &pevs[i]; + /* Convert with or without debuginfo */ + pkgs[i].ntevs = convert_to_kprobe_trace_events(pkgs[i].pev, + &pkgs[i].tevs); } - /* Settng up probe points */ - __add_trace_kprobe_events(probes, nr_probes, force_add); + /* Loop 2: add all events */ + for (i = 0; i < npevs; i++) + __add_kprobe_trace_events(pkgs[i].pev, pkgs[i].tevs, + pkgs[i].ntevs, force_add); + /* TODO: cleanup all trace events? */ } static void __del_trace_kprobe_event(int fd, struct str_node *ent) { char *p; char buf[128]; + int ret; /* Convert from perf-probe event to trace-kprobe event */ if (e_snprintf(buf, 128, "-:%s", ent->s) < 0) @@ -781,7 +952,10 @@ static void __del_trace_kprobe_event(int fd, struct str_node *ent) die("Internal error: %s should have ':' but not.", ent->s); *p = '/'; - write_trace_kprobe_event(fd, buf); + pr_debug("Writing event: %s\n", buf); + ret = write(fd, buf, strlen(buf)); + if (ret <= 0) + die("Failed to write event: %s", strerror(errno)); printf("Remove event: %s\n", ent->s); } @@ -814,7 +988,7 @@ static void del_trace_kprobe_event(int fd, const char *group, pr_info("Info: event \"%s\" does not exist, could not remove it.\n", buf); } -void del_trace_kprobe_events(struct strlist *dellist) +void del_perf_probe_events(struct strlist *dellist) { int fd; const char *group, *event; @@ -824,7 +998,7 @@ void del_trace_kprobe_events(struct strlist *dellist) fd = open_kprobe_events(true); /* Get current event names */ - namelist = get_perf_event_names(fd, true); + namelist = get_kprobe_trace_event_names(fd, true); strlist__for_each(ent, dellist) { str = xstrdup(ent->s); diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 703b887..2a2f0a2 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -2,24 +2,113 @@ #define _PROBE_EVENT_H #include <stdbool.h> -#include "probe-finder.h" #include "strlist.h" extern bool probe_event_dry_run; -extern void parse_line_range_desc(const char *arg, struct line_range *lr); -extern void parse_perf_probe_event(const char *str, struct probe_point *pp, - bool *need_dwarf); -extern int synthesize_perf_probe_point(struct probe_point *pp); -extern int synthesize_perf_probe_event(struct probe_point *pp); -extern void parse_trace_kprobe_event(const char *str, struct probe_point *pp); -extern int synthesize_trace_kprobe_event(struct probe_point *pp); -extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, - bool force_add, bool need_dwarf); -extern void del_trace_kprobe_events(struct strlist *dellist); +/* kprobe-tracer tracing point */ +struct kprobe_trace_point { + char *symbol; /* Base symbol */ + unsigned long offset; /* Offset from symbol */ + bool retprobe; /* Return probe flag */ +}; + +/* kprobe-tracer tracing argument referencing offset */ +struct kprobe_trace_arg_ref { + struct kprobe_trace_arg_ref *next; /* Next reference */ + long offset; /* Offset value */ +}; + +/* kprobe-tracer tracing argument */ +struct kprobe_trace_arg { + char *name; /* Argument name */ + char *value; /* Base value */ + struct kprobe_trace_arg_ref *ref; /* Referencing offset */ +}; + +/* kprobe-tracer tracing event (point + arg) */ +struct kprobe_trace_event { + char *event; /* Event name */ + char *group; /* Group name */ + struct kprobe_trace_point point; /* Trace point */ + int nargs; /* Number of args */ + struct kprobe_trace_arg *args; /* Arguments */ +}; + +/* Perf probe probing point */ +struct perf_probe_point { + char *file; /* File path */ + char *function; /* Function name */ + int line; /* Line number */ + char *lazy_line; /* Lazy matching pattern */ + unsigned long offset; /* Offset from function entry */ + bool retprobe; /* Return probe flag */ +}; + +/* Perf probe probing argument */ +struct perf_probe_arg { + char *name; /* Argument name */ +}; + +/* Perf probe probing event (point + arg) */ +struct perf_probe_event { + char *event; /* Event name */ + char *group; /* Group name */ + struct perf_probe_point point; /* Probe point */ + int nargs; /* Number of arguments */ + struct perf_probe_arg *args; /* Arguments */ +}; + + +/* Line number container */ +struct line_node { + struct list_head list; + unsigned int line; +}; + +/* Line range */ +struct line_range { + char *file; /* File name */ + char *function; /* Function name */ + unsigned int start; /* Start line number */ + unsigned int end; /* End line number */ + int offset; /* Start line offset */ + char *path; /* Real path name */ + struct list_head line_list; /* Visible lines */ +}; + +/* Command string to events */ +extern void parse_perf_probe_command(const char *cmd, + struct perf_probe_event *pev); +extern void parse_kprobe_trace_command(const char *cmd, + struct kprobe_trace_event *tev); + +/* Events to command string */ +extern char *synthesize_perf_probe_command(struct perf_probe_event *pev); +extern char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev); + +/* Check the perf_probe_event needs debuginfo */ +extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev); + +/* Convert from kprobe_trace_event to perf_probe_event */ +extern void convert_to_perf_probe_event(struct kprobe_trace_event *tev, + struct perf_probe_event *pev); + +/* Release event contents */ +extern void clear_perf_probe_event(struct perf_probe_event *pev); +extern void clear_kprobe_trace_event(struct kprobe_trace_event *tev); + +/* Command string to line-range */ +extern void parse_line_range_desc(const char *cmd, struct line_range *lr); + + +extern void add_perf_probe_events(struct perf_probe_event *pevs, int ntevs, + bool force_add); +extern void del_perf_probe_events(struct strlist *dellist); extern void show_perf_probe_events(void); extern void show_line_range(struct line_range *lr); + /* Maximum index number of event-name postfix */ #define MAX_EVENT_INDEX 1024 diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 3942e14..251b4c4 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -319,19 +319,20 @@ static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name, */ /* Show a location */ -static void show_location(Dwarf_Op *op, struct probe_finder *pf) +static void convert_location(Dwarf_Op *op, struct probe_finder *pf) { unsigned int regn; Dwarf_Word offs = 0; - int deref = 0, ret; + bool ref = false; const char *regs; + struct kprobe_trace_arg *tvar = pf->tvar; /* TODO: support CFA */ /* If this is based on frame buffer, set the offset */ if (op->atom == DW_OP_fbreg) { if (pf->fb_ops == NULL) die("The attribute of frame base is not supported.\n"); - deref = 1; + ref = true; offs = op->number; op = &pf->fb_ops[0]; } @@ -339,13 +340,13 @@ static void show_location(Dwarf_Op *op, struct probe_finder *pf) if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) { regn = op->atom - DW_OP_breg0; offs += op->number; - deref = 1; + ref = true; } else if (op->atom >= DW_OP_reg0 && op->atom <= DW_OP_reg31) { regn = op->atom - DW_OP_reg0; } else if (op->atom == DW_OP_bregx) { regn = op->number; offs += op->number2; - deref = 1; + ref = true; } else if (op->atom == DW_OP_regx) { regn = op->number; } else @@ -355,17 +356,15 @@ static void show_location(Dwarf_Op *op, struct probe_finder *pf) if (!regs) die("%u exceeds max register number.", regn); - if (deref) - ret = snprintf(pf->buf, pf->len, " %s=%+jd(%s)", - pf->var, (intmax_t)offs, regs); - else - ret = snprintf(pf->buf, pf->len, " %s=%s", pf->var, regs); - DIE_IF(ret < 0); - DIE_IF(ret >= pf->len); + tvar->value = xstrdup(regs); + if (ref) { + tvar->ref = xzalloc(sizeof(struct kprobe_trace_arg_ref)); + tvar->ref->offset = (long)offs; + } } /* Show a variables in kprobe event format */ -static void show_variable(Dwarf_Die *vr_die, struct probe_finder *pf) +static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) { Dwarf_Attribute attr; Dwarf_Op *expr; @@ -379,50 +378,51 @@ static void show_variable(Dwarf_Die *vr_die, struct probe_finder *pf) if (ret <= 0 || nexpr == 0) goto error; - show_location(expr, pf); + convert_location(expr, pf); /* *expr will be cached in libdw. Don't free it. */ return ; error: /* TODO: Support const_value */ die("Failed to find the location of %s at this address.\n" - " Perhaps, it has been optimized out.", pf->var); + " Perhaps, it has been optimized out.", pf->pvar->name); } /* Find a variable in a subprogram die */ static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) { - int ret; Dwarf_Die vr_die; /* TODO: Support struct members and arrays */ - if (!is_c_varname(pf->var)) { - /* Output raw parameters */ - ret = snprintf(pf->buf, pf->len, " %s", pf->var); - DIE_IF(ret < 0); - DIE_IF(ret >= pf->len); - return ; + if (!is_c_varname(pf->pvar->name)) { + /* Copy raw parameters */ + pf->tvar->value = xstrdup(pf->pvar->name); + } else { + pf->tvar->name = xstrdup(pf->pvar->name); + pr_debug("Searching '%s' variable in context.\n", + pf->pvar->name); + /* Search child die for local variables and parameters. */ + if (!die_find_variable(sp_die, pf->pvar->name, &vr_die)) + die("Failed to find '%s' in this function.", + pf->pvar->name); + convert_variable(&vr_die, pf); } - - pr_debug("Searching '%s' variable in context.\n", pf->var); - /* Search child die for local variables and parameters. */ - if (!die_find_variable(sp_die, pf->var, &vr_die)) - die("Failed to find '%s' in this function.", pf->var); - - show_variable(&vr_die, pf); } /* Show a probe point to output buffer */ -static void show_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) +static void convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) { - struct probe_point *pp = pf->pp; + struct kprobe_trace_event *tev; Dwarf_Addr eaddr; Dwarf_Die die_mem; const char *name; - char tmp[MAX_PROBE_BUFFER]; - int ret, i, len; + int ret, i; Dwarf_Attribute fb_attr; size_t nops; + if (pf->ntevs == MAX_PROBES) + die("Too many( > %d) probe point found.\n", MAX_PROBES); + tev = &pf->tevs[pf->ntevs++]; + /* If no real subprogram, find a real one */ if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) { sp_die = die_find_real_subprogram(&pf->cu_die, @@ -431,31 +431,18 @@ static void show_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) die("Probe point is not found in subprograms."); } - /* Output name of probe point */ + /* Copy the name of probe point */ name = dwarf_diename(sp_die); if (name) { dwarf_entrypc(sp_die, &eaddr); - ret = snprintf(tmp, MAX_PROBE_BUFFER, "%s+%lu", name, - (unsigned long)(pf->addr - eaddr)); - /* Copy the function name if possible */ - if (!pp->function) { - pp->function = xstrdup(name); - pp->offset = (size_t)(pf->addr - eaddr); - } - } else { + tev->point.symbol = xstrdup(name); + tev->point.offset = (unsigned long)(pf->addr - eaddr); + } else /* This function has no name. */ - ret = snprintf(tmp, MAX_PROBE_BUFFER, "0x%jx", - (uintmax_t)pf->addr); - if (!pp->function) { - /* TODO: Use _stext */ - pp->function = xstrdup(""); - pp->offset = (size_t)pf->addr; - } - } - DIE_IF(ret < 0); - DIE_IF(ret >= MAX_PROBE_BUFFER); - len = ret; - pr_debug("Probe point found: %s\n", tmp); + tev->point.offset = (unsigned long)pf->addr; + + pr_debug("Probe point found: %s+%lu\n", tev->point.symbol, + tev->point.offset); /* Get the frame base attribute/ops */ dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr); @@ -465,22 +452,16 @@ static void show_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) /* Find each argument */ /* TODO: use dwarf_cfi_addrframe */ - for (i = 0; i < pp->nr_args; i++) { - pf->var = pp->args[i]; - pf->buf = &tmp[len]; - pf->len = MAX_PROBE_BUFFER - len; + tev->nargs = pf->pev->nargs; + tev->args = xzalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); + for (i = 0; i < pf->pev->nargs; i++) { + pf->pvar = &pf->pev->args[i]; + pf->tvar = &tev->args[i]; find_variable(sp_die, pf); - len += strlen(pf->buf); } /* *pf->fb_ops will be cached in libdw. Don't free it. */ pf->fb_ops = NULL; - - if (pp->found == MAX_PROBES) - die("Too many( > %d) probe point found.\n", MAX_PROBES); - - pp->probes[pp->found] = xstrdup(tmp); - pp->found++; } /* Find probe point from its line number */ @@ -512,7 +493,7 @@ static void find_probe_point_by_line(struct probe_finder *pf) (int)i, lineno, (uintmax_t)addr); pf->addr = addr; - show_probe_point(NULL, pf); + convert_probe_point(NULL, pf); /* Continuing, because target line might be inlined. */ } } @@ -563,7 +544,7 @@ static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) if (list_empty(&pf->lcache)) { /* Matching lazy line pattern */ ret = find_lazy_match_lines(&pf->lcache, pf->fname, - pf->pp->lazy_line); + pf->pev->point.lazy_line); if (ret <= 0) die("No matched lines found in %s.", pf->fname); } @@ -596,7 +577,7 @@ static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) (int)i, lineno, (unsigned long long)addr); pf->addr = addr; - show_probe_point(sp_die, pf); + convert_probe_point(sp_die, pf); /* Continuing, because target line might be inlined. */ } /* TODO: deallocate lines, but how? */ @@ -605,7 +586,7 @@ static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) { struct probe_finder *pf = (struct probe_finder *)data; - struct probe_point *pp = pf->pp; + struct perf_probe_point *pp = &pf->pev->point; if (pp->lazy_line) find_probe_point_lazy(in_die, pf); @@ -616,7 +597,7 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) pr_debug("found inline addr: 0x%jx\n", (uintmax_t)pf->addr); - show_probe_point(in_die, pf); + convert_probe_point(in_die, pf); } return DWARF_CB_OK; @@ -626,7 +607,7 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) { struct probe_finder *pf = (struct probe_finder *)data; - struct probe_point *pp = pf->pp; + struct perf_probe_point *pp = &pf->pev->point; /* Check tag and diename */ if (dwarf_tag(sp_die) != DW_TAG_subprogram || @@ -646,7 +627,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) pf->addr = die_get_entrypc(sp_die); pf->addr += pp->offset; /* TODO: Check the address in this function */ - show_probe_point(sp_die, pf); + convert_probe_point(sp_die, pf); } } else /* Inlined function: search instances */ @@ -660,20 +641,25 @@ static void find_probe_point_by_func(struct probe_finder *pf) dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, pf, 0); } -/* Find a probe point */ -int find_probe_point(int fd, struct probe_point *pp) +/* Find kprobe_trace_events specified by perf_probe_event from debuginfo */ +int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, + struct kprobe_trace_event **tevs) { - struct probe_finder pf = {.pp = pp}; + struct probe_finder pf = {.pev = pev}; + struct perf_probe_point *pp = &pev->point; Dwarf_Off off, noff; size_t cuhl; Dwarf_Die *diep; Dwarf *dbg; + pf.tevs = xzalloc(sizeof(struct kprobe_trace_event) * MAX_PROBES); + *tevs = pf.tevs; + pf.ntevs = 0; + dbg = dwarf_begin(fd, DWARF_C_READ); if (!dbg) return -ENOENT; - pp->found = 0; off = 0; line_list__init(&pf.lcache); /* Loop on CUs (Compilation Unit) */ @@ -704,7 +690,7 @@ int find_probe_point(int fd, struct probe_point *pp) line_list__free(&pf.lcache); dwarf_end(dbg); - return pp->found; + return pf.ntevs; } /* Find line range from its line number */ diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index 21f7354..4949526 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h @@ -3,6 +3,7 @@ #include <stdbool.h> #include "util.h" +#include "probe-event.h" #define MAX_PATH_LEN 256 #define MAX_PROBE_BUFFER 1024 @@ -14,67 +15,32 @@ static inline int is_c_varname(const char *name) return isalpha(name[0]) || name[0] == '_'; } -struct probe_point { - char *event; /* Event name */ - char *group; /* Event group */ - - /* Inputs */ - char *file; /* File name */ - int line; /* Line number */ - char *lazy_line; /* Lazy line pattern */ - - char *function; /* Function name */ - int offset; /* Offset bytes */ - - int nr_args; /* Number of arguments */ - char **args; /* Arguments */ - - int retprobe; /* Return probe */ - - /* Output */ - int found; /* Number of found probe points */ - char *probes[MAX_PROBES]; /* Output buffers (will be allocated)*/ -}; - -/* Line number container */ -struct line_node { - struct list_head list; - unsigned int line; -}; - -/* Line range */ -struct line_range { - char *file; /* File name */ - char *function; /* Function name */ - unsigned int start; /* Start line number */ - unsigned int end; /* End line number */ - int offset; /* Start line offset */ - char *path; /* Real path name */ - struct list_head line_list; /* Visible lines */ -}; - #ifndef NO_DWARF_SUPPORT -extern int find_probe_point(int fd, struct probe_point *pp); +/* Find kprobe_trace_events specified by perf_probe_event from debuginfo */ +extern int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, + struct kprobe_trace_event **tevs); + extern int find_line_range(int fd, struct line_range *lr); #include <dwarf.h> #include <libdw.h> struct probe_finder { - struct probe_point *pp; /* Target probe point */ + struct perf_probe_event *pev; /* Target probe event */ + int ntevs; /* number of trace events */ + struct kprobe_trace_event *tevs; /* Result trace events */ /* For function searching */ Dwarf_Addr addr; /* Address */ - const char *fname; /* File name */ + const char *fname; /* Real file name */ int lno; /* Line number */ Dwarf_Die cu_die; /* Current CU */ + struct list_head lcache; /* Line cache for lazy match */ /* For variable searching */ Dwarf_Op *fb_ops; /* Frame base attribute */ - const char *var; /* Current variable name */ - char *buf; /* Current output buffer */ - int len; /* Length of output buffer */ - struct list_head lcache; /* Line cache for lazy match */ + struct perf_probe_arg *pvar; /* Current target variable */ + struct kprobe_trace_arg *tvar; /* Current result variable */ }; struct line_finder { |
|
From: tip-bot f. M. H. <mhi...@re...> - 2010-03-17 11:30:42
|
Commit-ID: 7df2f32956cf0f1a45df38cd0e0fe0c3467580e8 Gitweb: http://git.kernel.org/tip/7df2f32956cf0f1a45df38cd0e0fe0c3467580e8 Author: Masami Hiramatsu <mhi...@re...> AuthorDate: Tue, 16 Mar 2010 18:06:26 -0400 Committer: Ingo Molnar <mi...@el...> CommitDate: Wed, 17 Mar 2010 12:11:15 +0100 perf probe: Add data structure member access support Support accessing members in the data structures. With this, perf-probe accepts data-structure members(IOW, it now accepts dot '.' and arrow '->' operators) as probe arguemnts. e.g. ./perf probe --add 'schedule:44 rq->curr' ./perf probe --add 'vfs_read file->f_op->read file->f_path.dentry' Note that '>' can be interpreted as redirection in command-line. Signed-off-by: Masami Hiramatsu <mhi...@re...> Cc: systemtap <sys...@so...> Cc: DLE <dle...@li...> Cc: Frederic Weisbecker <fwe...@gm...> Cc: Arnaldo Carvalho de Melo <ac...@re...> Cc: Paul Mackerras <pa...@sa...> Cc: Mike Galbraith <ef...@gm...> Cc: Peter Zijlstra <a.p...@ch...> LKML-Reference: <20100316220626.32050.57552.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar <mi...@el...> --- tools/perf/util/probe-event.c | 90 ++++++++++++++++++++++++++++++++-- tools/perf/util/probe-event.h | 12 ++++- tools/perf/util/probe-finder.c | 105 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 201 insertions(+), 6 deletions(-) diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 4e3c1ae..64dea6c 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -262,6 +262,49 @@ static void parse_perf_probe_point(char *arg, struct perf_probe_event *pev) pp->lazy_line); } +/* Parse perf-probe event argument */ +static void parse_perf_probe_arg(const char *str, struct perf_probe_arg *arg) +{ + const char *tmp; + struct perf_probe_arg_field **fieldp; + + pr_debug("parsing arg: %s into ", str); + + tmp = strpbrk(str, "-."); + if (!is_c_varname(str) || !tmp) { + /* A variable, register, symbol or special value */ + arg->name = xstrdup(str); + pr_debug("%s\n", arg->name); + return; + } + + /* Structure fields */ + arg->name = xstrndup(str, tmp - str); + pr_debug("%s, ", arg->name); + fieldp = &arg->field; + + do { + *fieldp = xzalloc(sizeof(struct perf_probe_arg_field)); + if (*tmp == '.') { + str = tmp + 1; + (*fieldp)->ref = false; + } else if (tmp[1] == '>') { + str = tmp + 2; + (*fieldp)->ref = true; + } else + semantic_error("Argument parse error: %s", str); + + tmp = strpbrk(str, "-."); + if (tmp) { + (*fieldp)->name = xstrndup(str, tmp - str); + pr_debug("%s(%d), ", (*fieldp)->name, (*fieldp)->ref); + fieldp = &(*fieldp)->next; + } + } while (tmp); + (*fieldp)->name = xstrdup(str); + pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref); +} + /* Parse perf-probe event command */ void parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev) { @@ -281,7 +324,7 @@ void parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev) pev->nargs = argc - 1; pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs); for (i = 0; i < pev->nargs; i++) { - pev->args[i].name = xstrdup(argv[i + 1]); + parse_perf_probe_arg(argv[i + 1], &pev->args[i]); if (is_c_varname(pev->args[i].name) && pev->point.retprobe) semantic_error("You can't specify local variable for" " kretprobe"); @@ -353,6 +396,33 @@ void parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev) argv_free(argv); } +/* Compose only probe arg */ +int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len) +{ + struct perf_probe_arg_field *field = pa->field; + int ret; + char *tmp = buf; + + ret = e_snprintf(tmp, len, "%s", pa->name); + if (ret <= 0) + goto error; + tmp += ret; + len -= ret; + + while (field) { + ret = e_snprintf(tmp, len, "%s%s", field->ref ? "->" : ".", + field->name); + if (ret <= 0) + goto error; + tmp += ret; + len -= ret; + field = field->next; + } + return tmp - buf; +error: + die("Failed to synthesize perf probe argument: %s", strerror(-ret)); +} + /* Compose only probe point (not argument) */ static char *synthesize_perf_probe_point(struct perf_probe_point *pp) { @@ -563,6 +633,7 @@ void convert_to_perf_probe_event(struct kprobe_trace_event *tev, void clear_perf_probe_event(struct perf_probe_event *pev) { struct perf_probe_point *pp = &pev->point; + struct perf_probe_arg_field *field, *next; int i; if (pev->event) @@ -575,9 +646,18 @@ void clear_perf_probe_event(struct perf_probe_event *pev) free(pp->function); if (pp->lazy_line) free(pp->lazy_line); - for (i = 0; i < pev->nargs; i++) + for (i = 0; i < pev->nargs; i++) { if (pev->args[i].name) free(pev->args[i].name); + field = pev->args[i].field; + while (field) { + next = field->next; + if (field->name) + free(field->name); + free(field); + field = next; + } + } if (pev->args) free(pev->args); memset(pev, 0, sizeof(*pev)); @@ -682,8 +762,10 @@ static void show_perf_probe_event(struct perf_probe_event *pev) if (pev->nargs > 0) { printf(" with"); - for (i = 0; i < pev->nargs; i++) - printf(" %s", pev->args[i].name); + for (i = 0; i < pev->nargs; i++) { + synthesize_perf_probe_arg(&pev->args[i], buf, 128); + printf(" %s", buf); + } } printf(")\n"); free(place); diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 2a2f0a2..cd308b0 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -45,9 +45,17 @@ struct perf_probe_point { bool retprobe; /* Return probe flag */ }; +/* Perf probe probing argument field chain */ +struct perf_probe_arg_field { + struct perf_probe_arg_field *next; /* Next field */ + char *name; /* Name of the field */ + bool ref; /* Referencing flag */ +}; + /* Perf probe probing argument */ struct perf_probe_arg { - char *name; /* Argument name */ + char *name; /* Argument name */ + struct perf_probe_arg_field *field; /* Structure fields */ }; /* Perf probe probing event (point + arg) */ @@ -86,6 +94,8 @@ extern void parse_kprobe_trace_command(const char *cmd, /* Events to command string */ extern char *synthesize_perf_probe_command(struct perf_probe_event *pev); extern char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev); +extern int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, + size_t len); /* Check the perf_probe_event needs debuginfo */ extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev); diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index e02b607..db52ec2 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -206,6 +206,28 @@ static Dwarf_Addr die_get_entrypc(Dwarf_Die *dw_die) return epc; } +/* Get type die, but skip qualifiers and typedef */ +static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) +{ + Dwarf_Attribute attr; + int tag; + + do { + if (dwarf_attr(vr_die, DW_AT_type, &attr) == NULL || + dwarf_formref_die(&attr, die_mem) == NULL) + return NULL; + + tag = dwarf_tag(die_mem); + vr_die = die_mem; + } while (tag == DW_TAG_const_type || + tag == DW_TAG_restrict_type || + tag == DW_TAG_volatile_type || + tag == DW_TAG_shared_type || + tag == DW_TAG_typedef); + + return die_mem; +} + /* Return values for die_find callbacks */ enum { DIE_FIND_CB_FOUND = 0, /* End of Search */ @@ -314,6 +336,25 @@ static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name, die_mem); } +static int __die_find_member_cb(Dwarf_Die *die_mem, void *data) +{ + const char *name = data; + + if ((dwarf_tag(die_mem) == DW_TAG_member) && + (die_compare_name(die_mem, name) == 0)) + return DIE_FIND_CB_FOUND; + + return DIE_FIND_CB_SIBLING; +} + +/* Find a member called 'name' */ +static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name, + Dwarf_Die *die_mem) +{ + return die_find_child(st_die, __die_find_member_cb, (void *)name, + die_mem); +} + /* * Probe finder related functions */ @@ -363,6 +404,62 @@ static void convert_location(Dwarf_Op *op, struct probe_finder *pf) } } +static void convert_variable_fields(Dwarf_Die *vr_die, const char *varname, + struct perf_probe_arg_field *field, + struct kprobe_trace_arg_ref **ref_ptr) +{ + struct kprobe_trace_arg_ref *ref = *ref_ptr; + Dwarf_Attribute attr; + Dwarf_Die member; + Dwarf_Die type; + Dwarf_Word offs; + + pr_debug("converting %s in %s\n", field->name, varname); + if (die_get_real_type(vr_die, &type) == NULL) + die("Failed to get a type information of %s.", varname); + + /* Check the pointer and dereference */ + if (dwarf_tag(&type) == DW_TAG_pointer_type) { + if (!field->ref) + die("Semantic error: %s must be referred by '->'", + field->name); + /* Get the type pointed by this pointer */ + if (die_get_real_type(&type, &type) == NULL) + die("Failed to get a type information of %s.", varname); + + ref = xzalloc(sizeof(struct kprobe_trace_arg_ref)); + if (*ref_ptr) + (*ref_ptr)->next = ref; + else + *ref_ptr = ref; + } else { + if (field->ref) + die("Semantic error: %s must be referred by '.'", + field->name); + if (!ref) + die("Structure on a register is not supported yet."); + } + + /* Verify it is a data structure */ + if (dwarf_tag(&type) != DW_TAG_structure_type) + die("%s is not a data structure.", varname); + + if (die_find_member(&type, field->name, &member) == NULL) + die("%s(tyep:%s) has no member %s.", varname, + dwarf_diename(&type), field->name); + + /* Get the offset of the field */ + if (dwarf_attr(&member, DW_AT_data_member_location, &attr) == NULL || + dwarf_formudata(&attr, &offs) != 0) + die("Failed to get the offset of %s.", field->name); + ref->offset += (long)offs; + + /* Converting next field */ + if (field->next) + convert_variable_fields(&member, field->name, field->next, + &ref); +} + /* Show a variables in kprobe event format */ static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) { @@ -379,6 +476,10 @@ static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) goto error; convert_location(expr, pf); + + if (pf->pvar->field) + convert_variable_fields(vr_die, pf->pvar->name, + pf->pvar->field, &pf->tvar->ref); /* *expr will be cached in libdw. Don't free it. */ return ; error: @@ -391,13 +492,15 @@ error: static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) { Dwarf_Die vr_die; + char buf[128]; /* TODO: Support struct members and arrays */ if (!is_c_varname(pf->pvar->name)) { /* Copy raw parameters */ pf->tvar->value = xstrdup(pf->pvar->name); } else { - pf->tvar->name = xstrdup(pf->pvar->name); + synthesize_perf_probe_arg(pf->pvar, buf, 128); + pf->tvar->name = xstrdup(buf); pr_debug("Searching '%s' variable in context.\n", pf->pvar->name); /* Search child die for local variables and parameters. */ |
|
From: tip-bot f. M. H. <mhi...@re...> - 2010-03-17 11:29:54
|
Commit-ID: f4d7da499e4fc1fdff8f26fdeb1a058d475a7a6c Gitweb: http://git.kernel.org/tip/f4d7da499e4fc1fdff8f26fdeb1a058d475a7a6c Author: Masami Hiramatsu <mhi...@re...> AuthorDate: Tue, 16 Mar 2010 18:06:05 -0400 Committer: Ingo Molnar <mi...@el...> CommitDate: Wed, 17 Mar 2010 11:32:31 +0100 perf probe: Add --dry-run option Add --dry-run option for debugging and testing. Signed-off-by: Masami Hiramatsu <mhi...@re...> Cc: systemtap <sys...@so...> Cc: DLE <dle...@li...> Cc: Frederic Weisbecker <fwe...@gm...> Cc: Arnaldo Carvalho de Melo <ac...@re...> Cc: Paul Mackerras <pa...@sa...> Cc: Mike Galbraith <ef...@gm...> Cc: Peter Zijlstra <a.p...@ch...> LKML-Reference: <20100316220605.32050.6571.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar <mi...@el...> --- tools/perf/Documentation/perf-probe.txt | 5 +++++ tools/perf/builtin-probe.c | 1 + tools/perf/util/probe-event.c | 24 ++++++++++++++++-------- tools/perf/util/probe-event.h | 2 ++ 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index 34202b1..0f944b3 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt @@ -57,6 +57,11 @@ OPTIONS --force:: Forcibly add events with existing name. +-n:: +--dry-run:: + Dry run. With this option, --add and --del doesn't execute actual + adding and removal operations. + PROBE SYNTAX ------------ Probe points are defined by following syntax. diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index f577e14..a1a2891 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -175,6 +175,7 @@ static const struct option options[] = { "FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]", "Show source code lines.", opt_show_lines), #endif + OPT__DRY_RUN(&probe_event_dry_run), OPT_END() }; diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 1e60a65..ac41578 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -49,6 +49,8 @@ #define MAX_PROBE_ARGS 128 #define PERFPROBE_GROUP "probe" +bool probe_event_dry_run; /* Dry run flag */ + #define semantic_error(msg ...) die("Semantic error :" msg) /* If there is no space to write, returns -E2BIG. */ @@ -430,7 +432,7 @@ error: return ret; } -static int open_kprobe_events(int flags, int mode) +static int open_kprobe_events(bool readwrite) { char buf[PATH_MAX]; int ret; @@ -439,7 +441,11 @@ static int open_kprobe_events(int flags, int mode) if (ret < 0) die("Failed to make kprobe_events path."); - ret = open(buf, flags, mode); + if (readwrite && !probe_event_dry_run) + ret = open(buf, O_RDWR, O_APPEND); + else + ret = open(buf, O_RDONLY, 0); + if (ret < 0) { if (errno == ENOENT) die("kprobe_events file does not exist -" @@ -535,7 +541,7 @@ void show_perf_probe_events(void) setup_pager(); memset(&pp, 0, sizeof(pp)); - fd = open_kprobe_events(O_RDONLY, 0); + fd = open_kprobe_events(false); rawlist = get_trace_kprobe_event_rawlist(fd); close(fd); @@ -585,9 +591,11 @@ static void write_trace_kprobe_event(int fd, const char *buf) int ret; pr_debug("Writing event: %s\n", buf); - ret = write(fd, buf, strlen(buf)); - if (ret <= 0) - die("Failed to write event: %s", strerror(errno)); + if (!probe_event_dry_run) { + ret = write(fd, buf, strlen(buf)); + if (ret <= 0) + die("Failed to write event: %s", strerror(errno)); + } } static void get_new_event_name(char *buf, size_t len, const char *base, @@ -630,7 +638,7 @@ static void __add_trace_kprobe_events(struct probe_point *probes, struct strlist *namelist; bool allow_suffix; - fd = open_kprobe_events(O_RDWR, O_APPEND); + fd = open_kprobe_events(true); /* Get current event names */ namelist = get_perf_event_names(fd, false); @@ -814,7 +822,7 @@ void del_trace_kprobe_events(struct strlist *dellist) struct str_node *ent; struct strlist *namelist; - fd = open_kprobe_events(O_RDWR, O_APPEND); + fd = open_kprobe_events(true); /* Get current event names */ namelist = get_perf_event_names(fd, true); diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 3865e16..703b887 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -5,6 +5,8 @@ #include "probe-finder.h" #include "strlist.h" +extern bool probe_event_dry_run; + extern void parse_line_range_desc(const char *arg, struct line_range *lr); extern void parse_perf_probe_event(const char *str, struct probe_point *pp, bool *need_dwarf); |
|
From: tip-bot f. M. H. <mhi...@re...> - 2010-03-17 11:29:35
|
Commit-ID: 016f262e4fb10c6ecff709317098912f94a21efa Gitweb: http://git.kernel.org/tip/016f262e4fb10c6ecff709317098912f94a21efa Author: Masami Hiramatsu <mhi...@re...> AuthorDate: Tue, 16 Mar 2010 18:05:58 -0400 Committer: Ingo Molnar <mi...@el...> CommitDate: Wed, 17 Mar 2010 11:32:31 +0100 perf probe: Introduce die_find_child() function Introduce die_find_child() function to integrate DIE-tree searching functions. Signed-off-by: Masami Hiramatsu <mhi...@re...> Cc: systemtap <sys...@so...> Cc: DLE <dle...@li...> Cc: Frederic Weisbecker <fwe...@gm...> Cc: Arnaldo Carvalho de Melo <ac...@re...> Cc: Paul Mackerras <pa...@sa...> Cc: Mike Galbraith <ef...@gm...> Cc: Peter Zijlstra <a.p...@ch...> LKML-Reference: <20100316220558.32050.7905.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar <mi...@el...> --- tools/perf/util/probe-finder.c | 136 +++++++++++++++++++++++---------------- 1 files changed, 80 insertions(+), 56 deletions(-) diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index c91a960..3942e14 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -186,6 +186,62 @@ static const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname) return src; } +/* Compare diename and tname */ +static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) +{ + const char *name; + name = dwarf_diename(dw_die); + DIE_IF(name == NULL); + return strcmp(tname, name); +} + +/* Get entry pc(or low pc, 1st entry of ranges) of the die */ +static Dwarf_Addr die_get_entrypc(Dwarf_Die *dw_die) +{ + Dwarf_Addr epc; + int ret; + + ret = dwarf_entrypc(dw_die, &epc); + DIE_IF(ret == -1); + return epc; +} + +/* Return values for die_find callbacks */ +enum { + DIE_FIND_CB_FOUND = 0, /* End of Search */ + DIE_FIND_CB_CHILD = 1, /* Search only children */ + DIE_FIND_CB_SIBLING = 2, /* Search only siblings */ + DIE_FIND_CB_CONTINUE = 3, /* Search children and siblings */ +}; + +/* Search a child die */ +static Dwarf_Die *die_find_child(Dwarf_Die *rt_die, + int (*callback)(Dwarf_Die *, void *), + void *data, Dwarf_Die *die_mem) +{ + Dwarf_Die child_die; + int ret; + + ret = dwarf_child(rt_die, die_mem); + if (ret != 0) + return NULL; + + do { + ret = callback(die_mem, data); + if (ret == DIE_FIND_CB_FOUND) + return die_mem; + + if ((ret & DIE_FIND_CB_CHILD) && + die_find_child(die_mem, callback, data, &child_die)) { + memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); + return die_mem; + } + } while ((ret & DIE_FIND_CB_SIBLING) && + dwarf_siblingof(die_mem, die_mem) == 0); + + return NULL; +} + struct __addr_die_search_param { Dwarf_Addr addr; Dwarf_Die *die_mem; @@ -217,77 +273,45 @@ static Dwarf_Die *die_find_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr, return die_mem; } -/* Similar to dwarf_getfuncs, but returns inlined_subroutine if exists. */ -static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, - Dwarf_Die *die_mem) +/* die_find callback for inline function search */ +static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data) { - Dwarf_Die child_die; - int ret; + Dwarf_Addr *addr = data; - ret = dwarf_child(sp_die, die_mem); - if (ret != 0) - return NULL; - - do { - if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine && - dwarf_haspc(die_mem, addr)) - return die_mem; + if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine && + dwarf_haspc(die_mem, *addr)) + return DIE_FIND_CB_FOUND; - if (die_find_inlinefunc(die_mem, addr, &child_die)) { - memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); - return die_mem; - } - } while (dwarf_siblingof(die_mem, die_mem) == 0); - - return NULL; + return DIE_FIND_CB_CONTINUE; } -/* Compare diename and tname */ -static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) +/* Similar to dwarf_getfuncs, but returns inlined_subroutine if exists. */ +static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, + Dwarf_Die *die_mem) { - const char *name; - name = dwarf_diename(dw_die); - DIE_IF(name == NULL); - return strcmp(tname, name); + return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem); } -/* Get entry pc(or low pc, 1st entry of ranges) of the die */ -static Dwarf_Addr die_get_entrypc(Dwarf_Die *dw_die) +static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data) { - Dwarf_Addr epc; - int ret; + const char *name = data; + int tag; - ret = dwarf_entrypc(dw_die, &epc); - DIE_IF(ret == -1); - return epc; + tag = dwarf_tag(die_mem); + if ((tag == DW_TAG_formal_parameter || + tag == DW_TAG_variable) && + (die_compare_name(die_mem, name) == 0)) + return DIE_FIND_CB_FOUND; + + return DIE_FIND_CB_CONTINUE; } -/* Get a variable die */ +/* Find a variable called 'name' */ static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name, Dwarf_Die *die_mem) { - Dwarf_Die child_die; - int tag; - int ret; - - ret = dwarf_child(sp_die, die_mem); - if (ret != 0) - return NULL; - - do { - tag = dwarf_tag(die_mem); - if ((tag == DW_TAG_formal_parameter || - tag == DW_TAG_variable) && - (die_compare_name(die_mem, name) == 0)) - return die_mem; - - if (die_find_variable(die_mem, name, &child_die)) { - memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); - return die_mem; - } - } while (dwarf_siblingof(die_mem, die_mem) == 0); - - return NULL; + return die_find_child(sp_die, __die_find_variable_cb, (void *)name, + die_mem); } /* |
|
From: tip-bot f. M. H. <mhi...@re...> - 2010-03-17 11:29:24
|
Commit-ID: 95a3e4c4e21de1920a2ddb54bfc57c0af7e2561e Gitweb: http://git.kernel.org/tip/95a3e4c4e21de1920a2ddb54bfc57c0af7e2561e Author: Masami Hiramatsu <mhi...@re...> AuthorDate: Tue, 16 Mar 2010 18:05:51 -0400 Committer: Ingo Molnar <mi...@el...> CommitDate: Wed, 17 Mar 2010 11:32:30 +0100 perf probe: Rename some die_get_* functions Rename die_get_real_subprogram and die_get_inlinefunc to die_find_real_subprogram and die_find_inlinefunc respectively, because these functions search its children. After that, 'die_get_' means getting a property of that die, and 'die_find_' means searching DIE-tree to get an appropriate child die. Signed-off-by: Masami Hiramatsu <mhi...@re...> Cc: systemtap <sys...@so...> Cc: DLE <dle...@li...> Cc: Frederic Weisbecker <fwe...@gm...> Cc: Arnaldo Carvalho de Melo <ac...@re...> Cc: Paul Mackerras <pa...@sa...> Cc: Mike Galbraith <ef...@gm...> Cc: Peter Zijlstra <a.p...@ch...> LKML-Reference: <20100316220551.32050.36181.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar <mi...@el...> --- tools/perf/util/probe-finder.c | 16 ++++++++-------- 1 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index e887bb6..c91a960 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -204,8 +204,8 @@ static int __die_search_func_cb(Dwarf_Die *fn_die, void *data) } /* Search a real subprogram including this line, */ -static Dwarf_Die *die_get_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr, - Dwarf_Die *die_mem) +static Dwarf_Die *die_find_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr, + Dwarf_Die *die_mem) { struct __addr_die_search_param ad; ad.addr = addr; @@ -218,8 +218,8 @@ static Dwarf_Die *die_get_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr, } /* Similar to dwarf_getfuncs, but returns inlined_subroutine if exists. */ -static Dwarf_Die *die_get_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, - Dwarf_Die *die_mem) +static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, + Dwarf_Die *die_mem) { Dwarf_Die child_die; int ret; @@ -233,7 +233,7 @@ static Dwarf_Die *die_get_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, dwarf_haspc(die_mem, addr)) return die_mem; - if (die_get_inlinefunc(die_mem, addr, &child_die)) { + if (die_find_inlinefunc(die_mem, addr, &child_die)) { memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); return die_mem; } @@ -401,7 +401,7 @@ static void show_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) /* If no real subprogram, find a real one */ if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) { - sp_die = die_get_real_subprogram(&pf->cu_die, + sp_die = die_find_real_subprogram(&pf->cu_die, pf->addr, &die_mem); if (!sp_die) die("Probe point is not found in subprograms."); @@ -564,7 +564,7 @@ static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) if (!dwarf_haspc(sp_die, addr)) continue; /* Address filtering 2: No child include addr? */ - if (die_get_inlinefunc(sp_die, addr, &die_mem)) + if (die_find_inlinefunc(sp_die, addr, &die_mem)) continue; } @@ -714,7 +714,7 @@ static void find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) continue; /* Address filtering 2: No child include addr? */ - if (die_get_inlinefunc(sp_die, addr, &die_mem)) + if (die_find_inlinefunc(sp_die, addr, &die_mem)) continue; } |
|
From: tip-bot f. M. H. <mhi...@re...> - 2010-03-17 11:29:03
|
Commit-ID: 12a1fadb41b5a6733c36b488b881fb19a28c92d3 Gitweb: http://git.kernel.org/tip/12a1fadb41b5a6733c36b488b881fb19a28c92d3 Author: Masami Hiramatsu <mhi...@re...> AuthorDate: Tue, 16 Mar 2010 18:05:44 -0400 Committer: Ingo Molnar <mi...@el...> CommitDate: Wed, 17 Mar 2010 11:32:30 +0100 perf probe: Rename session to param Since this name 'session' conflicts with 'perf_session', and this structure just holds parameters anymore. Signed-off-by: Masami Hiramatsu <mhi...@re...> Cc: systemtap <sys...@so...> Cc: DLE <dle...@li...> Cc: Frederic Weisbecker <fwe...@gm...> Cc: Arnaldo Carvalho de Melo <ac...@re...> Cc: Paul Mackerras <pa...@sa...> Cc: Mike Galbraith <ef...@gm...> Cc: Peter Zijlstra <a.p...@ch...> LKML-Reference: <20100316220544.32050.8788.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar <mi...@el...> --- tools/perf/builtin-probe.c | 54 ++++++++++++++++++++++---------------------- 1 files changed, 27 insertions(+), 27 deletions(-) diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 2087034..f577e14 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -56,20 +56,20 @@ static struct { struct probe_point probes[MAX_PROBES]; struct strlist *dellist; struct line_range line_range; -} session; +} params; /* Parse an event definition. Note that any error must die. */ static void parse_probe_event(const char *str) { - struct probe_point *pp = &session.probes[session.nr_probe]; + struct probe_point *pp = ¶ms.probes[params.nr_probe]; - pr_debug("probe-definition(%d): %s\n", session.nr_probe, str); - if (++session.nr_probe == MAX_PROBES) + pr_debug("probe-definition(%d): %s\n", params.nr_probe, str); + if (++params.nr_probe == MAX_PROBES) die("Too many probes (> %d) are specified.", MAX_PROBES); /* Parse perf-probe event into probe_point */ - parse_perf_probe_event(str, pp, &session.need_dwarf); + parse_perf_probe_event(str, pp, ¶ms.need_dwarf); pr_debug("%d arguments\n", pp->nr_args); } @@ -103,9 +103,9 @@ static int opt_del_probe_event(const struct option *opt __used, const char *str, int unset __used) { if (str) { - if (!session.dellist) - session.dellist = strlist__new(true, NULL); - strlist__add(session.dellist, str); + if (!params.dellist) + params.dellist = strlist__new(true, NULL); + strlist__add(params.dellist, str); } return 0; } @@ -115,9 +115,9 @@ static int opt_show_lines(const struct option *opt __used, const char *str, int unset __used) { if (str) - parse_line_range_desc(str, &session.line_range); - INIT_LIST_HEAD(&session.line_range.line_list); - session.show_lines = true; + parse_line_range_desc(str, ¶ms.line_range); + INIT_LIST_HEAD(¶ms.line_range.line_list); + params.show_lines = true; return 0; } #endif @@ -140,7 +140,7 @@ static const struct option options[] = { OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, "file", "vmlinux pathname"), #endif - OPT_BOOLEAN('l', "list", &session.list_events, + OPT_BOOLEAN('l', "list", ¶ms.list_events, "list up current probe events"), OPT_CALLBACK('d', "del", NULL, "[GROUP:]EVENT", "delete a probe event.", opt_del_probe_event), @@ -168,7 +168,7 @@ static const struct option options[] = { #endif "\t\t\tkprobe-tracer argument format.)\n", opt_add_probe_event), - OPT_BOOLEAN('f', "force", &session.force_add, "forcibly add events" + OPT_BOOLEAN('f', "force", ¶ms.force_add, "forcibly add events" " with existing name"), #ifndef NO_DWARF_SUPPORT OPT_CALLBACK('L', "line", NULL, @@ -190,20 +190,20 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) parse_probe_event_argv(argc, argv); } - if ((!session.nr_probe && !session.dellist && !session.list_events && - !session.show_lines)) + if ((!params.nr_probe && !params.dellist && !params.list_events && + !params.show_lines)) usage_with_options(probe_usage, options); if (debugfs_valid_mountpoint(debugfs_path) < 0) die("Failed to find debugfs path."); - if (session.list_events) { - if (session.nr_probe != 0 || session.dellist) { + if (params.list_events) { + if (params.nr_probe != 0 || params.dellist) { pr_warning(" Error: Don't use --list with" " --add/--del.\n"); usage_with_options(probe_usage, options); } - if (session.show_lines) { + if (params.show_lines) { pr_warning(" Error: Don't use --list with --line.\n"); usage_with_options(probe_usage, options); } @@ -212,27 +212,27 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) } #ifndef NO_DWARF_SUPPORT - if (session.show_lines) { - if (session.nr_probe != 0 || session.dellist) { + if (params.show_lines) { + if (params.nr_probe != 0 || params.dellist) { pr_warning(" Error: Don't use --line with" " --add/--del.\n"); usage_with_options(probe_usage, options); } - show_line_range(&session.line_range); + show_line_range(¶ms.line_range); return 0; } #endif - if (session.dellist) { - del_trace_kprobe_events(session.dellist); - strlist__delete(session.dellist); - if (session.nr_probe == 0) + if (params.dellist) { + del_trace_kprobe_events(params.dellist); + strlist__delete(params.dellist); + if (params.nr_probe == 0) return 0; } - add_trace_kprobe_events(session.probes, session.nr_probe, - session.force_add, session.need_dwarf); + add_trace_kprobe_events(params.probes, params.nr_probe, + params.force_add, params.need_dwarf); return 0; } |