* [PATCH -tip 1/7] [BUGFIX] perf probe: Fix type searching
2010-10-21 10:12 [PATCH -tip 0/7] Perf probe update (--vars/--module) Masami Hiramatsu
@ 2010-10-21 10:13 ` Masami Hiramatsu
2010-10-23 19:40 ` [tip:perf/urgent] " tip-bot for Masami Hiramatsu
2010-10-21 10:13 ` [PATCH -tip 2/7] [BUGFIX] perf probe: Fix local variable searching loop Masami Hiramatsu
` (5 subsequent siblings)
6 siblings, 1 reply; 18+ messages in thread
From: Masami Hiramatsu @ 2010-10-21 10:13 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo, Ingo Molnar
Cc: Steven Rostedt, Srikar Dronamraju, linux-kernel, 2nddept-manager,
Masami Hiramatsu, Peter Zijlstra, Paul Mackerras, Ingo Molnar,
Arnaldo Carvalho de Melo, linux-kernel
Fix to get the actual type die of variables by
using dwarf_attr_integrate() which gets attribute
from die even if the type die is connected by
DW_AT_abstract_origin.
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: linux-kernel@vger.kernel.org
---
tools/perf/util/probe-finder.c | 25 +++++++++++++++++--------
1 files changed, 17 insertions(+), 8 deletions(-)
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 32b81f7..a2d1f79 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -160,26 +160,35 @@ static bool die_compare_name(Dwarf_Die *dw_die, const char *tname)
return name ? (strcmp(tname, name) == 0) : false;
}
+/* Get type die */
+static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
+{
+ Dwarf_Attribute attr;
+
+ if (dwarf_attr_integrate(vr_die, DW_AT_type, &attr) &&
+ dwarf_formref_die(&attr, die_mem))
+ return die_mem;
+ else
+ return NULL;
+}
+
/* 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;
+ vr_die = die_get_type(vr_die, die_mem);
+ if (!vr_die)
+ break;
+ tag = dwarf_tag(vr_die);
} 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 vr_die;
}
static bool die_is_signed_type(Dwarf_Die *tp_die)
^ permalink raw reply related [flat|nested] 18+ messages in thread* [tip:perf/urgent] perf probe: Fix type searching
2010-10-21 10:13 ` [PATCH -tip 1/7] [BUGFIX] perf probe: Fix type searching Masami Hiramatsu
@ 2010-10-23 19:40 ` tip-bot for Masami Hiramatsu
0 siblings, 0 replies; 18+ messages in thread
From: tip-bot for Masami Hiramatsu @ 2010-10-23 19:40 UTC (permalink / raw)
To: linux-tip-commits
Cc: acme, linux-kernel, paulus, hpa, mingo, a.p.zijlstra,
masami.hiramatsu.pt, tglx, mingo
Commit-ID: 4046b8bb5ffae9ee916e504cdadee804f10e0c56
Gitweb: http://git.kernel.org/tip/4046b8bb5ffae9ee916e504cdadee804f10e0c56
Author: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
AuthorDate: Thu, 21 Oct 2010 19:13:02 +0900
Committer: Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Thu, 21 Oct 2010 15:57:08 -0200
perf probe: Fix type searching
Fix to get the actual type die of variables by using dwarf_attr_integrate()
which gets attribute from die even if the type die is connected by
DW_AT_abstract_origin.
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Ingo Molnar <mingo@elte.hu>
LKML-Reference: <20101021101302.3542.38549.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
tools/perf/util/probe-finder.c | 25 +++++++++++++++++--------
1 files changed, 17 insertions(+), 8 deletions(-)
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 32b81f7..a2d1f79 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -160,26 +160,35 @@ static bool die_compare_name(Dwarf_Die *dw_die, const char *tname)
return name ? (strcmp(tname, name) == 0) : false;
}
+/* Get type die */
+static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
+{
+ Dwarf_Attribute attr;
+
+ if (dwarf_attr_integrate(vr_die, DW_AT_type, &attr) &&
+ dwarf_formref_die(&attr, die_mem))
+ return die_mem;
+ else
+ return NULL;
+}
+
/* 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;
+ vr_die = die_get_type(vr_die, die_mem);
+ if (!vr_die)
+ break;
+ tag = dwarf_tag(vr_die);
} 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 vr_die;
}
static bool die_is_signed_type(Dwarf_Die *tp_die)
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH -tip 2/7] [BUGFIX] perf probe: Fix local variable searching loop
2010-10-21 10:12 [PATCH -tip 0/7] Perf probe update (--vars/--module) Masami Hiramatsu
2010-10-21 10:13 ` [PATCH -tip 1/7] [BUGFIX] perf probe: Fix type searching Masami Hiramatsu
@ 2010-10-21 10:13 ` Masami Hiramatsu
2010-10-23 19:40 ` [tip:perf/urgent] " tip-bot for Masami Hiramatsu
2010-10-21 10:13 ` [PATCH -tip 3/7] perf probe: Support global variables Masami Hiramatsu
` (4 subsequent siblings)
6 siblings, 1 reply; 18+ messages in thread
From: Masami Hiramatsu @ 2010-10-21 10:13 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo, Ingo Molnar
Cc: Steven Rostedt, Srikar Dronamraju, linux-kernel, 2nddept-manager,
Masami Hiramatsu, Peter Zijlstra, Paul Mackerras, Ingo Molnar,
Arnaldo Carvalho de Melo, linux-kernel
Fix to check the die's address and search into the die
only if it has given address.
This will avoid finding wrong variables in wrong
basic block.
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: linux-kernel@vger.kernel.org
---
tools/perf/util/probe-finder.c | 26 ++++++++++++++++++--------
1 files changed, 18 insertions(+), 8 deletions(-)
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index a2d1f79..abcaec5 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -329,25 +329,35 @@ static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr,
return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem);
}
+struct __find_variable_param {
+ const char *name;
+ Dwarf_Addr addr;
+};
+
static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data)
{
- const char *name = data;
+ struct __find_variable_param *fvp = data;
int tag;
tag = dwarf_tag(die_mem);
if ((tag == DW_TAG_formal_parameter ||
tag == DW_TAG_variable) &&
- die_compare_name(die_mem, name))
+ die_compare_name(die_mem, fvp->name))
return DIE_FIND_CB_FOUND;
- return DIE_FIND_CB_CONTINUE;
+ if (dwarf_haspc(die_mem, fvp->addr))
+ return DIE_FIND_CB_CONTINUE;
+ else
+ return DIE_FIND_CB_SIBLING;
}
-/* Find a variable called 'name' */
-static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name,
- Dwarf_Die *die_mem)
+/* Find a variable called 'name' at given address */
+static Dwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name,
+ Dwarf_Addr addr, Dwarf_Die *die_mem)
{
- return die_find_child(sp_die, __die_find_variable_cb, (void *)name,
+ struct __find_variable_param fvp = { .name = name, .addr = addr};
+
+ return die_find_child(sp_die, __die_find_variable_cb, (void *)&fvp,
die_mem);
}
@@ -731,7 +741,7 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
pr_debug("Searching '%s' variable in context.\n",
pf->pvar->var);
/* Search child die for local variables and parameters. */
- if (die_find_variable(sp_die, pf->pvar->var, &vr_die))
+ if (die_find_variable_at(sp_die, pf->pvar->var, pf->addr, &vr_die))
ret = convert_variable(&vr_die, pf);
else {
/* Search upper class */
^ permalink raw reply related [flat|nested] 18+ messages in thread* [tip:perf/urgent] perf probe: Fix local variable searching loop
2010-10-21 10:13 ` [PATCH -tip 2/7] [BUGFIX] perf probe: Fix local variable searching loop Masami Hiramatsu
@ 2010-10-23 19:40 ` tip-bot for Masami Hiramatsu
0 siblings, 0 replies; 18+ messages in thread
From: tip-bot for Masami Hiramatsu @ 2010-10-23 19:40 UTC (permalink / raw)
To: linux-tip-commits
Cc: acme, linux-kernel, paulus, hpa, mingo, a.p.zijlstra,
masami.hiramatsu.pt, tglx, mingo
Commit-ID: 378eeaad3e1cfea7f6614018fb335de93df2ba1f
Gitweb: http://git.kernel.org/tip/378eeaad3e1cfea7f6614018fb335de93df2ba1f
Author: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
AuthorDate: Thu, 21 Oct 2010 19:13:09 +0900
Committer: Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Thu, 21 Oct 2010 15:58:05 -0200
perf probe: Fix local variable searching loop
Fix to check the die's address and search into the die only if it has given
address.
This will avoid finding wrong variables in wrong basic block.
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Ingo Molnar <mingo@elte.hu>
LKML-Reference: <20101021101309.3542.46434.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
tools/perf/util/probe-finder.c | 26 ++++++++++++++++++--------
1 files changed, 18 insertions(+), 8 deletions(-)
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index a2d1f79..abcaec5 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -329,25 +329,35 @@ static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr,
return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem);
}
+struct __find_variable_param {
+ const char *name;
+ Dwarf_Addr addr;
+};
+
static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data)
{
- const char *name = data;
+ struct __find_variable_param *fvp = data;
int tag;
tag = dwarf_tag(die_mem);
if ((tag == DW_TAG_formal_parameter ||
tag == DW_TAG_variable) &&
- die_compare_name(die_mem, name))
+ die_compare_name(die_mem, fvp->name))
return DIE_FIND_CB_FOUND;
- return DIE_FIND_CB_CONTINUE;
+ if (dwarf_haspc(die_mem, fvp->addr))
+ return DIE_FIND_CB_CONTINUE;
+ else
+ return DIE_FIND_CB_SIBLING;
}
-/* Find a variable called 'name' */
-static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name,
- Dwarf_Die *die_mem)
+/* Find a variable called 'name' at given address */
+static Dwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name,
+ Dwarf_Addr addr, Dwarf_Die *die_mem)
{
- return die_find_child(sp_die, __die_find_variable_cb, (void *)name,
+ struct __find_variable_param fvp = { .name = name, .addr = addr};
+
+ return die_find_child(sp_die, __die_find_variable_cb, (void *)&fvp,
die_mem);
}
@@ -731,7 +741,7 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
pr_debug("Searching '%s' variable in context.\n",
pf->pvar->var);
/* Search child die for local variables and parameters. */
- if (die_find_variable(sp_die, pf->pvar->var, &vr_die))
+ if (die_find_variable_at(sp_die, pf->pvar->var, pf->addr, &vr_die))
ret = convert_variable(&vr_die, pf);
else {
/* Search upper class */
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH -tip 3/7] perf probe: Support global variables
2010-10-21 10:12 [PATCH -tip 0/7] Perf probe update (--vars/--module) Masami Hiramatsu
2010-10-21 10:13 ` [PATCH -tip 1/7] [BUGFIX] perf probe: Fix type searching Masami Hiramatsu
2010-10-21 10:13 ` [PATCH -tip 2/7] [BUGFIX] perf probe: Fix local variable searching loop Masami Hiramatsu
@ 2010-10-21 10:13 ` Masami Hiramatsu
2010-10-23 19:40 ` [tip:perf/urgent] " tip-bot for Masami Hiramatsu
2010-10-21 10:13 ` [PATCH -tip 4/7] perf probe: Show accessible local variables Masami Hiramatsu
` (3 subsequent siblings)
6 siblings, 1 reply; 18+ messages in thread
From: Masami Hiramatsu @ 2010-10-21 10:13 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo, Ingo Molnar
Cc: Steven Rostedt, Srikar Dronamraju, linux-kernel, 2nddept-manager,
Masami Hiramatsu, Peter Zijlstra, Paul Mackerras, Ingo Molnar,
Arnaldo Carvalho de Melo, linux-kernel
Allow users to set external defined global variables
as event arguments (e.g. jiffies).
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: linux-kernel@vger.kernel.org
---
tools/perf/util/probe-finder.c | 25 +++++++++++++++++--------
1 files changed, 17 insertions(+), 8 deletions(-)
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index abcaec5..c6fe80e 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -406,6 +406,9 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
struct probe_trace_arg *tvar = pf->tvar;
int ret;
+ if (dwarf_attr(vr_die, DW_AT_external, &attr) != NULL)
+ goto static_var;
+
/* TODO: handle more than 1 exprs */
if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL ||
dwarf_getlocation_addr(&attr, pf->addr, &op, &nops, 1) <= 0 ||
@@ -417,6 +420,7 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
}
if (op->atom == DW_OP_addr) {
+static_var:
/* Static variables on memory (not stack), make @varname */
ret = strlen(dwarf_diename(vr_die));
tvar->value = zalloc(ret + 2);
@@ -746,17 +750,22 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
else {
/* Search upper class */
nscopes = dwarf_getscopes_die(sp_die, &scopes);
- if (nscopes > 0) {
- ret = dwarf_getscopevar(scopes, nscopes, pf->pvar->var,
- 0, NULL, 0, 0, &vr_die);
- if (ret >= 0)
+ while (nscopes-- > 1) {
+ pr_debug("Searching variables in %s\n",
+ dwarf_diename(&scopes[nscopes]));
+ /* We should check this scope, so give dummy address */
+ if (die_find_variable_at(&scopes[nscopes],
+ pf->pvar->var, 0,
+ &vr_die)) {
ret = convert_variable(&vr_die, pf);
- else
- ret = -ENOENT;
+ goto found;
+ }
+ }
+ if (scopes)
free(scopes);
- } else
- ret = -ENOENT;
+ ret = -ENOENT;
}
+found:
if (ret < 0)
pr_warning("Failed to find '%s' in this function.\n",
pf->pvar->var);
^ permalink raw reply related [flat|nested] 18+ messages in thread* [tip:perf/urgent] perf probe: Support global variables
2010-10-21 10:13 ` [PATCH -tip 3/7] perf probe: Support global variables Masami Hiramatsu
@ 2010-10-23 19:40 ` tip-bot for Masami Hiramatsu
0 siblings, 0 replies; 18+ messages in thread
From: tip-bot for Masami Hiramatsu @ 2010-10-23 19:40 UTC (permalink / raw)
To: linux-tip-commits
Cc: acme, linux-kernel, paulus, hpa, mingo, a.p.zijlstra,
masami.hiramatsu.pt, tglx, mingo
Commit-ID: 632941c4f8fbd5b90dcb1672cd0422dfd7332bc9
Gitweb: http://git.kernel.org/tip/632941c4f8fbd5b90dcb1672cd0422dfd7332bc9
Author: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
AuthorDate: Thu, 21 Oct 2010 19:13:16 +0900
Committer: Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Thu, 21 Oct 2010 15:58:27 -0200
perf probe: Support global variables
Allow users to set external defined global variables as event arguments (e.g.
jiffies).
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Ingo Molnar <mingo@elte.hu>
LKML-Reference: <20101021101316.3542.1999.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
tools/perf/util/probe-finder.c | 25 +++++++++++++++++--------
1 files changed, 17 insertions(+), 8 deletions(-)
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index abcaec5..c6fe80e 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -406,6 +406,9 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
struct probe_trace_arg *tvar = pf->tvar;
int ret;
+ if (dwarf_attr(vr_die, DW_AT_external, &attr) != NULL)
+ goto static_var;
+
/* TODO: handle more than 1 exprs */
if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL ||
dwarf_getlocation_addr(&attr, pf->addr, &op, &nops, 1) <= 0 ||
@@ -417,6 +420,7 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
}
if (op->atom == DW_OP_addr) {
+static_var:
/* Static variables on memory (not stack), make @varname */
ret = strlen(dwarf_diename(vr_die));
tvar->value = zalloc(ret + 2);
@@ -746,17 +750,22 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
else {
/* Search upper class */
nscopes = dwarf_getscopes_die(sp_die, &scopes);
- if (nscopes > 0) {
- ret = dwarf_getscopevar(scopes, nscopes, pf->pvar->var,
- 0, NULL, 0, 0, &vr_die);
- if (ret >= 0)
+ while (nscopes-- > 1) {
+ pr_debug("Searching variables in %s\n",
+ dwarf_diename(&scopes[nscopes]));
+ /* We should check this scope, so give dummy address */
+ if (die_find_variable_at(&scopes[nscopes],
+ pf->pvar->var, 0,
+ &vr_die)) {
ret = convert_variable(&vr_die, pf);
- else
- ret = -ENOENT;
+ goto found;
+ }
+ }
+ if (scopes)
free(scopes);
- } else
- ret = -ENOENT;
+ ret = -ENOENT;
}
+found:
if (ret < 0)
pr_warning("Failed to find '%s' in this function.\n",
pf->pvar->var);
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH -tip 4/7] perf probe: Show accessible local variables
2010-10-21 10:12 [PATCH -tip 0/7] Perf probe update (--vars/--module) Masami Hiramatsu
` (2 preceding siblings ...)
2010-10-21 10:13 ` [PATCH -tip 3/7] perf probe: Support global variables Masami Hiramatsu
@ 2010-10-21 10:13 ` Masami Hiramatsu
2010-10-23 19:41 ` [tip:perf/urgent] " tip-bot for Masami Hiramatsu
2010-10-21 10:13 ` [PATCH -tip 5/7] perf probe: Function style fix Masami Hiramatsu
` (2 subsequent siblings)
6 siblings, 1 reply; 18+ messages in thread
From: Masami Hiramatsu @ 2010-10-21 10:13 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo, Ingo Molnar
Cc: Steven Rostedt, Srikar Dronamraju, linux-kernel, 2nddept-manager,
Masami Hiramatsu, Peter Zijlstra, Paul Mackerras, Ingo Molnar,
Arnaldo Carvalho de Melo, Frederic Weisbecker, linux-kernel
Add -V (--vars) option for listing accessible local
variables at given probe point. This will help finding
which local variables are available for event arguments.
e.g.)
# perf probe -V call_timer_fn:23
Available variables at call_timer_fn:23
@<run_timer_softirq+345>
function_type* fn
int preempt_count
long unsigned int data
struct list_head work_list
struct list_head* head
struct timer_list* timer
struct tvec_base* base
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: linux-kernel@vger.kernel.org
---
tools/perf/Documentation/perf-probe.txt | 7 +
tools/perf/builtin-probe.c | 61 ++++-
tools/perf/util/probe-event.c | 72 +++++
tools/perf/util/probe-event.h | 8 +
tools/perf/util/probe-finder.c | 412 ++++++++++++++++++++++++-------
tools/perf/util/probe-finder.h | 25 ++
6 files changed, 480 insertions(+), 105 deletions(-)
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index 27d52da..72f5d9e 100644
--- a/tools/perf/Documentation/perf-probe.txt
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -17,6 +17,8 @@ or
'perf probe' --list
or
'perf probe' --line='FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]'
+or
+'perf probe' --vars='PROBEPOINT'
DESCRIPTION
-----------
@@ -57,6 +59,11 @@ OPTIONS
Show source code lines which can be probed. This needs an argument
which specifies a range of the source code. (see LINE SYNTAX for detail)
+-V::
+--vars=::
+ Show available local variables at given probe point. The argument
+ syntax is same as PROBE SYNTAX, but NO ARGs.
+
-f::
--force::
Forcibly add events with existing name.
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index 199d5e1..91bb6cf 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -50,6 +50,8 @@ static struct {
bool list_events;
bool force_add;
bool show_lines;
+ bool show_vars;
+ bool mod_events;
int nevents;
struct perf_probe_event events[MAX_PROBES];
struct strlist *dellist;
@@ -57,7 +59,6 @@ static struct {
int max_probe_points;
} params;
-
/* Parse an event definition. Note that any error must die. */
static int parse_probe_event(const char *str)
{
@@ -92,6 +93,7 @@ static int parse_probe_event_argv(int argc, const char **argv)
len = 0;
for (i = 0; i < argc; i++)
len += sprintf(&buf[len], "%s ", argv[i]);
+ params.mod_events = true;
ret = parse_probe_event(buf);
free(buf);
return ret;
@@ -100,9 +102,10 @@ static int parse_probe_event_argv(int argc, const char **argv)
static int opt_add_probe_event(const struct option *opt __used,
const char *str, int unset __used)
{
- if (str)
+ if (str) {
+ params.mod_events = true;
return parse_probe_event(str);
- else
+ } else
return 0;
}
@@ -110,6 +113,7 @@ static int opt_del_probe_event(const struct option *opt __used,
const char *str, int unset __used)
{
if (str) {
+ params.mod_events = true;
if (!params.dellist)
params.dellist = strlist__new(true, NULL);
strlist__add(params.dellist, str);
@@ -130,6 +134,25 @@ static int opt_show_lines(const struct option *opt __used,
return ret;
}
+
+static int opt_show_vars(const struct option *opt __used,
+ const char *str, int unset __used)
+{
+ struct perf_probe_event *pev = ¶ms.events[params.nevents];
+ int ret;
+
+ if (!str)
+ return 0;
+
+ ret = parse_probe_event(str);
+ if (!ret && pev->nargs != 0) {
+ pr_err(" Error: '--vars' doesn't accept arguments.\n");
+ return -EINVAL;
+ }
+ params.show_vars = true;
+
+ return ret;
+}
#endif
static const char * const probe_usage[] = {
@@ -139,6 +162,7 @@ static const char * const probe_usage[] = {
"perf probe --list",
#ifdef DWARF_SUPPORT
"perf probe --line 'LINEDESC'",
+ "perf probe --vars 'PROBEPOINT'",
#endif
NULL
};
@@ -180,6 +204,9 @@ static const struct option options[] = {
OPT_CALLBACK('L', "line", NULL,
"FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]",
"Show source code lines.", opt_show_lines),
+ OPT_CALLBACK('V', "vars", NULL,
+ "FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT",
+ "Show accessible variables on PROBEDEF", opt_show_vars),
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
"file", "vmlinux pathname"),
OPT_STRING('s', "source", &symbol_conf.source_prefix,
@@ -217,7 +244,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
usage_with_options(probe_usage, options);
if (params.list_events) {
- if (params.nevents != 0 || params.dellist) {
+ if (params.mod_events) {
pr_err(" Error: Don't use --list with --add/--del.\n");
usage_with_options(probe_usage, options);
}
@@ -225,6 +252,10 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
pr_err(" Error: Don't use --list with --line.\n");
usage_with_options(probe_usage, options);
}
+ if (params.show_vars) {
+ pr_err(" Error: Don't use --list with --vars.\n");
+ usage_with_options(probe_usage, options);
+ }
ret = show_perf_probe_events();
if (ret < 0)
pr_err(" Error: Failed to show event list. (%d)\n",
@@ -234,9 +265,13 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
#ifdef DWARF_SUPPORT
if (params.show_lines) {
- if (params.nevents != 0 || params.dellist) {
- pr_warning(" Error: Don't use --line with"
- " --add/--del.\n");
+ if (params.mod_events) {
+ pr_err(" Error: Don't use --line with"
+ " --add/--del.\n");
+ usage_with_options(probe_usage, options);
+ }
+ if (params.show_vars) {
+ pr_err(" Error: Don't use --line with --vars.\n");
usage_with_options(probe_usage, options);
}
@@ -245,6 +280,18 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
pr_err(" Error: Failed to show lines. (%d)\n", ret);
return ret;
}
+ if (params.show_vars) {
+ if (params.mod_events) {
+ pr_err(" Error: Don't use --vars with"
+ " --add/--del.\n");
+ usage_with_options(probe_usage, options);
+ }
+ ret = show_available_vars(params.events, params.nevents,
+ params.max_probe_points);
+ if (ret < 0)
+ pr_err(" Error: Failed to show vars. (%d)\n", ret);
+ return ret;
+ }
#endif
if (params.dellist) {
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index fcc16e4..3de265e 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -378,6 +378,72 @@ end:
return ret;
}
+static int show_available_vars_at(int fd, struct perf_probe_event *pev,
+ int max_vls)
+{
+ char *buf;
+ int ret, i;
+ struct str_node *node;
+ struct variable_list *vls = NULL, *vl;
+
+ buf = synthesize_perf_probe_point(&pev->point);
+ if (!buf)
+ return -EINVAL;
+ pr_debug("Searching variables at %s\n", buf);
+
+ ret = find_available_vars_at(fd, pev, &vls, max_vls);
+ if (ret > 0) {
+ /* Some variables are found */
+ fprintf(stdout, "Available variables at %s\n", buf);
+ for (i = 0; i < ret; i++) {
+ vl = &vls[i];
+ /*
+ * A probe point might be converted to
+ * several trace points.
+ */
+ fprintf(stdout, "\t@<%s+%lu>\n", vl->point.symbol,
+ vl->point.offset);
+ free(vl->point.symbol);
+ if (vl->vars) {
+ strlist__for_each(node, vl->vars)
+ fprintf(stdout, "\t\t%s\n", node->s);
+ strlist__delete(vl->vars);
+ } else
+ fprintf(stdout, "(No variables)\n");
+ }
+ free(vls);
+ } else
+ pr_err("Failed to find variables at %s (%d)\n", buf, ret);
+
+ free(buf);
+ return ret;
+}
+
+/* Show available variables on given probe point */
+int show_available_vars(struct perf_probe_event *pevs, int npevs,
+ int max_vls)
+{
+ int i, fd, ret = 0;
+
+ ret = init_vmlinux();
+ if (ret < 0)
+ return ret;
+
+ fd = open_vmlinux();
+ if (fd < 0) {
+ pr_warning("Failed to open debuginfo file.\n");
+ return fd;
+ }
+
+ setup_pager();
+
+ for (i = 0; i < npevs && ret >= 0; i++)
+ ret = show_available_vars_at(fd, &pevs[i], max_vls);
+
+ close(fd);
+ return ret;
+}
+
#else /* !DWARF_SUPPORT */
static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
@@ -409,6 +475,12 @@ int show_line_range(struct line_range *lr __unused)
return -ENOSYS;
}
+int show_available_vars(struct perf_probe_event *pevs __unused,
+ int npevs __unused, int max_probe_points __unused)
+{
+ pr_warning("Debuginfo-analysis is not supported.\n");
+ return -ENOSYS;
+}
#endif
int parse_line_range_desc(const char *arg, struct line_range *lr)
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 5af3924..727a7fe 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -90,6 +90,12 @@ struct line_range {
struct list_head line_list; /* Visible lines */
};
+/* List of variables */
+struct variable_list {
+ struct probe_trace_point point; /* Actual probepoint */
+ struct strlist *vars; /* Available variables */
+};
+
/* Command string to events */
extern int parse_perf_probe_command(const char *cmd,
struct perf_probe_event *pev);
@@ -115,6 +121,8 @@ extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
extern int del_perf_probe_events(struct strlist *dellist);
extern int show_perf_probe_events(void);
extern int show_line_range(struct line_range *lr);
+extern int show_available_vars(struct perf_probe_event *pevs, int npevs,
+ int max_probe_points);
/* Maximum index number of event-name postfix */
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index c6fe80e..986027f 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -172,8 +172,8 @@ static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
return NULL;
}
-/* Get type die, but skip qualifiers and typedef */
-static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
+/* Get a type die, but skip qualifiers */
+static Dwarf_Die *__die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
{
int tag;
@@ -185,8 +185,17 @@ static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_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);
+ tag == DW_TAG_shared_type);
+
+ return vr_die;
+}
+
+/* Get a type die, but skip qualifiers and typedef */
+static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
+{
+ do {
+ vr_die = __die_get_real_type(vr_die, die_mem);
+ } while (vr_die && dwarf_tag(vr_die) == DW_TAG_typedef);
return vr_die;
}
@@ -380,6 +389,60 @@ static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
die_mem);
}
+/* Get the name of given variable DIE */
+static int die_get_typename(Dwarf_Die *vr_die, char *buf, int len)
+{
+ Dwarf_Die type;
+ int tag, ret, ret2;
+ const char *tmp = "";
+
+ if (__die_get_real_type(vr_die, &type) == NULL)
+ return -ENOENT;
+
+ tag = dwarf_tag(&type);
+ if (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)
+ tmp = "*";
+ else if (tag == DW_TAG_subroutine_type) {
+ /* Function pointer */
+ ret = snprintf(buf, len, "(function_type)");
+ return (ret >= len) ? -E2BIG : ret;
+ } else {
+ if (!dwarf_diename(&type))
+ return -ENOENT;
+ if (tag == DW_TAG_union_type)
+ tmp = "union ";
+ else if (tag == DW_TAG_structure_type)
+ tmp = "struct ";
+ /* Write a base name */
+ ret = snprintf(buf, len, "%s%s", tmp, dwarf_diename(&type));
+ return (ret >= len) ? -E2BIG : ret;
+ }
+ ret = die_get_typename(&type, buf, len);
+ if (ret > 0) {
+ ret2 = snprintf(buf + ret, len - ret, "%s", tmp);
+ ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
+ }
+ return ret;
+}
+
+/* Get the name and type of given variable DIE, stored as "type\tname" */
+static int die_get_varname(Dwarf_Die *vr_die, char *buf, int len)
+{
+ int ret, ret2;
+
+ ret = die_get_typename(vr_die, buf, len);
+ if (ret < 0) {
+ pr_debug("Failed to get type, make it unknown.\n");
+ ret = snprintf(buf, len, "(unknown_type)");
+ }
+ if (ret > 0) {
+ ret2 = snprintf(buf + ret, len - ret, "\t%s",
+ dwarf_diename(vr_die));
+ ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
+ }
+ return ret;
+}
+
/*
* Probe finder related functions
*/
@@ -393,8 +456,13 @@ static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs)
return ref;
}
-/* Show a location */
-static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
+/*
+ * Convert a location into trace_arg.
+ * If tvar == NULL, this just checks variable can be converted.
+ */
+static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr,
+ Dwarf_Op *fb_ops,
+ struct probe_trace_arg *tvar)
{
Dwarf_Attribute attr;
Dwarf_Op *op;
@@ -403,7 +471,6 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
Dwarf_Word offs = 0;
bool ref = false;
const char *regs;
- struct probe_trace_arg *tvar = pf->tvar;
int ret;
if (dwarf_attr(vr_die, DW_AT_external, &attr) != NULL)
@@ -411,16 +478,16 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
/* TODO: handle more than 1 exprs */
if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL ||
- dwarf_getlocation_addr(&attr, pf->addr, &op, &nops, 1) <= 0 ||
+ dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0 ||
nops == 0) {
/* TODO: Support const_value */
- pr_err("Failed to find the location of %s at this address.\n"
- " Perhaps, it has been optimized out.\n", pf->pvar->var);
return -ENOENT;
}
if (op->atom == DW_OP_addr) {
static_var:
+ if (!tvar)
+ return 0;
/* Static variables on memory (not stack), make @varname */
ret = strlen(dwarf_diename(vr_die));
tvar->value = zalloc(ret + 2);
@@ -435,14 +502,11 @@ static_var:
/* If this is based on frame buffer, set the offset */
if (op->atom == DW_OP_fbreg) {
- if (pf->fb_ops == NULL) {
- pr_warning("The attribute of frame base is not "
- "supported.\n");
+ if (fb_ops == NULL)
return -ENOTSUP;
- }
ref = true;
offs = op->number;
- op = &pf->fb_ops[0];
+ op = &fb_ops[0];
}
if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) {
@@ -458,13 +522,18 @@ static_var:
} else if (op->atom == DW_OP_regx) {
regn = op->number;
} else {
- pr_warning("DW_OP %x is not supported.\n", op->atom);
+ pr_debug("DW_OP %x is not supported.\n", op->atom);
return -ENOTSUP;
}
+ if (!tvar)
+ return 0;
+
regs = get_arch_regstr(regn);
if (!regs) {
- pr_warning("Mapping for DWARF register number %u missing on this architecture.", regn);
+ /* This should be a bug in DWARF or this tool */
+ pr_warning("Mapping for DWARF register number %u "
+ "missing on this architecture.", regn);
return -ERANGE;
}
@@ -689,8 +758,14 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
pr_debug("Converting variable %s into trace event.\n",
dwarf_diename(vr_die));
- ret = convert_variable_location(vr_die, pf);
- if (ret == 0 && pf->pvar->field) {
+ ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops,
+ pf->tvar);
+ if (ret == -ENOENT)
+ pr_err("Failed to find the location of %s at this address.\n"
+ " Perhaps, it has been optimized out.\n", pf->pvar->var);
+ else if (ret == -ENOTSUP)
+ pr_err("Sorry, we don't support this variable location yet.\n");
+ else if (pf->pvar->field) {
ret = convert_variable_fields(vr_die, pf->pvar->var,
pf->pvar->field, &pf->tvar->ref,
&die_mem);
@@ -772,34 +847,12 @@ found:
return ret;
}
-/* Show a probe point to output buffer */
-static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
+/* Convert subprogram DIE to trace point */
+static int convert_to_trace_point(Dwarf_Die *sp_die, Dwarf_Addr paddr,
+ bool retprobe, struct probe_trace_point *tp)
{
- struct probe_trace_event *tev;
Dwarf_Addr eaddr;
- Dwarf_Die die_mem;
const char *name;
- int ret, i;
- Dwarf_Attribute fb_attr;
- size_t nops;
-
- if (pf->ntevs == pf->max_tevs) {
- pr_warning("Too many( > %d) probe point found.\n",
- pf->max_tevs);
- return -ERANGE;
- }
- 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,
- pf->addr, &die_mem);
- if (!sp_die) {
- pr_warning("Failed to find probe point in any "
- "functions.\n");
- return -ENOENT;
- }
- }
/* Copy the name of probe point */
name = dwarf_diename(sp_die);
@@ -809,26 +862,45 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
dwarf_diename(sp_die));
return -ENOENT;
}
- tev->point.symbol = strdup(name);
- if (tev->point.symbol == NULL)
+ tp->symbol = strdup(name);
+ if (tp->symbol == NULL)
return -ENOMEM;
- tev->point.offset = (unsigned long)(pf->addr - eaddr);
+ tp->offset = (unsigned long)(paddr - eaddr);
} else
/* This function has no name. */
- tev->point.offset = (unsigned long)pf->addr;
+ tp->offset = (unsigned long)paddr;
/* Return probe must be on the head of a subprogram */
- if (pf->pev->point.retprobe) {
- if (tev->point.offset != 0) {
+ if (retprobe) {
+ if (eaddr != paddr) {
pr_warning("Return probe must be on the head of"
" a real function\n");
return -EINVAL;
}
- tev->point.retprobe = true;
+ tp->retprobe = true;
}
- pr_debug("Probe point found: %s+%lu\n", tev->point.symbol,
- tev->point.offset);
+ return 0;
+}
+
+/* Call probe_finder callback with real subprogram DIE */
+static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf)
+{
+ Dwarf_Die die_mem;
+ Dwarf_Attribute fb_attr;
+ size_t nops;
+ int ret;
+
+ /* 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,
+ pf->addr, &die_mem);
+ if (!sp_die) {
+ pr_warning("Failed to find probe point in any "
+ "functions.\n");
+ return -ENOENT;
+ }
+ }
/* Get the frame base attribute/ops */
dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr);
@@ -848,22 +920,13 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
#endif
}
- /* Find each argument */
- tev->nargs = pf->pev->nargs;
- tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
- if (tev->args == NULL)
- return -ENOMEM;
- for (i = 0; i < pf->pev->nargs; i++) {
- pf->pvar = &pf->pev->args[i];
- pf->tvar = &tev->args[i];
- ret = find_variable(sp_die, pf);
- if (ret != 0)
- return ret;
- }
+ /* Call finder's callback handler */
+ ret = pf->callback(sp_die, pf);
/* *pf->fb_ops will be cached in libdw. Don't free it. */
pf->fb_ops = NULL;
- return 0;
+
+ return ret;
}
/* Find probe point from its line number */
@@ -899,7 +962,7 @@ static int find_probe_point_by_line(struct probe_finder *pf)
(int)i, lineno, (uintmax_t)addr);
pf->addr = addr;
- ret = convert_probe_point(NULL, pf);
+ ret = call_probe_finder(NULL, pf);
/* Continuing, because target line might be inlined. */
}
return ret;
@@ -1012,7 +1075,7 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
(int)i, lineno, (unsigned long long)addr);
pf->addr = addr;
- ret = convert_probe_point(sp_die, pf);
+ ret = call_probe_finder(sp_die, pf);
/* Continuing, because target line might be inlined. */
}
/* TODO: deallocate lines, but how? */
@@ -1047,7 +1110,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);
- param->retval = convert_probe_point(in_die, pf);
+ param->retval = call_probe_finder(in_die, pf);
if (param->retval < 0)
return DWARF_CB_ABORT;
}
@@ -1085,7 +1148,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
}
pf->addr += pp->offset;
/* TODO: Check the address in this function */
- param->retval = convert_probe_point(sp_die, pf);
+ param->retval = call_probe_finder(sp_die, pf);
}
} else {
struct dwarf_callback_param _param = {.data = (void *)pf,
@@ -1107,70 +1170,229 @@ static int find_probe_point_by_func(struct probe_finder *pf)
return _param.retval;
}
-/* Find probe_trace_events specified by perf_probe_event from debuginfo */
-int find_probe_trace_events(int fd, struct perf_probe_event *pev,
- struct probe_trace_event **tevs, int max_tevs)
+/* Find probe points from debuginfo */
+static int find_probes(int fd, struct probe_finder *pf)
{
- struct probe_finder pf = {.pev = pev, .max_tevs = max_tevs};
- struct perf_probe_point *pp = &pev->point;
+ struct perf_probe_point *pp = &pf->pev->point;
Dwarf_Off off, noff;
size_t cuhl;
Dwarf_Die *diep;
Dwarf *dbg;
int ret = 0;
- pf.tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs);
- if (pf.tevs == NULL)
- return -ENOMEM;
- *tevs = pf.tevs;
- pf.ntevs = 0;
-
dbg = dwarf_begin(fd, DWARF_C_READ);
if (!dbg) {
pr_warning("No dwarf info found in the vmlinux - "
"please rebuild with CONFIG_DEBUG_INFO=y.\n");
- free(pf.tevs);
- *tevs = NULL;
return -EBADF;
}
#if _ELFUTILS_PREREQ(0, 142)
/* Get the call frame information from this dwarf */
- pf.cfi = dwarf_getcfi(dbg);
+ pf->cfi = dwarf_getcfi(dbg);
#endif
off = 0;
- line_list__init(&pf.lcache);
+ line_list__init(&pf->lcache);
/* Loop on CUs (Compilation Unit) */
while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) &&
ret >= 0) {
/* Get the DIE(Debugging Information Entry) of this CU */
- diep = dwarf_offdie(dbg, off + cuhl, &pf.cu_die);
+ diep = dwarf_offdie(dbg, off + cuhl, &pf->cu_die);
if (!diep)
continue;
/* Check if target file is included. */
if (pp->file)
- pf.fname = cu_find_realpath(&pf.cu_die, pp->file);
+ pf->fname = cu_find_realpath(&pf->cu_die, pp->file);
else
- pf.fname = NULL;
+ pf->fname = NULL;
- if (!pp->file || pf.fname) {
+ if (!pp->file || pf->fname) {
if (pp->function)
- ret = find_probe_point_by_func(&pf);
+ ret = find_probe_point_by_func(pf);
else if (pp->lazy_line)
- ret = find_probe_point_lazy(NULL, &pf);
+ ret = find_probe_point_lazy(NULL, pf);
else {
- pf.lno = pp->line;
- ret = find_probe_point_by_line(&pf);
+ pf->lno = pp->line;
+ ret = find_probe_point_by_line(pf);
}
}
off = noff;
}
- line_list__free(&pf.lcache);
+ line_list__free(&pf->lcache);
dwarf_end(dbg);
- return (ret < 0) ? ret : pf.ntevs;
+ return ret;
+}
+
+/* Add a found probe point into trace event list */
+static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf)
+{
+ struct trace_event_finder *tf =
+ container_of(pf, struct trace_event_finder, pf);
+ struct probe_trace_event *tev;
+ int ret, i;
+
+ /* Check number of tevs */
+ if (tf->ntevs == tf->max_tevs) {
+ pr_warning("Too many( > %d) probe point found.\n",
+ tf->max_tevs);
+ return -ERANGE;
+ }
+ tev = &tf->tevs[tf->ntevs++];
+
+ ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe,
+ &tev->point);
+ if (ret < 0)
+ return ret;
+
+ pr_debug("Probe point found: %s+%lu\n", tev->point.symbol,
+ tev->point.offset);
+
+ /* Find each argument */
+ tev->nargs = pf->pev->nargs;
+ tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
+ if (tev->args == NULL)
+ return -ENOMEM;
+ for (i = 0; i < pf->pev->nargs; i++) {
+ pf->pvar = &pf->pev->args[i];
+ pf->tvar = &tev->args[i];
+ ret = find_variable(sp_die, pf);
+ if (ret != 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Find probe_trace_events specified by perf_probe_event from debuginfo */
+int find_probe_trace_events(int fd, struct perf_probe_event *pev,
+ struct probe_trace_event **tevs, int max_tevs)
+{
+ struct trace_event_finder tf = {
+ .pf = {.pev = pev, .callback = add_probe_trace_event},
+ .max_tevs = max_tevs};
+ int ret;
+
+ /* Allocate result tevs array */
+ *tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs);
+ if (*tevs == NULL)
+ return -ENOMEM;
+
+ tf.tevs = *tevs;
+ tf.ntevs = 0;
+
+ ret = find_probes(fd, &tf.pf);
+ if (ret < 0) {
+ free(*tevs);
+ *tevs = NULL;
+ return ret;
+ }
+
+ return (ret < 0) ? ret : tf.ntevs;
+}
+
+#define MAX_VAR_LEN 64
+
+/* Collect available variables in this scope */
+static int collect_variables_cb(Dwarf_Die *die_mem, void *data)
+{
+ struct available_var_finder *af = data;
+ struct variable_list *vl;
+ char buf[MAX_VAR_LEN];
+ int tag, ret;
+
+ vl = &af->vls[af->nvls - 1];
+
+ tag = dwarf_tag(die_mem);
+ if (tag == DW_TAG_formal_parameter ||
+ tag == DW_TAG_variable) {
+ ret = convert_variable_location(die_mem, af->pf.addr,
+ af->pf.fb_ops, NULL);
+ if (ret == 0) {
+ ret = die_get_varname(die_mem, buf, MAX_VAR_LEN);
+ if (ret > 0)
+ strlist__add(vl->vars, buf);
+ }
+ }
+
+ if (dwarf_haspc(die_mem, af->pf.addr))
+ return DIE_FIND_CB_CONTINUE;
+ else
+ return DIE_FIND_CB_SIBLING;
+}
+
+/* Add a found vars into available variables list */
+static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf)
+{
+ struct available_var_finder *af =
+ container_of(pf, struct available_var_finder, pf);
+ struct variable_list *vl;
+ Dwarf_Die die_mem;
+ int ret;
+
+ /* Check number of tevs */
+ if (af->nvls == af->max_vls) {
+ pr_warning("Too many( > %d) probe point found.\n", af->max_vls);
+ return -ERANGE;
+ }
+ vl = &af->vls[af->nvls++];
+
+ ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe,
+ &vl->point);
+ if (ret < 0)
+ return ret;
+
+ pr_debug("Probe point found: %s+%lu\n", vl->point.symbol,
+ vl->point.offset);
+
+ /* Find local variables */
+ vl->vars = strlist__new(true, NULL);
+ if (vl->vars == NULL)
+ return -ENOMEM;
+ die_find_child(sp_die, collect_variables_cb, (void *)af, &die_mem);
+
+ if (strlist__empty(vl->vars)) {
+ strlist__delete(vl->vars);
+ vl->vars = NULL;
+ }
+
+ return ret;
+}
+
+/* Find available variables at given probe point */
+int find_available_vars_at(int fd, struct perf_probe_event *pev,
+ struct variable_list **vls, int max_vls)
+{
+ struct available_var_finder af = {
+ .pf = {.pev = pev, .callback = add_available_vars},
+ .max_vls = max_vls};
+ int ret;
+
+ /* Allocate result vls array */
+ *vls = zalloc(sizeof(struct variable_list) * max_vls);
+ if (*vls == NULL)
+ return -ENOMEM;
+
+ af.vls = *vls;
+ af.nvls = 0;
+
+ ret = find_probes(fd, &af.pf);
+ if (ret < 0) {
+ /* Free vlist for error */
+ while (af.nvls--) {
+ if (af.vls[af.nvls].point.symbol)
+ free(af.vls[af.nvls].point.symbol);
+ if (af.vls[af.nvls].vars)
+ strlist__delete(af.vls[af.nvls].vars);
+ }
+ free(af.vls);
+ *vls = NULL;
+ return ret;
+ }
+
+ return (ret < 0) ? ret : af.nvls;
}
/* Reverse search */
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
index 4507d51..baffd25 100644
--- a/tools/perf/util/probe-finder.h
+++ b/tools/perf/util/probe-finder.h
@@ -25,17 +25,22 @@ extern int find_probe_trace_events(int fd, struct perf_probe_event *pev,
extern int find_perf_probe_point(int fd, unsigned long addr,
struct perf_probe_point *ppt);
+/* Find a line range */
extern int find_line_range(int fd, struct line_range *lr);
+/* Find available variables */
+extern int find_available_vars_at(int fd, struct perf_probe_event *pev,
+ struct variable_list **vls, int max_points);
+
#include <dwarf.h>
#include <libdw.h>
#include <version.h>
struct probe_finder {
struct perf_probe_event *pev; /* Target probe event */
- struct probe_trace_event *tevs; /* Result trace events */
- int ntevs; /* Number of trace events */
- int max_tevs; /* Max number of trace events */
+
+ /* Callback when a probe point is found */
+ int (*callback)(Dwarf_Die *sp_die, struct probe_finder *pf);
/* For function searching */
int lno; /* Line number */
@@ -53,6 +58,20 @@ struct probe_finder {
struct probe_trace_arg *tvar; /* Current result variable */
};
+struct trace_event_finder {
+ struct probe_finder pf;
+ struct probe_trace_event *tevs; /* Found trace events */
+ int ntevs; /* Number of trace events */
+ int max_tevs; /* Max number of trace events */
+};
+
+struct available_var_finder {
+ struct probe_finder pf;
+ struct variable_list *vls; /* Found variable lists */
+ int nvls; /* Number of variable lists */
+ int max_vls; /* Max no. of variable lists */
+};
+
struct line_finder {
struct line_range *lr; /* Target line range */
^ permalink raw reply related [flat|nested] 18+ messages in thread* [tip:perf/urgent] perf probe: Show accessible local variables
2010-10-21 10:13 ` [PATCH -tip 4/7] perf probe: Show accessible local variables Masami Hiramatsu
@ 2010-10-23 19:41 ` tip-bot for Masami Hiramatsu
0 siblings, 0 replies; 18+ messages in thread
From: tip-bot for Masami Hiramatsu @ 2010-10-23 19:41 UTC (permalink / raw)
To: linux-tip-commits
Cc: acme, linux-kernel, paulus, hpa, mingo, a.p.zijlstra,
masami.hiramatsu.pt, fweisbec, tglx, mingo
Commit-ID: cf6eb489e5c04c8f8d5fd7bf90b8346c987688bc
Gitweb: http://git.kernel.org/tip/cf6eb489e5c04c8f8d5fd7bf90b8346c987688bc
Author: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
AuthorDate: Thu, 21 Oct 2010 19:13:23 +0900
Committer: Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Thu, 21 Oct 2010 15:59:06 -0200
perf probe: Show accessible local variables
Add -V (--vars) option for listing accessible local variables at given probe
point. This will help finding which local variables are available for event
arguments.
e.g.)
# perf probe -V call_timer_fn:23
Available variables at call_timer_fn:23
@<run_timer_softirq+345>
function_type* fn
int preempt_count
long unsigned int data
struct list_head work_list
struct list_head* head
struct timer_list* timer
struct tvec_base* base
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
LKML-Reference: <20101021101323.3542.40282.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
tools/perf/Documentation/perf-probe.txt | 7 +
tools/perf/builtin-probe.c | 61 ++++-
tools/perf/util/probe-event.c | 72 ++++++
tools/perf/util/probe-event.h | 8 +
tools/perf/util/probe-finder.c | 412 ++++++++++++++++++++++++-------
tools/perf/util/probe-finder.h | 25 ++-
6 files changed, 480 insertions(+), 105 deletions(-)
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index 27d52da..72f5d9e 100644
--- a/tools/perf/Documentation/perf-probe.txt
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -17,6 +17,8 @@ or
'perf probe' --list
or
'perf probe' --line='FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]'
+or
+'perf probe' --vars='PROBEPOINT'
DESCRIPTION
-----------
@@ -57,6 +59,11 @@ OPTIONS
Show source code lines which can be probed. This needs an argument
which specifies a range of the source code. (see LINE SYNTAX for detail)
+-V::
+--vars=::
+ Show available local variables at given probe point. The argument
+ syntax is same as PROBE SYNTAX, but NO ARGs.
+
-f::
--force::
Forcibly add events with existing name.
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index 199d5e1..91bb6cf 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -50,6 +50,8 @@ static struct {
bool list_events;
bool force_add;
bool show_lines;
+ bool show_vars;
+ bool mod_events;
int nevents;
struct perf_probe_event events[MAX_PROBES];
struct strlist *dellist;
@@ -57,7 +59,6 @@ static struct {
int max_probe_points;
} params;
-
/* Parse an event definition. Note that any error must die. */
static int parse_probe_event(const char *str)
{
@@ -92,6 +93,7 @@ static int parse_probe_event_argv(int argc, const char **argv)
len = 0;
for (i = 0; i < argc; i++)
len += sprintf(&buf[len], "%s ", argv[i]);
+ params.mod_events = true;
ret = parse_probe_event(buf);
free(buf);
return ret;
@@ -100,9 +102,10 @@ static int parse_probe_event_argv(int argc, const char **argv)
static int opt_add_probe_event(const struct option *opt __used,
const char *str, int unset __used)
{
- if (str)
+ if (str) {
+ params.mod_events = true;
return parse_probe_event(str);
- else
+ } else
return 0;
}
@@ -110,6 +113,7 @@ static int opt_del_probe_event(const struct option *opt __used,
const char *str, int unset __used)
{
if (str) {
+ params.mod_events = true;
if (!params.dellist)
params.dellist = strlist__new(true, NULL);
strlist__add(params.dellist, str);
@@ -130,6 +134,25 @@ static int opt_show_lines(const struct option *opt __used,
return ret;
}
+
+static int opt_show_vars(const struct option *opt __used,
+ const char *str, int unset __used)
+{
+ struct perf_probe_event *pev = ¶ms.events[params.nevents];
+ int ret;
+
+ if (!str)
+ return 0;
+
+ ret = parse_probe_event(str);
+ if (!ret && pev->nargs != 0) {
+ pr_err(" Error: '--vars' doesn't accept arguments.\n");
+ return -EINVAL;
+ }
+ params.show_vars = true;
+
+ return ret;
+}
#endif
static const char * const probe_usage[] = {
@@ -139,6 +162,7 @@ static const char * const probe_usage[] = {
"perf probe --list",
#ifdef DWARF_SUPPORT
"perf probe --line 'LINEDESC'",
+ "perf probe --vars 'PROBEPOINT'",
#endif
NULL
};
@@ -180,6 +204,9 @@ static const struct option options[] = {
OPT_CALLBACK('L', "line", NULL,
"FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]",
"Show source code lines.", opt_show_lines),
+ OPT_CALLBACK('V', "vars", NULL,
+ "FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT",
+ "Show accessible variables on PROBEDEF", opt_show_vars),
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
"file", "vmlinux pathname"),
OPT_STRING('s', "source", &symbol_conf.source_prefix,
@@ -217,7 +244,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
usage_with_options(probe_usage, options);
if (params.list_events) {
- if (params.nevents != 0 || params.dellist) {
+ if (params.mod_events) {
pr_err(" Error: Don't use --list with --add/--del.\n");
usage_with_options(probe_usage, options);
}
@@ -225,6 +252,10 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
pr_err(" Error: Don't use --list with --line.\n");
usage_with_options(probe_usage, options);
}
+ if (params.show_vars) {
+ pr_err(" Error: Don't use --list with --vars.\n");
+ usage_with_options(probe_usage, options);
+ }
ret = show_perf_probe_events();
if (ret < 0)
pr_err(" Error: Failed to show event list. (%d)\n",
@@ -234,9 +265,13 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
#ifdef DWARF_SUPPORT
if (params.show_lines) {
- if (params.nevents != 0 || params.dellist) {
- pr_warning(" Error: Don't use --line with"
- " --add/--del.\n");
+ if (params.mod_events) {
+ pr_err(" Error: Don't use --line with"
+ " --add/--del.\n");
+ usage_with_options(probe_usage, options);
+ }
+ if (params.show_vars) {
+ pr_err(" Error: Don't use --line with --vars.\n");
usage_with_options(probe_usage, options);
}
@@ -245,6 +280,18 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
pr_err(" Error: Failed to show lines. (%d)\n", ret);
return ret;
}
+ if (params.show_vars) {
+ if (params.mod_events) {
+ pr_err(" Error: Don't use --vars with"
+ " --add/--del.\n");
+ usage_with_options(probe_usage, options);
+ }
+ ret = show_available_vars(params.events, params.nevents,
+ params.max_probe_points);
+ if (ret < 0)
+ pr_err(" Error: Failed to show vars. (%d)\n", ret);
+ return ret;
+ }
#endif
if (params.dellist) {
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index fcc16e4..288ebe8 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -378,6 +378,72 @@ end:
return ret;
}
+static int show_available_vars_at(int fd, struct perf_probe_event *pev,
+ int max_vls)
+{
+ char *buf;
+ int ret, i;
+ struct str_node *node;
+ struct variable_list *vls = NULL, *vl;
+
+ buf = synthesize_perf_probe_point(&pev->point);
+ if (!buf)
+ return -EINVAL;
+ pr_debug("Searching variables at %s\n", buf);
+
+ ret = find_available_vars_at(fd, pev, &vls, max_vls);
+ if (ret > 0) {
+ /* Some variables were found */
+ fprintf(stdout, "Available variables at %s\n", buf);
+ for (i = 0; i < ret; i++) {
+ vl = &vls[i];
+ /*
+ * A probe point might be converted to
+ * several trace points.
+ */
+ fprintf(stdout, "\t@<%s+%lu>\n", vl->point.symbol,
+ vl->point.offset);
+ free(vl->point.symbol);
+ if (vl->vars) {
+ strlist__for_each(node, vl->vars)
+ fprintf(stdout, "\t\t%s\n", node->s);
+ strlist__delete(vl->vars);
+ } else
+ fprintf(stdout, "(No variables)\n");
+ }
+ free(vls);
+ } else
+ pr_err("Failed to find variables at %s (%d)\n", buf, ret);
+
+ free(buf);
+ return ret;
+}
+
+/* Show available variables on given probe point */
+int show_available_vars(struct perf_probe_event *pevs, int npevs,
+ int max_vls)
+{
+ int i, fd, ret = 0;
+
+ ret = init_vmlinux();
+ if (ret < 0)
+ return ret;
+
+ fd = open_vmlinux();
+ if (fd < 0) {
+ pr_warning("Failed to open debuginfo file.\n");
+ return fd;
+ }
+
+ setup_pager();
+
+ for (i = 0; i < npevs && ret >= 0; i++)
+ ret = show_available_vars_at(fd, &pevs[i], max_vls);
+
+ close(fd);
+ return ret;
+}
+
#else /* !DWARF_SUPPORT */
static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
@@ -409,6 +475,12 @@ int show_line_range(struct line_range *lr __unused)
return -ENOSYS;
}
+int show_available_vars(struct perf_probe_event *pevs __unused,
+ int npevs __unused, int max_probe_points __unused)
+{
+ pr_warning("Debuginfo-analysis is not supported.\n");
+ return -ENOSYS;
+}
#endif
int parse_line_range_desc(const char *arg, struct line_range *lr)
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 5af3924..727a7fe 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -90,6 +90,12 @@ struct line_range {
struct list_head line_list; /* Visible lines */
};
+/* List of variables */
+struct variable_list {
+ struct probe_trace_point point; /* Actual probepoint */
+ struct strlist *vars; /* Available variables */
+};
+
/* Command string to events */
extern int parse_perf_probe_command(const char *cmd,
struct perf_probe_event *pev);
@@ -115,6 +121,8 @@ extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
extern int del_perf_probe_events(struct strlist *dellist);
extern int show_perf_probe_events(void);
extern int show_line_range(struct line_range *lr);
+extern int show_available_vars(struct perf_probe_event *pevs, int npevs,
+ int max_probe_points);
/* Maximum index number of event-name postfix */
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index c6fe80e..986027f 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -172,8 +172,8 @@ static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
return NULL;
}
-/* Get type die, but skip qualifiers and typedef */
-static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
+/* Get a type die, but skip qualifiers */
+static Dwarf_Die *__die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
{
int tag;
@@ -185,8 +185,17 @@ static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_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);
+ tag == DW_TAG_shared_type);
+
+ return vr_die;
+}
+
+/* Get a type die, but skip qualifiers and typedef */
+static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
+{
+ do {
+ vr_die = __die_get_real_type(vr_die, die_mem);
+ } while (vr_die && dwarf_tag(vr_die) == DW_TAG_typedef);
return vr_die;
}
@@ -380,6 +389,60 @@ static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
die_mem);
}
+/* Get the name of given variable DIE */
+static int die_get_typename(Dwarf_Die *vr_die, char *buf, int len)
+{
+ Dwarf_Die type;
+ int tag, ret, ret2;
+ const char *tmp = "";
+
+ if (__die_get_real_type(vr_die, &type) == NULL)
+ return -ENOENT;
+
+ tag = dwarf_tag(&type);
+ if (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)
+ tmp = "*";
+ else if (tag == DW_TAG_subroutine_type) {
+ /* Function pointer */
+ ret = snprintf(buf, len, "(function_type)");
+ return (ret >= len) ? -E2BIG : ret;
+ } else {
+ if (!dwarf_diename(&type))
+ return -ENOENT;
+ if (tag == DW_TAG_union_type)
+ tmp = "union ";
+ else if (tag == DW_TAG_structure_type)
+ tmp = "struct ";
+ /* Write a base name */
+ ret = snprintf(buf, len, "%s%s", tmp, dwarf_diename(&type));
+ return (ret >= len) ? -E2BIG : ret;
+ }
+ ret = die_get_typename(&type, buf, len);
+ if (ret > 0) {
+ ret2 = snprintf(buf + ret, len - ret, "%s", tmp);
+ ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
+ }
+ return ret;
+}
+
+/* Get the name and type of given variable DIE, stored as "type\tname" */
+static int die_get_varname(Dwarf_Die *vr_die, char *buf, int len)
+{
+ int ret, ret2;
+
+ ret = die_get_typename(vr_die, buf, len);
+ if (ret < 0) {
+ pr_debug("Failed to get type, make it unknown.\n");
+ ret = snprintf(buf, len, "(unknown_type)");
+ }
+ if (ret > 0) {
+ ret2 = snprintf(buf + ret, len - ret, "\t%s",
+ dwarf_diename(vr_die));
+ ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
+ }
+ return ret;
+}
+
/*
* Probe finder related functions
*/
@@ -393,8 +456,13 @@ static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs)
return ref;
}
-/* Show a location */
-static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
+/*
+ * Convert a location into trace_arg.
+ * If tvar == NULL, this just checks variable can be converted.
+ */
+static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr,
+ Dwarf_Op *fb_ops,
+ struct probe_trace_arg *tvar)
{
Dwarf_Attribute attr;
Dwarf_Op *op;
@@ -403,7 +471,6 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
Dwarf_Word offs = 0;
bool ref = false;
const char *regs;
- struct probe_trace_arg *tvar = pf->tvar;
int ret;
if (dwarf_attr(vr_die, DW_AT_external, &attr) != NULL)
@@ -411,16 +478,16 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
/* TODO: handle more than 1 exprs */
if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL ||
- dwarf_getlocation_addr(&attr, pf->addr, &op, &nops, 1) <= 0 ||
+ dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0 ||
nops == 0) {
/* TODO: Support const_value */
- pr_err("Failed to find the location of %s at this address.\n"
- " Perhaps, it has been optimized out.\n", pf->pvar->var);
return -ENOENT;
}
if (op->atom == DW_OP_addr) {
static_var:
+ if (!tvar)
+ return 0;
/* Static variables on memory (not stack), make @varname */
ret = strlen(dwarf_diename(vr_die));
tvar->value = zalloc(ret + 2);
@@ -435,14 +502,11 @@ static_var:
/* If this is based on frame buffer, set the offset */
if (op->atom == DW_OP_fbreg) {
- if (pf->fb_ops == NULL) {
- pr_warning("The attribute of frame base is not "
- "supported.\n");
+ if (fb_ops == NULL)
return -ENOTSUP;
- }
ref = true;
offs = op->number;
- op = &pf->fb_ops[0];
+ op = &fb_ops[0];
}
if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) {
@@ -458,13 +522,18 @@ static_var:
} else if (op->atom == DW_OP_regx) {
regn = op->number;
} else {
- pr_warning("DW_OP %x is not supported.\n", op->atom);
+ pr_debug("DW_OP %x is not supported.\n", op->atom);
return -ENOTSUP;
}
+ if (!tvar)
+ return 0;
+
regs = get_arch_regstr(regn);
if (!regs) {
- pr_warning("Mapping for DWARF register number %u missing on this architecture.", regn);
+ /* This should be a bug in DWARF or this tool */
+ pr_warning("Mapping for DWARF register number %u "
+ "missing on this architecture.", regn);
return -ERANGE;
}
@@ -689,8 +758,14 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
pr_debug("Converting variable %s into trace event.\n",
dwarf_diename(vr_die));
- ret = convert_variable_location(vr_die, pf);
- if (ret == 0 && pf->pvar->field) {
+ ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops,
+ pf->tvar);
+ if (ret == -ENOENT)
+ pr_err("Failed to find the location of %s at this address.\n"
+ " Perhaps, it has been optimized out.\n", pf->pvar->var);
+ else if (ret == -ENOTSUP)
+ pr_err("Sorry, we don't support this variable location yet.\n");
+ else if (pf->pvar->field) {
ret = convert_variable_fields(vr_die, pf->pvar->var,
pf->pvar->field, &pf->tvar->ref,
&die_mem);
@@ -772,34 +847,12 @@ found:
return ret;
}
-/* Show a probe point to output buffer */
-static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
+/* Convert subprogram DIE to trace point */
+static int convert_to_trace_point(Dwarf_Die *sp_die, Dwarf_Addr paddr,
+ bool retprobe, struct probe_trace_point *tp)
{
- struct probe_trace_event *tev;
Dwarf_Addr eaddr;
- Dwarf_Die die_mem;
const char *name;
- int ret, i;
- Dwarf_Attribute fb_attr;
- size_t nops;
-
- if (pf->ntevs == pf->max_tevs) {
- pr_warning("Too many( > %d) probe point found.\n",
- pf->max_tevs);
- return -ERANGE;
- }
- 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,
- pf->addr, &die_mem);
- if (!sp_die) {
- pr_warning("Failed to find probe point in any "
- "functions.\n");
- return -ENOENT;
- }
- }
/* Copy the name of probe point */
name = dwarf_diename(sp_die);
@@ -809,26 +862,45 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
dwarf_diename(sp_die));
return -ENOENT;
}
- tev->point.symbol = strdup(name);
- if (tev->point.symbol == NULL)
+ tp->symbol = strdup(name);
+ if (tp->symbol == NULL)
return -ENOMEM;
- tev->point.offset = (unsigned long)(pf->addr - eaddr);
+ tp->offset = (unsigned long)(paddr - eaddr);
} else
/* This function has no name. */
- tev->point.offset = (unsigned long)pf->addr;
+ tp->offset = (unsigned long)paddr;
/* Return probe must be on the head of a subprogram */
- if (pf->pev->point.retprobe) {
- if (tev->point.offset != 0) {
+ if (retprobe) {
+ if (eaddr != paddr) {
pr_warning("Return probe must be on the head of"
" a real function\n");
return -EINVAL;
}
- tev->point.retprobe = true;
+ tp->retprobe = true;
}
- pr_debug("Probe point found: %s+%lu\n", tev->point.symbol,
- tev->point.offset);
+ return 0;
+}
+
+/* Call probe_finder callback with real subprogram DIE */
+static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf)
+{
+ Dwarf_Die die_mem;
+ Dwarf_Attribute fb_attr;
+ size_t nops;
+ int ret;
+
+ /* 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,
+ pf->addr, &die_mem);
+ if (!sp_die) {
+ pr_warning("Failed to find probe point in any "
+ "functions.\n");
+ return -ENOENT;
+ }
+ }
/* Get the frame base attribute/ops */
dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr);
@@ -848,22 +920,13 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
#endif
}
- /* Find each argument */
- tev->nargs = pf->pev->nargs;
- tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
- if (tev->args == NULL)
- return -ENOMEM;
- for (i = 0; i < pf->pev->nargs; i++) {
- pf->pvar = &pf->pev->args[i];
- pf->tvar = &tev->args[i];
- ret = find_variable(sp_die, pf);
- if (ret != 0)
- return ret;
- }
+ /* Call finder's callback handler */
+ ret = pf->callback(sp_die, pf);
/* *pf->fb_ops will be cached in libdw. Don't free it. */
pf->fb_ops = NULL;
- return 0;
+
+ return ret;
}
/* Find probe point from its line number */
@@ -899,7 +962,7 @@ static int find_probe_point_by_line(struct probe_finder *pf)
(int)i, lineno, (uintmax_t)addr);
pf->addr = addr;
- ret = convert_probe_point(NULL, pf);
+ ret = call_probe_finder(NULL, pf);
/* Continuing, because target line might be inlined. */
}
return ret;
@@ -1012,7 +1075,7 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
(int)i, lineno, (unsigned long long)addr);
pf->addr = addr;
- ret = convert_probe_point(sp_die, pf);
+ ret = call_probe_finder(sp_die, pf);
/* Continuing, because target line might be inlined. */
}
/* TODO: deallocate lines, but how? */
@@ -1047,7 +1110,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);
- param->retval = convert_probe_point(in_die, pf);
+ param->retval = call_probe_finder(in_die, pf);
if (param->retval < 0)
return DWARF_CB_ABORT;
}
@@ -1085,7 +1148,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
}
pf->addr += pp->offset;
/* TODO: Check the address in this function */
- param->retval = convert_probe_point(sp_die, pf);
+ param->retval = call_probe_finder(sp_die, pf);
}
} else {
struct dwarf_callback_param _param = {.data = (void *)pf,
@@ -1107,70 +1170,229 @@ static int find_probe_point_by_func(struct probe_finder *pf)
return _param.retval;
}
-/* Find probe_trace_events specified by perf_probe_event from debuginfo */
-int find_probe_trace_events(int fd, struct perf_probe_event *pev,
- struct probe_trace_event **tevs, int max_tevs)
+/* Find probe points from debuginfo */
+static int find_probes(int fd, struct probe_finder *pf)
{
- struct probe_finder pf = {.pev = pev, .max_tevs = max_tevs};
- struct perf_probe_point *pp = &pev->point;
+ struct perf_probe_point *pp = &pf->pev->point;
Dwarf_Off off, noff;
size_t cuhl;
Dwarf_Die *diep;
Dwarf *dbg;
int ret = 0;
- pf.tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs);
- if (pf.tevs == NULL)
- return -ENOMEM;
- *tevs = pf.tevs;
- pf.ntevs = 0;
-
dbg = dwarf_begin(fd, DWARF_C_READ);
if (!dbg) {
pr_warning("No dwarf info found in the vmlinux - "
"please rebuild with CONFIG_DEBUG_INFO=y.\n");
- free(pf.tevs);
- *tevs = NULL;
return -EBADF;
}
#if _ELFUTILS_PREREQ(0, 142)
/* Get the call frame information from this dwarf */
- pf.cfi = dwarf_getcfi(dbg);
+ pf->cfi = dwarf_getcfi(dbg);
#endif
off = 0;
- line_list__init(&pf.lcache);
+ line_list__init(&pf->lcache);
/* Loop on CUs (Compilation Unit) */
while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) &&
ret >= 0) {
/* Get the DIE(Debugging Information Entry) of this CU */
- diep = dwarf_offdie(dbg, off + cuhl, &pf.cu_die);
+ diep = dwarf_offdie(dbg, off + cuhl, &pf->cu_die);
if (!diep)
continue;
/* Check if target file is included. */
if (pp->file)
- pf.fname = cu_find_realpath(&pf.cu_die, pp->file);
+ pf->fname = cu_find_realpath(&pf->cu_die, pp->file);
else
- pf.fname = NULL;
+ pf->fname = NULL;
- if (!pp->file || pf.fname) {
+ if (!pp->file || pf->fname) {
if (pp->function)
- ret = find_probe_point_by_func(&pf);
+ ret = find_probe_point_by_func(pf);
else if (pp->lazy_line)
- ret = find_probe_point_lazy(NULL, &pf);
+ ret = find_probe_point_lazy(NULL, pf);
else {
- pf.lno = pp->line;
- ret = find_probe_point_by_line(&pf);
+ pf->lno = pp->line;
+ ret = find_probe_point_by_line(pf);
}
}
off = noff;
}
- line_list__free(&pf.lcache);
+ line_list__free(&pf->lcache);
dwarf_end(dbg);
- return (ret < 0) ? ret : pf.ntevs;
+ return ret;
+}
+
+/* Add a found probe point into trace event list */
+static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf)
+{
+ struct trace_event_finder *tf =
+ container_of(pf, struct trace_event_finder, pf);
+ struct probe_trace_event *tev;
+ int ret, i;
+
+ /* Check number of tevs */
+ if (tf->ntevs == tf->max_tevs) {
+ pr_warning("Too many( > %d) probe point found.\n",
+ tf->max_tevs);
+ return -ERANGE;
+ }
+ tev = &tf->tevs[tf->ntevs++];
+
+ ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe,
+ &tev->point);
+ if (ret < 0)
+ return ret;
+
+ pr_debug("Probe point found: %s+%lu\n", tev->point.symbol,
+ tev->point.offset);
+
+ /* Find each argument */
+ tev->nargs = pf->pev->nargs;
+ tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
+ if (tev->args == NULL)
+ return -ENOMEM;
+ for (i = 0; i < pf->pev->nargs; i++) {
+ pf->pvar = &pf->pev->args[i];
+ pf->tvar = &tev->args[i];
+ ret = find_variable(sp_die, pf);
+ if (ret != 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Find probe_trace_events specified by perf_probe_event from debuginfo */
+int find_probe_trace_events(int fd, struct perf_probe_event *pev,
+ struct probe_trace_event **tevs, int max_tevs)
+{
+ struct trace_event_finder tf = {
+ .pf = {.pev = pev, .callback = add_probe_trace_event},
+ .max_tevs = max_tevs};
+ int ret;
+
+ /* Allocate result tevs array */
+ *tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs);
+ if (*tevs == NULL)
+ return -ENOMEM;
+
+ tf.tevs = *tevs;
+ tf.ntevs = 0;
+
+ ret = find_probes(fd, &tf.pf);
+ if (ret < 0) {
+ free(*tevs);
+ *tevs = NULL;
+ return ret;
+ }
+
+ return (ret < 0) ? ret : tf.ntevs;
+}
+
+#define MAX_VAR_LEN 64
+
+/* Collect available variables in this scope */
+static int collect_variables_cb(Dwarf_Die *die_mem, void *data)
+{
+ struct available_var_finder *af = data;
+ struct variable_list *vl;
+ char buf[MAX_VAR_LEN];
+ int tag, ret;
+
+ vl = &af->vls[af->nvls - 1];
+
+ tag = dwarf_tag(die_mem);
+ if (tag == DW_TAG_formal_parameter ||
+ tag == DW_TAG_variable) {
+ ret = convert_variable_location(die_mem, af->pf.addr,
+ af->pf.fb_ops, NULL);
+ if (ret == 0) {
+ ret = die_get_varname(die_mem, buf, MAX_VAR_LEN);
+ if (ret > 0)
+ strlist__add(vl->vars, buf);
+ }
+ }
+
+ if (dwarf_haspc(die_mem, af->pf.addr))
+ return DIE_FIND_CB_CONTINUE;
+ else
+ return DIE_FIND_CB_SIBLING;
+}
+
+/* Add a found vars into available variables list */
+static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf)
+{
+ struct available_var_finder *af =
+ container_of(pf, struct available_var_finder, pf);
+ struct variable_list *vl;
+ Dwarf_Die die_mem;
+ int ret;
+
+ /* Check number of tevs */
+ if (af->nvls == af->max_vls) {
+ pr_warning("Too many( > %d) probe point found.\n", af->max_vls);
+ return -ERANGE;
+ }
+ vl = &af->vls[af->nvls++];
+
+ ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe,
+ &vl->point);
+ if (ret < 0)
+ return ret;
+
+ pr_debug("Probe point found: %s+%lu\n", vl->point.symbol,
+ vl->point.offset);
+
+ /* Find local variables */
+ vl->vars = strlist__new(true, NULL);
+ if (vl->vars == NULL)
+ return -ENOMEM;
+ die_find_child(sp_die, collect_variables_cb, (void *)af, &die_mem);
+
+ if (strlist__empty(vl->vars)) {
+ strlist__delete(vl->vars);
+ vl->vars = NULL;
+ }
+
+ return ret;
+}
+
+/* Find available variables at given probe point */
+int find_available_vars_at(int fd, struct perf_probe_event *pev,
+ struct variable_list **vls, int max_vls)
+{
+ struct available_var_finder af = {
+ .pf = {.pev = pev, .callback = add_available_vars},
+ .max_vls = max_vls};
+ int ret;
+
+ /* Allocate result vls array */
+ *vls = zalloc(sizeof(struct variable_list) * max_vls);
+ if (*vls == NULL)
+ return -ENOMEM;
+
+ af.vls = *vls;
+ af.nvls = 0;
+
+ ret = find_probes(fd, &af.pf);
+ if (ret < 0) {
+ /* Free vlist for error */
+ while (af.nvls--) {
+ if (af.vls[af.nvls].point.symbol)
+ free(af.vls[af.nvls].point.symbol);
+ if (af.vls[af.nvls].vars)
+ strlist__delete(af.vls[af.nvls].vars);
+ }
+ free(af.vls);
+ *vls = NULL;
+ return ret;
+ }
+
+ return (ret < 0) ? ret : af.nvls;
}
/* Reverse search */
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
index 4507d51..baffd25 100644
--- a/tools/perf/util/probe-finder.h
+++ b/tools/perf/util/probe-finder.h
@@ -25,17 +25,22 @@ extern int find_probe_trace_events(int fd, struct perf_probe_event *pev,
extern int find_perf_probe_point(int fd, unsigned long addr,
struct perf_probe_point *ppt);
+/* Find a line range */
extern int find_line_range(int fd, struct line_range *lr);
+/* Find available variables */
+extern int find_available_vars_at(int fd, struct perf_probe_event *pev,
+ struct variable_list **vls, int max_points);
+
#include <dwarf.h>
#include <libdw.h>
#include <version.h>
struct probe_finder {
struct perf_probe_event *pev; /* Target probe event */
- struct probe_trace_event *tevs; /* Result trace events */
- int ntevs; /* Number of trace events */
- int max_tevs; /* Max number of trace events */
+
+ /* Callback when a probe point is found */
+ int (*callback)(Dwarf_Die *sp_die, struct probe_finder *pf);
/* For function searching */
int lno; /* Line number */
@@ -53,6 +58,20 @@ struct probe_finder {
struct probe_trace_arg *tvar; /* Current result variable */
};
+struct trace_event_finder {
+ struct probe_finder pf;
+ struct probe_trace_event *tevs; /* Found trace events */
+ int ntevs; /* Number of trace events */
+ int max_tevs; /* Max number of trace events */
+};
+
+struct available_var_finder {
+ struct probe_finder pf;
+ struct variable_list *vls; /* Found variable lists */
+ int nvls; /* Number of variable lists */
+ int max_vls; /* Max no. of variable lists */
+};
+
struct line_finder {
struct line_range *lr; /* Target line range */
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH -tip 5/7] perf probe: Function style fix
2010-10-21 10:12 [PATCH -tip 0/7] Perf probe update (--vars/--module) Masami Hiramatsu
` (3 preceding siblings ...)
2010-10-21 10:13 ` [PATCH -tip 4/7] perf probe: Show accessible local variables Masami Hiramatsu
@ 2010-10-21 10:13 ` Masami Hiramatsu
2010-10-23 19:41 ` [tip:perf/urgent] " tip-bot for Masami Hiramatsu
2010-10-21 10:13 ` [PATCH -tip 6/7] perf probe: Show accessible global variables Masami Hiramatsu
2010-10-21 10:13 ` [PATCH -tip 7/7] perf probe: Add basic module support Masami Hiramatsu
6 siblings, 1 reply; 18+ messages in thread
From: Masami Hiramatsu @ 2010-10-21 10:13 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo, Ingo Molnar
Cc: Steven Rostedt, Srikar Dronamraju, linux-kernel, 2nddept-manager,
Masami Hiramatsu, Peter Zijlstra, Paul Mackerras, Ingo Molnar,
Arnaldo Carvalho de Melo, Frederic Weisbecker, linux-kernel
Just change the order of function arguments for ease of read;
moving optional bool flag to the last.
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: linux-kernel@vger.kernel.org
---
tools/perf/builtin-probe.c | 4 ++--
tools/perf/util/probe-event.c | 2 +-
tools/perf/util/probe-event.h | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index 91bb6cf..c777bec 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -305,8 +305,8 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
if (params.nevents) {
ret = add_perf_probe_events(params.events, params.nevents,
- params.force_add,
- params.max_probe_points);
+ params.max_probe_points,
+ params.force_add);
if (ret < 0) {
pr_err(" Error: Failed to add events. (%d)\n", ret);
return ret;
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 3de265e..cafddc9 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -1668,7 +1668,7 @@ struct __event_package {
};
int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
- bool force_add, int max_tevs)
+ int max_tevs, bool force_add)
{
int i, j, ret;
struct __event_package *pkgs;
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 727a7fe..83130f6 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -117,7 +117,7 @@ extern int parse_line_range_desc(const char *cmd, struct line_range *lr);
extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
- bool force_add, int max_probe_points);
+ int max_probe_points, bool force_add);
extern int del_perf_probe_events(struct strlist *dellist);
extern int show_perf_probe_events(void);
extern int show_line_range(struct line_range *lr);
^ permalink raw reply related [flat|nested] 18+ messages in thread* [tip:perf/urgent] perf probe: Function style fix
2010-10-21 10:13 ` [PATCH -tip 5/7] perf probe: Function style fix Masami Hiramatsu
@ 2010-10-23 19:41 ` tip-bot for Masami Hiramatsu
0 siblings, 0 replies; 18+ messages in thread
From: tip-bot for Masami Hiramatsu @ 2010-10-23 19:41 UTC (permalink / raw)
To: linux-tip-commits
Cc: acme, linux-kernel, paulus, hpa, mingo, a.p.zijlstra,
masami.hiramatsu.pt, fweisbec, tglx, mingo
Commit-ID: c82ec0a2bd7725a2d2ac3065d8cde13e1f717d3c
Gitweb: http://git.kernel.org/tip/c82ec0a2bd7725a2d2ac3065d8cde13e1f717d3c
Author: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
AuthorDate: Thu, 21 Oct 2010 19:13:29 +0900
Committer: Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Thu, 21 Oct 2010 16:00:42 -0200
perf probe: Function style fix
Just change the order of function arguments for ease of read; moving optional
bool flag to the last.
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
LKML-Reference: <20101021101329.3542.51200.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
tools/perf/builtin-probe.c | 4 ++--
tools/perf/util/probe-event.c | 2 +-
tools/perf/util/probe-event.h | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index 91bb6cf..c777bec 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -305,8 +305,8 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
if (params.nevents) {
ret = add_perf_probe_events(params.events, params.nevents,
- params.force_add,
- params.max_probe_points);
+ params.max_probe_points,
+ params.force_add);
if (ret < 0) {
pr_err(" Error: Failed to add events. (%d)\n", ret);
return ret;
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 288ebe8..83192a5 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -1668,7 +1668,7 @@ struct __event_package {
};
int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
- bool force_add, int max_tevs)
+ int max_tevs, bool force_add)
{
int i, j, ret;
struct __event_package *pkgs;
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 727a7fe..83130f6 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -117,7 +117,7 @@ extern int parse_line_range_desc(const char *cmd, struct line_range *lr);
extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
- bool force_add, int max_probe_points);
+ int max_probe_points, bool force_add);
extern int del_perf_probe_events(struct strlist *dellist);
extern int show_perf_probe_events(void);
extern int show_line_range(struct line_range *lr);
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH -tip 6/7] perf probe: Show accessible global variables
2010-10-21 10:12 [PATCH -tip 0/7] Perf probe update (--vars/--module) Masami Hiramatsu
` (4 preceding siblings ...)
2010-10-21 10:13 ` [PATCH -tip 5/7] perf probe: Function style fix Masami Hiramatsu
@ 2010-10-21 10:13 ` Masami Hiramatsu
2010-10-21 20:50 ` Arnaldo Carvalho de Melo
2010-10-23 19:42 ` [tip:perf/urgent] " tip-bot for Masami Hiramatsu
2010-10-21 10:13 ` [PATCH -tip 7/7] perf probe: Add basic module support Masami Hiramatsu
6 siblings, 2 replies; 18+ messages in thread
From: Masami Hiramatsu @ 2010-10-21 10:13 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo, Ingo Molnar
Cc: Steven Rostedt, Srikar Dronamraju, linux-kernel, 2nddept-manager,
Masami Hiramatsu, Peter Zijlstra, Paul Mackerras, Ingo Molnar,
Arnaldo Carvalho de Melo, Frederic Weisbecker, linux-kernel
Add --externs for allowing --vars to show accessible
global(externally defined) variables from a given probe
point too.
This will give you a hint which globals can be accessible
from the probe point.
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: linux-kernel@vger.kernel.org
---
tools/perf/Documentation/perf-probe.txt | 6 +++++-
tools/perf/builtin-probe.c | 8 ++++++--
tools/perf/util/probe-event.c | 8 ++++----
tools/perf/util/probe-event.h | 2 +-
tools/perf/util/probe-finder.c | 26 +++++++++++++++++++++-----
tools/perf/util/probe-finder.h | 5 ++++-
6 files changed, 41 insertions(+), 14 deletions(-)
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index 72f5d9e..148c178 100644
--- a/tools/perf/Documentation/perf-probe.txt
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -18,7 +18,7 @@ or
or
'perf probe' --line='FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]'
or
-'perf probe' --vars='PROBEPOINT'
+'perf probe' [--externs] --vars='PROBEPOINT'
DESCRIPTION
-----------
@@ -64,6 +64,10 @@ OPTIONS
Show available local variables at given probe point. The argument
syntax is same as PROBE SYNTAX, but NO ARGs.
+--externs::
+ (Only for --vars) Show external defined variables in addition to local
+ variables.
+
-f::
--force::
Forcibly add events with existing name.
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index c777bec..bdf60cf 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -51,6 +51,7 @@ static struct {
bool force_add;
bool show_lines;
bool show_vars;
+ bool show_ext_vars;
bool mod_events;
int nevents;
struct perf_probe_event events[MAX_PROBES];
@@ -162,7 +163,7 @@ static const char * const probe_usage[] = {
"perf probe --list",
#ifdef DWARF_SUPPORT
"perf probe --line 'LINEDESC'",
- "perf probe --vars 'PROBEPOINT'",
+ "perf probe [--externs] --vars 'PROBEPOINT'",
#endif
NULL
};
@@ -207,6 +208,8 @@ static const struct option options[] = {
OPT_CALLBACK('V', "vars", NULL,
"FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT",
"Show accessible variables on PROBEDEF", opt_show_vars),
+ OPT_BOOLEAN('\0', "externs", ¶ms.show_ext_vars,
+ "Show external variables too (with --vars only)"),
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
"file", "vmlinux pathname"),
OPT_STRING('s', "source", &symbol_conf.source_prefix,
@@ -287,7 +290,8 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
usage_with_options(probe_usage, options);
}
ret = show_available_vars(params.events, params.nevents,
- params.max_probe_points);
+ params.max_probe_points,
+ params.show_ext_vars);
if (ret < 0)
pr_err(" Error: Failed to show vars. (%d)\n", ret);
return ret;
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index cafddc9..1b462cc 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -379,7 +379,7 @@ end:
}
static int show_available_vars_at(int fd, struct perf_probe_event *pev,
- int max_vls)
+ int max_vls, bool externs)
{
char *buf;
int ret, i;
@@ -391,7 +391,7 @@ static int show_available_vars_at(int fd, struct perf_probe_event *pev,
return -EINVAL;
pr_debug("Searching variables at %s\n", buf);
- ret = find_available_vars_at(fd, pev, &vls, max_vls);
+ ret = find_available_vars_at(fd, pev, &vls, max_vls, externs);
if (ret > 0) {
/* Some variables are found */
fprintf(stdout, "Available variables at %s\n", buf);
@@ -421,7 +421,7 @@ static int show_available_vars_at(int fd, struct perf_probe_event *pev,
/* Show available variables on given probe point */
int show_available_vars(struct perf_probe_event *pevs, int npevs,
- int max_vls)
+ int max_vls, bool externs)
{
int i, fd, ret = 0;
@@ -438,7 +438,7 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
setup_pager();
for (i = 0; i < npevs && ret >= 0; i++)
- ret = show_available_vars_at(fd, &pevs[i], max_vls);
+ ret = show_available_vars_at(fd, &pevs[i], max_vls, externs);
close(fd);
return ret;
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 83130f6..c74b1fd 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -122,7 +122,7 @@ extern int del_perf_probe_events(struct strlist *dellist);
extern int show_perf_probe_events(void);
extern int show_line_range(struct line_range *lr);
extern int show_available_vars(struct perf_probe_event *pevs, int npevs,
- int max_probe_points);
+ int max_probe_points, bool externs);
/* Maximum index number of event-name postfix */
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 986027f..a274fd0 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -1312,12 +1312,13 @@ static int collect_variables_cb(Dwarf_Die *die_mem, void *data)
af->pf.fb_ops, NULL);
if (ret == 0) {
ret = die_get_varname(die_mem, buf, MAX_VAR_LEN);
+ pr_debug2("Add new var: %s\n", buf);
if (ret > 0)
strlist__add(vl->vars, buf);
}
}
- if (dwarf_haspc(die_mem, af->pf.addr))
+ if (af->child && dwarf_haspc(die_mem, af->pf.addr))
return DIE_FIND_CB_CONTINUE;
else
return DIE_FIND_CB_SIBLING;
@@ -1329,8 +1330,8 @@ static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf)
struct available_var_finder *af =
container_of(pf, struct available_var_finder, pf);
struct variable_list *vl;
- Dwarf_Die die_mem;
- int ret;
+ Dwarf_Die die_mem, *scopes = NULL;
+ int ret, nscopes;
/* Check number of tevs */
if (af->nvls == af->max_vls) {
@@ -1351,8 +1352,22 @@ static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf)
vl->vars = strlist__new(true, NULL);
if (vl->vars == NULL)
return -ENOMEM;
+ af->child = true;
die_find_child(sp_die, collect_variables_cb, (void *)af, &die_mem);
+ /* Find external variables */
+ if (!af->externs)
+ goto out;
+ /* Don't need to search child DIE for externs. */
+ af->child = false;
+ nscopes = dwarf_getscopes_die(sp_die, &scopes);
+ while (nscopes-- > 1)
+ die_find_child(&scopes[nscopes], collect_variables_cb,
+ (void *)af, &die_mem);
+ if (scopes)
+ free(scopes);
+
+out:
if (strlist__empty(vl->vars)) {
strlist__delete(vl->vars);
vl->vars = NULL;
@@ -1363,11 +1378,12 @@ static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf)
/* Find available variables at given probe point */
int find_available_vars_at(int fd, struct perf_probe_event *pev,
- struct variable_list **vls, int max_vls)
+ struct variable_list **vls, int max_vls,
+ bool externs)
{
struct available_var_finder af = {
.pf = {.pev = pev, .callback = add_available_vars},
- .max_vls = max_vls};
+ .max_vls = max_vls, .externs = externs};
int ret;
/* Allocate result vls array */
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
index baffd25..516912a 100644
--- a/tools/perf/util/probe-finder.h
+++ b/tools/perf/util/probe-finder.h
@@ -30,7 +30,8 @@ extern int find_line_range(int fd, struct line_range *lr);
/* Find available variables */
extern int find_available_vars_at(int fd, struct perf_probe_event *pev,
- struct variable_list **vls, int max_points);
+ struct variable_list **vls, int max_points,
+ bool externs);
#include <dwarf.h>
#include <libdw.h>
@@ -70,6 +71,8 @@ struct available_var_finder {
struct variable_list *vls; /* Found variable lists */
int nvls; /* Number of variable lists */
int max_vls; /* Max no. of variable lists */
+ bool externs; /* Find external vars too */
+ bool child; /* Search child scopes */
};
struct line_finder {
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH -tip 6/7] perf probe: Show accessible global variables
2010-10-21 10:13 ` [PATCH -tip 6/7] perf probe: Show accessible global variables Masami Hiramatsu
@ 2010-10-21 20:50 ` Arnaldo Carvalho de Melo
2010-10-22 2:25 ` Masami Hiramatsu
2010-10-23 19:42 ` [tip:perf/urgent] " tip-bot for Masami Hiramatsu
1 sibling, 1 reply; 18+ messages in thread
From: Arnaldo Carvalho de Melo @ 2010-10-21 20:50 UTC (permalink / raw)
To: Masami Hiramatsu
Cc: Ingo Molnar, Steven Rostedt, Srikar Dronamraju, linux-kernel,
2nddept-manager, Peter Zijlstra, Paul Mackerras,
Frederic Weisbecker
Em Thu, Oct 21, 2010 at 07:13:35PM +0900, Masami Hiramatsu escreveu:
> Add --externs for allowing --vars to show accessible
> global(externally defined) variables from a given probe
> point too.
> This will give you a hint which globals can be accessible
> from the probe point.
Cool, but perhaps we need to filter out some of the globals? Things
like:
long unsigned int __kcrctab_tcp_hashinfo
long unsigned int __kcrctab_tcp_proc_register
long unsigned int __kcrctab_tcp_proc_unregister
long unsigned int __kcrctab_tcp_prot
If --verbose is specified we could then show everything.
So that it doesn't get this long:
[root@emilia ~]# perf probe -V tcp_v4_do_rcv:28 --externs
Available variables at tcp_v4_do_rcv:28
@<tcp_v4_do_rcv+390>
(function_type)* ip_nat_decode_session
(unknown_type) __crc_ipv4_specific
(unknown_type) __crc_sysctl_tcp_low_latency
(unknown_type) __crc_tcp4_gro_complete
(unknown_type) __crc_tcp4_gro_receive
(unknown_type) __crc_tcp_hashinfo
(unknown_type) __crc_tcp_proc_register
(unknown_type) __crc_tcp_proc_unregister
(unknown_type) __crc_tcp_prot
(unknown_type) __crc_tcp_twsk_unique
(unknown_type) __crc_tcp_v4_conn_request
(unknown_type) __crc_tcp_v4_connect
(unknown_type) __crc_tcp_v4_destroy_sock
(unknown_type) __crc_tcp_v4_do_rcv
(unknown_type) __crc_tcp_v4_md5_do_add
(unknown_type) __crc_tcp_v4_md5_do_del
(unknown_type) __crc_tcp_v4_md5_hash_skb
(unknown_type) __crc_tcp_v4_md5_lookup
(unknown_type) __crc_tcp_v4_remember_stamp
(unknown_type) __crc_tcp_v4_send_check
(unknown_type) __crc_tcp_v4_syn_recv_sock
__u8* ip_tos2prio
atomic_long_t* vm_stat
atomic_t tcp_memory_allocated
bool c1e_detected
char __pcpu_scope_cpu_core_map
char __pcpu_scope_cpu_info
char __pcpu_scope_cpu_llc_id
char __pcpu_scope_cpu_number
char __pcpu_scope_cpu_sibling_map
char __pcpu_scope_current_task
char __pcpu_scope_init_tss
char __pcpu_scope_irq_count
char __pcpu_scope_irq_regs
char __pcpu_scope_irq_stack_ptr
char __pcpu_scope_irq_stack_union
char __pcpu_scope_irq_stat
char __pcpu_scope_kernel_stack
char __pcpu_scope_numa_node
char __pcpu_scope_orig_ist
char __pcpu_scope_process_counts
char __pcpu_scope_softirq_work_list
char __pcpu_scope_softnet_data
char __pcpu_scope_this_cpu_off
char __pcpu_scope_tick_cpu_device
char __pcpu_scope_vector_irq
char __pcpu_scope_vm_event_states
char __pcpu_scope_x2apic_extra_bits
char __pcpu_scope_x86_bios_cpu_apicid
char __pcpu_scope_x86_cpu_to_apicid
char __pcpu_scope_x86_cpu_to_node_map
char* __kstrtab_ipv4_specific
char* __kstrtab_sysctl_tcp_low_latency
char* __kstrtab_tcp4_gro_complete
char* __kstrtab_tcp4_gro_receive
char* __kstrtab_tcp_hashinfo
char* __kstrtab_tcp_proc_register
char* __kstrtab_tcp_proc_unregister
char* __kstrtab_tcp_prot
char* __kstrtab_tcp_twsk_unique
char* __kstrtab_tcp_v4_conn_request
char* __kstrtab_tcp_v4_connect
char* __kstrtab_tcp_v4_destroy_sock
char* __kstrtab_tcp_v4_do_rcv
char* __kstrtab_tcp_v4_md5_do_add
char* __kstrtab_tcp_v4_md5_do_del
char* __kstrtab_tcp_v4_md5_hash_skb
char* __kstrtab_tcp_v4_md5_lookup
char* __kstrtab_tcp_v4_remember_stamp
char* __kstrtab_tcp_v4_send_check
char* __kstrtab_tcp_v4_syn_recv_sock
char* hex_asc
char* inet_csk_timer_bug_msg
cpumask_var_t cpu_callout_mask
cpumask_var_t cpu_core_map
cpumask_var_t cpu_sibling_map
cpumask_var_t* node_to_cpumask_map
int acpi_disabled
int acpi_noirq
int acpi_pci_disabled
int audit_enabled
int cpu_number
int debug_locks
int disable_apic
int net_msg_warn
int nr_cpu_ids
int nr_online_nodes
int numa_node
int page_group_by_mobility_disabled
int percpu_counter_batch
int prof_on
int sched_mc_power_savings
int sched_smt_power_savings
int smp_found_config
int sysctl_max_syn_backlog
int sysctl_tcp_adv_win_scale
int sysctl_tcp_cookie_size
int sysctl_tcp_ecn
int sysctl_tcp_fin_timeout
int sysctl_tcp_keepalive_intvl
int sysctl_tcp_keepalive_probes
int sysctl_tcp_keepalive_time
int sysctl_tcp_low_latency
int sysctl_tcp_max_orphans
int sysctl_tcp_reordering
int sysctl_tcp_syncookies
int sysctl_tcp_tw_reuse
int tcp_memory_pressure
int time_status
int timer_stats_active
int x2apic_phys
int x86_cpu_to_node_map
int* console_printk
int* sysctl_tcp_mem
int* sysctl_tcp_rmem
int* sysctl_tcp_wmem
int* x86_cpu_to_node_map_early_ptr
irq_cpustat_t irq_stat
long unsigned int __kcrctab_ipv4_specific
long unsigned int __kcrctab_sysctl_tcp_low_latency
long unsigned int __kcrctab_tcp4_gro_complete
long unsigned int __kcrctab_tcp4_gro_receive
long unsigned int __kcrctab_tcp_hashinfo
long unsigned int __kcrctab_tcp_proc_register
long unsigned int __kcrctab_tcp_proc_unregister
long unsigned int __kcrctab_tcp_prot
long unsigned int __kcrctab_tcp_twsk_unique
long unsigned int __kcrctab_tcp_v4_conn_request
long unsigned int __kcrctab_tcp_v4_connect
long unsigned int __kcrctab_tcp_v4_destroy_sock
long unsigned int __kcrctab_tcp_v4_do_rcv
long unsigned int __kcrctab_tcp_v4_md5_do_add
long unsigned int __kcrctab_tcp_v4_md5_do_del
long unsigned int __kcrctab_tcp_v4_md5_hash_skb
long unsigned int __kcrctab_tcp_v4_md5_lookup
long unsigned int __kcrctab_tcp_v4_remember_stamp
long unsigned int __kcrctab_tcp_v4_send_check
long unsigned int __kcrctab_tcp_v4_syn_recv_sock
long unsigned int jiffies
long unsigned int kernel_stack
long unsigned int mmap_min_addr
long unsigned int mmu_cr4_features
long unsigned int* __per_cpu_offset
long unsigned int* cpu_bit_bitmap
long unsigned int* sysctl_local_reserved_ports
nodemask_t* node_states
pg_data_t** node_data
physid_mask_t phys_cpu_present_map
pteval_t __supported_pte_mask
spinlock_t dcache_lock
struct address_space swapper_space
struct apic* apic
struct cache_sizes* malloc_sizes
struct cpuinfo_x86 boot_cpu_data
struct cpuinfo_x86 cpu_info
struct cpumask* cpu_online_mask
struct cpumask* cpu_possible_mask
struct cpumask* cpu_present_mask
struct device x86_dma_fallback_dev
struct dma_map_ops* dma_ops
struct dqstats dqstats
struct icmp_err* icmp_err_convert
struct inet_connection_sock_af_ops ipv4_specific
struct inet_hashinfo tcp_hashinfo
struct inet_timewait_death_row tcp_death_row
struct kernel_symbol __ksymtab_ipv4_specific
struct kernel_symbol __ksymtab_sysctl_tcp_low_latency
struct kernel_symbol __ksymtab_tcp4_gro_complete
struct kernel_symbol __ksymtab_tcp4_gro_receive
struct kernel_symbol __ksymtab_tcp_hashinfo
struct kernel_symbol __ksymtab_tcp_proc_register
struct kernel_symbol __ksymtab_tcp_proc_unregister
struct kernel_symbol __ksymtab_tcp_prot
struct kernel_symbol __ksymtab_tcp_twsk_unique
struct kernel_symbol __ksymtab_tcp_v4_conn_request
struct kernel_symbol __ksymtab_tcp_v4_connect
struct kernel_symbol __ksymtab_tcp_v4_destroy_sock
struct kernel_symbol __ksymtab_tcp_v4_do_rcv
struct kernel_symbol __ksymtab_tcp_v4_md5_do_add
struct kernel_symbol __ksymtab_tcp_v4_md5_do_del
struct kernel_symbol __ksymtab_tcp_v4_md5_hash_skb
struct kernel_symbol __ksymtab_tcp_v4_md5_lookup
struct kernel_symbol __ksymtab_tcp_v4_remember_stamp
struct kernel_symbol __ksymtab_tcp_v4_send_check
struct kernel_symbol __ksymtab_tcp_v4_syn_recv_sock
struct list_head* nf_hooks
struct mem_section** mem_section
struct memnode memnode
struct neigh_table nd_tbl
struct nf_afinfo** nf_afinfo
struct percpu_counter tcp_orphan_count
struct percpu_counter tcp_sockets_allocated
struct pernet_operations tcp4_net_ops
struct pernet_operations tcp_sk_ops
struct pglist_data** node_data
struct pid* cad_pid
struct pid_namespace init_pid_ns
struct proto tcp_prot
struct pt_regs* irq_regs
struct pv_apic_ops pv_apic_ops
struct pv_cpu_ops pv_cpu_ops
struct pv_info pv_info
struct pv_irq_ops pv_irq_ops
struct pv_mmu_ops pv_mmu_ops
struct pv_time_ops pv_time_ops
struct request_sock_ops tcp_request_sock_ops
struct resource ioport_resource
struct rps_sock_flow_table* rps_sock_flow_table
struct sk_buff* skb
struct smp_ops smp_ops
struct sock* sk
struct task_struct* current_task
struct tcp_congestion_ops tcp_init_congestion_ops
struct tcp_request_sock_ops tcp_request_sock_ipv4_ops
struct tcp_seq_afinfo tcp4_seq_afinfo
struct tcp_sock_af_ops tcp_sock_ipv4_specific
struct timewait_sock_ops tcp_timewait_sock_ops
struct tracepoint __tracepoint_irq_handler_entry
struct tracepoint __tracepoint_irq_handler_exit
struct tracepoint __tracepoint_kfree
struct tracepoint __tracepoint_kmalloc
struct tracepoint __tracepoint_kmalloc_node
struct tracepoint __tracepoint_kmem_cache_alloc
struct tracepoint __tracepoint_kmem_cache_alloc_node
struct tracepoint __tracepoint_kmem_cache_free
struct tracepoint __tracepoint_mm_page_alloc
struct tracepoint __tracepoint_mm_page_alloc_extfrag
struct tracepoint __tracepoint_mm_page_alloc_zone_locked
struct tracepoint __tracepoint_mm_page_free_direct
struct tracepoint __tracepoint_mm_page_pcpu_drain
struct tracepoint __tracepoint_mm_pagevec_free
struct tracepoint __tracepoint_module_free
struct tracepoint __tracepoint_module_get
struct tracepoint __tracepoint_module_load
struct tracepoint __tracepoint_module_put
struct tracepoint __tracepoint_module_request
struct tracepoint __tracepoint_softirq_entry
struct tracepoint __tracepoint_softirq_exit
struct tracepoint __tracepoint_softirq_raise
struct vm_event_state vm_event_states
struct x86_init_ops x86_init
struct x86_platform_ops x86_platform
u16 x86_bios_cpu_apicid
u32 inet_ehash_secret
union irq_stack_union irq_stack_union
unsigned int apic_verbosity
unsigned int sysctl_timer_migration
- Arnaldo
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH -tip 6/7] perf probe: Show accessible global variables
2010-10-21 20:50 ` Arnaldo Carvalho de Melo
@ 2010-10-22 2:25 ` Masami Hiramatsu
2010-10-22 5:31 ` Masami Hiramatsu
0 siblings, 1 reply; 18+ messages in thread
From: Masami Hiramatsu @ 2010-10-22 2:25 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Ingo Molnar, Steven Rostedt, Srikar Dronamraju, linux-kernel,
2nddept-manager, Peter Zijlstra, Paul Mackerras,
Frederic Weisbecker
(2010/10/22 5:50), Arnaldo Carvalho de Melo wrote:
> Em Thu, Oct 21, 2010 at 07:13:35PM +0900, Masami Hiramatsu escreveu:
>> Add --externs for allowing --vars to show accessible
>> global(externally defined) variables from a given probe
>> point too.
>> This will give you a hint which globals can be accessible
>> from the probe point.
>
> Cool, but perhaps we need to filter out some of the globals? Things
> like:
Hmm, yeah. So is that enough to filter out symbols which start with
__k???tab_ and __crc_ ?
>
> long unsigned int __kcrctab_tcp_hashinfo
> long unsigned int __kcrctab_tcp_proc_register
> long unsigned int __kcrctab_tcp_proc_unregister
> long unsigned int __kcrctab_tcp_prot
>
> If --verbose is specified we could then show everything.
--verbose is already used for showing debug messages, ;)
so how about using --all/--allextras?
Or, perhaps we can add more generic filtering for listing
events/vars/funcs.
Thank you,
>
> So that it doesn't get this long:
>
> [root@emilia ~]# perf probe -V tcp_v4_do_rcv:28 --externs
> Available variables at tcp_v4_do_rcv:28
> @<tcp_v4_do_rcv+390>
> (function_type)* ip_nat_decode_session
> (unknown_type) __crc_ipv4_specific
> (unknown_type) __crc_sysctl_tcp_low_latency
> (unknown_type) __crc_tcp4_gro_complete
> (unknown_type) __crc_tcp4_gro_receive
> (unknown_type) __crc_tcp_hashinfo
> (unknown_type) __crc_tcp_proc_register
> (unknown_type) __crc_tcp_proc_unregister
> (unknown_type) __crc_tcp_prot
> (unknown_type) __crc_tcp_twsk_unique
> (unknown_type) __crc_tcp_v4_conn_request
> (unknown_type) __crc_tcp_v4_connect
> (unknown_type) __crc_tcp_v4_destroy_sock
> (unknown_type) __crc_tcp_v4_do_rcv
> (unknown_type) __crc_tcp_v4_md5_do_add
> (unknown_type) __crc_tcp_v4_md5_do_del
> (unknown_type) __crc_tcp_v4_md5_hash_skb
> (unknown_type) __crc_tcp_v4_md5_lookup
> (unknown_type) __crc_tcp_v4_remember_stamp
> (unknown_type) __crc_tcp_v4_send_check
> (unknown_type) __crc_tcp_v4_syn_recv_sock
> __u8* ip_tos2prio
> atomic_long_t* vm_stat
> atomic_t tcp_memory_allocated
> bool c1e_detected
> char __pcpu_scope_cpu_core_map
> char __pcpu_scope_cpu_info
> char __pcpu_scope_cpu_llc_id
> char __pcpu_scope_cpu_number
> char __pcpu_scope_cpu_sibling_map
> char __pcpu_scope_current_task
> char __pcpu_scope_init_tss
> char __pcpu_scope_irq_count
> char __pcpu_scope_irq_regs
> char __pcpu_scope_irq_stack_ptr
> char __pcpu_scope_irq_stack_union
> char __pcpu_scope_irq_stat
> char __pcpu_scope_kernel_stack
> char __pcpu_scope_numa_node
> char __pcpu_scope_orig_ist
> char __pcpu_scope_process_counts
> char __pcpu_scope_softirq_work_list
> char __pcpu_scope_softnet_data
> char __pcpu_scope_this_cpu_off
> char __pcpu_scope_tick_cpu_device
> char __pcpu_scope_vector_irq
> char __pcpu_scope_vm_event_states
> char __pcpu_scope_x2apic_extra_bits
> char __pcpu_scope_x86_bios_cpu_apicid
> char __pcpu_scope_x86_cpu_to_apicid
> char __pcpu_scope_x86_cpu_to_node_map
> char* __kstrtab_ipv4_specific
> char* __kstrtab_sysctl_tcp_low_latency
> char* __kstrtab_tcp4_gro_complete
> char* __kstrtab_tcp4_gro_receive
> char* __kstrtab_tcp_hashinfo
> char* __kstrtab_tcp_proc_register
> char* __kstrtab_tcp_proc_unregister
> char* __kstrtab_tcp_prot
> char* __kstrtab_tcp_twsk_unique
> char* __kstrtab_tcp_v4_conn_request
> char* __kstrtab_tcp_v4_connect
> char* __kstrtab_tcp_v4_destroy_sock
> char* __kstrtab_tcp_v4_do_rcv
> char* __kstrtab_tcp_v4_md5_do_add
> char* __kstrtab_tcp_v4_md5_do_del
> char* __kstrtab_tcp_v4_md5_hash_skb
> char* __kstrtab_tcp_v4_md5_lookup
> char* __kstrtab_tcp_v4_remember_stamp
> char* __kstrtab_tcp_v4_send_check
> char* __kstrtab_tcp_v4_syn_recv_sock
> char* hex_asc
> char* inet_csk_timer_bug_msg
> cpumask_var_t cpu_callout_mask
> cpumask_var_t cpu_core_map
> cpumask_var_t cpu_sibling_map
> cpumask_var_t* node_to_cpumask_map
> int acpi_disabled
> int acpi_noirq
> int acpi_pci_disabled
> int audit_enabled
> int cpu_number
> int debug_locks
> int disable_apic
> int net_msg_warn
> int nr_cpu_ids
> int nr_online_nodes
> int numa_node
> int page_group_by_mobility_disabled
> int percpu_counter_batch
> int prof_on
> int sched_mc_power_savings
> int sched_smt_power_savings
> int smp_found_config
> int sysctl_max_syn_backlog
> int sysctl_tcp_adv_win_scale
> int sysctl_tcp_cookie_size
> int sysctl_tcp_ecn
> int sysctl_tcp_fin_timeout
> int sysctl_tcp_keepalive_intvl
> int sysctl_tcp_keepalive_probes
> int sysctl_tcp_keepalive_time
> int sysctl_tcp_low_latency
> int sysctl_tcp_max_orphans
> int sysctl_tcp_reordering
> int sysctl_tcp_syncookies
> int sysctl_tcp_tw_reuse
> int tcp_memory_pressure
> int time_status
> int timer_stats_active
> int x2apic_phys
> int x86_cpu_to_node_map
> int* console_printk
> int* sysctl_tcp_mem
> int* sysctl_tcp_rmem
> int* sysctl_tcp_wmem
> int* x86_cpu_to_node_map_early_ptr
> irq_cpustat_t irq_stat
> long unsigned int __kcrctab_ipv4_specific
> long unsigned int __kcrctab_sysctl_tcp_low_latency
> long unsigned int __kcrctab_tcp4_gro_complete
> long unsigned int __kcrctab_tcp4_gro_receive
> long unsigned int __kcrctab_tcp_hashinfo
> long unsigned int __kcrctab_tcp_proc_register
> long unsigned int __kcrctab_tcp_proc_unregister
> long unsigned int __kcrctab_tcp_prot
> long unsigned int __kcrctab_tcp_twsk_unique
> long unsigned int __kcrctab_tcp_v4_conn_request
> long unsigned int __kcrctab_tcp_v4_connect
> long unsigned int __kcrctab_tcp_v4_destroy_sock
> long unsigned int __kcrctab_tcp_v4_do_rcv
> long unsigned int __kcrctab_tcp_v4_md5_do_add
> long unsigned int __kcrctab_tcp_v4_md5_do_del
> long unsigned int __kcrctab_tcp_v4_md5_hash_skb
> long unsigned int __kcrctab_tcp_v4_md5_lookup
> long unsigned int __kcrctab_tcp_v4_remember_stamp
> long unsigned int __kcrctab_tcp_v4_send_check
> long unsigned int __kcrctab_tcp_v4_syn_recv_sock
> long unsigned int jiffies
> long unsigned int kernel_stack
> long unsigned int mmap_min_addr
> long unsigned int mmu_cr4_features
> long unsigned int* __per_cpu_offset
> long unsigned int* cpu_bit_bitmap
> long unsigned int* sysctl_local_reserved_ports
> nodemask_t* node_states
> pg_data_t** node_data
> physid_mask_t phys_cpu_present_map
> pteval_t __supported_pte_mask
> spinlock_t dcache_lock
> struct address_space swapper_space
> struct apic* apic
> struct cache_sizes* malloc_sizes
> struct cpuinfo_x86 boot_cpu_data
> struct cpuinfo_x86 cpu_info
> struct cpumask* cpu_online_mask
> struct cpumask* cpu_possible_mask
> struct cpumask* cpu_present_mask
> struct device x86_dma_fallback_dev
> struct dma_map_ops* dma_ops
> struct dqstats dqstats
> struct icmp_err* icmp_err_convert
> struct inet_connection_sock_af_ops ipv4_specific
> struct inet_hashinfo tcp_hashinfo
> struct inet_timewait_death_row tcp_death_row
> struct kernel_symbol __ksymtab_ipv4_specific
> struct kernel_symbol __ksymtab_sysctl_tcp_low_latency
> struct kernel_symbol __ksymtab_tcp4_gro_complete
> struct kernel_symbol __ksymtab_tcp4_gro_receive
> struct kernel_symbol __ksymtab_tcp_hashinfo
> struct kernel_symbol __ksymtab_tcp_proc_register
> struct kernel_symbol __ksymtab_tcp_proc_unregister
> struct kernel_symbol __ksymtab_tcp_prot
> struct kernel_symbol __ksymtab_tcp_twsk_unique
> struct kernel_symbol __ksymtab_tcp_v4_conn_request
> struct kernel_symbol __ksymtab_tcp_v4_connect
> struct kernel_symbol __ksymtab_tcp_v4_destroy_sock
> struct kernel_symbol __ksymtab_tcp_v4_do_rcv
> struct kernel_symbol __ksymtab_tcp_v4_md5_do_add
> struct kernel_symbol __ksymtab_tcp_v4_md5_do_del
> struct kernel_symbol __ksymtab_tcp_v4_md5_hash_skb
> struct kernel_symbol __ksymtab_tcp_v4_md5_lookup
> struct kernel_symbol __ksymtab_tcp_v4_remember_stamp
> struct kernel_symbol __ksymtab_tcp_v4_send_check
> struct kernel_symbol __ksymtab_tcp_v4_syn_recv_sock
> struct list_head* nf_hooks
> struct mem_section** mem_section
> struct memnode memnode
> struct neigh_table nd_tbl
> struct nf_afinfo** nf_afinfo
> struct percpu_counter tcp_orphan_count
> struct percpu_counter tcp_sockets_allocated
> struct pernet_operations tcp4_net_ops
> struct pernet_operations tcp_sk_ops
> struct pglist_data** node_data
> struct pid* cad_pid
> struct pid_namespace init_pid_ns
> struct proto tcp_prot
> struct pt_regs* irq_regs
> struct pv_apic_ops pv_apic_ops
> struct pv_cpu_ops pv_cpu_ops
> struct pv_info pv_info
> struct pv_irq_ops pv_irq_ops
> struct pv_mmu_ops pv_mmu_ops
> struct pv_time_ops pv_time_ops
> struct request_sock_ops tcp_request_sock_ops
> struct resource ioport_resource
> struct rps_sock_flow_table* rps_sock_flow_table
> struct sk_buff* skb
> struct smp_ops smp_ops
> struct sock* sk
> struct task_struct* current_task
> struct tcp_congestion_ops tcp_init_congestion_ops
> struct tcp_request_sock_ops tcp_request_sock_ipv4_ops
> struct tcp_seq_afinfo tcp4_seq_afinfo
> struct tcp_sock_af_ops tcp_sock_ipv4_specific
> struct timewait_sock_ops tcp_timewait_sock_ops
> struct tracepoint __tracepoint_irq_handler_entry
> struct tracepoint __tracepoint_irq_handler_exit
> struct tracepoint __tracepoint_kfree
> struct tracepoint __tracepoint_kmalloc
> struct tracepoint __tracepoint_kmalloc_node
> struct tracepoint __tracepoint_kmem_cache_alloc
> struct tracepoint __tracepoint_kmem_cache_alloc_node
> struct tracepoint __tracepoint_kmem_cache_free
> struct tracepoint __tracepoint_mm_page_alloc
> struct tracepoint __tracepoint_mm_page_alloc_extfrag
> struct tracepoint __tracepoint_mm_page_alloc_zone_locked
> struct tracepoint __tracepoint_mm_page_free_direct
> struct tracepoint __tracepoint_mm_page_pcpu_drain
> struct tracepoint __tracepoint_mm_pagevec_free
> struct tracepoint __tracepoint_module_free
> struct tracepoint __tracepoint_module_get
> struct tracepoint __tracepoint_module_load
> struct tracepoint __tracepoint_module_put
> struct tracepoint __tracepoint_module_request
> struct tracepoint __tracepoint_softirq_entry
> struct tracepoint __tracepoint_softirq_exit
> struct tracepoint __tracepoint_softirq_raise
> struct vm_event_state vm_event_states
> struct x86_init_ops x86_init
> struct x86_platform_ops x86_platform
> u16 x86_bios_cpu_apicid
> u32 inet_ehash_secret
> union irq_stack_union irq_stack_union
> unsigned int apic_verbosity
> unsigned int sysctl_timer_migration
>
> - Arnaldo
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
--
Masami HIRAMATSU
2nd Dept. Linux Technology Center
Hitachi, Ltd., Systems Development Laboratory
E-mail: masami.hiramatsu.pt@hitachi.com
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH -tip 6/7] perf probe: Show accessible global variables
2010-10-22 2:25 ` Masami Hiramatsu
@ 2010-10-22 5:31 ` Masami Hiramatsu
0 siblings, 0 replies; 18+ messages in thread
From: Masami Hiramatsu @ 2010-10-22 5:31 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Ingo Molnar, Steven Rostedt, Srikar Dronamraju, linux-kernel,
2nddept-manager, Peter Zijlstra, Paul Mackerras,
Frederic Weisbecker
(2010/10/22 11:25), Masami Hiramatsu wrote:
> (2010/10/22 5:50), Arnaldo Carvalho de Melo wrote:
>> Em Thu, Oct 21, 2010 at 07:13:35PM +0900, Masami Hiramatsu escreveu:
>>> Add --externs for allowing --vars to show accessible
>>> global(externally defined) variables from a given probe
>>> point too.
>>> This will give you a hint which globals can be accessible
>>> from the probe point.
>>
>> Cool, but perhaps we need to filter out some of the globals? Things
>> like:
>
> Hmm, yeah. So is that enough to filter out symbols which start with
> __k???tab_ and __crc_ ?
>
>
>>
>> long unsigned int __kcrctab_tcp_hashinfo
>> long unsigned int __kcrctab_tcp_proc_register
>> long unsigned int __kcrctab_tcp_proc_unregister
>> long unsigned int __kcrctab_tcp_prot
BTW, with my kernel (yesterday -tip/x86-64/gcc-4.4.4-10),
perf probe doesn't find these __k???tab_ symbols in
dwarf. (but those are still in /proc/kallsyms)
Maybe, something has been changed or it depends on some
config/environment.
Thank you,
--
Masami HIRAMATSU
2nd Dept. Linux Technology Center
Hitachi, Ltd., Systems Development Laboratory
E-mail: masami.hiramatsu.pt@hitachi.com
^ permalink raw reply [flat|nested] 18+ messages in thread
* [tip:perf/urgent] perf probe: Show accessible global variables
2010-10-21 10:13 ` [PATCH -tip 6/7] perf probe: Show accessible global variables Masami Hiramatsu
2010-10-21 20:50 ` Arnaldo Carvalho de Melo
@ 2010-10-23 19:42 ` tip-bot for Masami Hiramatsu
1 sibling, 0 replies; 18+ messages in thread
From: tip-bot for Masami Hiramatsu @ 2010-10-23 19:42 UTC (permalink / raw)
To: linux-tip-commits
Cc: acme, linux-kernel, paulus, hpa, mingo, a.p.zijlstra,
masami.hiramatsu.pt, fweisbec, tglx, mingo
Commit-ID: fb8c5a56c7ddbc2b0d2ee7a8da60fe1355f75141
Gitweb: http://git.kernel.org/tip/fb8c5a56c7ddbc2b0d2ee7a8da60fe1355f75141
Author: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
AuthorDate: Thu, 21 Oct 2010 19:13:35 +0900
Committer: Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Thu, 21 Oct 2010 16:06:42 -0200
perf probe: Show accessible global variables
Add --externs for allowing --vars to show accessible global (externally
defined) variables from a given probe point too.
This will give you a hint which globals can be accessible from the probe point.
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
LKML-Reference: <20101021101335.3542.31003.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
tools/perf/Documentation/perf-probe.txt | 6 +++++-
tools/perf/builtin-probe.c | 8 ++++++--
tools/perf/util/probe-event.c | 8 ++++----
tools/perf/util/probe-event.h | 2 +-
tools/perf/util/probe-finder.c | 26 +++++++++++++++++++++-----
tools/perf/util/probe-finder.h | 5 ++++-
6 files changed, 41 insertions(+), 14 deletions(-)
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index 72f5d9e..148c178 100644
--- a/tools/perf/Documentation/perf-probe.txt
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -18,7 +18,7 @@ or
or
'perf probe' --line='FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]'
or
-'perf probe' --vars='PROBEPOINT'
+'perf probe' [--externs] --vars='PROBEPOINT'
DESCRIPTION
-----------
@@ -64,6 +64,10 @@ OPTIONS
Show available local variables at given probe point. The argument
syntax is same as PROBE SYNTAX, but NO ARGs.
+--externs::
+ (Only for --vars) Show external defined variables in addition to local
+ variables.
+
-f::
--force::
Forcibly add events with existing name.
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index c777bec..bdf60cf 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -51,6 +51,7 @@ static struct {
bool force_add;
bool show_lines;
bool show_vars;
+ bool show_ext_vars;
bool mod_events;
int nevents;
struct perf_probe_event events[MAX_PROBES];
@@ -162,7 +163,7 @@ static const char * const probe_usage[] = {
"perf probe --list",
#ifdef DWARF_SUPPORT
"perf probe --line 'LINEDESC'",
- "perf probe --vars 'PROBEPOINT'",
+ "perf probe [--externs] --vars 'PROBEPOINT'",
#endif
NULL
};
@@ -207,6 +208,8 @@ static const struct option options[] = {
OPT_CALLBACK('V', "vars", NULL,
"FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT",
"Show accessible variables on PROBEDEF", opt_show_vars),
+ OPT_BOOLEAN('\0', "externs", ¶ms.show_ext_vars,
+ "Show external variables too (with --vars only)"),
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
"file", "vmlinux pathname"),
OPT_STRING('s', "source", &symbol_conf.source_prefix,
@@ -287,7 +290,8 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
usage_with_options(probe_usage, options);
}
ret = show_available_vars(params.events, params.nevents,
- params.max_probe_points);
+ params.max_probe_points,
+ params.show_ext_vars);
if (ret < 0)
pr_err(" Error: Failed to show vars. (%d)\n", ret);
return ret;
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 83192a5..82b0976 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -379,7 +379,7 @@ end:
}
static int show_available_vars_at(int fd, struct perf_probe_event *pev,
- int max_vls)
+ int max_vls, bool externs)
{
char *buf;
int ret, i;
@@ -391,7 +391,7 @@ static int show_available_vars_at(int fd, struct perf_probe_event *pev,
return -EINVAL;
pr_debug("Searching variables at %s\n", buf);
- ret = find_available_vars_at(fd, pev, &vls, max_vls);
+ ret = find_available_vars_at(fd, pev, &vls, max_vls, externs);
if (ret > 0) {
/* Some variables were found */
fprintf(stdout, "Available variables at %s\n", buf);
@@ -421,7 +421,7 @@ static int show_available_vars_at(int fd, struct perf_probe_event *pev,
/* Show available variables on given probe point */
int show_available_vars(struct perf_probe_event *pevs, int npevs,
- int max_vls)
+ int max_vls, bool externs)
{
int i, fd, ret = 0;
@@ -438,7 +438,7 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
setup_pager();
for (i = 0; i < npevs && ret >= 0; i++)
- ret = show_available_vars_at(fd, &pevs[i], max_vls);
+ ret = show_available_vars_at(fd, &pevs[i], max_vls, externs);
close(fd);
return ret;
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 83130f6..c74b1fd 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -122,7 +122,7 @@ extern int del_perf_probe_events(struct strlist *dellist);
extern int show_perf_probe_events(void);
extern int show_line_range(struct line_range *lr);
extern int show_available_vars(struct perf_probe_event *pevs, int npevs,
- int max_probe_points);
+ int max_probe_points, bool externs);
/* Maximum index number of event-name postfix */
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 986027f..a274fd0 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -1312,12 +1312,13 @@ static int collect_variables_cb(Dwarf_Die *die_mem, void *data)
af->pf.fb_ops, NULL);
if (ret == 0) {
ret = die_get_varname(die_mem, buf, MAX_VAR_LEN);
+ pr_debug2("Add new var: %s\n", buf);
if (ret > 0)
strlist__add(vl->vars, buf);
}
}
- if (dwarf_haspc(die_mem, af->pf.addr))
+ if (af->child && dwarf_haspc(die_mem, af->pf.addr))
return DIE_FIND_CB_CONTINUE;
else
return DIE_FIND_CB_SIBLING;
@@ -1329,8 +1330,8 @@ static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf)
struct available_var_finder *af =
container_of(pf, struct available_var_finder, pf);
struct variable_list *vl;
- Dwarf_Die die_mem;
- int ret;
+ Dwarf_Die die_mem, *scopes = NULL;
+ int ret, nscopes;
/* Check number of tevs */
if (af->nvls == af->max_vls) {
@@ -1351,8 +1352,22 @@ static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf)
vl->vars = strlist__new(true, NULL);
if (vl->vars == NULL)
return -ENOMEM;
+ af->child = true;
die_find_child(sp_die, collect_variables_cb, (void *)af, &die_mem);
+ /* Find external variables */
+ if (!af->externs)
+ goto out;
+ /* Don't need to search child DIE for externs. */
+ af->child = false;
+ nscopes = dwarf_getscopes_die(sp_die, &scopes);
+ while (nscopes-- > 1)
+ die_find_child(&scopes[nscopes], collect_variables_cb,
+ (void *)af, &die_mem);
+ if (scopes)
+ free(scopes);
+
+out:
if (strlist__empty(vl->vars)) {
strlist__delete(vl->vars);
vl->vars = NULL;
@@ -1363,11 +1378,12 @@ static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf)
/* Find available variables at given probe point */
int find_available_vars_at(int fd, struct perf_probe_event *pev,
- struct variable_list **vls, int max_vls)
+ struct variable_list **vls, int max_vls,
+ bool externs)
{
struct available_var_finder af = {
.pf = {.pev = pev, .callback = add_available_vars},
- .max_vls = max_vls};
+ .max_vls = max_vls, .externs = externs};
int ret;
/* Allocate result vls array */
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
index baffd25..516912a 100644
--- a/tools/perf/util/probe-finder.h
+++ b/tools/perf/util/probe-finder.h
@@ -30,7 +30,8 @@ extern int find_line_range(int fd, struct line_range *lr);
/* Find available variables */
extern int find_available_vars_at(int fd, struct perf_probe_event *pev,
- struct variable_list **vls, int max_points);
+ struct variable_list **vls, int max_points,
+ bool externs);
#include <dwarf.h>
#include <libdw.h>
@@ -70,6 +71,8 @@ struct available_var_finder {
struct variable_list *vls; /* Found variable lists */
int nvls; /* Number of variable lists */
int max_vls; /* Max no. of variable lists */
+ bool externs; /* Find external vars too */
+ bool child; /* Search child scopes */
};
struct line_finder {
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH -tip 7/7] perf probe: Add basic module support
2010-10-21 10:12 [PATCH -tip 0/7] Perf probe update (--vars/--module) Masami Hiramatsu
` (5 preceding siblings ...)
2010-10-21 10:13 ` [PATCH -tip 6/7] perf probe: Show accessible global variables Masami Hiramatsu
@ 2010-10-21 10:13 ` Masami Hiramatsu
2010-10-23 19:42 ` [tip:perf/urgent] " tip-bot for Masami Hiramatsu
6 siblings, 1 reply; 18+ messages in thread
From: Masami Hiramatsu @ 2010-10-21 10:13 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo, Ingo Molnar
Cc: Steven Rostedt, Srikar Dronamraju, linux-kernel, 2nddept-manager,
Masami Hiramatsu, Peter Zijlstra, Paul Mackerras, Ingo Molnar,
Arnaldo Carvalho de Melo, Frederic Weisbecker, linux-kernel
Add basic module probe support on perf probe. This introduces
"--module <MODNAME>" option to perf probe for putting probes
and showing lines and variables in the given module.
Currently, this supports only probing on running modules.
Supporting off-line module probing is the next step.
e.g.)
[show lines]
# ./perf probe --module drm -L drm_vblank_info
<drm_vblank_info:0>
0 int drm_vblank_info(struct seq_file *m, void *data)
1 {
struct drm_info_node *node = (struct drm_info_node *) m->private
3 struct drm_device *dev = node->minor->dev;
...
[show vars]
# ./perf probe --module drm -V drm_vblank_info:3
Available variables at drm_vblank_info:3
@<drm_vblank_info+20>
(unknown_type) data
struct drm_info_node* node
struct seq_file* m
[put a probe]
# ./perf probe --module drm drm_vblank_info:3 node m
Add new event:
probe:drm_vblank_info (on drm_vblank_info:3 with node m)
You can now use it on all perf tools, such as:
perf record -e probe:drm_vblank_info -aR sleep 1
[list probes]
# ./perf probe -l
probe:drm_vblank_info (on drm_vblank_info:3@drivers/gpu/drm/drm_info.c with ...
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: linux-kernel@vger.kernel.org
---
tools/perf/Documentation/perf-probe.txt | 9 ++
tools/perf/builtin-probe.c | 11 ++
tools/perf/util/map.h | 10 ++
tools/perf/util/probe-event.c | 123 +++++++++++++++++----------
tools/perf/util/probe-event.h | 10 ++
tools/perf/util/probe-finder.c | 142 ++++++++++++++++++++++++++++---
tools/perf/util/probe-finder.h | 3 -
7 files changed, 239 insertions(+), 69 deletions(-)
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index 148c178..62de1b7 100644
--- a/tools/perf/Documentation/perf-probe.txt
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -16,9 +16,9 @@ or
or
'perf probe' --list
or
-'perf probe' --line='FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]'
+'perf probe' [options] --line='FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]'
or
-'perf probe' [--externs] --vars='PROBEPOINT'
+'perf probe' [options] --vars='PROBEPOINT'
DESCRIPTION
-----------
@@ -33,6 +33,11 @@ OPTIONS
--vmlinux=PATH::
Specify vmlinux path which has debuginfo (Dwarf binary).
+-m::
+--module=MODNAME::
+ Specify module name in which perf-probe searches probe points
+ or lines.
+
-s::
--source=PATH::
Specify path to kernel source.
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index bdf60cf..2e000c0 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -57,6 +57,7 @@ static struct {
struct perf_probe_event events[MAX_PROBES];
struct strlist *dellist;
struct line_range line_range;
+ const char *target_module;
int max_probe_points;
} params;
@@ -162,8 +163,8 @@ static const char * const probe_usage[] = {
"perf probe [<options>] --del '[GROUP:]EVENT' ...",
"perf probe --list",
#ifdef DWARF_SUPPORT
- "perf probe --line 'LINEDESC'",
- "perf probe [--externs] --vars 'PROBEPOINT'",
+ "perf probe [<options>] --line 'LINEDESC'",
+ "perf probe [<options>] --vars 'PROBEPOINT'",
#endif
NULL
};
@@ -214,6 +215,8 @@ static const struct option options[] = {
"file", "vmlinux pathname"),
OPT_STRING('s', "source", &symbol_conf.source_prefix,
"directory", "path to kernel source"),
+ OPT_STRING('m', "module", ¶ms.target_module,
+ "modname", "target module name"),
#endif
OPT__DRY_RUN(&probe_event_dry_run),
OPT_INTEGER('\0', "max-probes", ¶ms.max_probe_points,
@@ -278,7 +281,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
usage_with_options(probe_usage, options);
}
- ret = show_line_range(¶ms.line_range);
+ ret = show_line_range(¶ms.line_range, params.target_module);
if (ret < 0)
pr_err(" Error: Failed to show lines. (%d)\n", ret);
return ret;
@@ -291,6 +294,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
}
ret = show_available_vars(params.events, params.nevents,
params.max_probe_points,
+ params.target_module,
params.show_ext_vars);
if (ret < 0)
pr_err(" Error: Failed to show vars. (%d)\n", ret);
@@ -310,6 +314,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
if (params.nevents) {
ret = add_perf_probe_events(params.events, params.nevents,
params.max_probe_points,
+ params.target_module,
params.force_add);
if (ret < 0) {
pr_err(" Error: Failed to add events. (%d)\n", ret);
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index 7857579..b397c03 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -215,6 +215,16 @@ struct symbol *map_groups__find_function_by_name(struct map_groups *self,
return map_groups__find_symbol_by_name(self, MAP__FUNCTION, name, mapp, filter);
}
+static inline
+struct symbol *machine__find_kernel_function_by_name(struct machine *self,
+ const char *name,
+ struct map **mapp,
+ symbol_filter_t filter)
+{
+ return map_groups__find_function_by_name(&self->kmaps, name, mapp,
+ filter);
+}
+
int map_groups__fixup_overlappings(struct map_groups *self, struct map *map,
int verbose, FILE *fp);
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 1b462cc..79ef201 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -74,10 +74,9 @@ static int e_snprintf(char *str, size_t size, const char *format, ...)
static char *synthesize_perf_probe_point(struct perf_probe_point *pp);
static struct machine machine;
-/* Initialize symbol maps and path of vmlinux */
+/* Initialize symbol maps and path of vmlinux/modules */
static int init_vmlinux(void)
{
- struct dso *kernel;
int ret;
symbol_conf.sort_by_name = true;
@@ -91,33 +90,61 @@ static int init_vmlinux(void)
goto out;
}
- ret = machine__init(&machine, "/", 0);
+ ret = machine__init(&machine, "", HOST_KERNEL_ID);
if (ret < 0)
goto out;
- kernel = dso__new_kernel(symbol_conf.vmlinux_name);
- if (kernel == NULL)
- die("Failed to create kernel dso.");
-
- ret = __machine__create_kernel_maps(&machine, kernel);
- if (ret < 0)
- pr_debug("Failed to create kernel maps.\n");
-
+ if (machine__create_kernel_maps(&machine) < 0) {
+ pr_debug("machine__create_kernel_maps ");
+ goto out;
+ }
out:
if (ret < 0)
pr_warning("Failed to init vmlinux path.\n");
return ret;
}
+static struct symbol *__find_kernel_function_by_name(const char *name,
+ struct map **mapp)
+{
+ return machine__find_kernel_function_by_name(&machine, name, mapp,
+ NULL);
+}
+
+const char *kernel_get_module_path(const char *module)
+{
+ struct dso *dso;
+
+ if (module) {
+ list_for_each_entry(dso, &machine.kernel_dsos, node) {
+ if (strncmp(dso->short_name + 1, module,
+ dso->short_name_len - 2) == 0)
+ goto found;
+ }
+ pr_debug("Failed to find module %s.\n", module);
+ return NULL;
+ } else {
+ dso = machine.vmlinux_maps[MAP__FUNCTION]->dso;
+ if (dso__load_vmlinux_path(dso,
+ machine.vmlinux_maps[MAP__FUNCTION], NULL) < 0) {
+ pr_debug("Failed to load kernel map.\n");
+ return NULL;
+ }
+ }
+found:
+ return dso->long_name;
+}
+
#ifdef DWARF_SUPPORT
-static int open_vmlinux(void)
+static int open_vmlinux(const char *module)
{
- if (map__load(machine.vmlinux_maps[MAP__FUNCTION], NULL) < 0) {
- pr_debug("Failed to load kernel map.\n");
- return -EINVAL;
+ const char *path = kernel_get_module_path(module);
+ if (!path) {
+ pr_err("Failed to find path of %s module", module ?: "kernel");
+ return -ENOENT;
}
- pr_debug("Try to open %s\n", machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name);
- return open(machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name, O_RDONLY);
+ pr_debug("Try to open %s\n", path);
+ return open(path, O_RDONLY);
}
/*
@@ -125,20 +152,19 @@ static int open_vmlinux(void)
* Currently only handles kprobes.
*/
static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
- struct perf_probe_point *pp)
+ struct perf_probe_point *pp)
{
struct symbol *sym;
- int fd, ret = -ENOENT;
+ struct map *map;
+ u64 addr;
+ int ret = -ENOENT;
- sym = map__find_symbol_by_name(machine.vmlinux_maps[MAP__FUNCTION],
- tp->symbol, NULL);
+ sym = __find_kernel_function_by_name(tp->symbol, &map);
if (sym) {
- fd = open_vmlinux();
- if (fd >= 0) {
- ret = find_perf_probe_point(fd,
- sym->start + tp->offset, pp);
- close(fd);
- }
+ addr = map->unmap_ip(map, sym->start + tp->offset);
+ pr_debug("try to find %s+%ld@%llx\n", tp->symbol,
+ tp->offset, addr);
+ ret = find_perf_probe_point((unsigned long)addr, pp);
}
if (ret <= 0) {
pr_debug("Failed to find corresponding probes from "
@@ -156,12 +182,12 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
/* Try to find perf_probe_event with debuginfo */
static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
struct probe_trace_event **tevs,
- int max_tevs)
+ int max_tevs, const char *module)
{
bool need_dwarf = perf_probe_event_need_dwarf(pev);
int fd, ntevs;
- fd = open_vmlinux();
+ fd = open_vmlinux(module);
if (fd < 0) {
if (need_dwarf) {
pr_warning("Failed to open debuginfo file.\n");
@@ -300,7 +326,7 @@ error:
* Show line-range always requires debuginfo to find source file and
* line number.
*/
-int show_line_range(struct line_range *lr)
+int show_line_range(struct line_range *lr, const char *module)
{
int l = 1;
struct line_node *ln;
@@ -313,7 +339,7 @@ int show_line_range(struct line_range *lr)
if (ret < 0)
return ret;
- fd = open_vmlinux();
+ fd = open_vmlinux(module);
if (fd < 0) {
pr_warning("Failed to open debuginfo file.\n");
return fd;
@@ -421,7 +447,7 @@ static int show_available_vars_at(int fd, struct perf_probe_event *pev,
/* Show available variables on given probe point */
int show_available_vars(struct perf_probe_event *pevs, int npevs,
- int max_vls, bool externs)
+ int max_vls, const char *module, bool externs)
{
int i, fd, ret = 0;
@@ -429,7 +455,7 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
if (ret < 0)
return ret;
- fd = open_vmlinux();
+ fd = open_vmlinux(module);
if (fd < 0) {
pr_warning("Failed to open debuginfo file.\n");
return fd;
@@ -447,8 +473,15 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
#else /* !DWARF_SUPPORT */
static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
- struct perf_probe_point *pp)
+ struct perf_probe_point *pp)
{
+ struct symbol *sym;
+
+ sym = __find_kernel_function_by_name(tp->symbol, NULL);
+ if (!sym) {
+ pr_err("Failed to find symbol %s in kernel.\n", tp->symbol);
+ return -ENOENT;
+ }
pp->function = strdup(tp->symbol);
if (pp->function == NULL)
return -ENOMEM;
@@ -460,7 +493,7 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
struct probe_trace_event **tevs __unused,
- int max_tevs __unused)
+ int max_tevs __unused, const char *mod __unused)
{
if (perf_probe_event_need_dwarf(pev)) {
pr_warning("Debuginfo-analysis is not supported.\n");
@@ -469,14 +502,15 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
return 0;
}
-int show_line_range(struct line_range *lr __unused)
+int show_line_range(struct line_range *lr __unused, const char *module __unused)
{
pr_warning("Debuginfo-analysis is not supported.\n");
return -ENOSYS;
}
int show_available_vars(struct perf_probe_event *pevs __unused,
- int npevs __unused, int max_probe_points __unused)
+ int npevs __unused, int max_vls __unused,
+ const char *module __unused, bool externs __unused)
{
pr_warning("Debuginfo-analysis is not supported.\n");
return -ENOSYS;
@@ -1159,7 +1193,7 @@ error:
}
static int convert_to_perf_probe_event(struct probe_trace_event *tev,
- struct perf_probe_event *pev)
+ struct perf_probe_event *pev)
{
char buf[64] = "";
int i, ret;
@@ -1588,14 +1622,14 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
static int convert_to_probe_trace_events(struct perf_probe_event *pev,
struct probe_trace_event **tevs,
- int max_tevs)
+ int max_tevs, const char *module)
{
struct symbol *sym;
int ret = 0, i;
struct probe_trace_event *tev;
/* Convert perf_probe_event with debuginfo */
- ret = try_to_find_probe_trace_events(pev, tevs, max_tevs);
+ ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, module);
if (ret != 0)
return ret;
@@ -1644,8 +1678,7 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
}
/* Currently just checking function name from symbol map */
- sym = map__find_symbol_by_name(machine.vmlinux_maps[MAP__FUNCTION],
- tev->point.symbol, NULL);
+ sym = __find_kernel_function_by_name(tev->point.symbol, NULL);
if (!sym) {
pr_warning("Kernel symbol \'%s\' not found.\n",
tev->point.symbol);
@@ -1668,7 +1701,7 @@ struct __event_package {
};
int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
- int max_tevs, bool force_add)
+ int max_tevs, const char *module, bool force_add)
{
int i, j, ret;
struct __event_package *pkgs;
@@ -1689,7 +1722,9 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
pkgs[i].pev = &pevs[i];
/* Convert with or without debuginfo */
ret = convert_to_probe_trace_events(pkgs[i].pev,
- &pkgs[i].tevs, max_tevs);
+ &pkgs[i].tevs,
+ max_tevs,
+ module);
if (ret < 0)
goto end;
pkgs[i].ntevs = ret;
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index c74b1fd..5accbed 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -115,14 +115,18 @@ extern void clear_perf_probe_event(struct perf_probe_event *pev);
/* Command string to line-range */
extern int parse_line_range_desc(const char *cmd, struct line_range *lr);
+/* Internal use: Return kernel/module path */
+extern const char *kernel_get_module_path(const char *module);
extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
- int max_probe_points, bool force_add);
+ int max_probe_points, const char *module,
+ bool force_add);
extern int del_perf_probe_events(struct strlist *dellist);
extern int show_perf_probe_events(void);
-extern int show_line_range(struct line_range *lr);
+extern int show_line_range(struct line_range *lr, const char *module);
extern int show_available_vars(struct perf_probe_event *pevs, int npevs,
- int max_probe_points, bool externs);
+ int max_probe_points, const char *module,
+ bool externs);
/* Maximum index number of event-name postfix */
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index a274fd0..c20bd52 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -116,6 +116,101 @@ static void line_list__free(struct list_head *head)
}
}
+/* Dwarf FL wrappers */
+
+static int __linux_kernel_find_elf(Dwfl_Module *mod,
+ void **userdata,
+ const char *module_name,
+ Dwarf_Addr base,
+ char **file_name, Elf **elfp)
+{
+ int fd;
+ const char *path = kernel_get_module_path(module_name);
+
+ if (path) {
+ fd = open(path, O_RDONLY);
+ if (fd >= 0) {
+ *file_name = strdup(path);
+ return fd;
+ }
+ }
+ /* If failed, try to call standard method */
+ return dwfl_linux_kernel_find_elf(mod, userdata, module_name, base,
+ file_name, elfp);
+}
+
+static char *debuginfo_path; /* Currently dummy */
+
+static const Dwfl_Callbacks offline_callbacks = {
+ .find_debuginfo = dwfl_standard_find_debuginfo,
+ .debuginfo_path = &debuginfo_path,
+
+ .section_address = dwfl_offline_section_address,
+
+ /* We use this table for core files too. */
+ .find_elf = dwfl_build_id_find_elf,
+};
+
+static const Dwfl_Callbacks kernel_callbacks = {
+ .find_debuginfo = dwfl_standard_find_debuginfo,
+ .debuginfo_path = &debuginfo_path,
+
+ .find_elf = __linux_kernel_find_elf,
+ .section_address = dwfl_linux_kernel_module_section_address,
+};
+
+/* Get a Dwarf from offline image */
+static Dwarf *dwfl_init_offline_dwarf(int fd, Dwfl **dwflp, Dwarf_Addr *bias)
+{
+ Dwfl_Module *mod;
+ Dwarf *dbg = NULL;
+
+ if (!dwflp)
+ return NULL;
+
+ *dwflp = dwfl_begin(&offline_callbacks);
+ if (!*dwflp)
+ return NULL;
+
+ mod = dwfl_report_offline(*dwflp, "", "", fd);
+ if (!mod)
+ goto error;
+
+ dbg = dwfl_module_getdwarf(mod, bias);
+ if (!dbg) {
+error:
+ dwfl_end(*dwflp);
+ *dwflp = NULL;
+ }
+ return dbg;
+}
+
+/* Get a Dwarf from live kernel image */
+static Dwarf *dwfl_init_live_kernel_dwarf(Dwarf_Addr addr, Dwfl **dwflp,
+ Dwarf_Addr *bias)
+{
+ Dwarf *dbg;
+
+ if (!dwflp)
+ return NULL;
+
+ *dwflp = dwfl_begin(&kernel_callbacks);
+ if (!*dwflp)
+ return NULL;
+
+ /* Load the kernel dwarves: Don't care the result here */
+ dwfl_linux_kernel_report_kernel(*dwflp);
+ dwfl_linux_kernel_report_modules(*dwflp);
+
+ dbg = dwfl_addrdwarf(*dwflp, addr, bias);
+ /* Here, check whether we could get a real dwarf */
+ if (!dbg) {
+ dwfl_end(*dwflp);
+ *dwflp = NULL;
+ }
+ return dbg;
+}
+
/* Dwarf wrappers */
/* Find the realpath of the target file. */
@@ -1177,10 +1272,12 @@ static int find_probes(int fd, struct probe_finder *pf)
Dwarf_Off off, noff;
size_t cuhl;
Dwarf_Die *diep;
- Dwarf *dbg;
+ Dwarf *dbg = NULL;
+ Dwfl *dwfl;
+ Dwarf_Addr bias; /* Currently ignored */
int ret = 0;
- dbg = dwarf_begin(fd, DWARF_C_READ);
+ dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias);
if (!dbg) {
pr_warning("No dwarf info found in the vmlinux - "
"please rebuild with CONFIG_DEBUG_INFO=y.\n");
@@ -1221,7 +1318,8 @@ static int find_probes(int fd, struct probe_finder *pf)
off = noff;
}
line_list__free(&pf->lcache);
- dwarf_end(dbg);
+ if (dwfl)
+ dwfl_end(dwfl);
return ret;
}
@@ -1412,23 +1510,31 @@ int find_available_vars_at(int fd, struct perf_probe_event *pev,
}
/* Reverse search */
-int find_perf_probe_point(int fd, unsigned long addr,
- struct perf_probe_point *ppt)
+int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt)
{
Dwarf_Die cudie, spdie, indie;
- Dwarf *dbg;
+ Dwarf *dbg = NULL;
+ Dwfl *dwfl = NULL;
Dwarf_Line *line;
- Dwarf_Addr laddr, eaddr;
+ Dwarf_Addr laddr, eaddr, bias = 0;
const char *tmp;
int lineno, ret = 0;
bool found = false;
- dbg = dwarf_begin(fd, DWARF_C_READ);
- if (!dbg)
- return -EBADF;
+ /* Open the live linux kernel */
+ dbg = dwfl_init_live_kernel_dwarf(addr, &dwfl, &bias);
+ if (!dbg) {
+ pr_warning("No dwarf info found in the vmlinux - "
+ "please rebuild with CONFIG_DEBUG_INFO=y.\n");
+ ret = -EINVAL;
+ goto end;
+ }
+ /* Adjust address with bias */
+ addr += bias;
/* Find cu die */
- if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr, &cudie)) {
+ if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr - bias, &cudie)) {
+ pr_warning("No CU DIE is found at %lx\n", addr);
ret = -EINVAL;
goto end;
}
@@ -1491,7 +1597,8 @@ found:
}
end:
- dwarf_end(dbg);
+ if (dwfl)
+ dwfl_end(dwfl);
if (ret >= 0)
ret = found ? 1 : 0;
return ret;
@@ -1624,6 +1731,8 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
struct line_finder *lf = param->data;
struct line_range *lr = lf->lr;
+ pr_debug("find (%lx) %s\n", dwarf_dieoffset(sp_die),
+ dwarf_diename(sp_die));
if (dwarf_tag(sp_die) == DW_TAG_subprogram &&
die_compare_name(sp_die, lr->function)) {
lf->fname = dwarf_decl_file(sp_die);
@@ -1667,10 +1776,12 @@ int find_line_range(int fd, struct line_range *lr)
Dwarf_Off off = 0, noff;
size_t cuhl;
Dwarf_Die *diep;
- Dwarf *dbg;
+ Dwarf *dbg = NULL;
+ Dwfl *dwfl;
+ Dwarf_Addr bias; /* Currently ignored */
const char *comp_dir;
- dbg = dwarf_begin(fd, DWARF_C_READ);
+ dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias);
if (!dbg) {
pr_warning("No dwarf info found in the vmlinux - "
"please rebuild with CONFIG_DEBUG_INFO=y.\n");
@@ -1716,8 +1827,7 @@ int find_line_range(int fd, struct line_range *lr)
}
pr_debug("path: %s\n", lr->path);
- dwarf_end(dbg);
-
+ dwfl_end(dwfl);
return (ret < 0) ? ret : lf.found;
}
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
index 516912a..bba69d4 100644
--- a/tools/perf/util/probe-finder.h
+++ b/tools/perf/util/probe-finder.h
@@ -22,7 +22,7 @@ extern int find_probe_trace_events(int fd, struct perf_probe_event *pev,
int max_tevs);
/* Find a perf_probe_point from debuginfo */
-extern int find_perf_probe_point(int fd, unsigned long addr,
+extern int find_perf_probe_point(unsigned long addr,
struct perf_probe_point *ppt);
/* Find a line range */
@@ -35,6 +35,7 @@ extern int find_available_vars_at(int fd, struct perf_probe_event *pev,
#include <dwarf.h>
#include <libdw.h>
+#include <libdwfl.h>
#include <version.h>
struct probe_finder {
^ permalink raw reply related [flat|nested] 18+ messages in thread* [tip:perf/urgent] perf probe: Add basic module support
2010-10-21 10:13 ` [PATCH -tip 7/7] perf probe: Add basic module support Masami Hiramatsu
@ 2010-10-23 19:42 ` tip-bot for Masami Hiramatsu
0 siblings, 0 replies; 18+ messages in thread
From: tip-bot for Masami Hiramatsu @ 2010-10-23 19:42 UTC (permalink / raw)
To: linux-tip-commits
Cc: acme, linux-kernel, paulus, hpa, mingo, a.p.zijlstra,
masami.hiramatsu.pt, fweisbec, tglx, mingo
Commit-ID: 469b9b88488e89114bb3e9ac5ee7906b7b96123f
Gitweb: http://git.kernel.org/tip/469b9b88488e89114bb3e9ac5ee7906b7b96123f
Author: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
AuthorDate: Thu, 21 Oct 2010 19:13:41 +0900
Committer: Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Thu, 21 Oct 2010 16:11:44 -0200
perf probe: Add basic module support
Add basic module probe support on perf probe. This introduces "--module
<MODNAME>" option to perf probe for putting probes and showing lines and
variables in the given module.
Currently, this supports only probing on running modules. Supporting off-line
module probing is the next step.
e.g.)
[show lines]
# ./perf probe --module drm -L drm_vblank_info
<drm_vblank_info:0>
0 int drm_vblank_info(struct seq_file *m, void *data)
1 {
struct drm_info_node *node = (struct drm_info_node *) m->private
3 struct drm_device *dev = node->minor->dev;
...
[show vars]
# ./perf probe --module drm -V drm_vblank_info:3
Available variables at drm_vblank_info:3
@<drm_vblank_info+20>
(unknown_type) data
struct drm_info_node* node
struct seq_file* m
[put a probe]
# ./perf probe --module drm drm_vblank_info:3 node m
Add new event:
probe:drm_vblank_info (on drm_vblank_info:3 with node m)
You can now use it on all perf tools, such as:
perf record -e probe:drm_vblank_info -aR sleep 1
[list probes]
# ./perf probe -l
probe:drm_vblank_info (on drm_vblank_info:3@drivers/gpu/drm/drm_info.c with ...
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
LKML-Reference: <20101021101341.3542.71638.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
tools/perf/Documentation/perf-probe.txt | 9 ++-
tools/perf/builtin-probe.c | 11 ++-
tools/perf/util/map.h | 10 ++
tools/perf/util/probe-event.c | 123 +++++++++++++++++----------
tools/perf/util/probe-event.h | 10 ++-
tools/perf/util/probe-finder.c | 142 +++++++++++++++++++++++++++----
tools/perf/util/probe-finder.h | 3 +-
7 files changed, 239 insertions(+), 69 deletions(-)
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index 148c178..62de1b7 100644
--- a/tools/perf/Documentation/perf-probe.txt
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -16,9 +16,9 @@ or
or
'perf probe' --list
or
-'perf probe' --line='FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]'
+'perf probe' [options] --line='FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]'
or
-'perf probe' [--externs] --vars='PROBEPOINT'
+'perf probe' [options] --vars='PROBEPOINT'
DESCRIPTION
-----------
@@ -33,6 +33,11 @@ OPTIONS
--vmlinux=PATH::
Specify vmlinux path which has debuginfo (Dwarf binary).
+-m::
+--module=MODNAME::
+ Specify module name in which perf-probe searches probe points
+ or lines.
+
-s::
--source=PATH::
Specify path to kernel source.
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index bdf60cf..2e000c0 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -57,6 +57,7 @@ static struct {
struct perf_probe_event events[MAX_PROBES];
struct strlist *dellist;
struct line_range line_range;
+ const char *target_module;
int max_probe_points;
} params;
@@ -162,8 +163,8 @@ static const char * const probe_usage[] = {
"perf probe [<options>] --del '[GROUP:]EVENT' ...",
"perf probe --list",
#ifdef DWARF_SUPPORT
- "perf probe --line 'LINEDESC'",
- "perf probe [--externs] --vars 'PROBEPOINT'",
+ "perf probe [<options>] --line 'LINEDESC'",
+ "perf probe [<options>] --vars 'PROBEPOINT'",
#endif
NULL
};
@@ -214,6 +215,8 @@ static const struct option options[] = {
"file", "vmlinux pathname"),
OPT_STRING('s', "source", &symbol_conf.source_prefix,
"directory", "path to kernel source"),
+ OPT_STRING('m', "module", ¶ms.target_module,
+ "modname", "target module name"),
#endif
OPT__DRY_RUN(&probe_event_dry_run),
OPT_INTEGER('\0', "max-probes", ¶ms.max_probe_points,
@@ -278,7 +281,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
usage_with_options(probe_usage, options);
}
- ret = show_line_range(¶ms.line_range);
+ ret = show_line_range(¶ms.line_range, params.target_module);
if (ret < 0)
pr_err(" Error: Failed to show lines. (%d)\n", ret);
return ret;
@@ -291,6 +294,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
}
ret = show_available_vars(params.events, params.nevents,
params.max_probe_points,
+ params.target_module,
params.show_ext_vars);
if (ret < 0)
pr_err(" Error: Failed to show vars. (%d)\n", ret);
@@ -310,6 +314,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
if (params.nevents) {
ret = add_perf_probe_events(params.events, params.nevents,
params.max_probe_points,
+ params.target_module,
params.force_add);
if (ret < 0) {
pr_err(" Error: Failed to add events. (%d)\n", ret);
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index 7857579..b397c03 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -215,6 +215,16 @@ struct symbol *map_groups__find_function_by_name(struct map_groups *self,
return map_groups__find_symbol_by_name(self, MAP__FUNCTION, name, mapp, filter);
}
+static inline
+struct symbol *machine__find_kernel_function_by_name(struct machine *self,
+ const char *name,
+ struct map **mapp,
+ symbol_filter_t filter)
+{
+ return map_groups__find_function_by_name(&self->kmaps, name, mapp,
+ filter);
+}
+
int map_groups__fixup_overlappings(struct map_groups *self, struct map *map,
int verbose, FILE *fp);
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 82b0976..3b6a529 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -74,10 +74,9 @@ static int e_snprintf(char *str, size_t size, const char *format, ...)
static char *synthesize_perf_probe_point(struct perf_probe_point *pp);
static struct machine machine;
-/* Initialize symbol maps and path of vmlinux */
+/* Initialize symbol maps and path of vmlinux/modules */
static int init_vmlinux(void)
{
- struct dso *kernel;
int ret;
symbol_conf.sort_by_name = true;
@@ -91,33 +90,61 @@ static int init_vmlinux(void)
goto out;
}
- ret = machine__init(&machine, "/", 0);
+ ret = machine__init(&machine, "", HOST_KERNEL_ID);
if (ret < 0)
goto out;
- kernel = dso__new_kernel(symbol_conf.vmlinux_name);
- if (kernel == NULL)
- die("Failed to create kernel dso.");
-
- ret = __machine__create_kernel_maps(&machine, kernel);
- if (ret < 0)
- pr_debug("Failed to create kernel maps.\n");
-
+ if (machine__create_kernel_maps(&machine) < 0) {
+ pr_debug("machine__create_kernel_maps ");
+ goto out;
+ }
out:
if (ret < 0)
pr_warning("Failed to init vmlinux path.\n");
return ret;
}
+static struct symbol *__find_kernel_function_by_name(const char *name,
+ struct map **mapp)
+{
+ return machine__find_kernel_function_by_name(&machine, name, mapp,
+ NULL);
+}
+
+const char *kernel_get_module_path(const char *module)
+{
+ struct dso *dso;
+
+ if (module) {
+ list_for_each_entry(dso, &machine.kernel_dsos, node) {
+ if (strncmp(dso->short_name + 1, module,
+ dso->short_name_len - 2) == 0)
+ goto found;
+ }
+ pr_debug("Failed to find module %s.\n", module);
+ return NULL;
+ } else {
+ dso = machine.vmlinux_maps[MAP__FUNCTION]->dso;
+ if (dso__load_vmlinux_path(dso,
+ machine.vmlinux_maps[MAP__FUNCTION], NULL) < 0) {
+ pr_debug("Failed to load kernel map.\n");
+ return NULL;
+ }
+ }
+found:
+ return dso->long_name;
+}
+
#ifdef DWARF_SUPPORT
-static int open_vmlinux(void)
+static int open_vmlinux(const char *module)
{
- if (map__load(machine.vmlinux_maps[MAP__FUNCTION], NULL) < 0) {
- pr_debug("Failed to load kernel map.\n");
- return -EINVAL;
+ const char *path = kernel_get_module_path(module);
+ if (!path) {
+ pr_err("Failed to find path of %s module", module ?: "kernel");
+ return -ENOENT;
}
- pr_debug("Try to open %s\n", machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name);
- return open(machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name, O_RDONLY);
+ pr_debug("Try to open %s\n", path);
+ return open(path, O_RDONLY);
}
/*
@@ -125,20 +152,19 @@ static int open_vmlinux(void)
* Currently only handles kprobes.
*/
static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
- struct perf_probe_point *pp)
+ struct perf_probe_point *pp)
{
struct symbol *sym;
- int fd, ret = -ENOENT;
+ struct map *map;
+ u64 addr;
+ int ret = -ENOENT;
- sym = map__find_symbol_by_name(machine.vmlinux_maps[MAP__FUNCTION],
- tp->symbol, NULL);
+ sym = __find_kernel_function_by_name(tp->symbol, &map);
if (sym) {
- fd = open_vmlinux();
- if (fd >= 0) {
- ret = find_perf_probe_point(fd,
- sym->start + tp->offset, pp);
- close(fd);
- }
+ addr = map->unmap_ip(map, sym->start + tp->offset);
+ pr_debug("try to find %s+%ld@%llx\n", tp->symbol,
+ tp->offset, addr);
+ ret = find_perf_probe_point((unsigned long)addr, pp);
}
if (ret <= 0) {
pr_debug("Failed to find corresponding probes from "
@@ -156,12 +182,12 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
/* Try to find perf_probe_event with debuginfo */
static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
struct probe_trace_event **tevs,
- int max_tevs)
+ int max_tevs, const char *module)
{
bool need_dwarf = perf_probe_event_need_dwarf(pev);
int fd, ntevs;
- fd = open_vmlinux();
+ fd = open_vmlinux(module);
if (fd < 0) {
if (need_dwarf) {
pr_warning("Failed to open debuginfo file.\n");
@@ -300,7 +326,7 @@ error:
* Show line-range always requires debuginfo to find source file and
* line number.
*/
-int show_line_range(struct line_range *lr)
+int show_line_range(struct line_range *lr, const char *module)
{
int l = 1;
struct line_node *ln;
@@ -313,7 +339,7 @@ int show_line_range(struct line_range *lr)
if (ret < 0)
return ret;
- fd = open_vmlinux();
+ fd = open_vmlinux(module);
if (fd < 0) {
pr_warning("Failed to open debuginfo file.\n");
return fd;
@@ -421,7 +447,7 @@ static int show_available_vars_at(int fd, struct perf_probe_event *pev,
/* Show available variables on given probe point */
int show_available_vars(struct perf_probe_event *pevs, int npevs,
- int max_vls, bool externs)
+ int max_vls, const char *module, bool externs)
{
int i, fd, ret = 0;
@@ -429,7 +455,7 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
if (ret < 0)
return ret;
- fd = open_vmlinux();
+ fd = open_vmlinux(module);
if (fd < 0) {
pr_warning("Failed to open debuginfo file.\n");
return fd;
@@ -447,8 +473,15 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
#else /* !DWARF_SUPPORT */
static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
- struct perf_probe_point *pp)
+ struct perf_probe_point *pp)
{
+ struct symbol *sym;
+
+ sym = __find_kernel_function_by_name(tp->symbol, NULL);
+ if (!sym) {
+ pr_err("Failed to find symbol %s in kernel.\n", tp->symbol);
+ return -ENOENT;
+ }
pp->function = strdup(tp->symbol);
if (pp->function == NULL)
return -ENOMEM;
@@ -460,7 +493,7 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
struct probe_trace_event **tevs __unused,
- int max_tevs __unused)
+ int max_tevs __unused, const char *mod __unused)
{
if (perf_probe_event_need_dwarf(pev)) {
pr_warning("Debuginfo-analysis is not supported.\n");
@@ -469,14 +502,15 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
return 0;
}
-int show_line_range(struct line_range *lr __unused)
+int show_line_range(struct line_range *lr __unused, const char *module __unused)
{
pr_warning("Debuginfo-analysis is not supported.\n");
return -ENOSYS;
}
int show_available_vars(struct perf_probe_event *pevs __unused,
- int npevs __unused, int max_probe_points __unused)
+ int npevs __unused, int max_vls __unused,
+ const char *module __unused, bool externs __unused)
{
pr_warning("Debuginfo-analysis is not supported.\n");
return -ENOSYS;
@@ -1159,7 +1193,7 @@ error:
}
static int convert_to_perf_probe_event(struct probe_trace_event *tev,
- struct perf_probe_event *pev)
+ struct perf_probe_event *pev)
{
char buf[64] = "";
int i, ret;
@@ -1588,14 +1622,14 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
static int convert_to_probe_trace_events(struct perf_probe_event *pev,
struct probe_trace_event **tevs,
- int max_tevs)
+ int max_tevs, const char *module)
{
struct symbol *sym;
int ret = 0, i;
struct probe_trace_event *tev;
/* Convert perf_probe_event with debuginfo */
- ret = try_to_find_probe_trace_events(pev, tevs, max_tevs);
+ ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, module);
if (ret != 0)
return ret;
@@ -1644,8 +1678,7 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
}
/* Currently just checking function name from symbol map */
- sym = map__find_symbol_by_name(machine.vmlinux_maps[MAP__FUNCTION],
- tev->point.symbol, NULL);
+ sym = __find_kernel_function_by_name(tev->point.symbol, NULL);
if (!sym) {
pr_warning("Kernel symbol \'%s\' not found.\n",
tev->point.symbol);
@@ -1668,7 +1701,7 @@ struct __event_package {
};
int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
- int max_tevs, bool force_add)
+ int max_tevs, const char *module, bool force_add)
{
int i, j, ret;
struct __event_package *pkgs;
@@ -1689,7 +1722,9 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
pkgs[i].pev = &pevs[i];
/* Convert with or without debuginfo */
ret = convert_to_probe_trace_events(pkgs[i].pev,
- &pkgs[i].tevs, max_tevs);
+ &pkgs[i].tevs,
+ max_tevs,
+ module);
if (ret < 0)
goto end;
pkgs[i].ntevs = ret;
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index c74b1fd..5accbed 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -115,14 +115,18 @@ extern void clear_perf_probe_event(struct perf_probe_event *pev);
/* Command string to line-range */
extern int parse_line_range_desc(const char *cmd, struct line_range *lr);
+/* Internal use: Return kernel/module path */
+extern const char *kernel_get_module_path(const char *module);
extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
- int max_probe_points, bool force_add);
+ int max_probe_points, const char *module,
+ bool force_add);
extern int del_perf_probe_events(struct strlist *dellist);
extern int show_perf_probe_events(void);
-extern int show_line_range(struct line_range *lr);
+extern int show_line_range(struct line_range *lr, const char *module);
extern int show_available_vars(struct perf_probe_event *pevs, int npevs,
- int max_probe_points, bool externs);
+ int max_probe_points, const char *module,
+ bool externs);
/* Maximum index number of event-name postfix */
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index a274fd0..c20bd52 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -116,6 +116,101 @@ static void line_list__free(struct list_head *head)
}
}
+/* Dwarf FL wrappers */
+
+static int __linux_kernel_find_elf(Dwfl_Module *mod,
+ void **userdata,
+ const char *module_name,
+ Dwarf_Addr base,
+ char **file_name, Elf **elfp)
+{
+ int fd;
+ const char *path = kernel_get_module_path(module_name);
+
+ if (path) {
+ fd = open(path, O_RDONLY);
+ if (fd >= 0) {
+ *file_name = strdup(path);
+ return fd;
+ }
+ }
+ /* If failed, try to call standard method */
+ return dwfl_linux_kernel_find_elf(mod, userdata, module_name, base,
+ file_name, elfp);
+}
+
+static char *debuginfo_path; /* Currently dummy */
+
+static const Dwfl_Callbacks offline_callbacks = {
+ .find_debuginfo = dwfl_standard_find_debuginfo,
+ .debuginfo_path = &debuginfo_path,
+
+ .section_address = dwfl_offline_section_address,
+
+ /* We use this table for core files too. */
+ .find_elf = dwfl_build_id_find_elf,
+};
+
+static const Dwfl_Callbacks kernel_callbacks = {
+ .find_debuginfo = dwfl_standard_find_debuginfo,
+ .debuginfo_path = &debuginfo_path,
+
+ .find_elf = __linux_kernel_find_elf,
+ .section_address = dwfl_linux_kernel_module_section_address,
+};
+
+/* Get a Dwarf from offline image */
+static Dwarf *dwfl_init_offline_dwarf(int fd, Dwfl **dwflp, Dwarf_Addr *bias)
+{
+ Dwfl_Module *mod;
+ Dwarf *dbg = NULL;
+
+ if (!dwflp)
+ return NULL;
+
+ *dwflp = dwfl_begin(&offline_callbacks);
+ if (!*dwflp)
+ return NULL;
+
+ mod = dwfl_report_offline(*dwflp, "", "", fd);
+ if (!mod)
+ goto error;
+
+ dbg = dwfl_module_getdwarf(mod, bias);
+ if (!dbg) {
+error:
+ dwfl_end(*dwflp);
+ *dwflp = NULL;
+ }
+ return dbg;
+}
+
+/* Get a Dwarf from live kernel image */
+static Dwarf *dwfl_init_live_kernel_dwarf(Dwarf_Addr addr, Dwfl **dwflp,
+ Dwarf_Addr *bias)
+{
+ Dwarf *dbg;
+
+ if (!dwflp)
+ return NULL;
+
+ *dwflp = dwfl_begin(&kernel_callbacks);
+ if (!*dwflp)
+ return NULL;
+
+ /* Load the kernel dwarves: Don't care the result here */
+ dwfl_linux_kernel_report_kernel(*dwflp);
+ dwfl_linux_kernel_report_modules(*dwflp);
+
+ dbg = dwfl_addrdwarf(*dwflp, addr, bias);
+ /* Here, check whether we could get a real dwarf */
+ if (!dbg) {
+ dwfl_end(*dwflp);
+ *dwflp = NULL;
+ }
+ return dbg;
+}
+
/* Dwarf wrappers */
/* Find the realpath of the target file. */
@@ -1177,10 +1272,12 @@ static int find_probes(int fd, struct probe_finder *pf)
Dwarf_Off off, noff;
size_t cuhl;
Dwarf_Die *diep;
- Dwarf *dbg;
+ Dwarf *dbg = NULL;
+ Dwfl *dwfl;
+ Dwarf_Addr bias; /* Currently ignored */
int ret = 0;
- dbg = dwarf_begin(fd, DWARF_C_READ);
+ dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias);
if (!dbg) {
pr_warning("No dwarf info found in the vmlinux - "
"please rebuild with CONFIG_DEBUG_INFO=y.\n");
@@ -1221,7 +1318,8 @@ static int find_probes(int fd, struct probe_finder *pf)
off = noff;
}
line_list__free(&pf->lcache);
- dwarf_end(dbg);
+ if (dwfl)
+ dwfl_end(dwfl);
return ret;
}
@@ -1412,23 +1510,31 @@ int find_available_vars_at(int fd, struct perf_probe_event *pev,
}
/* Reverse search */
-int find_perf_probe_point(int fd, unsigned long addr,
- struct perf_probe_point *ppt)
+int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt)
{
Dwarf_Die cudie, spdie, indie;
- Dwarf *dbg;
+ Dwarf *dbg = NULL;
+ Dwfl *dwfl = NULL;
Dwarf_Line *line;
- Dwarf_Addr laddr, eaddr;
+ Dwarf_Addr laddr, eaddr, bias = 0;
const char *tmp;
int lineno, ret = 0;
bool found = false;
- dbg = dwarf_begin(fd, DWARF_C_READ);
- if (!dbg)
- return -EBADF;
+ /* Open the live linux kernel */
+ dbg = dwfl_init_live_kernel_dwarf(addr, &dwfl, &bias);
+ if (!dbg) {
+ pr_warning("No dwarf info found in the vmlinux - "
+ "please rebuild with CONFIG_DEBUG_INFO=y.\n");
+ ret = -EINVAL;
+ goto end;
+ }
+ /* Adjust address with bias */
+ addr += bias;
/* Find cu die */
- if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr, &cudie)) {
+ if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr - bias, &cudie)) {
+ pr_warning("No CU DIE is found at %lx\n", addr);
ret = -EINVAL;
goto end;
}
@@ -1491,7 +1597,8 @@ found:
}
end:
- dwarf_end(dbg);
+ if (dwfl)
+ dwfl_end(dwfl);
if (ret >= 0)
ret = found ? 1 : 0;
return ret;
@@ -1624,6 +1731,8 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
struct line_finder *lf = param->data;
struct line_range *lr = lf->lr;
+ pr_debug("find (%lx) %s\n", dwarf_dieoffset(sp_die),
+ dwarf_diename(sp_die));
if (dwarf_tag(sp_die) == DW_TAG_subprogram &&
die_compare_name(sp_die, lr->function)) {
lf->fname = dwarf_decl_file(sp_die);
@@ -1667,10 +1776,12 @@ int find_line_range(int fd, struct line_range *lr)
Dwarf_Off off = 0, noff;
size_t cuhl;
Dwarf_Die *diep;
- Dwarf *dbg;
+ Dwarf *dbg = NULL;
+ Dwfl *dwfl;
+ Dwarf_Addr bias; /* Currently ignored */
const char *comp_dir;
- dbg = dwarf_begin(fd, DWARF_C_READ);
+ dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias);
if (!dbg) {
pr_warning("No dwarf info found in the vmlinux - "
"please rebuild with CONFIG_DEBUG_INFO=y.\n");
@@ -1716,8 +1827,7 @@ int find_line_range(int fd, struct line_range *lr)
}
pr_debug("path: %s\n", lr->path);
- dwarf_end(dbg);
-
+ dwfl_end(dwfl);
return (ret < 0) ? ret : lf.found;
}
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
index 516912a..bba69d4 100644
--- a/tools/perf/util/probe-finder.h
+++ b/tools/perf/util/probe-finder.h
@@ -22,7 +22,7 @@ extern int find_probe_trace_events(int fd, struct perf_probe_event *pev,
int max_tevs);
/* Find a perf_probe_point from debuginfo */
-extern int find_perf_probe_point(int fd, unsigned long addr,
+extern int find_perf_probe_point(unsigned long addr,
struct perf_probe_point *ppt);
/* Find a line range */
@@ -35,6 +35,7 @@ extern int find_available_vars_at(int fd, struct perf_probe_event *pev,
#include <dwarf.h>
#include <libdw.h>
+#include <libdwfl.h>
#include <version.h>
struct probe_finder {
^ permalink raw reply related [flat|nested] 18+ messages in thread