2010-04-12 17:10:26

by Masami Hiramatsu

[permalink] [raw]
Subject: [PATCH -tip v3 00/10] perf-probe updates - data-structure support improvements, etc.

Hi Ingo and Arnaldo,

Here are several updates of perf-probe. This series improves
data structure accessing. In this version, I added 'removing x*()'
patches.

- Set the name of argument which traces a data structure member
as the last member of the data structure reference (e.g. f_mode
of file->f_mode). This allows us to use perf-trace for tracin
data-structure members.

- Add the basic type support. This allows us to fetch the memory
with specified bitwidth. Usually, data-structure members are
packed on the memory, this means if we want to read a member
from memory, we have to access it with type casting.
kprobe-tracer now support tracing argument with basic types
(u8,u16,u32,u64,s8,s16,s32,s64), and perf-probe decodes the type
information of the members.

- Support canonical frame address, which is used for refering
frame-base on the kernel built without CONFIG_FRAME_POINTER.

- Remove almost all die()s and xzalloc/xstr*dup from util code,
according to acme's advice. Thanks!

TODOs (possible features):
- Support string/dynamic arrays (*var, var[N..M])
- Support tracing static variables (non global)
- Support array element (var[N])
- Support dynamic array-indexing (var[var2])
- Support force type-casting ((type)var)
- Show what deta-structure member is assigned to each argument.
- Better support for probes on modules
- More debugger like enhancements(%next, --disasm, etc.)

Thank you,

---

Masami Hiramatsu (10):
perf probe: Remove xstrdup()/xstrndup() from util/probe-{event,finder}.c
perf probe: Remove xzalloc() from util/probe-{event,finder}.c
perf probe: Remove die() from probe-event code
perf probe: Remove die() from probe-finder code
perf probe: Support DW_OP_call_frame_cfa in debuginfo
perf probe: Support basic type casting
perf probe: Query basic types from debuginfo
tracing/kprobes: Support basic types on dynamic events
perf probe: Use the last field name as the argument name
perf probe: Support argument name


Documentation/trace/kprobetrace.txt | 4
kernel/trace/trace.h | 16 -
kernel/trace/trace_kprobe.c | 535 ++++++++++++-------
tools/perf/Documentation/perf-probe.txt | 11
tools/perf/builtin-probe.c | 74 ++-
tools/perf/util/probe-event.c | 855 ++++++++++++++++++++++---------
tools/perf/util/probe-event.h | 27 +
tools/perf/util/probe-finder.c | 647 ++++++++++++++++-------
tools/perf/util/probe-finder.h | 1
9 files changed, 1469 insertions(+), 701 deletions(-)

--
Masami Hiramatsu
e-mail: [email protected]


2010-04-12 17:10:30

by Masami Hiramatsu

[permalink] [raw]
Subject: [PATCH -tip v3 01/10] perf probe: Support argument name

Set given names to event arguments. The syntax is same as kprobe-tracer,
you can add 'NAME=' right before each argument.

e.g.
./perf probe vfs_read foo=file

then, 'foo' is set to the argument name as below.

./perf probe -l
probe:vfs_read (on [email protected]/fs/read_write.c with foo)


Signed-off-by: Masami Hiramatsu <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Mike Galbraith <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
---

tools/perf/Documentation/perf-probe.txt | 10 ++++++++-
tools/perf/builtin-probe.c | 4 ++--
tools/perf/util/probe-event.c | 34 ++++++++++++++++++++++---------
tools/perf/util/probe-event.h | 1 +
tools/perf/util/probe-finder.c | 27 +++++++++++++++----------
5 files changed, 52 insertions(+), 24 deletions(-)

diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index bb671b3..e36ed4d 100644
--- a/tools/perf/Documentation/perf-probe.txt
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -79,7 +79,15 @@ Probe points are defined by following syntax.
'EVENT' specifies the name of new event, if omitted, it will be set the name of the probed function. Currently, event group name is set as 'probe'.
'FUNC' specifies a probed function name, and it may have one of the following options; '+OFFS' is the offset from function entry address in bytes, ':RLN' is the relative-line number from function entry line, and '%return' means that it probes function return. And ';PTN' means lazy matching pattern (see LAZY MATCHING). Note that ';PTN' must be the end of the probe point definition. In addition, '@SRC' specifies a source file which has that function.
It is also possible to specify a probe point by the source line number or lazy matching by using 'SRC:ALN' or 'SRC;PTN' syntax, where 'SRC' is the source file path, ':ALN' is the line number and ';PTN' is the lazy matching pattern.
-'ARG' specifies the arguments of this probe point. You can use the name of local variable, or kprobe-tracer argument format (e.g. $retval, %ax, etc).
+'ARG' specifies the arguments of this probe point, (see PROBE ARGUMENT).
+
+PROBE ARGUMENT
+--------------
+Each probe argument follows below syntax.
+
+ [NAME=]LOCALVAR|$retval|%REG|@SYMBOL
+
+'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), or kprobe-tracer argument format (e.g. $retval, %ax, etc).

LINE SYNTAX
-----------
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index b3ba25a..dafaf76 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -142,9 +142,9 @@ static const struct option options[] = {
OPT_CALLBACK('a', "add", NULL,
#ifdef DWARF_SUPPORT
"[EVENT=]FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT"
- " [ARG ...]",
+ " [[NAME=]ARG ...]",
#else
- "[EVENT=]FUNC[+OFF|%return] [ARG ...]",
+ "[EVENT=]FUNC[+OFF|%return] [[NAME=]ARG ...]",
#endif
"probe point definition, where\n"
"\t\tGROUP:\tGroup name (optional)\n"
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 3fc0be7..ab6f53d 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -437,22 +437,28 @@ static void parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
/* Parse perf-probe event argument */
static void parse_perf_probe_arg(const char *str, struct perf_probe_arg *arg)
{
- const char *tmp;
+ char *tmp;
struct perf_probe_arg_field **fieldp;

pr_debug("parsing arg: %s into ", str);

+ tmp = strchr(str, '=');
+ if (tmp) {
+ arg->name = xstrndup(str, tmp - str);
+ str = tmp + 1;
+ }
+
tmp = strpbrk(str, "-.");
if (!is_c_varname(str) || !tmp) {
/* A variable, register, symbol or special value */
- arg->name = xstrdup(str);
- pr_debug("%s\n", arg->name);
+ arg->var = xstrdup(str);
+ pr_debug("%s\n", arg->var);
return;
}

/* Structure fields */
- arg->name = xstrndup(str, tmp - str);
- pr_debug("%s, ", arg->name);
+ arg->var = xstrndup(str, tmp - str);
+ pr_debug("%s, ", arg->var);
fieldp = &arg->field;

do {
@@ -497,7 +503,7 @@ void parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev)
pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs);
for (i = 0; i < pev->nargs; i++) {
parse_perf_probe_arg(argv[i + 1], &pev->args[i]);
- if (is_c_varname(pev->args[i].name) && pev->point.retprobe)
+ if (is_c_varname(pev->args[i].var) && pev->point.retprobe)
semantic_error("You can't specify local variable for"
" kretprobe");
}
@@ -514,7 +520,7 @@ bool perf_probe_event_need_dwarf(struct perf_probe_event *pev)
return true;

for (i = 0; i < pev->nargs; i++)
- if (is_c_varname(pev->args[i].name))
+ if (is_c_varname(pev->args[i].var))
return true;

return false;
@@ -575,7 +581,10 @@ int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len)
int ret;
char *tmp = buf;

- ret = e_snprintf(tmp, len, "%s", pa->name);
+ if (pa->name && pa->var)
+ ret = e_snprintf(tmp, len, "%s=%s", pa->name, pa->var);
+ else
+ ret = e_snprintf(tmp, len, "%s", pa->name ? pa->name : pa->var);
if (ret <= 0)
goto error;
tmp += ret;
@@ -803,6 +812,8 @@ void clear_perf_probe_event(struct perf_probe_event *pev)
for (i = 0; i < pev->nargs; i++) {
if (pev->args[i].name)
free(pev->args[i].name);
+ if (pev->args[i].var)
+ free(pev->args[i].var);
field = pev->args[i].field;
while (field) {
next = field->next;
@@ -1117,8 +1128,11 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev,
if (tev->nargs) {
tev->args = xzalloc(sizeof(struct kprobe_trace_arg)
* tev->nargs);
- for (i = 0; i < tev->nargs; i++)
- tev->args[i].value = xstrdup(pev->args[i].name);
+ for (i = 0; i < tev->nargs; i++) {
+ if (pev->args[i].name)
+ tev->args[i].name = xstrdup(pev->args[i].name);
+ tev->args[i].value = xstrdup(pev->args[i].var);
+ }
}

/* Currently just checking function name from symbol map */
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index cd308b0..a73ede6 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -55,6 +55,7 @@ struct perf_probe_arg_field {
/* Perf probe probing argument */
struct perf_probe_arg {
char *name; /* Argument name */
+ char *var; /* Variable name */
struct perf_probe_arg_field *field; /* Structure fields */
};

diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index a851377..105e95c 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -484,35 +484,40 @@ static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
convert_location(expr, pf);

if (pf->pvar->field)
- convert_variable_fields(vr_die, pf->pvar->name,
+ convert_variable_fields(vr_die, pf->pvar->var,
pf->pvar->field, &pf->tvar->ref);
/* *expr will be cached in libdw. Don't free it. */
return ;
error:
/* TODO: Support const_value */
die("Failed to find the location of %s at this address.\n"
- " Perhaps, it has been optimized out.", pf->pvar->name);
+ " Perhaps, it has been optimized out.", pf->pvar->var);
}

/* Find a variable in a subprogram die */
static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
{
Dwarf_Die vr_die;
- char buf[128];
+ char buf[32];

- /* TODO: Support struct members and arrays */
- if (!is_c_varname(pf->pvar->name)) {
+ /* TODO: Support arrays */
+ if (pf->pvar->name)
+ pf->tvar->name = xstrdup(pf->pvar->name);
+ else {
+ synthesize_perf_probe_arg(pf->pvar, buf, 32);
+ pf->tvar->name = xstrdup(buf);
+ }
+
+ if (!is_c_varname(pf->pvar->var)) {
/* Copy raw parameters */
- pf->tvar->value = xstrdup(pf->pvar->name);
+ pf->tvar->value = xstrdup(pf->pvar->var);
} else {
- synthesize_perf_probe_arg(pf->pvar, buf, 128);
- pf->tvar->name = xstrdup(buf);
pr_debug("Searching '%s' variable in context.\n",
- pf->pvar->name);
+ pf->pvar->var);
/* Search child die for local variables and parameters. */
- if (!die_find_variable(sp_die, pf->pvar->name, &vr_die))
+ if (!die_find_variable(sp_die, pf->pvar->var, &vr_die))
die("Failed to find '%s' in this function.",
- pf->pvar->name);
+ pf->pvar->var);
convert_variable(&vr_die, pf);
}
}


--
Masami Hiramatsu
e-mail: [email protected]

2010-04-12 17:10:36

by Masami Hiramatsu

[permalink] [raw]
Subject: [PATCH -tip v3 02/10] perf probe: Use the last field name as the argument name

Set the last field name to the argument name when the argument
is refering a data-structure member.

e.g.
./perf probe --add 'vfs_read file->f_mode'
Add new event:
probe:vfs_read (on vfs_read with f_mode=file->f_mode)

This probe records file->f_mode, but the argument name becomes "f_mode".

This enables perf-trace command to parse trace event format correctly.

Signed-off-by: Masami Hiramatsu <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Mike Galbraith <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
---

tools/perf/Documentation/perf-probe.txt | 2 +-
tools/perf/util/probe-event.c | 4 ++++
2 files changed, 5 insertions(+), 1 deletions(-)

diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index e36ed4d..441324f 100644
--- a/tools/perf/Documentation/perf-probe.txt
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -87,7 +87,7 @@ Each probe argument follows below syntax.

[NAME=]LOCALVAR|$retval|%REG|@SYMBOL

-'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), or kprobe-tracer argument format (e.g. $retval, %ax, etc).
+'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), or kprobe-tracer argument format (e.g. $retval, %ax, etc). Note that the name of this argument will be set as the last member name if you specify a local data structure member (e.g. field2 for 'var->field1.field2'.)

LINE SYNTAX
-----------
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index ab6f53d..19de8b7 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -481,6 +481,10 @@ static void parse_perf_probe_arg(const char *str, struct perf_probe_arg *arg)
} while (tmp);
(*fieldp)->name = xstrdup(str);
pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref);
+
+ /* If no name is specified, set the last field name */
+ if (!arg->name)
+ arg->name = xstrdup((*fieldp)->name);
}

/* Parse perf-probe event command */


--
Masami Hiramatsu
e-mail: [email protected]

2010-04-12 17:10:44

by Masami Hiramatsu

[permalink] [raw]
Subject: [PATCH -tip v3 03/10] tracing/kprobes: Support basic types on dynamic events

Support basic types of integer (u8, u16, u32, u64, s8, s16, s32, s64) in
kprobe tracer. With this patch, users can specify above basic types on
each arguments after ':'. If omitted, the argument type is set as
unsigned long (u32 or u64, arch-dependent).

e.g.
echo 'p account_system_time+0 hardirq_offset=%si:s32' > kprobe_events

adds a probe recording hardirq_offset in signed-32bits value on the
entry of account_system_time.

Signed-off-by: Masami Hiramatsu <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Steven Rostedt <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Mike Galbraith <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
---

Documentation/trace/kprobetrace.txt | 4
kernel/trace/trace.h | 16 -
kernel/trace/trace_kprobe.c | 535 ++++++++++++++++++++++-------------
3 files changed, 334 insertions(+), 221 deletions(-)

diff --git a/Documentation/trace/kprobetrace.txt b/Documentation/trace/kprobetrace.txt
index a9100b2..ec94748 100644
--- a/Documentation/trace/kprobetrace.txt
+++ b/Documentation/trace/kprobetrace.txt
@@ -40,7 +40,9 @@ Synopsis of kprobe_events
$stack : Fetch stack address.
$retval : Fetch return value.(*)
+|-offs(FETCHARG) : Fetch memory at FETCHARG +|- offs address.(**)
- NAME=FETCHARG: Set NAME as the argument name of FETCHARG.
+ NAME=FETCHARG : Set NAME as the argument name of FETCHARG.
+ FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types
+ (u8/u16/u32/u64/s8/s16/s32/s64) are supported.

(*) only for return probe.
(**) this is useful for fetching a field of data structures.
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index bec2c97..3ebdb6b 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -102,29 +102,17 @@ struct syscall_trace_exit {
long ret;
};

-struct kprobe_trace_entry {
+struct kprobe_trace_entry_head {
struct trace_entry ent;
unsigned long ip;
- int nargs;
- unsigned long args[];
};

-#define SIZEOF_KPROBE_TRACE_ENTRY(n) \
- (offsetof(struct kprobe_trace_entry, args) + \
- (sizeof(unsigned long) * (n)))
-
-struct kretprobe_trace_entry {
+struct kretprobe_trace_entry_head {
struct trace_entry ent;
unsigned long func;
unsigned long ret_ip;
- int nargs;
- unsigned long args[];
};

-#define SIZEOF_KRETPROBE_TRACE_ENTRY(n) \
- (offsetof(struct kretprobe_trace_entry, args) + \
- (sizeof(unsigned long) * (n)))
-
/*
* trace_flag_type is an enumeration that holds different
* states when a trace occurs. These are:
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index 1251e36..a751432 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -29,6 +29,8 @@
#include <linux/ctype.h>
#include <linux/ptrace.h>
#include <linux/perf_event.h>
+#include <linux/stringify.h>
+#include <asm/bitsperlong.h>

#include "trace.h"
#include "trace_output.h"
@@ -40,7 +42,6 @@

/* Reserved field names */
#define FIELD_STRING_IP "__probe_ip"
-#define FIELD_STRING_NARGS "__probe_nargs"
#define FIELD_STRING_RETIP "__probe_ret_ip"
#define FIELD_STRING_FUNC "__probe_func"

@@ -52,56 +53,102 @@ const char *reserved_field_names[] = {
"common_tgid",
"common_lock_depth",
FIELD_STRING_IP,
- FIELD_STRING_NARGS,
FIELD_STRING_RETIP,
FIELD_STRING_FUNC,
};

-struct fetch_func {
- unsigned long (*func)(struct pt_regs *, void *);
+/* Printing function type */
+typedef int (*print_type_func_t)(struct trace_seq *, const char *, void *);
+#define PRINT_TYPE_FUNC_NAME(type) print_type_##type
+#define PRINT_TYPE_FMT_NAME(type) print_type_format_##type
+
+/* Printing in basic type function template */
+#define DEFINE_BASIC_PRINT_TYPE_FUNC(type, fmt, cast) \
+static __kprobes int PRINT_TYPE_FUNC_NAME(type)(struct trace_seq *s, \
+ const char *name, void *data)\
+{ \
+ return trace_seq_printf(s, " %s=" fmt, name, (cast)*(type *)data);\
+} \
+static const char PRINT_TYPE_FMT_NAME(type)[] = fmt;
+
+DEFINE_BASIC_PRINT_TYPE_FUNC(u8, "%x", unsigned int)
+DEFINE_BASIC_PRINT_TYPE_FUNC(u16, "%x", unsigned int)
+DEFINE_BASIC_PRINT_TYPE_FUNC(u32, "%lx", unsigned long)
+DEFINE_BASIC_PRINT_TYPE_FUNC(u64, "%llx", unsigned long long)
+DEFINE_BASIC_PRINT_TYPE_FUNC(s8, "%d", int)
+DEFINE_BASIC_PRINT_TYPE_FUNC(s16, "%d", int)
+DEFINE_BASIC_PRINT_TYPE_FUNC(s32, "%ld", long)
+DEFINE_BASIC_PRINT_TYPE_FUNC(s64, "%lld", long long)
+
+/* Data fetch function type */
+typedef void (*fetch_func_t)(struct pt_regs *, void *, void *);
+
+struct fetch_param {
+ fetch_func_t fn;
void *data;
};

-static __kprobes unsigned long call_fetch(struct fetch_func *f,
- struct pt_regs *regs)
+static __kprobes void call_fetch(struct fetch_param *fprm,
+ struct pt_regs *regs, void *dest)
{
- return f->func(regs, f->data);
+ return fprm->fn(regs, fprm->data, dest);
}

-/* fetch handlers */
-static __kprobes unsigned long fetch_register(struct pt_regs *regs,
- void *offset)
-{
- return regs_get_register(regs, (unsigned int)((unsigned long)offset));
+#define FETCH_FUNC_NAME(kind, type) fetch_##kind##_##type
+/*
+ * Define macro for basic types - we don't need to define s* types, because
+ * we have to care only about bitwidth at recording time.
+ */
+#define DEFINE_BASIC_FETCH_FUNCS(kind) \
+DEFINE_FETCH_##kind(u8) \
+DEFINE_FETCH_##kind(u16) \
+DEFINE_FETCH_##kind(u32) \
+DEFINE_FETCH_##kind(u64)
+
+#define CHECK_BASIC_FETCH_FUNCS(kind, fn) \
+ ((FETCH_FUNC_NAME(kind, u8) == fn) || \
+ (FETCH_FUNC_NAME(kind, u16) == fn) || \
+ (FETCH_FUNC_NAME(kind, u32) == fn) || \
+ (FETCH_FUNC_NAME(kind, u64) == fn))
+
+/* Data fetch function templates */
+#define DEFINE_FETCH_reg(type) \
+static __kprobes void FETCH_FUNC_NAME(reg, type)(struct pt_regs *regs, \
+ void *offset, void *dest) \
+{ \
+ *(type *)dest = (type)regs_get_register(regs, \
+ (unsigned int)((unsigned long)offset)); \
}
-
-static __kprobes unsigned long fetch_stack(struct pt_regs *regs,
- void *num)
-{
- return regs_get_kernel_stack_nth(regs,
- (unsigned int)((unsigned long)num));
+DEFINE_BASIC_FETCH_FUNCS(reg)
+
+#define DEFINE_FETCH_stack(type) \
+static __kprobes void FETCH_FUNC_NAME(stack, type)(struct pt_regs *regs,\
+ void *offset, void *dest) \
+{ \
+ *(type *)dest = (type)regs_get_kernel_stack_nth(regs, \
+ (unsigned int)((unsigned long)offset)); \
}
+DEFINE_BASIC_FETCH_FUNCS(stack)

-static __kprobes unsigned long fetch_memory(struct pt_regs *regs, void *addr)
-{
- unsigned long retval;
-
- if (probe_kernel_address(addr, retval))
- return 0;
- return retval;
+#define DEFINE_FETCH_retval(type) \
+static __kprobes void FETCH_FUNC_NAME(retval, type)(struct pt_regs *regs,\
+ void *dummy, void *dest) \
+{ \
+ *(type *)dest = (type)regs_return_value(regs); \
}
-
-static __kprobes unsigned long fetch_retvalue(struct pt_regs *regs,
- void *dummy)
-{
- return regs_return_value(regs);
-}
-
-static __kprobes unsigned long fetch_stack_address(struct pt_regs *regs,
- void *dummy)
-{
- return kernel_stack_pointer(regs);
+DEFINE_BASIC_FETCH_FUNCS(retval)
+
+#define DEFINE_FETCH_memory(type) \
+static __kprobes void FETCH_FUNC_NAME(memory, type)(struct pt_regs *regs,\
+ void *addr, void *dest) \
+{ \
+ type retval; \
+ if (probe_kernel_address(addr, retval)) \
+ *(type *)dest = 0; \
+ else \
+ *(type *)dest = retval; \
}
+DEFINE_BASIC_FETCH_FUNCS(memory)

/* Memory fetching by symbol */
struct symbol_cache {
@@ -145,51 +192,126 @@ static struct symbol_cache *alloc_symbol_cache(const char *sym, long offset)
return sc;
}

-static __kprobes unsigned long fetch_symbol(struct pt_regs *regs, void *data)
-{
- struct symbol_cache *sc = data;
-
- if (sc->addr)
- return fetch_memory(regs, (void *)sc->addr);
- else
- return 0;
+#define DEFINE_FETCH_symbol(type) \
+static __kprobes void FETCH_FUNC_NAME(symbol, type)(struct pt_regs *regs,\
+ void *data, void *dest) \
+{ \
+ struct symbol_cache *sc = data; \
+ if (sc->addr) \
+ fetch_memory_##type(regs, (void *)sc->addr, dest); \
+ else \
+ *(type *)dest = 0; \
}
+DEFINE_BASIC_FETCH_FUNCS(symbol)

-/* Special indirect memory access interface */
-struct indirect_fetch_data {
- struct fetch_func orig;
+/* Dereference memory access function */
+struct deref_fetch_param {
+ struct fetch_param orig;
long offset;
};

-static __kprobes unsigned long fetch_indirect(struct pt_regs *regs, void *data)
-{
- struct indirect_fetch_data *ind = data;
- unsigned long addr;
-
- addr = call_fetch(&ind->orig, regs);
- if (addr) {
- addr += ind->offset;
- return fetch_memory(regs, (void *)addr);
- } else
- return 0;
+#define DEFINE_FETCH_deref(type) \
+static __kprobes void FETCH_FUNC_NAME(deref, type)(struct pt_regs *regs,\
+ void *data, void *dest) \
+{ \
+ struct deref_fetch_param *dprm = data; \
+ unsigned long addr; \
+ call_fetch(&dprm->orig, regs, &addr); \
+ if (addr) { \
+ addr += dprm->offset; \
+ fetch_memory_##type(regs, (void *)addr, dest); \
+ } else \
+ *(type *)dest = 0; \
}
+DEFINE_BASIC_FETCH_FUNCS(deref)

-static __kprobes void free_indirect_fetch_data(struct indirect_fetch_data *data)
+static __kprobes void free_deref_fetch_param(struct deref_fetch_param *data)
{
- if (data->orig.func == fetch_indirect)
- free_indirect_fetch_data(data->orig.data);
- else if (data->orig.func == fetch_symbol)
+ if (CHECK_BASIC_FETCH_FUNCS(deref, data->orig.fn))
+ free_deref_fetch_param(data->orig.data);
+ else if (CHECK_BASIC_FETCH_FUNCS(symbol, data->orig.fn))
free_symbol_cache(data->orig.data);
kfree(data);
}

+/* Default (unsigned long) fetch type */
+#define __DEFAULT_FETCH_TYPE(t) u##t
+#define _DEFAULT_FETCH_TYPE(t) __DEFAULT_FETCH_TYPE(t)
+#define DEFAULT_FETCH_TYPE _DEFAULT_FETCH_TYPE(BITS_PER_LONG)
+#define DEFAULT_FETCH_TYPE_STR __stringify(DEFAULT_FETCH_TYPE)
+
+#define ASSIGN_FETCH_FUNC(kind, type) \
+ .kind = FETCH_FUNC_NAME(kind, type)
+
+#define ASSIGN_FETCH_TYPE(ptype, ftype, sign) \
+ {.name = #ptype, \
+ .size = sizeof(ftype), \
+ .is_signed = sign, \
+ .print = PRINT_TYPE_FUNC_NAME(ptype), \
+ .fmt = PRINT_TYPE_FMT_NAME(ptype), \
+ASSIGN_FETCH_FUNC(reg, ftype), \
+ASSIGN_FETCH_FUNC(stack, ftype), \
+ASSIGN_FETCH_FUNC(retval, ftype), \
+ASSIGN_FETCH_FUNC(memory, ftype), \
+ASSIGN_FETCH_FUNC(symbol, ftype), \
+ASSIGN_FETCH_FUNC(deref, ftype), \
+ }
+
+/* Fetch type information table */
+static const struct fetch_type {
+ const char *name; /* Name of type */
+ size_t size; /* Byte size of type */
+ int is_signed; /* Signed flag */
+ print_type_func_t print; /* Print functions */
+ const char *fmt; /* Fromat string */
+ /* Fetch functions */
+ fetch_func_t reg;
+ fetch_func_t stack;
+ fetch_func_t retval;
+ fetch_func_t memory;
+ fetch_func_t symbol;
+ fetch_func_t deref;
+} fetch_type_table[] = {
+ ASSIGN_FETCH_TYPE(u8, u8, 0),
+ ASSIGN_FETCH_TYPE(u16, u16, 0),
+ ASSIGN_FETCH_TYPE(u32, u32, 0),
+ ASSIGN_FETCH_TYPE(u64, u64, 0),
+ ASSIGN_FETCH_TYPE(s8, u8, 1),
+ ASSIGN_FETCH_TYPE(s16, u16, 1),
+ ASSIGN_FETCH_TYPE(s32, u32, 1),
+ ASSIGN_FETCH_TYPE(s64, u64, 1),
+};
+
+static const struct fetch_type *find_fetch_type(const char *type)
+{
+ int i;
+
+ if (!type)
+ type = DEFAULT_FETCH_TYPE_STR;
+
+ for (i = 0; i < ARRAY_SIZE(fetch_type_table); i++)
+ if (strcmp(type, fetch_type_table[i].name) == 0)
+ return &fetch_type_table[i];
+ return NULL;
+}
+
+/* Special function : only accept unsigned long */
+static __kprobes void fetch_stack_address(struct pt_regs *regs,
+ void *dummy, void *dest)
+{
+ *(unsigned long *)dest = kernel_stack_pointer(regs);
+}
+
/**
* Kprobe event core functions
*/

struct probe_arg {
- struct fetch_func fetch;
- const char *name;
+ struct fetch_param fetch;
+ unsigned int offset; /* Offset from argument entry */
+ const char *name; /* Name of this argument */
+ const char *comm; /* Command of this argument */
+ const struct fetch_type *type; /* Type of this argument */
};

/* Flags for trace_probe */
@@ -204,6 +326,7 @@ struct trace_probe {
const char *symbol; /* symbol name */
struct ftrace_event_call call;
struct trace_event event;
+ ssize_t size; /* trace entry size */
unsigned int nr_args;
struct probe_arg args[];
};
@@ -212,6 +335,7 @@ struct trace_probe {
(offsetof(struct trace_probe, args) + \
(sizeof(struct probe_arg) * (n)))

+
static __kprobes int probe_is_return(struct trace_probe *tp)
{
return tp->rp.handler != NULL;
@@ -222,49 +346,6 @@ static __kprobes const char *probe_symbol(struct trace_probe *tp)
return tp->symbol ? tp->symbol : "unknown";
}

-static int probe_arg_string(char *buf, size_t n, struct fetch_func *ff)
-{
- int ret = -EINVAL;
-
- if (ff->func == fetch_register) {
- const char *name;
- name = regs_query_register_name((unsigned int)((long)ff->data));
- ret = snprintf(buf, n, "%%%s", name);
- } else if (ff->func == fetch_stack)
- ret = snprintf(buf, n, "$stack%lu", (unsigned long)ff->data);
- else if (ff->func == fetch_memory)
- ret = snprintf(buf, n, "@0x%p", ff->data);
- else if (ff->func == fetch_symbol) {
- struct symbol_cache *sc = ff->data;
- if (sc->offset)
- ret = snprintf(buf, n, "@%s%+ld", sc->symbol,
- sc->offset);
- else
- ret = snprintf(buf, n, "@%s", sc->symbol);
- } else if (ff->func == fetch_retvalue)
- ret = snprintf(buf, n, "$retval");
- else if (ff->func == fetch_stack_address)
- ret = snprintf(buf, n, "$stack");
- else if (ff->func == fetch_indirect) {
- struct indirect_fetch_data *id = ff->data;
- size_t l = 0;
- ret = snprintf(buf, n, "%+ld(", id->offset);
- if (ret >= n)
- goto end;
- l += ret;
- ret = probe_arg_string(buf + l, n - l, &id->orig);
- if (ret < 0)
- goto end;
- l += ret;
- ret = snprintf(buf + l, n - l, ")");
- ret += l;
- }
-end:
- if (ret >= n)
- return -ENOSPC;
- return ret;
-}
-
static int register_probe_event(struct trace_probe *tp);
static void unregister_probe_event(struct trace_probe *tp);

@@ -347,11 +428,12 @@ error:

static void free_probe_arg(struct probe_arg *arg)
{
- if (arg->fetch.func == fetch_symbol)
+ if (CHECK_BASIC_FETCH_FUNCS(deref, arg->fetch.fn))
+ free_deref_fetch_param(arg->fetch.data);
+ else if (CHECK_BASIC_FETCH_FUNCS(symbol, arg->fetch.fn))
free_symbol_cache(arg->fetch.data);
- else if (arg->fetch.func == fetch_indirect)
- free_indirect_fetch_data(arg->fetch.data);
kfree(arg->name);
+ kfree(arg->comm);
}

static void free_trace_probe(struct trace_probe *tp)
@@ -457,28 +539,30 @@ static int split_symbol_offset(char *symbol, unsigned long *offset)
#define PARAM_MAX_ARGS 16
#define PARAM_MAX_STACK (THREAD_SIZE / sizeof(unsigned long))

-static int parse_probe_vars(char *arg, struct fetch_func *ff, int is_return)
+static int parse_probe_vars(char *arg, const struct fetch_type *t,
+ struct fetch_param *f, int is_return)
{
int ret = 0;
unsigned long param;

if (strcmp(arg, "retval") == 0) {
- if (is_return) {
- ff->func = fetch_retvalue;
- ff->data = NULL;
- } else
+ if (is_return)
+ f->fn = t->retval;
+ else
ret = -EINVAL;
} else if (strncmp(arg, "stack", 5) == 0) {
if (arg[5] == '\0') {
- ff->func = fetch_stack_address;
- ff->data = NULL;
+ if (strcmp(t->name, DEFAULT_FETCH_TYPE_STR) == 0)
+ f->fn = fetch_stack_address;
+ else
+ ret = -EINVAL;
} else if (isdigit(arg[5])) {
ret = strict_strtoul(arg + 5, 10, &param);
if (ret || param > PARAM_MAX_STACK)
ret = -EINVAL;
else {
- ff->func = fetch_stack;
- ff->data = (void *)param;
+ f->fn = t->stack;
+ f->data = (void *)param;
}
} else
ret = -EINVAL;
@@ -488,7 +572,8 @@ static int parse_probe_vars(char *arg, struct fetch_func *ff, int is_return)
}

/* Recursive argument parser */
-static int __parse_probe_arg(char *arg, struct fetch_func *ff, int is_return)
+static int __parse_probe_arg(char *arg, const struct fetch_type *t,
+ struct fetch_param *f, int is_return)
{
int ret = 0;
unsigned long param;
@@ -497,13 +582,13 @@ static int __parse_probe_arg(char *arg, struct fetch_func *ff, int is_return)

switch (arg[0]) {
case '$':
- ret = parse_probe_vars(arg + 1, ff, is_return);
+ ret = parse_probe_vars(arg + 1, t, f, is_return);
break;
case '%': /* named register */
ret = regs_query_register_offset(arg + 1);
if (ret >= 0) {
- ff->func = fetch_register;
- ff->data = (void *)(unsigned long)ret;
+ f->fn = t->reg;
+ f->data = (void *)(unsigned long)ret;
ret = 0;
}
break;
@@ -512,26 +597,22 @@ static int __parse_probe_arg(char *arg, struct fetch_func *ff, int is_return)
ret = strict_strtoul(arg + 1, 0, &param);
if (ret)
break;
- ff->func = fetch_memory;
- ff->data = (void *)param;
+ f->fn = t->memory;
+ f->data = (void *)param;
} else {
ret = split_symbol_offset(arg + 1, &offset);
if (ret)
break;
- ff->data = alloc_symbol_cache(arg + 1, offset);
- if (ff->data)
- ff->func = fetch_symbol;
- else
- ret = -EINVAL;
+ f->data = alloc_symbol_cache(arg + 1, offset);
+ if (f->data)
+ f->fn = t->symbol;
}
break;
- case '+': /* indirect memory */
+ case '+': /* deref memory */
case '-':
tmp = strchr(arg, '(');
- if (!tmp) {
- ret = -EINVAL;
+ if (!tmp)
break;
- }
*tmp = '\0';
ret = strict_strtol(arg + 1, 0, &offset);
if (ret)
@@ -541,38 +622,58 @@ static int __parse_probe_arg(char *arg, struct fetch_func *ff, int is_return)
arg = tmp + 1;
tmp = strrchr(arg, ')');
if (tmp) {
- struct indirect_fetch_data *id;
+ struct deref_fetch_param *dprm;
+ const struct fetch_type *t2 = find_fetch_type(NULL);
*tmp = '\0';
- id = kzalloc(sizeof(struct indirect_fetch_data),
- GFP_KERNEL);
- if (!id)
+ dprm = kzalloc(sizeof(struct deref_fetch_param),
+ GFP_KERNEL);
+ if (!dprm)
return -ENOMEM;
- id->offset = offset;
- ret = __parse_probe_arg(arg, &id->orig, is_return);
+ dprm->offset = offset;
+ ret = __parse_probe_arg(arg, t2, &dprm->orig,
+ is_return);
if (ret)
- kfree(id);
+ kfree(dprm);
else {
- ff->func = fetch_indirect;
- ff->data = (void *)id;
+ f->fn = t->deref;
+ f->data = (void *)dprm;
}
- } else
- ret = -EINVAL;
+ }
break;
- default:
- /* TODO: support custom handler */
- ret = -EINVAL;
}
+ if (!ret && !f->fn)
+ ret = -EINVAL;
return ret;
}

/* String length checking wrapper */
-static int parse_probe_arg(char *arg, struct fetch_func *ff, int is_return)
+static int parse_probe_arg(char *arg, struct trace_probe *tp,
+ struct probe_arg *parg, int is_return)
{
+ const char *t;
+
if (strlen(arg) > MAX_ARGSTR_LEN) {
pr_info("Argument is too long.: %s\n", arg);
return -ENOSPC;
}
- return __parse_probe_arg(arg, ff, is_return);
+ parg->comm = kstrdup(arg, GFP_KERNEL);
+ if (!parg->comm) {
+ pr_info("Failed to allocate memory for command '%s'.\n", arg);
+ return -ENOMEM;
+ }
+ t = strchr(parg->comm, ':');
+ if (t) {
+ arg[t - parg->comm] = '\0';
+ t++;
+ }
+ parg->type = find_fetch_type(t);
+ if (!parg->type) {
+ pr_info("Unsupported type: %s\n", t);
+ return -EINVAL;
+ }
+ parg->offset = tp->size;
+ tp->size += parg->type->size;
+ return __parse_probe_arg(arg, parg->type, &parg->fetch, is_return);
}

/* Return 1 if name is reserved or already used by another argument */
@@ -602,15 +703,18 @@ static int create_trace_probe(int argc, char **argv)
* @ADDR : fetch memory at ADDR (ADDR should be in kernel)
* @SYM[+|-offs] : fetch memory at SYM +|- offs (SYM is a data symbol)
* %REG : fetch register REG
- * Indirect memory fetch:
+ * Dereferencing memory fetch:
* +|-offs(ARG) : fetch memory at ARG +|- offs address.
* Alias name of args:
* NAME=FETCHARG : set NAME as alias of FETCHARG.
+ * Type of args:
+ * FETCHARG:TYPE : use TYPE instead of unsigned long.
*/
struct trace_probe *tp;
int i, ret = 0;
int is_return = 0, is_delete = 0;
- char *symbol = NULL, *event = NULL, *arg = NULL, *group = NULL;
+ char *symbol = NULL, *event = NULL, *group = NULL;
+ char *arg, *tmp;
unsigned long offset = 0;
void *addr = NULL;
char buf[MAX_EVENT_NAME_LEN];
@@ -723,13 +827,6 @@ static int create_trace_probe(int argc, char **argv)
else
arg = argv[i];

- if (conflict_field_name(argv[i], tp->args, i)) {
- pr_info("Argument%d name '%s' conflicts with "
- "another field.\n", i, argv[i]);
- ret = -EINVAL;
- goto error;
- }
-
tp->args[i].name = kstrdup(argv[i], GFP_KERNEL);
if (!tp->args[i].name) {
pr_info("Failed to allocate argument%d name '%s'.\n",
@@ -737,9 +834,19 @@ static int create_trace_probe(int argc, char **argv)
ret = -ENOMEM;
goto error;
}
+ tmp = strchr(tp->args[i].name, ':');
+ if (tmp)
+ *tmp = '_'; /* convert : to _ */
+
+ if (conflict_field_name(tp->args[i].name, tp->args, i)) {
+ pr_info("Argument%d name '%s' conflicts with "
+ "another field.\n", i, argv[i]);
+ ret = -EINVAL;
+ goto error;
+ }

/* Parse fetch argument */
- ret = parse_probe_arg(arg, &tp->args[i].fetch, is_return);
+ ret = parse_probe_arg(arg, tp, &tp->args[i], is_return);
if (ret) {
pr_info("Parse error at argument%d. (%d)\n", i, ret);
kfree(tp->args[i].name);
@@ -794,8 +901,7 @@ static void probes_seq_stop(struct seq_file *m, void *v)
static int probes_seq_show(struct seq_file *m, void *v)
{
struct trace_probe *tp = v;
- int i, ret;
- char buf[MAX_ARGSTR_LEN + 1];
+ int i;

seq_printf(m, "%c", probe_is_return(tp) ? 'r' : 'p');
seq_printf(m, ":%s/%s", tp->call.system, tp->call.name);
@@ -807,15 +913,10 @@ static int probes_seq_show(struct seq_file *m, void *v)
else
seq_printf(m, " %s", probe_symbol(tp));

- for (i = 0; i < tp->nr_args; i++) {
- ret = probe_arg_string(buf, MAX_ARGSTR_LEN, &tp->args[i].fetch);
- if (ret < 0) {
- pr_warning("Argument%d decoding error(%d).\n", i, ret);
- return ret;
- }
- seq_printf(m, " %s=%s", tp->args[i].name, buf);
- }
+ for (i = 0; i < tp->nr_args; i++)
+ seq_printf(m, " %s=%s", tp->args[i].name, tp->args[i].comm);
seq_printf(m, "\n");
+
return 0;
}

@@ -945,9 +1046,10 @@ static const struct file_operations kprobe_profile_ops = {
static __kprobes void kprobe_trace_func(struct kprobe *kp, struct pt_regs *regs)
{
struct trace_probe *tp = container_of(kp, struct trace_probe, rp.kp);
- struct kprobe_trace_entry *entry;
+ struct kprobe_trace_entry_head *entry;
struct ring_buffer_event *event;
struct ring_buffer *buffer;
+ u8 *data;
int size, i, pc;
unsigned long irq_flags;
struct ftrace_event_call *call = &tp->call;
@@ -957,7 +1059,7 @@ static __kprobes void kprobe_trace_func(struct kprobe *kp, struct pt_regs *regs)
local_save_flags(irq_flags);
pc = preempt_count();

- size = SIZEOF_KPROBE_TRACE_ENTRY(tp->nr_args);
+ size = sizeof(*entry) + tp->size;

event = trace_current_buffer_lock_reserve(&buffer, call->id, size,
irq_flags, pc);
@@ -965,10 +1067,10 @@ static __kprobes void kprobe_trace_func(struct kprobe *kp, struct pt_regs *regs)
return;

entry = ring_buffer_event_data(event);
- entry->nargs = tp->nr_args;
entry->ip = (unsigned long)kp->addr;
+ data = (u8 *)&entry[1];
for (i = 0; i < tp->nr_args; i++)
- entry->args[i] = call_fetch(&tp->args[i].fetch, regs);
+ call_fetch(&tp->args[i].fetch, regs, data + tp->args[i].offset);

if (!filter_current_check_discard(buffer, call, entry, event))
trace_nowake_buffer_unlock_commit(buffer, event, irq_flags, pc);
@@ -979,9 +1081,10 @@ static __kprobes void kretprobe_trace_func(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct trace_probe *tp = container_of(ri->rp, struct trace_probe, rp);
- struct kretprobe_trace_entry *entry;
+ struct kretprobe_trace_entry_head *entry;
struct ring_buffer_event *event;
struct ring_buffer *buffer;
+ u8 *data;
int size, i, pc;
unsigned long irq_flags;
struct ftrace_event_call *call = &tp->call;
@@ -989,7 +1092,7 @@ static __kprobes void kretprobe_trace_func(struct kretprobe_instance *ri,
local_save_flags(irq_flags);
pc = preempt_count();

- size = SIZEOF_KRETPROBE_TRACE_ENTRY(tp->nr_args);
+ size = sizeof(*entry) + tp->size;

event = trace_current_buffer_lock_reserve(&buffer, call->id, size,
irq_flags, pc);
@@ -997,11 +1100,11 @@ static __kprobes void kretprobe_trace_func(struct kretprobe_instance *ri,
return;

entry = ring_buffer_event_data(event);
- entry->nargs = tp->nr_args;
entry->func = (unsigned long)tp->rp.kp.addr;
entry->ret_ip = (unsigned long)ri->ret_addr;
+ data = (u8 *)&entry[1];
for (i = 0; i < tp->nr_args; i++)
- entry->args[i] = call_fetch(&tp->args[i].fetch, regs);
+ call_fetch(&tp->args[i].fetch, regs, data + tp->args[i].offset);

if (!filter_current_check_discard(buffer, call, entry, event))
trace_nowake_buffer_unlock_commit(buffer, event, irq_flags, pc);
@@ -1011,13 +1114,14 @@ static __kprobes void kretprobe_trace_func(struct kretprobe_instance *ri,
enum print_line_t
print_kprobe_event(struct trace_iterator *iter, int flags)
{
- struct kprobe_trace_entry *field;
+ struct kprobe_trace_entry_head *field;
struct trace_seq *s = &iter->seq;
struct trace_event *event;
struct trace_probe *tp;
+ u8 *data;
int i;

- field = (struct kprobe_trace_entry *)iter->ent;
+ field = (struct kprobe_trace_entry_head *)iter->ent;
event = ftrace_find_event(field->ent.type);
tp = container_of(event, struct trace_probe, event);

@@ -1030,9 +1134,10 @@ print_kprobe_event(struct trace_iterator *iter, int flags)
if (!trace_seq_puts(s, ")"))
goto partial;

- for (i = 0; i < field->nargs; i++)
- if (!trace_seq_printf(s, " %s=%lx",
- tp->args[i].name, field->args[i]))
+ data = (u8 *)&field[1];
+ for (i = 0; i < tp->nr_args; i++)
+ if (!tp->args[i].type->print(s, tp->args[i].name,
+ data + tp->args[i].offset))
goto partial;

if (!trace_seq_puts(s, "\n"))
@@ -1046,13 +1151,14 @@ partial:
enum print_line_t
print_kretprobe_event(struct trace_iterator *iter, int flags)
{
- struct kretprobe_trace_entry *field;
+ struct kretprobe_trace_entry_head *field;
struct trace_seq *s = &iter->seq;
struct trace_event *event;
struct trace_probe *tp;
+ u8 *data;
int i;

- field = (struct kretprobe_trace_entry *)iter->ent;
+ field = (struct kretprobe_trace_entry_head *)iter->ent;
event = ftrace_find_event(field->ent.type);
tp = container_of(event, struct trace_probe, event);

@@ -1071,9 +1177,10 @@ print_kretprobe_event(struct trace_iterator *iter, int flags)
if (!trace_seq_puts(s, ")"))
goto partial;

- for (i = 0; i < field->nargs; i++)
- if (!trace_seq_printf(s, " %s=%lx",
- tp->args[i].name, field->args[i]))
+ data = (u8 *)&field[1];
+ for (i = 0; i < tp->nr_args; i++)
+ if (!tp->args[i].type->print(s, tp->args[i].name,
+ data + tp->args[i].offset))
goto partial;

if (!trace_seq_puts(s, "\n"))
@@ -1129,29 +1236,43 @@ static int probe_event_raw_init(struct ftrace_event_call *event_call)
static int kprobe_event_define_fields(struct ftrace_event_call *event_call)
{
int ret, i;
- struct kprobe_trace_entry field;
+ struct kprobe_trace_entry_head field;
struct trace_probe *tp = (struct trace_probe *)event_call->data;

DEFINE_FIELD(unsigned long, ip, FIELD_STRING_IP, 0);
- DEFINE_FIELD(int, nargs, FIELD_STRING_NARGS, 1);
/* Set argument names as fields */
- for (i = 0; i < tp->nr_args; i++)
- DEFINE_FIELD(unsigned long, args[i], tp->args[i].name, 0);
+ for (i = 0; i < tp->nr_args; i++) {
+ ret = trace_define_field(event_call, tp->args[i].type->name,
+ tp->args[i].name,
+ sizeof(field) + tp->args[i].offset,
+ tp->args[i].type->size,
+ tp->args[i].type->is_signed,
+ FILTER_OTHER);
+ if (ret)
+ return ret;
+ }
return 0;
}

static int kretprobe_event_define_fields(struct ftrace_event_call *event_call)
{
int ret, i;
- struct kretprobe_trace_entry field;
+ struct kretprobe_trace_entry_head field;
struct trace_probe *tp = (struct trace_probe *)event_call->data;

DEFINE_FIELD(unsigned long, func, FIELD_STRING_FUNC, 0);
DEFINE_FIELD(unsigned long, ret_ip, FIELD_STRING_RETIP, 0);
- DEFINE_FIELD(int, nargs, FIELD_STRING_NARGS, 1);
/* Set argument names as fields */
- for (i = 0; i < tp->nr_args; i++)
- DEFINE_FIELD(unsigned long, args[i], tp->args[i].name, 0);
+ for (i = 0; i < tp->nr_args; i++) {
+ ret = trace_define_field(event_call, tp->args[i].type->name,
+ tp->args[i].name,
+ sizeof(field) + tp->args[i].offset,
+ tp->args[i].type->size,
+ tp->args[i].type->is_signed,
+ FILTER_OTHER);
+ if (ret)
+ return ret;
+ }
return 0;
}

@@ -1176,8 +1297,8 @@ static int __set_print_fmt(struct trace_probe *tp, char *buf, int len)
pos += snprintf(buf + pos, LEN_OR_ZERO, "\"%s", fmt);

for (i = 0; i < tp->nr_args; i++) {
- pos += snprintf(buf + pos, LEN_OR_ZERO, " %s=%%lx",
- tp->args[i].name);
+ pos += snprintf(buf + pos, LEN_OR_ZERO, " %s=%s",
+ tp->args[i].name, tp->args[i].type->fmt);
}

pos += snprintf(buf + pos, LEN_OR_ZERO, "\", %s", arg);
@@ -1219,12 +1340,13 @@ static __kprobes void kprobe_perf_func(struct kprobe *kp,
{
struct trace_probe *tp = container_of(kp, struct trace_probe, rp.kp);
struct ftrace_event_call *call = &tp->call;
- struct kprobe_trace_entry *entry;
+ struct kprobe_trace_entry_head *entry;
+ u8 *data;
int size, __size, i;
unsigned long irq_flags;
int rctx;

- __size = SIZEOF_KPROBE_TRACE_ENTRY(tp->nr_args);
+ __size = sizeof(*entry) + tp->size;
size = ALIGN(__size + sizeof(u32), sizeof(u64));
size -= sizeof(u32);
if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE,
@@ -1235,10 +1357,10 @@ static __kprobes void kprobe_perf_func(struct kprobe *kp,
if (!entry)
return;

- entry->nargs = tp->nr_args;
entry->ip = (unsigned long)kp->addr;
+ data = (u8 *)&entry[1];
for (i = 0; i < tp->nr_args; i++)
- entry->args[i] = call_fetch(&tp->args[i].fetch, regs);
+ call_fetch(&tp->args[i].fetch, regs, data + tp->args[i].offset);

perf_trace_buf_submit(entry, size, rctx, entry->ip, 1, irq_flags, regs);
}
@@ -1249,12 +1371,13 @@ static __kprobes void kretprobe_perf_func(struct kretprobe_instance *ri,
{
struct trace_probe *tp = container_of(ri->rp, struct trace_probe, rp);
struct ftrace_event_call *call = &tp->call;
- struct kretprobe_trace_entry *entry;
+ struct kretprobe_trace_entry_head *entry;
+ u8 *data;
int size, __size, i;
unsigned long irq_flags;
int rctx;

- __size = SIZEOF_KRETPROBE_TRACE_ENTRY(tp->nr_args);
+ __size = sizeof(*entry) + tp->size;
size = ALIGN(__size + sizeof(u32), sizeof(u64));
size -= sizeof(u32);
if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE,
@@ -1265,11 +1388,11 @@ static __kprobes void kretprobe_perf_func(struct kretprobe_instance *ri,
if (!entry)
return;

- entry->nargs = tp->nr_args;
entry->func = (unsigned long)tp->rp.kp.addr;
entry->ret_ip = (unsigned long)ri->ret_addr;
+ data = (u8 *)&entry[1];
for (i = 0; i < tp->nr_args; i++)
- entry->args[i] = call_fetch(&tp->args[i].fetch, regs);
+ call_fetch(&tp->args[i].fetch, regs, data + tp->args[i].offset);

perf_trace_buf_submit(entry, size, rctx, entry->ret_ip, 1,
irq_flags, regs);


--
Masami Hiramatsu
e-mail: [email protected]

2010-04-12 17:11:25

by Masami Hiramatsu

[permalink] [raw]
Subject: [PATCH -tip v3 10/10] perf probe: Remove xstrdup()/xstrndup() from util/probe-{event, finder}.c

Remove all xstr*dup() calls from util/probe-{event,finder}.c since
it may cause 'sudden death' in utility functions and it makes
reusing it from other code difficult.

Signed-off-by: Masami Hiramatsu <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Mike Galbraith <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
---

tools/perf/util/probe-event.c | 159 ++++++++++++++++++++++++++++------------
tools/perf/util/probe-finder.c | 58 +++++++++++----
2 files changed, 156 insertions(+), 61 deletions(-)

diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index aacbf73..ca108b2 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -133,7 +133,9 @@ static int convert_to_perf_probe_point(struct kprobe_trace_point *tp,
if (ret <= 0) {
pr_debug("Failed to find corresponding probes from "
"debuginfo. Use kprobe event information.\n");
- pp->function = xstrdup(tp->symbol);
+ pp->function = strdup(tp->symbol);
+ if (pp->function == NULL)
+ return -ENOMEM;
pp->offset = tp->offset;
}
pp->retprobe = tp->retprobe;
@@ -300,7 +302,9 @@ end:
static int convert_to_perf_probe_point(struct kprobe_trace_point *tp,
struct perf_probe_point *pp)
{
- pp->function = xstrdup(tp->symbol);
+ pp->function = strdup(tp->symbol);
+ if (pp->function == NULL)
+ return -ENOMEM;
pp->offset = tp->offset;
pp->retprobe = tp->retprobe;

@@ -355,9 +359,12 @@ int parse_line_range_desc(const char *arg, struct line_range *lr)
*tmp);
return -EINVAL;
}
- tmp = xstrndup(arg, (ptr - arg));
+ tmp = strndup(arg, (ptr - arg));
} else
- tmp = xstrdup(arg);
+ tmp = strdup(arg);
+
+ if (tmp == NULL)
+ return -ENOMEM;

if (strchr(tmp, '.'))
lr->file = tmp;
@@ -406,7 +413,9 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
"follow C symbol-naming rule.\n", arg);
return -EINVAL;
}
- pev->event = xstrdup(arg);
+ pev->event = strdup(arg);
+ if (pev->event == NULL)
+ return -ENOMEM;
pev->group = NULL;
arg = tmp;
}
@@ -417,18 +426,24 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
*ptr++ = '\0';
}

+ tmp = strdup(arg);
+ if (tmp == NULL)
+ return -ENOMEM;
+
/* Check arg is function or file and copy it */
- if (strchr(arg, '.')) /* File */
- pp->file = xstrdup(arg);
+ if (strchr(tmp, '.')) /* File */
+ pp->file = tmp;
else /* Function */
- pp->function = xstrdup(arg);
+ pp->function = tmp;

/* Parse other options */
while (ptr) {
arg = ptr;
c = nc;
if (c == ';') { /* Lazy pattern must be the last part */
- pp->lazy_line = xstrdup(arg);
+ pp->lazy_line = strdup(arg);
+ if (pp->lazy_line == NULL)
+ return -ENOMEM;
break;
}
ptr = strpbrk(arg, ";:+@%");
@@ -458,7 +473,9 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
semantic_error("SRC@SRC is not allowed.\n");
return -EINVAL;
}
- pp->file = xstrdup(arg);
+ pp->file = strdup(arg);
+ if (pp->file == NULL)
+ return -ENOMEM;
break;
case '%': /* Probe places */
if (strcmp(arg, "return") == 0) {
@@ -530,7 +547,9 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)

tmp = strchr(str, '=');
if (tmp) {
- arg->name = xstrndup(str, tmp - str);
+ arg->name = strndup(str, tmp - str);
+ if (arg->name == NULL)
+ return -ENOMEM;
pr_debug("name:%s ", arg->name);
str = tmp + 1;
}
@@ -538,20 +557,26 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
tmp = strchr(str, ':');
if (tmp) { /* Type setting */
*tmp = '\0';
- arg->type = xstrdup(tmp + 1);
+ arg->type = strdup(tmp + 1);
+ if (arg->type == NULL)
+ return -ENOMEM;
pr_debug("type:%s ", arg->type);
}

tmp = strpbrk(str, "-.");
if (!is_c_varname(str) || !tmp) {
/* A variable, register, symbol or special value */
- arg->var = xstrdup(str);
+ arg->var = strdup(str);
+ if (arg->var == NULL)
+ return -ENOMEM;
pr_debug("%s\n", arg->var);
return 0;
}

/* Structure fields */
- arg->var = xstrndup(str, tmp - str);
+ arg->var = strndup(str, tmp - str);
+ if (arg->var == NULL)
+ return -ENOMEM;
pr_debug("%s, ", arg->var);
fieldp = &arg->field;

@@ -572,18 +597,24 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)

tmp = strpbrk(str, "-.");
if (tmp) {
- (*fieldp)->name = xstrndup(str, tmp - str);
+ (*fieldp)->name = strndup(str, tmp - str);
+ if ((*fieldp)->name == NULL)
+ return -ENOMEM;
pr_debug("%s(%d), ", (*fieldp)->name, (*fieldp)->ref);
fieldp = &(*fieldp)->next;
}
} while (tmp);
- (*fieldp)->name = xstrdup(str);
+ (*fieldp)->name = strdup(str);
+ if ((*fieldp)->name == NULL)
+ return -ENOMEM;
pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref);

/* If no name is specified, set the last field name */
- if (!arg->name)
- arg->name = xstrdup((*fieldp)->name);
-
+ if (!arg->name) {
+ arg->name = strdup((*fieldp)->name);
+ if (arg->name == NULL)
+ return -ENOMEM;
+ }
return 0;
}

@@ -697,9 +728,13 @@ int parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev)
*p++ = '\0';
else
p = argv[i + 2];
- tev->args[i].name = xstrdup(argv[i + 2]);
+ tev->args[i].name = strdup(argv[i + 2]);
/* TODO: parse regs and offset */
- tev->args[i].value = xstrdup(p);
+ tev->args[i].value = strdup(p);
+ if (tev->args[i].name == NULL || tev->args[i].value == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
}
ret = 0;
out:
@@ -933,12 +968,14 @@ error:
int convert_to_perf_probe_event(struct kprobe_trace_event *tev,
struct perf_probe_event *pev)
{
- char buf[64];
+ char buf[64] = "";
int i, ret;

/* Convert event/group name */
- pev->event = xstrdup(tev->event);
- pev->group = xstrdup(tev->group);
+ pev->event = strdup(tev->event);
+ pev->group = strdup(tev->group);
+ if (pev->event == NULL || pev->group == NULL)
+ return -ENOMEM;

/* Convert trace_point to probe_point */
ret = convert_to_perf_probe_point(&tev->point, &pev->point);
@@ -950,14 +987,17 @@ int convert_to_perf_probe_event(struct kprobe_trace_event *tev,
pev->args = zalloc(sizeof(struct perf_probe_arg) * pev->nargs);
if (pev->args == NULL)
return -ENOMEM;
- for (i = 0; i < tev->nargs && ret >= 0; i++)
+ for (i = 0; i < tev->nargs && ret >= 0; i++) {
if (tev->args[i].name)
- pev->args[i].name = xstrdup(tev->args[i].name);
+ pev->args[i].name = strdup(tev->args[i].name);
else {
ret = synthesize_kprobe_trace_arg(&tev->args[i],
buf, 64);
- pev->args[i].name = xstrdup(buf);
+ pev->args[i].name = strdup(buf);
}
+ if (pev->args[i].name == NULL && ret >= 0)
+ ret = -ENOMEM;
+ }

if (ret < 0)
clear_perf_probe_event(pev);
@@ -1282,7 +1322,7 @@ static int __add_kprobe_trace_events(struct perf_probe_event *pev,

ret = 0;
printf("Add new event%s\n", (ntevs > 1) ? "s:" : ":");
- for (i = 0; i < ntevs && ret >= 0; i++) {
+ for (i = 0; i < ntevs; i++) {
tev = &tevs[i];
if (pev->event)
event = pev->event;
@@ -1303,8 +1343,12 @@ static int __add_kprobe_trace_events(struct perf_probe_event *pev,
break;
event = buf;

- tev->event = xstrdup(event);
- tev->group = xstrdup(group);
+ tev->event = strdup(event);
+ tev->group = strdup(group);
+ if (tev->event == NULL || tev->group == NULL) {
+ ret = -ENOMEM;
+ break;
+ }
ret = write_kprobe_trace_event(fd, tev);
if (ret < 0)
break;
@@ -1360,23 +1404,40 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev,
return -ENOMEM;

/* Copy parameters */
- tev->point.symbol = xstrdup(pev->point.function);
+ tev->point.symbol = strdup(pev->point.function);
+ if (tev->point.symbol == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
tev->point.offset = pev->point.offset;
tev->nargs = pev->nargs;
if (tev->nargs) {
tev->args = zalloc(sizeof(struct kprobe_trace_arg)
* tev->nargs);
if (tev->args == NULL) {
- free(tev);
- *tevs = NULL;
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto error;
}
for (i = 0; i < tev->nargs; i++) {
- if (pev->args[i].name)
- tev->args[i].name = xstrdup(pev->args[i].name);
- tev->args[i].value = xstrdup(pev->args[i].var);
- if (pev->args[i].type)
- tev->args[i].type = xstrdup(pev->args[i].type);
+ if (pev->args[i].name) {
+ tev->args[i].name = strdup(pev->args[i].name);
+ if (tev->args[i].name == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ }
+ tev->args[i].value = strdup(pev->args[i].var);
+ if (tev->args[i].value == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ if (pev->args[i].type) {
+ tev->args[i].type = strdup(pev->args[i].type);
+ if (tev->args[i].type == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ }
}
}

@@ -1386,13 +1447,15 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev,
if (!sym) {
pr_warning("Kernel symbol \'%s\' not found.\n",
tev->point.symbol);
- clear_kprobe_trace_event(tev);
- free(tev);
- *tevs = NULL;
- return -ENOENT;
- } else
- ret = 1;
+ ret = -ENOENT;
+ goto error;
+ }

+ return 1;
+error:
+ clear_kprobe_trace_event(tev);
+ free(tev);
+ *tevs = NULL;
return ret;
}

@@ -1528,7 +1591,11 @@ int del_perf_probe_events(struct strlist *dellist)
return -EINVAL;

strlist__for_each(ent, dellist) {
- str = xstrdup(ent->s);
+ str = strdup(ent->s);
+ if (str == NULL) {
+ ret = -ENOMEM;
+ break;
+ }
pr_debug("Parsing: %s\n", str);
p = strchr(str, ':');
if (p) {
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index ce1ac82..e443e69 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -424,7 +424,10 @@ static int convert_location(Dwarf_Op *op, struct probe_finder *pf)
return -ERANGE;
}

- tvar->value = xstrdup(regs);
+ tvar->value = strdup(regs);
+ if (tvar->value == NULL)
+ return -ENOMEM;
+
if (ref) {
tvar->ref = zalloc(sizeof(struct kprobe_trace_arg_ref));
if (tvar->ref == NULL)
@@ -466,7 +469,9 @@ static int convert_variable_type(Dwarf_Die *vr_die,
strerror(-ret));
return ret;
}
- targ->type = xstrdup(buf);
+ targ->type = strdup(buf);
+ if (targ->type == NULL)
+ return -ENOMEM;
}
return 0;
}
@@ -576,9 +581,11 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
vr_die = &die_mem;
}
if (ret == 0) {
- if (pf->pvar->type)
- pf->tvar->type = xstrdup(pf->pvar->type);
- else
+ if (pf->pvar->type) {
+ pf->tvar->type = strdup(pf->pvar->type);
+ if (pf->tvar->type == NULL)
+ ret = -ENOMEM;
+ } else
ret = convert_variable_type(vr_die, pf->tvar);
}
/* *expr will be cached in libdw. Don't free it. */
@@ -595,22 +602,30 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
{
Dwarf_Die vr_die;
char buf[32], *ptr;
+ int ret;

/* TODO: Support arrays */
if (pf->pvar->name)
- pf->tvar->name = xstrdup(pf->pvar->name);
+ pf->tvar->name = strdup(pf->pvar->name);
else {
- synthesize_perf_probe_arg(pf->pvar, buf, 32);
+ ret = synthesize_perf_probe_arg(pf->pvar, buf, 32);
+ if (ret < 0)
+ return ret;
ptr = strchr(buf, ':'); /* Change type separator to _ */
if (ptr)
*ptr = '_';
- pf->tvar->name = xstrdup(buf);
+ pf->tvar->name = strdup(buf);
}
+ if (pf->tvar->name == NULL)
+ return -ENOMEM;

if (!is_c_varname(pf->pvar->var)) {
/* Copy raw parameters */
- pf->tvar->value = xstrdup(pf->pvar->var);
- return 0;
+ pf->tvar->value = strdup(pf->pvar->var);
+ if (pf->tvar->value == NULL)
+ return -ENOMEM;
+ else
+ return 0;
}

pr_debug("Searching '%s' variable in context.\n",
@@ -660,7 +675,9 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
dwarf_diename(sp_die));
return -ENOENT;
}
- tev->point.symbol = xstrdup(name);
+ tev->point.symbol = strdup(name);
+ if (tev->point.symbol == NULL)
+ return -ENOMEM;
tev->point.offset = (unsigned long)(pf->addr - eaddr);
} else
/* This function has no name. */
@@ -1028,7 +1045,11 @@ int find_perf_probe_point(int fd, unsigned long addr,
tmp = dwarf_linesrc(line, NULL, NULL);
if (tmp) {
ppt->line = lineno;
- ppt->file = xstrdup(tmp);
+ ppt->file = strdup(tmp);
+ if (ppt->file == NULL) {
+ ret = -ENOMEM;
+ goto end;
+ }
found = true;
}
}
@@ -1064,7 +1085,11 @@ int find_perf_probe_point(int fd, unsigned long addr,
/* We don't have a line number, let's use offset */
ppt->offset = addr - (unsigned long)eaddr;
found:
- ppt->function = xstrdup(tmp);
+ ppt->function = strdup(tmp);
+ if (ppt->function == NULL) {
+ ret = -ENOMEM;
+ goto end;
+ }
found = true;
}

@@ -1116,8 +1141,11 @@ static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf)
continue;

/* Copy real path */
- if (!lf->lr->path)
- lf->lr->path = xstrdup(src);
+ if (!lf->lr->path) {
+ lf->lr->path = strdup(src);
+ if (lf->lr->path == NULL)
+ return -ENOMEM;
+ }
line_list__add_line(&lf->lr->line_list, (unsigned int)lineno);
}
/* Update status */


--
Masami Hiramatsu
e-mail: [email protected]

2010-04-12 17:11:01

by Masami Hiramatsu

[permalink] [raw]
Subject: [PATCH -tip v3 07/10] perf probe: Remove die() from probe-finder code

Remove die() and DIE_IF() code from util/probe-finder.c since
these 'sudden death' in utility functions make reusing it from
other code (especially tui/gui) difficult.

Signed-off-by: Masami Hiramatsu <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Mike Galbraith <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
---

tools/perf/util/probe-event.c | 4
tools/perf/util/probe-finder.c | 517 +++++++++++++++++++++++++---------------
2 files changed, 322 insertions(+), 199 deletions(-)

diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index bef2805..7893b32 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -151,10 +151,10 @@ static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev,

/* Error path */
if (need_dwarf) {
- if (ntevs == -ENOENT)
+ if (ntevs == -EBADF)
pr_warning("No dwarf info found in the vmlinux - "
"please rebuild with CONFIG_DEBUG_INFO=y.\n");
- die("Could not analyze debuginfo.");
+ die("Failed to analyze debuginfo.");
}
pr_debug("An error occurred in debuginfo analysis."
" Try to use symbols.\n");
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 1f45285..54daa91 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -196,19 +196,7 @@ static bool die_compare_name(Dwarf_Die *dw_die, const char *tname)
{
const char *name;
name = dwarf_diename(dw_die);
- DIE_IF(name == NULL);
- return strcmp(tname, name);
-}
-
-/* Get entry pc(or low pc, 1st entry of ranges) of the die */
-static Dwarf_Addr die_get_entrypc(Dwarf_Die *dw_die)
-{
- Dwarf_Addr epc;
- int ret;
-
- ret = dwarf_entrypc(dw_die, &epc);
- DIE_IF(ret == -1);
- return epc;
+ return name ? strcmp(tname, name) : -1;
}

/* Get type die, but skip qualifiers and typedef */
@@ -390,7 +378,7 @@ static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
*/

/* Show a location */
-static void convert_location(Dwarf_Op *op, struct probe_finder *pf)
+static int convert_location(Dwarf_Op *op, struct probe_finder *pf)
{
unsigned int regn;
Dwarf_Word offs = 0;
@@ -400,8 +388,11 @@ static void convert_location(Dwarf_Op *op, struct probe_finder *pf)

/* If this is based on frame buffer, set the offset */
if (op->atom == DW_OP_fbreg) {
- if (pf->fb_ops == NULL)
- die("The attribute of frame base is not supported.\n");
+ if (pf->fb_ops == NULL) {
+ pr_warning("The attribute of frame base is not "
+ "supported.\n");
+ return -ENOTSUP;
+ }
ref = true;
offs = op->number;
op = &pf->fb_ops[0];
@@ -419,50 +410,63 @@ static void convert_location(Dwarf_Op *op, struct probe_finder *pf)
ref = true;
} else if (op->atom == DW_OP_regx) {
regn = op->number;
- } else
- die("DW_OP %d is not supported.", op->atom);
+ } else {
+ pr_warning("DW_OP %x is not supported.\n", op->atom);
+ return -ENOTSUP;
+ }

regs = get_arch_regstr(regn);
- if (!regs)
- die("%u exceeds max register number.", regn);
+ if (!regs) {
+ pr_warning("%u exceeds max register number.\n", regn);
+ return -ERANGE;
+ }

tvar->value = xstrdup(regs);
if (ref) {
tvar->ref = xzalloc(sizeof(struct kprobe_trace_arg_ref));
tvar->ref->offset = (long)offs;
}
+ return 0;
}

-static void convert_variable_type(Dwarf_Die *vr_die,
- struct kprobe_trace_arg *targ)
+static int convert_variable_type(Dwarf_Die *vr_die,
+ struct kprobe_trace_arg *targ)
{
Dwarf_Die type;
char buf[16];
int ret;

- if (die_get_real_type(vr_die, &type) == NULL)
- die("Failed to get a type information of %s.",
- dwarf_diename(vr_die));
+ if (die_get_real_type(vr_die, &type) == NULL) {
+ pr_warning("Failed to get a type information of %s.\n",
+ dwarf_diename(vr_die));
+ return -ENOENT;
+ }

ret = die_get_byte_size(&type) * 8;
if (ret) {
/* Check the bitwidth */
if (ret > MAX_BASIC_TYPE_BITS) {
- pr_warning(" Warning: %s exceeds max-bitwidth."
- " Cut down to %d bits.\n",
- dwarf_diename(&type), MAX_BASIC_TYPE_BITS);
+ pr_info("%s exceeds max-bitwidth."
+ " Cut down to %d bits.\n",
+ dwarf_diename(&type), MAX_BASIC_TYPE_BITS);
ret = MAX_BASIC_TYPE_BITS;
}

ret = snprintf(buf, 16, "%c%d",
die_is_signed_type(&type) ? 's' : 'u', ret);
- if (ret < 0 || ret >= 16)
- die("Failed to convert variable type.");
+ if (ret < 0 || ret >= 16) {
+ if (ret >= 16)
+ ret = -E2BIG;
+ pr_warning("Failed to convert variable type: %s\n",
+ strerror(-ret));
+ return ret;
+ }
targ->type = xstrdup(buf);
}
+ return 0;
}

-static void convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
+static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
struct perf_probe_arg_field *field,
struct kprobe_trace_arg_ref **ref_ptr,
Dwarf_Die *die_mem)
@@ -473,21 +477,28 @@ static void convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
Dwarf_Word offs;

pr_debug("converting %s in %s\n", field->name, varname);
- if (die_get_real_type(vr_die, &type) == NULL)
- die("Failed to get a type information of %s.", varname);
+ if (die_get_real_type(vr_die, &type) == NULL) {
+ pr_warning("Failed to get the type of %s.\n", varname);
+ return -ENOENT;
+ }

/* Check the pointer and dereference */
if (dwarf_tag(&type) == DW_TAG_pointer_type) {
- if (!field->ref)
- die("Semantic error: %s must be referred by '->'",
- field->name);
+ if (!field->ref) {
+ pr_err("Semantic error: %s must be referred by '->'\n",
+ field->name);
+ return -EINVAL;
+ }
/* Get the type pointed by this pointer */
- if (die_get_real_type(&type, &type) == NULL)
- die("Failed to get a type information of %s.", varname);
-
+ if (die_get_real_type(&type, &type) == NULL) {
+ pr_warning("Failed to get the type of %s.\n", varname);
+ return -ENOENT;
+ }
/* Verify it is a data structure */
- if (dwarf_tag(&type) != DW_TAG_structure_type)
- die("%s is not a data structure.", varname);
+ if (dwarf_tag(&type) != DW_TAG_structure_type) {
+ pr_warning("%s is not a data structure.\n", varname);
+ return -EINVAL;
+ }

ref = xzalloc(sizeof(struct kprobe_trace_arg_ref));
if (*ref_ptr)
@@ -496,34 +507,46 @@ static void convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
*ref_ptr = ref;
} else {
/* Verify it is a data structure */
- if (dwarf_tag(&type) != DW_TAG_structure_type)
- die("%s is not a data structure.", varname);
-
- if (field->ref)
- die("Semantic error: %s must be referred by '.'",
- field->name);
- if (!ref)
- die("Structure on a register is not supported yet.");
+ if (dwarf_tag(&type) != DW_TAG_structure_type) {
+ pr_warning("%s is not a data structure.\n", varname);
+ return -EINVAL;
+ }
+ if (field->ref) {
+ pr_err("Semantic error: %s must be referred by '.'\n",
+ field->name);
+ return -EINVAL;
+ }
+ if (!ref) {
+ pr_warning("Structure on a register is not "
+ "supported yet.\n");
+ return -ENOTSUP;
+ }
}

- if (die_find_member(&type, field->name, die_mem) == NULL)
- die("%s(tyep:%s) has no member %s.", varname,
- dwarf_diename(&type), field->name);
+ if (die_find_member(&type, field->name, die_mem) == NULL) {
+ pr_warning("%s(tyep:%s) has no member %s.\n", varname,
+ dwarf_diename(&type), field->name);
+ return -EINVAL;
+ }

/* Get the offset of the field */
if (dwarf_attr(die_mem, DW_AT_data_member_location, &attr) == NULL ||
- dwarf_formudata(&attr, &offs) != 0)
- die("Failed to get the offset of %s.", field->name);
+ dwarf_formudata(&attr, &offs) != 0) {
+ pr_warning("Failed to get the offset of %s.\n", field->name);
+ return -ENOENT;
+ }
ref->offset += (long)offs;

/* Converting next field */
if (field->next)
- convert_variable_fields(die_mem, field->name, field->next,
- &ref, die_mem);
+ return convert_variable_fields(die_mem, field->name,
+ field->next, &ref, die_mem);
+ else
+ return 0;
}

/* Show a variables in kprobe event format */
-static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
+static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
{
Dwarf_Attribute attr;
Dwarf_Die die_mem;
@@ -538,28 +561,30 @@ static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
if (ret <= 0 || nexpr == 0)
goto error;

- convert_location(expr, pf);
-
- if (pf->pvar->field) {
- convert_variable_fields(vr_die, pf->pvar->var,
- pf->pvar->field, &pf->tvar->ref,
- &die_mem);
+ ret = convert_location(expr, pf);
+ if (ret == 0 && pf->pvar->field) {
+ ret = convert_variable_fields(vr_die, pf->pvar->var,
+ pf->pvar->field, &pf->tvar->ref,
+ &die_mem);
vr_die = &die_mem;
}
- if (pf->pvar->type)
- pf->tvar->type = xstrdup(pf->pvar->type);
- else
- convert_variable_type(vr_die, pf->tvar);
+ if (ret == 0) {
+ if (pf->pvar->type)
+ pf->tvar->type = xstrdup(pf->pvar->type);
+ else
+ ret = convert_variable_type(vr_die, pf->tvar);
+ }
/* *expr will be cached in libdw. Don't free it. */
- return ;
+ return ret;
error:
/* TODO: Support const_value */
- die("Failed to find the location of %s at this address.\n"
- " Perhaps, it has been optimized out.", pf->pvar->var);
+ 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;
}

/* Find a variable in a subprogram die */
-static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
+static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
{
Dwarf_Die vr_die;
char buf[32], *ptr;
@@ -578,19 +603,22 @@ static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
if (!is_c_varname(pf->pvar->var)) {
/* Copy raw parameters */
pf->tvar->value = xstrdup(pf->pvar->var);
- } else {
- 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))
- die("Failed to find '%s' in this function.",
- pf->pvar->var);
- convert_variable(&vr_die, pf);
+ return 0;
}
+
+ 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)) {
+ pr_warning("Failed to find '%s' in this function.\n",
+ pf->pvar->var);
+ return -ENOENT;
+ }
+ return convert_variable(&vr_die, pf);
}

/* Show a probe point to output buffer */
-static void convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
+static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
{
struct kprobe_trace_event *tev;
Dwarf_Addr eaddr;
@@ -600,22 +628,31 @@ static void convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
Dwarf_Attribute fb_attr;
size_t nops;

- if (pf->ntevs == MAX_PROBES)
- die("Too many( > %d) probe point found.\n", MAX_PROBES);
+ if (pf->ntevs == MAX_PROBES) {
+ pr_warning("Too many( > %d) probe point found.\n", MAX_PROBES);
+ 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)
- die("Probe point is not found in subprograms.");
+ 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);
if (name) {
- dwarf_entrypc(sp_die, &eaddr);
+ if (dwarf_entrypc(sp_die, &eaddr) != 0) {
+ pr_warning("Failed to get entry pc of %s\n",
+ dwarf_diename(sp_die));
+ return -ENOENT;
+ }
tev->point.symbol = xstrdup(name);
tev->point.offset = (unsigned long)(pf->addr - eaddr);
} else
@@ -633,9 +670,12 @@ static void convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
} else if (nops == 1 && pf->fb_ops[0].atom == DW_OP_call_frame_cfa &&
pf->cfi != NULL) {
Dwarf_Frame *frame;
- ret = dwarf_cfi_addrframe(pf->cfi, pf->addr, &frame);
- DIE_IF(ret != 0);
- dwarf_frame_cfa(frame, &pf->fb_ops, &nops);
+ if (dwarf_cfi_addrframe(pf->cfi, pf->addr, &frame) != 0 ||
+ dwarf_frame_cfa(frame, &pf->fb_ops, &nops) != 0) {
+ pr_warning("Failed to get CFA on 0x%jx\n",
+ (uintmax_t)pf->addr);
+ return -ENOENT;
+ }
}

/* Find each argument */
@@ -644,45 +684,53 @@ static void convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
for (i = 0; i < pf->pev->nargs; i++) {
pf->pvar = &pf->pev->args[i];
pf->tvar = &tev->args[i];
- find_variable(sp_die, pf);
+ ret = find_variable(sp_die, pf);
+ if (ret != 0)
+ return ret;
}

/* *pf->fb_ops will be cached in libdw. Don't free it. */
pf->fb_ops = NULL;
+ return 0;
}

/* Find probe point from its line number */
-static void find_probe_point_by_line(struct probe_finder *pf)
+static int find_probe_point_by_line(struct probe_finder *pf)
{
Dwarf_Lines *lines;
Dwarf_Line *line;
size_t nlines, i;
Dwarf_Addr addr;
int lineno;
- int ret;
+ int ret = 0;

- ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines);
- DIE_IF(ret != 0);
+ if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) {
+ pr_warning("No source lines found in this CU.\n");
+ return -ENOENT;
+ }

- for (i = 0; i < nlines; i++) {
+ for (i = 0; i < nlines && ret == 0; i++) {
line = dwarf_onesrcline(lines, i);
- dwarf_lineno(line, &lineno);
- if (lineno != pf->lno)
+ if (dwarf_lineno(line, &lineno) != 0 ||
+ lineno != pf->lno)
continue;

/* TODO: Get fileno from line, but how? */
if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0)
continue;

- ret = dwarf_lineaddr(line, &addr);
- DIE_IF(ret != 0);
+ if (dwarf_lineaddr(line, &addr) != 0) {
+ pr_warning("Failed to get the address of the line.\n");
+ return -ENOENT;
+ }
pr_debug("Probe line found: line[%d]:%d addr:0x%jx\n",
(int)i, lineno, (uintmax_t)addr);
pf->addr = addr;

- convert_probe_point(NULL, pf);
+ ret = convert_probe_point(NULL, pf);
/* Continuing, because target line might be inlined. */
}
+ return ret;
}

/* Find lines which match lazy pattern */
@@ -690,15 +738,27 @@ static int find_lazy_match_lines(struct list_head *head,
const char *fname, const char *pat)
{
char *fbuf, *p1, *p2;
- int fd, line, nlines = 0;
+ int fd, ret, line, nlines = 0;
struct stat st;

fd = open(fname, O_RDONLY);
- if (fd < 0)
- die("failed to open %s", fname);
- DIE_IF(fstat(fd, &st) < 0);
+ if (fd < 0) {
+ pr_warning("Failed to open %s: %s\n", fname, strerror(-fd));
+ return fd;
+ }
+
+ ret = fstat(fd, &st);
+ if (ret < 0) {
+ pr_warning("Failed to get the size of %s: %s\n",
+ fname, strerror(errno));
+ return ret;
+ }
fbuf = xmalloc(st.st_size + 2);
- DIE_IF(read(fd, fbuf, st.st_size) < 0);
+ ret = read(fd, fbuf, st.st_size);
+ if (ret < 0) {
+ pr_warning("Failed to read %s: %s\n", fname, strerror(errno));
+ return ret;
+ }
close(fd);
fbuf[st.st_size] = '\n'; /* Dummy line */
fbuf[st.st_size + 1] = '\0';
@@ -718,7 +778,7 @@ static int find_lazy_match_lines(struct list_head *head,
}

/* Find probe points from lazy pattern */
-static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
+static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
{
Dwarf_Lines *lines;
Dwarf_Line *line;
@@ -726,31 +786,40 @@ static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
Dwarf_Addr addr;
Dwarf_Die die_mem;
int lineno;
- int ret;
+ int ret = 0;

if (list_empty(&pf->lcache)) {
/* Matching lazy line pattern */
ret = find_lazy_match_lines(&pf->lcache, pf->fname,
pf->pev->point.lazy_line);
- if (ret <= 0)
- die("No matched lines found in %s.", pf->fname);
+ if (ret == 0) {
+ pr_debug("No matched lines found in %s.\n", pf->fname);
+ return 0;
+ } else if (ret < 0)
+ return ret;
}

- ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines);
- DIE_IF(ret != 0);
- for (i = 0; i < nlines; i++) {
+ if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) {
+ pr_warning("No source lines found in this CU.\n");
+ return -ENOENT;
+ }
+
+ for (i = 0; i < nlines && ret >= 0; i++) {
line = dwarf_onesrcline(lines, i);

- dwarf_lineno(line, &lineno);
- if (!line_list__has_line(&pf->lcache, lineno))
+ if (dwarf_lineno(line, &lineno) != 0 ||
+ !line_list__has_line(&pf->lcache, lineno))
continue;

/* TODO: Get fileno from line, but how? */
if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0)
continue;

- ret = dwarf_lineaddr(line, &addr);
- DIE_IF(ret != 0);
+ if (dwarf_lineaddr(line, &addr) != 0) {
+ pr_debug("Failed to get the address of line %d.\n",
+ lineno);
+ continue;
+ }
if (sp_die) {
/* Address filtering 1: does sp_die include addr? */
if (!dwarf_haspc(sp_die, addr))
@@ -764,27 +833,42 @@ static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
(int)i, lineno, (unsigned long long)addr);
pf->addr = addr;

- convert_probe_point(sp_die, pf);
+ ret = convert_probe_point(sp_die, pf);
/* Continuing, because target line might be inlined. */
}
/* TODO: deallocate lines, but how? */
+ return ret;
}

+/* Callback parameter with return value */
+struct dwarf_callback_param {
+ void *data;
+ int retval;
+};
+
static int probe_point_inline_cb(Dwarf_Die *in_die, void *data)
{
- struct probe_finder *pf = (struct probe_finder *)data;
+ struct dwarf_callback_param *param = data;
+ struct probe_finder *pf = param->data;
struct perf_probe_point *pp = &pf->pev->point;
+ Dwarf_Addr addr;

if (pp->lazy_line)
- find_probe_point_lazy(in_die, pf);
+ param->retval = find_probe_point_lazy(in_die, pf);
else {
/* Get probe address */
- pf->addr = die_get_entrypc(in_die);
+ if (dwarf_entrypc(in_die, &addr) != 0) {
+ pr_warning("Failed to get entry pc of %s.\n",
+ dwarf_diename(in_die));
+ param->retval = -ENOENT;
+ return DWARF_CB_ABORT;
+ }
+ pf->addr = addr;
pf->addr += pp->offset;
pr_debug("found inline addr: 0x%jx\n",
(uintmax_t)pf->addr);

- convert_probe_point(in_die, pf);
+ param->retval = convert_probe_point(in_die, pf);
}

return DWARF_CB_OK;
@@ -793,39 +877,53 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data)
/* Search function from function name */
static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
{
- struct probe_finder *pf = (struct probe_finder *)data;
+ struct dwarf_callback_param *param = data;
+ struct probe_finder *pf = param->data;
struct perf_probe_point *pp = &pf->pev->point;

/* Check tag and diename */
if (dwarf_tag(sp_die) != DW_TAG_subprogram ||
die_compare_name(sp_die, pp->function) != 0)
- return 0;
+ return DWARF_CB_OK;

pf->fname = dwarf_decl_file(sp_die);
if (pp->line) { /* Function relative line */
dwarf_decl_line(sp_die, &pf->lno);
pf->lno += pp->line;
- find_probe_point_by_line(pf);
+ param->retval = find_probe_point_by_line(pf);
} else if (!dwarf_func_inline(sp_die)) {
/* Real function */
if (pp->lazy_line)
- find_probe_point_lazy(sp_die, pf);
+ param->retval = find_probe_point_lazy(sp_die, pf);
else {
- pf->addr = die_get_entrypc(sp_die);
+ if (dwarf_entrypc(sp_die, &pf->addr) != 0) {
+ pr_warning("Failed to get entry pc of %s.\n",
+ dwarf_diename(sp_die));
+ param->retval = -ENOENT;
+ return DWARF_CB_ABORT;
+ }
pf->addr += pp->offset;
/* TODO: Check the address in this function */
- convert_probe_point(sp_die, pf);
+ param->retval = convert_probe_point(sp_die, pf);
}
- } else
+ } else {
+ struct dwarf_callback_param _param = {.data = (void *)pf,
+ .retval = 0};
/* Inlined function: search instances */
- dwarf_func_inline_instances(sp_die, probe_point_inline_cb, pf);
+ dwarf_func_inline_instances(sp_die, probe_point_inline_cb,
+ &_param);
+ param->retval = _param.retval;
+ }

- return 1; /* Exit; no same symbol in this CU. */
+ return DWARF_CB_ABORT; /* Exit; no same symbol in this CU. */
}

-static void find_probe_point_by_func(struct probe_finder *pf)
+static int find_probe_point_by_func(struct probe_finder *pf)
{
- dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, pf, 0);
+ struct dwarf_callback_param _param = {.data = (void *)pf,
+ .retval = 0};
+ dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, &_param, 0);
+ return _param.retval;
}

/* Find kprobe_trace_events specified by perf_probe_event from debuginfo */
@@ -838,14 +936,18 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev,
size_t cuhl;
Dwarf_Die *diep;
Dwarf *dbg;
+ int ret = 0;

pf.tevs = xzalloc(sizeof(struct kprobe_trace_event) * MAX_PROBES);
*tevs = pf.tevs;
pf.ntevs = 0;

dbg = dwarf_begin(fd, DWARF_C_READ);
- if (!dbg)
- return -ENOENT;
+ if (!dbg) {
+ pr_warning("No dwarf info found in the vmlinux - "
+ "please rebuild with CONFIG_DEBUG_INFO=y.\n");
+ return -EBADF;
+ }

/* Get the call frame information from this dwarf */
pf.cfi = dwarf_getcfi(dbg);
@@ -853,7 +955,8 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev,
off = 0;
line_list__init(&pf.lcache);
/* Loop on CUs (Compilation Unit) */
- while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL)) {
+ 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);
if (!diep)
@@ -867,12 +970,12 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev,

if (!pp->file || pf.fname) {
if (pp->function)
- find_probe_point_by_func(&pf);
+ ret = find_probe_point_by_func(&pf);
else if (pp->lazy_line)
- find_probe_point_lazy(NULL, &pf);
+ ret = find_probe_point_lazy(NULL, &pf);
else {
pf.lno = pp->line;
- find_probe_point_by_line(&pf);
+ ret = find_probe_point_by_line(&pf);
}
}
off = noff;
@@ -880,7 +983,7 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev,
line_list__free(&pf.lcache);
dwarf_end(dbg);

- return pf.ntevs;
+ return (ret < 0) ? ret : pf.ntevs;
}

/* Reverse search */
@@ -893,10 +996,11 @@ int find_perf_probe_point(int fd, unsigned long addr,
Dwarf_Addr laddr, eaddr;
const char *tmp;
int lineno, ret = 0;
+ bool found = false;

dbg = dwarf_begin(fd, DWARF_C_READ);
if (!dbg)
- return -ENOENT;
+ return -EBADF;

/* Find cu die */
if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr, &cudie)) {
@@ -907,82 +1011,87 @@ int find_perf_probe_point(int fd, unsigned long addr,
/* Find a corresponding line */
line = dwarf_getsrc_die(&cudie, (Dwarf_Addr)addr);
if (line) {
- dwarf_lineaddr(line, &laddr);
- if ((Dwarf_Addr)addr == laddr) {
- dwarf_lineno(line, &lineno);
- ppt->line = lineno;
-
+ if (dwarf_lineaddr(line, &laddr) == 0 &&
+ (Dwarf_Addr)addr == laddr &&
+ dwarf_lineno(line, &lineno) == 0) {
tmp = dwarf_linesrc(line, NULL, NULL);
- DIE_IF(!tmp);
- ppt->file = xstrdup(tmp);
- ret = 1;
+ if (tmp) {
+ ppt->line = lineno;
+ ppt->file = xstrdup(tmp);
+ found = true;
+ }
}
}

/* Find a corresponding function */
if (die_find_real_subprogram(&cudie, (Dwarf_Addr)addr, &spdie)) {
tmp = dwarf_diename(&spdie);
- if (!tmp)
+ if (!tmp || dwarf_entrypc(&spdie, &eaddr) != 0)
goto end;

- dwarf_entrypc(&spdie, &eaddr);
- if (!lineno) {
- /* We don't have a line number, let's use offset */
- ppt->function = xstrdup(tmp);
- ppt->offset = addr - (unsigned long)eaddr;
- ret = 1;
- goto end;
- }
- if (die_find_inlinefunc(&spdie, (Dwarf_Addr)addr, &indie)) {
- /* addr in an inline function */
- tmp = dwarf_diename(&indie);
- if (!tmp)
- goto end;
- dwarf_decl_line(&indie, &lineno);
- } else {
- if (eaddr == addr) /* No offset: function entry */
- lineno = ppt->line;
- else
- dwarf_decl_line(&spdie, &lineno);
+ if (ppt->line) {
+ if (die_find_inlinefunc(&spdie, (Dwarf_Addr)addr,
+ &indie)) {
+ /* addr in an inline function */
+ tmp = dwarf_diename(&indie);
+ if (!tmp)
+ goto end;
+ ret = dwarf_decl_line(&indie, &lineno);
+ } else {
+ if (eaddr == addr) { /* Function entry */
+ lineno = ppt->line;
+ ret = 0;
+ } else
+ ret = dwarf_decl_line(&spdie, &lineno);
+ }
+ if (ret == 0) {
+ /* Make a relative line number */
+ ppt->line -= lineno;
+ goto found;
+ }
}
+ /* We don't have a line number, let's use offset */
+ ppt->offset = addr - (unsigned long)eaddr;
+found:
ppt->function = xstrdup(tmp);
- ppt->line -= lineno; /* Make a relative line number */
+ found = true;
}

end:
dwarf_end(dbg);
+ if (ret >= 0)
+ ret = found ? 1 : 0;
return ret;
}


/* Find line range from its line number */
-static void find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf)
+static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf)
{
Dwarf_Lines *lines;
Dwarf_Line *line;
size_t nlines, i;
Dwarf_Addr addr;
int lineno;
- int ret;
const char *src;
Dwarf_Die die_mem;

line_list__init(&lf->lr->line_list);
- ret = dwarf_getsrclines(&lf->cu_die, &lines, &nlines);
- DIE_IF(ret != 0);
+ if (dwarf_getsrclines(&lf->cu_die, &lines, &nlines) != 0) {
+ pr_warning("No source lines found in this CU.\n");
+ return -ENOENT;
+ }

for (i = 0; i < nlines; i++) {
line = dwarf_onesrcline(lines, i);
- ret = dwarf_lineno(line, &lineno);
- DIE_IF(ret != 0);
- if (lf->lno_s > lineno || lf->lno_e < lineno)
+ if (dwarf_lineno(line, &lineno) != 0 ||
+ (lf->lno_s > lineno || lf->lno_e < lineno))
continue;

if (sp_die) {
/* Address filtering 1: does sp_die include addr? */
- ret = dwarf_lineaddr(line, &addr);
- DIE_IF(ret != 0);
- if (!dwarf_haspc(sp_die, addr))
+ if (dwarf_lineaddr(line, &addr) != 0 ||
+ !dwarf_haspc(sp_die, addr))
continue;

/* Address filtering 2: No child include addr? */
@@ -1007,18 +1116,22 @@ static void find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf)
free(lf->lr->path);
lf->lr->path = NULL;
}
+ return lf->found;
}

static int line_range_inline_cb(Dwarf_Die *in_die, void *data)
{
- find_line_range_by_line(in_die, (struct line_finder *)data);
+ struct dwarf_callback_param *param = data;
+
+ param->retval = find_line_range_by_line(in_die, param->data);
return DWARF_CB_ABORT; /* No need to find other instances */
}

/* Search function from function name */
static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
{
- struct line_finder *lf = (struct line_finder *)data;
+ struct dwarf_callback_param *param = data;
+ struct line_finder *lf = param->data;
struct line_range *lr = lf->lr;

if (dwarf_tag(sp_die) == DW_TAG_subprogram &&
@@ -1033,38 +1146,47 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
lf->lno_e = lr->offset + lr->end;
lr->start = lf->lno_s;
lr->end = lf->lno_e;
- if (dwarf_func_inline(sp_die))
+ if (dwarf_func_inline(sp_die)) {
+ struct dwarf_callback_param _param;
+ _param.data = (void *)lf;
+ _param.retval = 0;
dwarf_func_inline_instances(sp_die,
- line_range_inline_cb, lf);
- else
- find_line_range_by_line(sp_die, lf);
- return 1;
+ line_range_inline_cb,
+ &_param);
+ param->retval = _param.retval;
+ } else
+ param->retval = find_line_range_by_line(sp_die, lf);
+ return DWARF_CB_ABORT;
}
- return 0;
+ return DWARF_CB_OK;
}

-static void find_line_range_by_func(struct line_finder *lf)
+static int find_line_range_by_func(struct line_finder *lf)
{
- dwarf_getfuncs(&lf->cu_die, line_range_search_cb, lf, 0);
+ struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0};
+ dwarf_getfuncs(&lf->cu_die, line_range_search_cb, &param, 0);
+ return param.retval;
}

int find_line_range(int fd, struct line_range *lr)
{
struct line_finder lf = {.lr = lr, .found = 0};
- int ret;
+ int ret = 0;
Dwarf_Off off = 0, noff;
size_t cuhl;
Dwarf_Die *diep;
Dwarf *dbg;

dbg = dwarf_begin(fd, DWARF_C_READ);
- if (!dbg)
- return -ENOENT;
+ if (!dbg) {
+ pr_warning("No dwarf info found in the vmlinux - "
+ "please rebuild with CONFIG_DEBUG_INFO=y.\n");
+ return -EBADF;
+ }

/* Loop on CUs (Compilation Unit) */
- while (!lf.found) {
- ret = dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL);
- if (ret != 0)
+ while (!lf.found && ret >= 0) {
+ if (dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) != 0)
break;

/* Get the DIE(Debugging Information Entry) of this CU */
@@ -1080,20 +1202,21 @@ int find_line_range(int fd, struct line_range *lr)

if (!lr->file || lf.fname) {
if (lr->function)
- find_line_range_by_func(&lf);
+ ret = find_line_range_by_func(&lf);
else {
lf.lno_s = lr->start;
if (!lr->end)
lf.lno_e = INT_MAX;
else
lf.lno_e = lr->end;
- find_line_range_by_line(NULL, &lf);
+ ret = find_line_range_by_line(NULL, &lf);
}
}
off = noff;
}
pr_debug("path: %lx\n", (unsigned long)lr->path);
dwarf_end(dbg);
- return lf.found;
+
+ return (ret < 0) ? ret : lf.found;
}



--
Masami Hiramatsu
e-mail: [email protected]

2010-04-12 17:10:56

by Masami Hiramatsu

[permalink] [raw]
Subject: [PATCH -tip v3 06/10] perf probe: Support DW_OP_call_frame_cfa in debuginfo

When building kernel without CONFIG_FRAME_POINTER, gcc uses
CFA (canonical frame address) for frame base. With this patch,
perf probe just gets CFI (call frame information) from debuginfo
and search corresponding CFA from the CFI. IOW, this allows
perf probe works correctly on the kernel without CONFIG_FRAME_POINTER.

<Before>
./perf probe -fn sched_slice:12 lw.weight
Fatal: DW_OP 156 is not supported.
(^^^ DW_OP_call_frame_cfa)

<After>
./perf probe -fn sched_slice:12 lw.weight
Add new event:
probe:sched_slice (on sched_slice:12 with weight=lw.weight)

Signed-off-by: Masami Hiramatsu <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Mike Galbraith <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
---

tools/perf/util/probe-finder.c | 14 +++++++++++---
tools/perf/util/probe-finder.h | 1 +
2 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index ab47673..1f45285 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -398,7 +398,6 @@ static void convert_location(Dwarf_Op *op, struct probe_finder *pf)
const char *regs;
struct kprobe_trace_arg *tvar = pf->tvar;

- /* TODO: support CFA */
/* If this is based on frame buffer, set the offset */
if (op->atom == DW_OP_fbreg) {
if (pf->fb_ops == NULL)
@@ -629,11 +628,17 @@ static void convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
/* Get the frame base attribute/ops */
dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr);
ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1);
- if (ret <= 0 || nops == 0)
+ if (ret <= 0 || nops == 0) {
pf->fb_ops = NULL;
+ } else if (nops == 1 && pf->fb_ops[0].atom == DW_OP_call_frame_cfa &&
+ pf->cfi != NULL) {
+ Dwarf_Frame *frame;
+ ret = dwarf_cfi_addrframe(pf->cfi, pf->addr, &frame);
+ DIE_IF(ret != 0);
+ dwarf_frame_cfa(frame, &pf->fb_ops, &nops);
+ }

/* Find each argument */
- /* TODO: use dwarf_cfi_addrframe */
tev->nargs = pf->pev->nargs;
tev->args = xzalloc(sizeof(struct kprobe_trace_arg) * tev->nargs);
for (i = 0; i < pf->pev->nargs; i++) {
@@ -842,6 +847,9 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev,
if (!dbg)
return -ENOENT;

+ /* Get the call frame information from this dwarf */
+ pf.cfi = dwarf_getcfi(dbg);
+
off = 0;
line_list__init(&pf.lcache);
/* Loop on CUs (Compilation Unit) */
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
index 3564f22..ddaa2a7 100644
--- a/tools/perf/util/probe-finder.h
+++ b/tools/perf/util/probe-finder.h
@@ -42,6 +42,7 @@ struct probe_finder {
struct list_head lcache; /* Line cache for lazy match */

/* For variable searching */
+ Dwarf_CFI *cfi; /* Call Frame Information */
Dwarf_Op *fb_ops; /* Frame base attribute */
struct perf_probe_arg *pvar; /* Current target variable */
struct kprobe_trace_arg *tvar; /* Current result variable */


--
Masami Hiramatsu
e-mail: [email protected]

2010-04-12 17:10:49

by Masami Hiramatsu

[permalink] [raw]
Subject: [PATCH -tip v3 05/10] perf probe: Support basic type casting

Add basic type casting for arguments to perf probe. This allows
users to specify the actual type of arguments. Of course, if
user sets invalid types, kprobe-tracer rejects that.

Signed-off-by: Masami Hiramatsu <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Mike Galbraith <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
---

tools/perf/Documentation/perf-probe.txt | 3 ++-
tools/perf/util/probe-event.c | 23 ++++++++++++++++++++++-
tools/perf/util/probe-event.h | 1 +
tools/perf/util/probe-finder.c | 10 ++++++++--
4 files changed, 33 insertions(+), 4 deletions(-)

diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index 441324f..63c25d3 100644
--- a/tools/perf/Documentation/perf-probe.txt
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -85,9 +85,10 @@ PROBE ARGUMENT
--------------
Each probe argument follows below syntax.

- [NAME=]LOCALVAR|$retval|%REG|@SYMBOL
+ [NAME=]LOCALVAR|$retval|%REG|@SYMBOL[:TYPE]

'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), or kprobe-tracer argument format (e.g. $retval, %ax, etc). Note that the name of this argument will be set as the last member name if you specify a local data structure member (e.g. field2 for 'var->field1.field2'.)
+'TYPE' casts the type of this argument (optional). If omitted, perf probe automatically set the type based on debuginfo.

LINE SYNTAX
-----------
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 05ca4a9..bef2805 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -435,7 +435,7 @@ static void parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
}

/* Parse perf-probe event argument */
-static void parse_perf_probe_arg(const char *str, struct perf_probe_arg *arg)
+static void parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
{
char *tmp;
struct perf_probe_arg_field **fieldp;
@@ -445,9 +445,17 @@ static void parse_perf_probe_arg(const char *str, struct perf_probe_arg *arg)
tmp = strchr(str, '=');
if (tmp) {
arg->name = xstrndup(str, tmp - str);
+ pr_debug("name:%s ", arg->name);
str = tmp + 1;
}

+ tmp = strchr(str, ':');
+ if (tmp) { /* Type setting */
+ *tmp = '\0';
+ arg->type = xstrdup(tmp + 1);
+ pr_debug("type:%s ", arg->type);
+ }
+
tmp = strpbrk(str, "-.");
if (!is_c_varname(str) || !tmp) {
/* A variable, register, symbol or special value */
@@ -603,6 +611,15 @@ int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len)
len -= ret;
field = field->next;
}
+
+ if (pa->type) {
+ ret = e_snprintf(tmp, len, ":%s", pa->type);
+ if (ret <= 0)
+ goto error;
+ tmp += ret;
+ len -= ret;
+ }
+
return tmp - buf;
error:
die("Failed to synthesize perf probe argument: %s", strerror(-ret));
@@ -825,6 +842,8 @@ void clear_perf_probe_event(struct perf_probe_event *pev)
free(pev->args[i].name);
if (pev->args[i].var)
free(pev->args[i].var);
+ if (pev->args[i].type)
+ free(pev->args[i].type);
field = pev->args[i].field;
while (field) {
next = field->next;
@@ -1145,6 +1164,8 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev,
if (pev->args[i].name)
tev->args[i].name = xstrdup(pev->args[i].name);
tev->args[i].value = xstrdup(pev->args[i].var);
+ if (pev->args[i].type)
+ tev->args[i].type = xstrdup(pev->args[i].type);
}
}

diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 0759db6..5a9837c 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -57,6 +57,7 @@ struct perf_probe_arg_field {
struct perf_probe_arg {
char *name; /* Argument name */
char *var; /* Variable name */
+ char *type; /* Type name */
struct perf_probe_arg_field *field; /* Structure fields */
};

diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index ebeb413..ab47673 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -547,7 +547,10 @@ static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
&die_mem);
vr_die = &die_mem;
}
- convert_variable_type(vr_die, pf->tvar);
+ if (pf->pvar->type)
+ pf->tvar->type = xstrdup(pf->pvar->type);
+ else
+ convert_variable_type(vr_die, pf->tvar);
/* *expr will be cached in libdw. Don't free it. */
return ;
error:
@@ -560,13 +563,16 @@ error:
static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
{
Dwarf_Die vr_die;
- char buf[32];
+ char buf[32], *ptr;

/* TODO: Support arrays */
if (pf->pvar->name)
pf->tvar->name = xstrdup(pf->pvar->name);
else {
synthesize_perf_probe_arg(pf->pvar, buf, 32);
+ ptr = strchr(buf, ':'); /* Change type separator to _ */
+ if (ptr)
+ *ptr = '_';
pf->tvar->name = xstrdup(buf);
}



--
Masami Hiramatsu
e-mail: [email protected]

2010-04-12 17:10:53

by Masami Hiramatsu

[permalink] [raw]
Subject: [PATCH -tip v3 04/10] perf probe: Query basic types from debuginfo

Query the basic type information (byte-size and signed-flag) from
debuginfo and pass that to kprobe-tracer. This is especially useful
for tracing the members of data structure, because each member has
different byte-size on the memory.

Signed-off-by: Masami Hiramatsu <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Mike Galbraith <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
---

tools/perf/util/probe-event.c | 9 +++++
tools/perf/util/probe-event.h | 1 +
tools/perf/util/probe-finder.c | 78 ++++++++++++++++++++++++++++++++++++----
3 files changed, 80 insertions(+), 8 deletions(-)

diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 19de8b7..05ca4a9 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -740,6 +740,13 @@ static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg,
buf += ret;
buflen -= ret;
}
+ /* Print argument type */
+ if (arg->type) {
+ ret = e_snprintf(buf, buflen, ":%s", arg->type);
+ if (ret <= 0)
+ return ret;
+ buf += ret;
+ }

return buf - tmp;
}
@@ -848,6 +855,8 @@ void clear_kprobe_trace_event(struct kprobe_trace_event *tev)
free(tev->args[i].name);
if (tev->args[i].value)
free(tev->args[i].value);
+ if (tev->args[i].type)
+ free(tev->args[i].type);
ref = tev->args[i].ref;
while (ref) {
next = ref->next;
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index a73ede6..0759db6 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -23,6 +23,7 @@ struct kprobe_trace_arg_ref {
struct kprobe_trace_arg {
char *name; /* Argument name */
char *value; /* Base value */
+ char *type; /* Type name */
struct kprobe_trace_arg_ref *ref; /* Referencing offset */
};

diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 105e95c..ebeb413 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -84,6 +84,9 @@ const char *x86_64_regs_table[X86_64_MAX_REGS] = {
#define arch_regs_table x86_32_regs_table
#endif

+/* Kprobe tracer basic type is up to u64 */
+#define MAX_BASIC_TYPE_BITS 64
+
/* Return architecture dependent register string (for kprobe-tracer) */
static const char *get_arch_regstr(unsigned int n)
{
@@ -230,6 +233,31 @@ static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
return die_mem;
}

+static bool die_is_signed_type(Dwarf_Die *tp_die)
+{
+ Dwarf_Attribute attr;
+ Dwarf_Word ret;
+
+ if (dwarf_attr(tp_die, DW_AT_encoding, &attr) == NULL ||
+ dwarf_formudata(&attr, &ret) != 0)
+ return false;
+
+ return (ret == DW_ATE_signed_char || ret == DW_ATE_signed ||
+ ret == DW_ATE_signed_fixed);
+}
+
+static int die_get_byte_size(Dwarf_Die *tp_die)
+{
+ Dwarf_Attribute attr;
+ Dwarf_Word ret;
+
+ if (dwarf_attr(tp_die, DW_AT_byte_size, &attr) == NULL ||
+ dwarf_formudata(&attr, &ret) != 0)
+ return 0;
+
+ return (int)ret;
+}
+
/* Return values for die_find callbacks */
enum {
DIE_FIND_CB_FOUND = 0, /* End of Search */
@@ -406,13 +434,42 @@ static void convert_location(Dwarf_Op *op, struct probe_finder *pf)
}
}

+static void convert_variable_type(Dwarf_Die *vr_die,
+ struct kprobe_trace_arg *targ)
+{
+ Dwarf_Die type;
+ char buf[16];
+ int ret;
+
+ if (die_get_real_type(vr_die, &type) == NULL)
+ die("Failed to get a type information of %s.",
+ dwarf_diename(vr_die));
+
+ ret = die_get_byte_size(&type) * 8;
+ if (ret) {
+ /* Check the bitwidth */
+ if (ret > MAX_BASIC_TYPE_BITS) {
+ pr_warning(" Warning: %s exceeds max-bitwidth."
+ " Cut down to %d bits.\n",
+ dwarf_diename(&type), MAX_BASIC_TYPE_BITS);
+ ret = MAX_BASIC_TYPE_BITS;
+ }
+
+ ret = snprintf(buf, 16, "%c%d",
+ die_is_signed_type(&type) ? 's' : 'u', ret);
+ if (ret < 0 || ret >= 16)
+ die("Failed to convert variable type.");
+ targ->type = xstrdup(buf);
+ }
+}
+
static void convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
struct perf_probe_arg_field *field,
- struct kprobe_trace_arg_ref **ref_ptr)
+ struct kprobe_trace_arg_ref **ref_ptr,
+ Dwarf_Die *die_mem)
{
struct kprobe_trace_arg_ref *ref = *ref_ptr;
Dwarf_Attribute attr;
- Dwarf_Die member;
Dwarf_Die type;
Dwarf_Word offs;

@@ -450,26 +507,27 @@ static void convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
die("Structure on a register is not supported yet.");
}

- if (die_find_member(&type, field->name, &member) == NULL)
+ if (die_find_member(&type, field->name, die_mem) == NULL)
die("%s(tyep:%s) has no member %s.", varname,
dwarf_diename(&type), field->name);

/* Get the offset of the field */
- if (dwarf_attr(&member, DW_AT_data_member_location, &attr) == NULL ||
+ if (dwarf_attr(die_mem, DW_AT_data_member_location, &attr) == NULL ||
dwarf_formudata(&attr, &offs) != 0)
die("Failed to get the offset of %s.", field->name);
ref->offset += (long)offs;

/* Converting next field */
if (field->next)
- convert_variable_fields(&member, field->name, field->next,
- &ref);
+ convert_variable_fields(die_mem, field->name, field->next,
+ &ref, die_mem);
}

/* Show a variables in kprobe event format */
static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
{
Dwarf_Attribute attr;
+ Dwarf_Die die_mem;
Dwarf_Op *expr;
size_t nexpr;
int ret;
@@ -483,9 +541,13 @@ static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)

convert_location(expr, pf);

- if (pf->pvar->field)
+ if (pf->pvar->field) {
convert_variable_fields(vr_die, pf->pvar->var,
- pf->pvar->field, &pf->tvar->ref);
+ pf->pvar->field, &pf->tvar->ref,
+ &die_mem);
+ vr_die = &die_mem;
+ }
+ convert_variable_type(vr_die, pf->tvar);
/* *expr will be cached in libdw. Don't free it. */
return ;
error:


--
Masami Hiramatsu
e-mail: [email protected]

2010-04-12 17:11:11

by Masami Hiramatsu

[permalink] [raw]
Subject: [PATCH -tip v3 08/10] perf probe: Remove die() from probe-event code

Remove die() and DIE_IF() code from util/probe-event.c since
these 'sudden death' in utility functions make reusing it from
other code (especially tui/gui) difficult.

Signed-off-by: Masami Hiramatsu <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Mike Galbraith <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
---

tools/perf/builtin-probe.c | 70 +++--
tools/perf/util/probe-event.c | 615 ++++++++++++++++++++++++++++-------------
tools/perf/util/probe-event.h | 24 +-
3 files changed, 480 insertions(+), 229 deletions(-)

diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index dafaf76..5259c5a 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -59,23 +59,25 @@ static struct {


/* Parse an event definition. Note that any error must die. */
-static void parse_probe_event(const char *str)
+static int parse_probe_event(const char *str)
{
struct perf_probe_event *pev = &params.events[params.nevents];
+ int ret;

pr_debug("probe-definition(%d): %s\n", params.nevents, str);
if (++params.nevents == MAX_PROBES)
die("Too many probes (> %d) are specified.", MAX_PROBES);

/* Parse a perf-probe command into event */
- parse_perf_probe_command(str, pev);
-
+ ret = parse_perf_probe_command(str, pev);
pr_debug("%d arguments\n", pev->nargs);
+
+ return ret;
}

-static void parse_probe_event_argv(int argc, const char **argv)
+static int parse_probe_event_argv(int argc, const char **argv)
{
- int i, len;
+ int i, len, ret;
char *buf;

/* Bind up rest arguments */
@@ -86,16 +88,18 @@ static void parse_probe_event_argv(int argc, const char **argv)
len = 0;
for (i = 0; i < argc; i++)
len += sprintf(&buf[len], "%s ", argv[i]);
- parse_probe_event(buf);
+ ret = parse_probe_event(buf);
free(buf);
+ return ret;
}

static int opt_add_probe_event(const struct option *opt __used,
const char *str, int unset __used)
{
if (str)
- parse_probe_event(str);
- return 0;
+ return parse_probe_event(str);
+ else
+ return 0;
}

static int opt_del_probe_event(const struct option *opt __used,
@@ -113,11 +117,14 @@ static int opt_del_probe_event(const struct option *opt __used,
static int opt_show_lines(const struct option *opt __used,
const char *str, int unset __used)
{
+ int ret = 0;
+
if (str)
- parse_line_range_desc(str, &params.line_range);
+ ret = parse_line_range_desc(str, &params.line_range);
INIT_LIST_HEAD(&params.line_range.line_list);
params.show_lines = true;
- return 0;
+
+ return ret;
}
#endif

@@ -178,6 +185,8 @@ static const struct option options[] = {

int cmd_probe(int argc, const char **argv, const char *prefix __used)
{
+ int ret;
+
argc = parse_options(argc, argv, options, probe_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
if (argc > 0) {
@@ -185,7 +194,11 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
pr_warning(" Error: '-' is not supported.\n");
usage_with_options(probe_usage, options);
}
- parse_probe_event_argv(argc, argv);
+ ret = parse_probe_event_argv(argc, argv);
+ if (ret < 0) {
+ pr_err(" Error: Parse Error. (%d)\n", ret);
+ return ret;
+ }
}

if ((!params.nevents && !params.dellist && !params.list_events &&
@@ -197,16 +210,18 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)

if (params.list_events) {
if (params.nevents != 0 || params.dellist) {
- pr_warning(" Error: Don't use --list with"
- " --add/--del.\n");
+ pr_err(" Error: Don't use --list with --add/--del.\n");
usage_with_options(probe_usage, options);
}
if (params.show_lines) {
- pr_warning(" Error: Don't use --list with --line.\n");
+ pr_err(" Error: Don't use --list with --line.\n");
usage_with_options(probe_usage, options);
}
- show_perf_probe_events();
- return 0;
+ ret = show_perf_probe_events();
+ if (ret < 0)
+ pr_err(" Error: Failed to show event list. (%d)\n",
+ ret);
+ return ret;
}

#ifdef DWARF_SUPPORT
@@ -217,19 +232,30 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
usage_with_options(probe_usage, options);
}

- show_line_range(&params.line_range);
- return 0;
+ ret = show_line_range(&params.line_range);
+ if (ret < 0)
+ pr_err(" Error: Failed to show lines. (%d)\n", ret);
+ return ret;
}
#endif

if (params.dellist) {
- del_perf_probe_events(params.dellist);
+ ret = del_perf_probe_events(params.dellist);
strlist__delete(params.dellist);
- if (params.nevents == 0)
- return 0;
+ if (ret < 0) {
+ pr_err(" Error: Failed to delete events. (%d)\n", ret);
+ return ret;
+ }
}

- add_perf_probe_events(params.events, params.nevents, params.force_add);
+ if (params.nevents) {
+ ret = add_perf_probe_events(params.events, params.nevents,
+ params.force_add);
+ if (ret < 0) {
+ pr_err(" Error: Failed to add events. (%d)\n", ret);
+ return ret;
+ }
+ }
return 0;
}

diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 7893b32..bd68f7b 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -53,7 +53,7 @@

bool probe_event_dry_run; /* Dry run flag */

-#define semantic_error(msg ...) die("Semantic error :" msg)
+#define semantic_error(msg ...) pr_err("Semantic error :" msg)

/* If there is no space to write, returns -E2BIG. */
static int e_snprintf(char *str, size_t size, const char *format, ...)
@@ -76,19 +76,30 @@ static struct map_groups kmap_groups;
static struct map *kmaps[MAP__NR_TYPES];

/* Initialize symbol maps and path of vmlinux */
-static void init_vmlinux(void)
+static int init_vmlinux(void)
{
+ int ret;
+
symbol_conf.sort_by_name = true;
if (symbol_conf.vmlinux_name == NULL)
symbol_conf.try_vmlinux_path = true;
else
pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name);
- if (symbol__init() < 0)
- die("Failed to init symbol map.");
+ ret = symbol__init();
+ if (ret < 0) {
+ pr_debug("Failed to init symbol map.\n");
+ goto out;
+ }

map_groups__init(&kmap_groups);
- if (map_groups__create_kernel_maps(&kmap_groups, kmaps) < 0)
- die("Failed to create kernel maps.");
+ ret = map_groups__create_kernel_maps(&kmap_groups, kmaps);
+ if (ret < 0)
+ pr_debug("Failed to create kernel maps.\n");
+
+out:
+ if (ret < 0)
+ pr_warning("Failed to init vmlinux path.\n");
+ return ret;
}

#ifdef DWARF_SUPPORT
@@ -102,24 +113,32 @@ static int open_vmlinux(void)
return open(kmaps[MAP__FUNCTION]->dso->long_name, O_RDONLY);
}

-static void convert_to_perf_probe_point(struct kprobe_trace_point *tp,
- struct perf_probe_point *pp)
+/* Convert trace point to probe point with debuginfo */
+static int convert_to_perf_probe_point(struct kprobe_trace_point *tp,
+ struct perf_probe_point *pp)
{
struct symbol *sym;
- int fd, ret = 0;
+ int fd, ret = -ENOENT;

sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION],
tp->symbol, NULL);
if (sym) {
fd = open_vmlinux();
- ret = find_perf_probe_point(fd, sym->start + tp->offset, pp);
- close(fd);
+ if (fd >= 0) {
+ ret = find_perf_probe_point(fd,
+ sym->start + tp->offset, pp);
+ close(fd);
+ }
}
if (ret <= 0) {
+ pr_debug("Failed to find corresponding probes from "
+ "debuginfo. Use kprobe event information.\n");
pp->function = xstrdup(tp->symbol);
pp->offset = tp->offset;
}
pp->retprobe = tp->retprobe;
+
+ return 0;
}

/* Try to find perf_probe_event with debuginfo */
@@ -131,9 +150,10 @@ static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev,

fd = open_vmlinux();
if (fd < 0) {
- if (need_dwarf)
- die("Could not open debuginfo file.");
-
+ if (need_dwarf) {
+ pr_warning("Failed to open debuginfo file.\n");
+ return fd;
+ }
pr_debug("Could not open vmlinux. Try to use symbols.\n");
return 0;
}
@@ -142,30 +162,32 @@ static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev,
ntevs = find_kprobe_trace_events(fd, pev, tevs);
close(fd);

- if (ntevs > 0) /* Succeeded to find trace events */
+ if (ntevs > 0) { /* Succeeded to find trace events */
+ pr_debug("find %d kprobe_trace_events.\n", ntevs);
return ntevs;
+ }

- if (ntevs == 0) /* No error but failed to find probe point. */
- die("Probe point '%s' not found. - probe not added.",
- synthesize_perf_probe_point(&pev->point));
-
- /* Error path */
+ if (ntevs == 0) { /* No error but failed to find probe point. */
+ pr_warning("Probe point '%s' not found.\n",
+ synthesize_perf_probe_point(&pev->point));
+ return -ENOENT;
+ }
+ /* Error path : ntevs < 0 */
if (need_dwarf) {
if (ntevs == -EBADF)
pr_warning("No dwarf info found in the vmlinux - "
"please rebuild with CONFIG_DEBUG_INFO=y.\n");
- die("Failed to analyze debuginfo.");
+ return ntevs;
}
pr_debug("An error occurred in debuginfo analysis."
" Try to use symbols.\n");
return 0;
-
}

#define LINEBUF_SIZE 256
#define NR_ADDITIONAL_LINES 2

-static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num)
+static int show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num)
{
char buf[LINEBUF_SIZE];
const char *color = PERF_COLOR_BLUE;
@@ -190,19 +212,22 @@ static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num)
color_fprintf(stdout, color, "%s", buf);
}
}
- return;
+
+ return 0;
error:
if (feof(fp))
- die("Source file is shorter than expected.");
+ pr_warning("Source file is shorter than expected.\n");
else
- die("File read error: %s", strerror(errno));
+ pr_warning("File read error: %s\n", strerror(errno));
+
+ return -1;
}

/*
* Show line-range always requires debuginfo to find source file and
* line number.
*/
-void show_line_range(struct line_range *lr)
+int show_line_range(struct line_range *lr)
{
unsigned int l = 1;
struct line_node *ln;
@@ -210,14 +235,25 @@ void show_line_range(struct line_range *lr)
int fd, ret;

/* Search a line range */
- init_vmlinux();
+ ret = init_vmlinux();
+ if (ret < 0)
+ return ret;
+
fd = open_vmlinux();
- if (fd < 0)
- die("Could not open debuginfo file.");
+ if (fd < 0) {
+ pr_warning("Failed to open debuginfo file.\n");
+ return fd;
+ }
+
ret = find_line_range(fd, lr);
- if (ret <= 0)
- die("Source line is not found.\n");
close(fd);
+ if (ret == 0) {
+ pr_warning("Specified source line is not found.\n");
+ return -ENOENT;
+ } else if (ret < 0) {
+ pr_warning("Debuginfo analysis failed. (%d)\n", ret);
+ return ret;
+ }

setup_pager();

@@ -228,52 +264,68 @@ void show_line_range(struct line_range *lr)
fprintf(stdout, "<%s:%d>\n", lr->file, lr->start);

fp = fopen(lr->path, "r");
- if (fp == NULL)
- die("Failed to open %s: %s", lr->path, strerror(errno));
+ if (fp == NULL) {
+ pr_warning("Failed to open %s: %s\n", lr->path,
+ strerror(errno));
+ return -errno;
+ }
/* Skip to starting line number */
- while (l < lr->start)
- show_one_line(fp, l++, true, false);
+ while (l < lr->start && ret >= 0)
+ ret = show_one_line(fp, l++, true, false);
+ if (ret < 0)
+ goto end;

list_for_each_entry(ln, &lr->line_list, list) {
- while (ln->line > l)
- show_one_line(fp, (l++) - lr->offset, false, false);
- show_one_line(fp, (l++) - lr->offset, false, true);
+ while (ln->line > l && ret >= 0)
+ ret = show_one_line(fp, (l++) - lr->offset,
+ false, false);
+ if (ret >= 0)
+ ret = show_one_line(fp, (l++) - lr->offset,
+ false, true);
+ if (ret < 0)
+ goto end;
}

if (lr->end == INT_MAX)
lr->end = l + NR_ADDITIONAL_LINES;
- while (l < lr->end && !feof(fp))
- show_one_line(fp, (l++) - lr->offset, false, false);
-
+ while (l < lr->end && !feof(fp) && ret >= 0)
+ ret = show_one_line(fp, (l++) - lr->offset, false, false);
+end:
fclose(fp);
+ return ret;
}

#else /* !DWARF_SUPPORT */

-static void convert_to_perf_probe_point(struct kprobe_trace_point *tp,
+static int convert_to_perf_probe_point(struct kprobe_trace_point *tp,
struct perf_probe_point *pp)
{
pp->function = xstrdup(tp->symbol);
pp->offset = tp->offset;
pp->retprobe = tp->retprobe;
+
+ return 0;
}

static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev,
struct kprobe_trace_event **tevs __unused)
{
- if (perf_probe_event_need_dwarf(pev))
- die("Debuginfo-analysis is not supported");
+ if (perf_probe_event_need_dwarf(pev)) {
+ pr_warning("Debuginfo-analysis is not supported.\n");
+ return -ENOSYS;
+ }
return 0;
}

-void show_line_range(struct line_range *lr __unused)
+int show_line_range(struct line_range *lr __unused)
{
- die("Debuginfo-analysis is not supported");
+ pr_warning("Debuginfo-analysis is not supported.\n");
+ return -ENOSYS;
}

#endif

-void parse_line_range_desc(const char *arg, struct line_range *lr)
+int parse_line_range_desc(const char *arg, struct line_range *lr)
{
const char *ptr;
char *tmp;
@@ -293,12 +345,16 @@ void parse_line_range_desc(const char *arg, struct line_range *lr)
else
lr->end = 0;
pr_debug("Line range is %u to %u\n", lr->start, lr->end);
- if (lr->end && lr->start > lr->end)
+ if (lr->end && lr->start > lr->end) {
semantic_error("Start line must be smaller"
- " than end line.");
- if (*tmp != '\0')
- semantic_error("Tailing with invalid character '%d'.",
+ " than end line.\n");
+ return -EINVAL;
+ }
+ if (*tmp != '\0') {
+ semantic_error("Tailing with invalid character '%d'.\n",
*tmp);
+ return -EINVAL;
+ }
tmp = xstrndup(arg, (ptr - arg));
} else
tmp = xstrdup(arg);
@@ -307,6 +363,8 @@ void parse_line_range_desc(const char *arg, struct line_range *lr)
lr->file = tmp;
else
lr->function = tmp;
+
+ return 0;
}

/* Check the name is good for event/group */
@@ -322,7 +380,7 @@ static bool check_event_name(const char *name)
}

/* Parse probepoint definition. */
-static void parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
+static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
{
struct perf_probe_point *pp = &pev->point;
char *ptr, *tmp;
@@ -339,12 +397,15 @@ static void parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
if (ptr && *ptr == '=') { /* Event name */
*ptr = '\0';
tmp = ptr + 1;
- ptr = strchr(arg, ':');
- if (ptr) /* Group name is not supported yet. */
- semantic_error("Group name is not supported yet.");
- if (!check_event_name(arg))
+ if (strchr(arg, ':')) {
+ semantic_error("Group name is not supported yet.\n");
+ return -ENOTSUP;
+ }
+ if (!check_event_name(arg)) {
semantic_error("%s is bad for event name -it must "
- "follow C symbol-naming rule.", arg);
+ "follow C symbol-naming rule.\n", arg);
+ return -EINVAL;
+ }
pev->event = xstrdup(arg);
pev->group = NULL;
arg = tmp;
@@ -378,64 +439,89 @@ static void parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
switch (c) {
case ':': /* Line number */
pp->line = strtoul(arg, &tmp, 0);
- if (*tmp != '\0')
+ if (*tmp != '\0') {
semantic_error("There is non-digit char"
- " in line number.");
+ " in line number.\n");
+ return -EINVAL;
+ }
break;
case '+': /* Byte offset from a symbol */
pp->offset = strtoul(arg, &tmp, 0);
- if (*tmp != '\0')
+ if (*tmp != '\0') {
semantic_error("There is non-digit character"
- " in offset.");
+ " in offset.\n");
+ return -EINVAL;
+ }
break;
case '@': /* File name */
- if (pp->file)
- semantic_error("SRC@SRC is not allowed.");
+ if (pp->file) {
+ semantic_error("SRC@SRC is not allowed.\n");
+ return -EINVAL;
+ }
pp->file = xstrdup(arg);
break;
case '%': /* Probe places */
if (strcmp(arg, "return") == 0) {
pp->retprobe = 1;
- } else /* Others not supported yet */
- semantic_error("%%%s is not supported.", arg);
+ } else { /* Others not supported yet */
+ semantic_error("%%%s is not supported.\n", arg);
+ return -ENOTSUP;
+ }
break;
- default:
- DIE_IF("Program has a bug.");
+ default: /* Buggy case */
+ pr_err("This program has a bug at %s:%d.\n",
+ __FILE__, __LINE__);
+ return -ENOTSUP;
break;
}
}

/* Exclusion check */
- if (pp->lazy_line && pp->line)
+ if (pp->lazy_line && pp->line) {
semantic_error("Lazy pattern can't be used with line number.");
+ return -EINVAL;
+ }

- if (pp->lazy_line && pp->offset)
+ if (pp->lazy_line && pp->offset) {
semantic_error("Lazy pattern can't be used with offset.");
+ return -EINVAL;
+ }

- if (pp->line && pp->offset)
+ if (pp->line && pp->offset) {
semantic_error("Offset can't be used with line number.");
+ return -EINVAL;
+ }

- if (!pp->line && !pp->lazy_line && pp->file && !pp->function)
+ if (!pp->line && !pp->lazy_line && pp->file && !pp->function) {
semantic_error("File always requires line number or "
"lazy pattern.");
+ return -EINVAL;
+ }

- if (pp->offset && !pp->function)
+ if (pp->offset && !pp->function) {
semantic_error("Offset requires an entry function.");
+ return -EINVAL;
+ }

- if (pp->retprobe && !pp->function)
+ if (pp->retprobe && !pp->function) {
semantic_error("Return probe requires an entry function.");
+ return -EINVAL;
+ }

- if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe)
+ if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe) {
semantic_error("Offset/Line/Lazy pattern can't be used with "
"return probe.");
+ return -EINVAL;
+ }

pr_debug("symbol:%s file:%s line:%d offset:%lu return:%d lazy:%s\n",
pp->function, pp->file, pp->line, pp->offset, pp->retprobe,
pp->lazy_line);
+ return 0;
}

/* Parse perf-probe event argument */
-static void parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
+static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
{
char *tmp;
struct perf_probe_arg_field **fieldp;
@@ -461,7 +547,7 @@ static void parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
/* A variable, register, symbol or special value */
arg->var = xstrdup(str);
pr_debug("%s\n", arg->var);
- return;
+ return 0;
}

/* Structure fields */
@@ -477,8 +563,10 @@ static void parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
} else if (tmp[1] == '>') {
str = tmp + 2;
(*fieldp)->ref = true;
- } else
- semantic_error("Argument parse error: %s", str);
+ } else {
+ semantic_error("Argument parse error: %s\n", str);
+ return -EINVAL;
+ }

tmp = strpbrk(str, "-.");
if (tmp) {
@@ -493,34 +581,47 @@ static void parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
/* If no name is specified, set the last field name */
if (!arg->name)
arg->name = xstrdup((*fieldp)->name);
+
+ return 0;
}

/* Parse perf-probe event command */
-void parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev)
+int parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev)
{
char **argv;
- int argc, i;
+ int argc, i, ret = 0;

argv = argv_split(cmd, &argc);
- if (!argv)
- die("argv_split failed.");
- if (argc > MAX_PROBE_ARGS + 1)
- semantic_error("Too many arguments");
-
+ if (!argv) {
+ pr_debug("Failed to split arguments.\n");
+ return -ENOMEM;
+ }
+ if (argc - 1 > MAX_PROBE_ARGS) {
+ semantic_error("Too many probe arguments (%d).\n", argc - 1);
+ ret = -ERANGE;
+ goto out;
+ }
/* Parse probe point */
- parse_perf_probe_point(argv[0], pev);
+ ret = parse_perf_probe_point(argv[0], pev);
+ if (ret < 0)
+ goto out;

/* Copy arguments and ensure return probe has no C argument */
pev->nargs = argc - 1;
pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs);
- for (i = 0; i < pev->nargs; i++) {
- parse_perf_probe_arg(argv[i + 1], &pev->args[i]);
- if (is_c_varname(pev->args[i].var) && pev->point.retprobe)
+ for (i = 0; i < pev->nargs && ret >= 0; i++) {
+ ret = parse_perf_probe_arg(argv[i + 1], &pev->args[i]);
+ if (ret >= 0 &&
+ is_c_varname(pev->args[i].var) && pev->point.retprobe) {
semantic_error("You can't specify local variable for"
- " kretprobe");
+ " kretprobe.\n");
+ ret = -EINVAL;
+ }
}
-
+out:
argv_free(argv);
+
+ return ret;
}

/* Return true if this perf_probe_event requires debuginfo */
@@ -539,7 +640,7 @@ bool perf_probe_event_need_dwarf(struct perf_probe_event *pev)
}

/* Parse kprobe_events event into struct probe_point */
-void parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev)
+int parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev)
{
struct kprobe_trace_point *tp = &tev->point;
char pr;
@@ -549,17 +650,25 @@ void parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev)

pr_debug("Parsing kprobe_events: %s\n", cmd);
argv = argv_split(cmd, &argc);
- if (!argv)
- die("argv_split failed.");
- if (argc < 2)
- semantic_error("Too less arguments.");
+ if (!argv) {
+ pr_debug("Failed to split arguments.\n");
+ return -ENOMEM;
+ }
+ if (argc < 2) {
+ semantic_error("Too few probe arguments.\n");
+ ret = -ERANGE;
+ goto out;
+ }

/* Scan event and group name. */
ret = sscanf(argv[0], "%c:%a[^/ \t]/%a[^ \t]",
&pr, (float *)(void *)&tev->group,
(float *)(void *)&tev->event);
- if (ret != 3)
- semantic_error("Failed to parse event name: %s", argv[0]);
+ if (ret != 3) {
+ semantic_error("Failed to parse event name: %s\n", argv[0]);
+ ret = -EINVAL;
+ goto out;
+ }
pr_debug("Group:%s Event:%s probe:%c\n", tev->group, tev->event, pr);

tp->retprobe = (pr == 'r');
@@ -582,8 +691,10 @@ void parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev)
/* TODO: parse regs and offset */
tev->args[i].value = xstrdup(p);
}
-
+ ret = 0;
+out:
argv_free(argv);
+ return ret;
}

/* Compose only probe arg */
@@ -622,7 +733,9 @@ int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len)

return tmp - buf;
error:
- die("Failed to synthesize perf probe argument: %s", strerror(-ret));
+ pr_debug("Failed to synthesize perf probe argument: %s",
+ strerror(-ret));
+ return ret;
}

/* Compose only probe point (not argument) */
@@ -666,7 +779,10 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp)

return buf;
error:
- die("Failed to synthesize perf probe point: %s", strerror(-ret));
+ pr_debug("Failed to synthesize perf probe point: %s",
+ strerror(-ret));
+ free(buf);
+ return NULL;
}

#if 0
@@ -796,29 +912,37 @@ error:
return NULL;
}

-void convert_to_perf_probe_event(struct kprobe_trace_event *tev,
- struct perf_probe_event *pev)
+int convert_to_perf_probe_event(struct kprobe_trace_event *tev,
+ struct perf_probe_event *pev)
{
char buf[64];
- int i;
+ int i, ret;

/* Convert event/group name */
pev->event = xstrdup(tev->event);
pev->group = xstrdup(tev->group);

/* Convert trace_point to probe_point */
- convert_to_perf_probe_point(&tev->point, &pev->point);
+ ret = convert_to_perf_probe_point(&tev->point, &pev->point);
+ if (ret < 0)
+ return ret;

/* Convert trace_arg to probe_arg */
pev->nargs = tev->nargs;
pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs);
- for (i = 0; i < tev->nargs; i++)
+ for (i = 0; i < tev->nargs && ret >= 0; i++)
if (tev->args[i].name)
pev->args[i].name = xstrdup(tev->args[i].name);
else {
- synthesize_kprobe_trace_arg(&tev->args[i], buf, 64);
+ ret = synthesize_kprobe_trace_arg(&tev->args[i],
+ buf, 64);
pev->args[i].name = xstrdup(buf);
}
+
+ if (ret < 0)
+ clear_perf_probe_event(pev);
+
+ return ret;
}

void clear_perf_probe_event(struct perf_probe_event *pev)
@@ -894,21 +1018,20 @@ static int open_kprobe_events(bool readwrite)
int ret;

ret = e_snprintf(buf, PATH_MAX, "%s/../kprobe_events", debugfs_path);
- if (ret < 0)
- die("Failed to make kprobe_events path.");
-
- if (readwrite && !probe_event_dry_run)
- ret = open(buf, O_RDWR, O_APPEND);
- else
- ret = open(buf, O_RDONLY, 0);
+ if (ret >= 0) {
+ if (readwrite && !probe_event_dry_run)
+ ret = open(buf, O_RDWR, O_APPEND);
+ else
+ ret = open(buf, O_RDONLY, 0);
+ }

if (ret < 0) {
if (errno == ENOENT)
- die("kprobe_events file does not exist -"
- " please rebuild with CONFIG_KPROBE_EVENT.");
+ pr_warning("kprobe_events file does not exist - please"
+ " rebuild kernel with CONFIG_KPROBE_EVENT.\n");
else
- die("Could not open kprobe_events file: %s",
- strerror(errno));
+ pr_warning("Failed to open kprobe_events file: %s\n",
+ strerror(errno));
}
return ret;
}
@@ -934,8 +1057,11 @@ static struct strlist *get_kprobe_trace_command_rawlist(int fd)
if (p[idx] == '\n')
p[idx] = '\0';
ret = strlist__add(sl, buf);
- if (ret < 0)
- die("strlist__add failed: %s", strerror(-ret));
+ if (ret < 0) {
+ pr_debug("strlist__add failed: %s\n", strerror(-ret));
+ strlist__delete(sl);
+ return NULL;
+ }
}
fclose(fp);

@@ -943,7 +1069,7 @@ static struct strlist *get_kprobe_trace_command_rawlist(int fd)
}

/* Show an event */
-static void show_perf_probe_event(struct perf_probe_event *pev)
+static int show_perf_probe_event(struct perf_probe_event *pev)
{
int i, ret;
char buf[128];
@@ -951,52 +1077,71 @@ static void show_perf_probe_event(struct perf_probe_event *pev)

/* Synthesize only event probe point */
place = synthesize_perf_probe_point(&pev->point);
+ if (!place)
+ return -EINVAL;

ret = e_snprintf(buf, 128, "%s:%s", pev->group, pev->event);
if (ret < 0)
- die("Failed to copy event: %s", strerror(-ret));
+ return ret;
+
printf(" %-20s (on %s", buf, place);

if (pev->nargs > 0) {
printf(" with");
for (i = 0; i < pev->nargs; i++) {
- synthesize_perf_probe_arg(&pev->args[i], buf, 128);
+ ret = synthesize_perf_probe_arg(&pev->args[i],
+ buf, 128);
+ if (ret < 0)
+ break;
printf(" %s", buf);
}
}
printf(")\n");
free(place);
+ return ret;
}

/* List up current perf-probe events */
-void show_perf_probe_events(void)
+int show_perf_probe_events(void)
{
- int fd;
+ int fd, ret;
struct kprobe_trace_event tev;
struct perf_probe_event pev;
struct strlist *rawlist;
struct str_node *ent;

setup_pager();
- init_vmlinux();
+ ret = init_vmlinux();
+ if (ret < 0)
+ return ret;

memset(&tev, 0, sizeof(tev));
memset(&pev, 0, sizeof(pev));

fd = open_kprobe_events(false);
+ if (fd < 0)
+ return fd;
+
rawlist = get_kprobe_trace_command_rawlist(fd);
close(fd);
+ if (!rawlist)
+ return -ENOENT;

strlist__for_each(ent, rawlist) {
- parse_kprobe_trace_command(ent->s, &tev);
- convert_to_perf_probe_event(&tev, &pev);
- /* Show an event */
- show_perf_probe_event(&pev);
+ ret = parse_kprobe_trace_command(ent->s, &tev);
+ if (ret >= 0) {
+ ret = convert_to_perf_probe_event(&tev, &pev);
+ if (ret >= 0)
+ ret = show_perf_probe_event(&pev);
+ }
clear_perf_probe_event(&pev);
clear_kprobe_trace_event(&tev);
+ if (ret < 0)
+ break;
}
-
strlist__delete(rawlist);
+
+ return ret;
}

/* Get current perf-probe event names */
@@ -1006,88 +1151,118 @@ static struct strlist *get_kprobe_trace_event_names(int fd, bool include_group)
struct strlist *sl, *rawlist;
struct str_node *ent;
struct kprobe_trace_event tev;
+ int ret = 0;

memset(&tev, 0, sizeof(tev));

rawlist = get_kprobe_trace_command_rawlist(fd);
sl = strlist__new(true, NULL);
strlist__for_each(ent, rawlist) {
- parse_kprobe_trace_command(ent->s, &tev);
+ ret = parse_kprobe_trace_command(ent->s, &tev);
+ if (ret < 0)
+ break;
if (include_group) {
- if (e_snprintf(buf, 128, "%s:%s", tev.group,
- tev.event) < 0)
- die("Failed to copy group:event name.");
- strlist__add(sl, buf);
+ ret = e_snprintf(buf, 128, "%s:%s", tev.group,
+ tev.event);
+ if (ret >= 0)
+ ret = strlist__add(sl, buf);
} else
- strlist__add(sl, tev.event);
+ ret = strlist__add(sl, tev.event);
clear_kprobe_trace_event(&tev);
+ if (ret < 0)
+ break;
}
-
strlist__delete(rawlist);

+ if (ret < 0) {
+ strlist__delete(sl);
+ return NULL;
+ }
return sl;
}

-static void write_kprobe_trace_event(int fd, struct kprobe_trace_event *tev)
+static int write_kprobe_trace_event(int fd, struct kprobe_trace_event *tev)
{
int ret;
char *buf = synthesize_kprobe_trace_command(tev);

+ if (!buf) {
+ pr_debug("Failed to synthesize kprobe trace event.\n");
+ return -EINVAL;
+ }
+
pr_debug("Writing event: %s\n", buf);
if (!probe_event_dry_run) {
ret = write(fd, buf, strlen(buf));
if (ret <= 0)
- die("Failed to write event: %s", strerror(errno));
+ pr_warning("Failed to write event: %s\n",
+ strerror(errno));
}
free(buf);
+ return ret;
}

-static void get_new_event_name(char *buf, size_t len, const char *base,
- struct strlist *namelist, bool allow_suffix)
+static int get_new_event_name(char *buf, size_t len, const char *base,
+ struct strlist *namelist, bool allow_suffix)
{
int i, ret;

/* Try no suffix */
ret = e_snprintf(buf, len, "%s", base);
- if (ret < 0)
- die("snprintf() failed: %s", strerror(-ret));
+ if (ret < 0) {
+ pr_debug("snprintf() failed: %s\n", strerror(-ret));
+ return ret;
+ }
if (!strlist__has_entry(namelist, buf))
- return;
+ return 0;

if (!allow_suffix) {
pr_warning("Error: event \"%s\" already exists. "
"(Use -f to force duplicates.)\n", base);
- die("Can't add new event.");
+ return -EEXIST;
}

/* Try to add suffix */
for (i = 1; i < MAX_EVENT_INDEX; i++) {
ret = e_snprintf(buf, len, "%s_%d", base, i);
- if (ret < 0)
- die("snprintf() failed: %s", strerror(-ret));
+ if (ret < 0) {
+ pr_debug("snprintf() failed: %s\n", strerror(-ret));
+ return ret;
+ }
if (!strlist__has_entry(namelist, buf))
break;
}
- if (i == MAX_EVENT_INDEX)
- die("Too many events are on the same function.");
+ if (i == MAX_EVENT_INDEX) {
+ pr_warning("Too many events are on the same function.\n");
+ ret = -ERANGE;
+ }
+
+ return ret;
}

-static void __add_kprobe_trace_events(struct perf_probe_event *pev,
- struct kprobe_trace_event *tevs,
- int ntevs, bool allow_suffix)
+static int __add_kprobe_trace_events(struct perf_probe_event *pev,
+ struct kprobe_trace_event *tevs,
+ int ntevs, bool allow_suffix)
{
- int i, fd;
+ int i, fd, ret;
struct kprobe_trace_event *tev = NULL;
char buf[64];
const char *event, *group;
struct strlist *namelist;

fd = open_kprobe_events(true);
+ if (fd < 0)
+ return fd;
/* Get current event names */
namelist = get_kprobe_trace_event_names(fd, false);
+ if (!namelist) {
+ pr_debug("Failed to get current event list.\n");
+ return -EIO;
+ }

+ ret = 0;
printf("Add new event%s\n", (ntevs > 1) ? "s:" : ":");
- for (i = 0; i < ntevs; i++) {
+ for (i = 0; i < ntevs && ret >= 0; i++) {
tev = &tevs[i];
if (pev->event)
event = pev->event;
@@ -1102,12 +1277,17 @@ static void __add_kprobe_trace_events(struct perf_probe_event *pev,
group = PERFPROBE_GROUP;

/* Get an unused new event name */
- get_new_event_name(buf, 64, event, namelist, allow_suffix);
+ ret = get_new_event_name(buf, 64, event,
+ namelist, allow_suffix);
+ if (ret < 0)
+ break;
event = buf;

tev->event = xstrdup(event);
tev->group = xstrdup(group);
- write_kprobe_trace_event(fd, tev);
+ ret = write_kprobe_trace_event(fd, tev);
+ if (ret < 0)
+ break;
/* Add added event name to namelist */
strlist__add(namelist, event);

@@ -1129,12 +1309,17 @@ static void __add_kprobe_trace_events(struct perf_probe_event *pev,
*/
allow_suffix = true;
}
- /* Show how to use the event. */
- printf("\nYou can now use it on all perf tools, such as:\n\n");
- printf("\tperf record -e %s:%s -a sleep 1\n\n", tev->group, tev->event);
+
+ if (ret >= 0) {
+ /* Show how to use the event. */
+ printf("\nYou can now use it on all perf tools, such as:\n\n");
+ printf("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group,
+ tev->event);
+ }

strlist__delete(namelist);
close(fd);
+ return ret;
}

static int convert_to_kprobe_trace_events(struct perf_probe_event *pev,
@@ -1146,7 +1331,7 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev,

/* Convert perf_probe_event with debuginfo */
ntevs = try_to_find_kprobe_trace_events(pev, tevs);
- if (ntevs > 0)
+ if (ntevs != 0)
return ntevs;

/* Allocate trace event buffer */
@@ -1172,10 +1357,11 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev,
/* Currently just checking function name from symbol map */
sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION],
tev->point.symbol, NULL);
- if (!sym)
- die("Kernel symbol \'%s\' not found - probe not added.",
- tev->point.symbol);
-
+ if (!sym) {
+ pr_warning("Kernel symbol \'%s\' not found.\n",
+ tev->point.symbol);
+ return -ENOENT;
+ }
return ntevs;
}

@@ -1185,93 +1371,128 @@ struct __event_package {
int ntevs;
};

-void add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
- bool force_add)
+int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
+ bool force_add)
{
- int i;
+ int i, j, ret;
struct __event_package *pkgs;

pkgs = xzalloc(sizeof(struct __event_package) * npevs);

/* Init vmlinux path */
- init_vmlinux();
+ ret = init_vmlinux();
+ if (ret < 0)
+ return ret;

/* Loop 1: convert all events */
for (i = 0; i < npevs; i++) {
pkgs[i].pev = &pevs[i];
/* Convert with or without debuginfo */
- pkgs[i].ntevs = convert_to_kprobe_trace_events(pkgs[i].pev,
- &pkgs[i].tevs);
+ ret = convert_to_kprobe_trace_events(pkgs[i].pev,
+ &pkgs[i].tevs);
+ if (ret < 0)
+ goto end;
+ pkgs[i].ntevs = ret;
}

/* Loop 2: add all events */
+ for (i = 0; i < npevs && ret >= 0; i++)
+ ret = __add_kprobe_trace_events(pkgs[i].pev, pkgs[i].tevs,
+ pkgs[i].ntevs, force_add);
+end:
+ /* Loop 3: cleanup trace events */
for (i = 0; i < npevs; i++)
- __add_kprobe_trace_events(pkgs[i].pev, pkgs[i].tevs,
- pkgs[i].ntevs, force_add);
- /* TODO: cleanup all trace events? */
+ for (j = 0; j < pkgs[i].ntevs; j++)
+ clear_kprobe_trace_event(&pkgs[i].tevs[j]);
+
+ return ret;
}

-static void __del_trace_kprobe_event(int fd, struct str_node *ent)
+static int __del_trace_kprobe_event(int fd, struct str_node *ent)
{
char *p;
char buf[128];
int ret;

/* Convert from perf-probe event to trace-kprobe event */
- if (e_snprintf(buf, 128, "-:%s", ent->s) < 0)
- die("Failed to copy event.");
+ ret = e_snprintf(buf, 128, "-:%s", ent->s);
+ if (ret < 0)
+ goto error;
+
p = strchr(buf + 2, ':');
- if (!p)
- die("Internal error: %s should have ':' but not.", ent->s);
+ if (!p) {
+ pr_debug("Internal error: %s should have ':' but not.\n",
+ ent->s);
+ ret = -ENOTSUP;
+ goto error;
+ }
*p = '/';

pr_debug("Writing event: %s\n", buf);
ret = write(fd, buf, strlen(buf));
- if (ret <= 0)
- die("Failed to write event: %s", strerror(errno));
+ if (ret < 0)
+ goto error;
+
printf("Remove event: %s\n", ent->s);
+ return 0;
+error:
+ pr_warning("Failed to delete event: %s\n", strerror(-ret));
+ return ret;
}

-static void del_trace_kprobe_event(int fd, const char *group,
- const char *event, struct strlist *namelist)
+static int del_trace_kprobe_event(int fd, const char *group,
+ const char *event, struct strlist *namelist)
{
char buf[128];
struct str_node *ent, *n;
- int found = 0;
+ int found = 0, ret = 0;

- if (e_snprintf(buf, 128, "%s:%s", group, event) < 0)
- die("Failed to copy event.");
+ ret = e_snprintf(buf, 128, "%s:%s", group, event);
+ if (ret < 0) {
+ pr_err("Failed to copy event.");
+ return ret;
+ }

if (strpbrk(buf, "*?")) { /* Glob-exp */
strlist__for_each_safe(ent, n, namelist)
if (strglobmatch(ent->s, buf)) {
found++;
- __del_trace_kprobe_event(fd, ent);
+ ret = __del_trace_kprobe_event(fd, ent);
+ if (ret < 0)
+ break;
strlist__remove(namelist, ent);
}
} else {
ent = strlist__find(namelist, buf);
if (ent) {
found++;
- __del_trace_kprobe_event(fd, ent);
- strlist__remove(namelist, ent);
+ ret = __del_trace_kprobe_event(fd, ent);
+ if (ret >= 0)
+ strlist__remove(namelist, ent);
}
}
- if (found == 0)
- pr_info("Info: event \"%s\" does not exist, could not remove it.\n", buf);
+ if (found == 0 && ret >= 0)
+ pr_info("Info: Event \"%s\" does not exist.\n", buf);
+
+ return ret;
}

-void del_perf_probe_events(struct strlist *dellist)
+int del_perf_probe_events(struct strlist *dellist)
{
- int fd;
+ int fd, ret = 0;
const char *group, *event;
char *p, *str;
struct str_node *ent;
struct strlist *namelist;

fd = open_kprobe_events(true);
+ if (fd < 0)
+ return fd;
+
/* Get current event names */
namelist = get_kprobe_trace_event_names(fd, true);
+ if (namelist == NULL)
+ return -EINVAL;

strlist__for_each(ent, dellist) {
str = xstrdup(ent->s);
@@ -1286,10 +1507,14 @@ void del_perf_probe_events(struct strlist *dellist)
event = str;
}
pr_debug("Group: %s, Event: %s\n", group, event);
- del_trace_kprobe_event(fd, group, event, namelist);
+ ret = del_trace_kprobe_event(fd, group, event, namelist);
free(str);
+ if (ret < 0)
+ break;
}
strlist__delete(namelist);
close(fd);
+
+ return ret;
}

diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 5a9837c..aee60e1 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -89,10 +89,10 @@ struct line_range {
};

/* Command string to events */
-extern void parse_perf_probe_command(const char *cmd,
- struct perf_probe_event *pev);
-extern void parse_kprobe_trace_command(const char *cmd,
- struct kprobe_trace_event *tev);
+extern int parse_perf_probe_command(const char *cmd,
+ struct perf_probe_event *pev);
+extern int parse_kprobe_trace_command(const char *cmd,
+ struct kprobe_trace_event *tev);

/* Events to command string */
extern char *synthesize_perf_probe_command(struct perf_probe_event *pev);
@@ -104,22 +104,22 @@ extern int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf,
extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev);

/* Convert from kprobe_trace_event to perf_probe_event */
-extern void convert_to_perf_probe_event(struct kprobe_trace_event *tev,
- struct perf_probe_event *pev);
+extern int convert_to_perf_probe_event(struct kprobe_trace_event *tev,
+ struct perf_probe_event *pev);

/* Release event contents */
extern void clear_perf_probe_event(struct perf_probe_event *pev);
extern void clear_kprobe_trace_event(struct kprobe_trace_event *tev);

/* Command string to line-range */
-extern void parse_line_range_desc(const char *cmd, struct line_range *lr);
+extern int parse_line_range_desc(const char *cmd, struct line_range *lr);


-extern void add_perf_probe_events(struct perf_probe_event *pevs, int ntevs,
- bool force_add);
-extern void del_perf_probe_events(struct strlist *dellist);
-extern void show_perf_probe_events(void);
-extern void show_line_range(struct line_range *lr);
+extern int add_perf_probe_events(struct perf_probe_event *pevs, int ntevs,
+ 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);


/* Maximum index number of event-name postfix */


--
Masami Hiramatsu
e-mail: [email protected]

2010-04-12 17:11:19

by Masami Hiramatsu

[permalink] [raw]
Subject: [PATCH -tip v3 09/10] perf probe: Remove xzalloc() from util/probe-{event, finder}.c

Remove all xzalloc() calls from util/probe-{event,finder}.c since
it may cause 'sudden death' in utility functions and it makes
reusing it from other code difficult.

Signed-off-by: Masami Hiramatsu <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Mike Galbraith <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
---

tools/perf/util/probe-event.c | 69 ++++++++++++++++++++++++++++++----------
tools/perf/util/probe-finder.c | 25 ++++++++++----
2 files changed, 69 insertions(+), 25 deletions(-)

diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index bd68f7b..aacbf73 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -556,7 +556,9 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
fieldp = &arg->field;

do {
- *fieldp = xzalloc(sizeof(struct perf_probe_arg_field));
+ *fieldp = zalloc(sizeof(struct perf_probe_arg_field));
+ if (*fieldp == NULL)
+ return -ENOMEM;
if (*tmp == '.') {
str = tmp + 1;
(*fieldp)->ref = false;
@@ -608,7 +610,11 @@ int parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev)

/* Copy arguments and ensure return probe has no C argument */
pev->nargs = argc - 1;
- pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs);
+ pev->args = zalloc(sizeof(struct perf_probe_arg) * pev->nargs);
+ if (pev->args == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
for (i = 0; i < pev->nargs && ret >= 0; i++) {
ret = parse_perf_probe_arg(argv[i + 1], &pev->args[i]);
if (ret >= 0 &&
@@ -680,7 +686,11 @@ int parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev)
tp->offset = 0;

tev->nargs = argc - 2;
- tev->args = xzalloc(sizeof(struct kprobe_trace_arg) * tev->nargs);
+ tev->args = zalloc(sizeof(struct kprobe_trace_arg) * tev->nargs);
+ if (tev->args == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
for (i = 0; i < tev->nargs; i++) {
p = strchr(argv[i + 2], '=');
if (p) /* We don't need which register is assigned. */
@@ -745,7 +755,11 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp)
char offs[32] = "", line[32] = "", file[32] = "";
int ret, len;

- buf = xzalloc(MAX_CMDLEN);
+ buf = zalloc(MAX_CMDLEN);
+ if (buf == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
if (pp->offset) {
ret = e_snprintf(offs, 32, "+%lu", pp->offset);
if (ret <= 0)
@@ -781,7 +795,8 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp)
error:
pr_debug("Failed to synthesize perf probe point: %s",
strerror(-ret));
- free(buf);
+ if (buf)
+ free(buf);
return NULL;
}

@@ -890,7 +905,10 @@ char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev)
char *buf;
int i, len, ret;

- buf = xzalloc(MAX_CMDLEN);
+ buf = zalloc(MAX_CMDLEN);
+ if (buf == NULL)
+ return NULL;
+
len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s+%lu",
tp->retprobe ? 'r' : 'p',
tev->group, tev->event,
@@ -929,7 +947,9 @@ int convert_to_perf_probe_event(struct kprobe_trace_event *tev,

/* Convert trace_arg to probe_arg */
pev->nargs = tev->nargs;
- pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs);
+ pev->args = zalloc(sizeof(struct perf_probe_arg) * pev->nargs);
+ if (pev->args == NULL)
+ return -ENOMEM;
for (i = 0; i < tev->nargs && ret >= 0; i++)
if (tev->args[i].name)
pev->args[i].name = xstrdup(tev->args[i].name);
@@ -1326,25 +1346,31 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev,
struct kprobe_trace_event **tevs)
{
struct symbol *sym;
- int ntevs = 0, i;
+ int ret = 0, i;
struct kprobe_trace_event *tev;

/* Convert perf_probe_event with debuginfo */
- ntevs = try_to_find_kprobe_trace_events(pev, tevs);
- if (ntevs != 0)
- return ntevs;
+ ret = try_to_find_kprobe_trace_events(pev, tevs);
+ if (ret != 0)
+ return ret;

/* Allocate trace event buffer */
- ntevs = 1;
- tev = *tevs = xzalloc(sizeof(struct kprobe_trace_event));
+ tev = *tevs = zalloc(sizeof(struct kprobe_trace_event));
+ if (tev == NULL)
+ return -ENOMEM;

/* Copy parameters */
tev->point.symbol = xstrdup(pev->point.function);
tev->point.offset = pev->point.offset;
tev->nargs = pev->nargs;
if (tev->nargs) {
- tev->args = xzalloc(sizeof(struct kprobe_trace_arg)
- * tev->nargs);
+ tev->args = zalloc(sizeof(struct kprobe_trace_arg)
+ * tev->nargs);
+ if (tev->args == NULL) {
+ free(tev);
+ *tevs = NULL;
+ return -ENOMEM;
+ }
for (i = 0; i < tev->nargs; i++) {
if (pev->args[i].name)
tev->args[i].name = xstrdup(pev->args[i].name);
@@ -1360,9 +1386,14 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev,
if (!sym) {
pr_warning("Kernel symbol \'%s\' not found.\n",
tev->point.symbol);
+ clear_kprobe_trace_event(tev);
+ free(tev);
+ *tevs = NULL;
return -ENOENT;
- }
- return ntevs;
+ } else
+ ret = 1;
+
+ return ret;
}

struct __event_package {
@@ -1377,7 +1408,9 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
int i, j, ret;
struct __event_package *pkgs;

- pkgs = xzalloc(sizeof(struct __event_package) * npevs);
+ pkgs = zalloc(sizeof(struct __event_package) * npevs);
+ if (pkgs == NULL)
+ return -ENOMEM;

/* Init vmlinux path */
ret = init_vmlinux();
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 54daa91..ce1ac82 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -111,7 +111,7 @@ static int strtailcmp(const char *s1, const char *s2)
/* Line number list operations */

/* Add a line to line number list */
-static void line_list__add_line(struct list_head *head, unsigned int line)
+static int line_list__add_line(struct list_head *head, unsigned int line)
{
struct line_node *ln;
struct list_head *p;
@@ -122,16 +122,19 @@ static void line_list__add_line(struct list_head *head, unsigned int line)
p = &ln->list;
goto found;
} else if (ln->line == line) /* Already exist */
- return ;
+ return 1;
}
/* List is empty, or the smallest entry */
p = head;
found:
pr_debug("line list: add a line %u\n", line);
- ln = xzalloc(sizeof(struct line_node));
+ ln = zalloc(sizeof(struct line_node));
+ if (ln == NULL)
+ return -ENOMEM;
ln->line = line;
INIT_LIST_HEAD(&ln->list);
list_add(&ln->list, p);
+ return 0;
}

/* Check if the line in line number list */
@@ -423,7 +426,9 @@ static int convert_location(Dwarf_Op *op, struct probe_finder *pf)

tvar->value = xstrdup(regs);
if (ref) {
- tvar->ref = xzalloc(sizeof(struct kprobe_trace_arg_ref));
+ tvar->ref = zalloc(sizeof(struct kprobe_trace_arg_ref));
+ if (tvar->ref == NULL)
+ return -ENOMEM;
tvar->ref->offset = (long)offs;
}
return 0;
@@ -500,7 +505,9 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
return -EINVAL;
}

- ref = xzalloc(sizeof(struct kprobe_trace_arg_ref));
+ ref = zalloc(sizeof(struct kprobe_trace_arg_ref));
+ if (ref == NULL)
+ return -ENOMEM;
if (*ref_ptr)
(*ref_ptr)->next = ref;
else
@@ -680,7 +687,9 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)

/* Find each argument */
tev->nargs = pf->pev->nargs;
- tev->args = xzalloc(sizeof(struct kprobe_trace_arg) * tev->nargs);
+ tev->args = zalloc(sizeof(struct kprobe_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];
@@ -938,7 +947,9 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev,
Dwarf *dbg;
int ret = 0;

- pf.tevs = xzalloc(sizeof(struct kprobe_trace_event) * MAX_PROBES);
+ pf.tevs = zalloc(sizeof(struct kprobe_trace_event) * MAX_PROBES);
+ if (pf.tevs == NULL)
+ return -ENOMEM;
*tevs = pf.tevs;
pf.ntevs = 0;



--
Masami Hiramatsu
e-mail: [email protected]

2010-04-12 17:45:36

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH -tip v3 04/10] perf probe: Query basic types from debuginfo

Em Mon, Apr 12, 2010 at 01:17:15PM -0400, Masami Hiramatsu escreveu:
> Query the basic type information (byte-size and signed-flag) from
> debuginfo and pass that to kprobe-tracer. This is especially useful
> for tracing the members of data structure, because each member has
> different byte-size on the memory.
>
> Signed-off-by: Masami Hiramatsu <[email protected]>
> Cc: Ingo Molnar <[email protected]>
> Cc: Paul Mackerras <[email protected]>
> Cc: Arnaldo Carvalho de Melo <[email protected]>
> Cc: Peter Zijlstra <[email protected]>
> Cc: Mike Galbraith <[email protected]>
> Cc: Frederic Weisbecker <[email protected]>
> ---
>
> tools/perf/util/probe-event.c | 9 +++++
> tools/perf/util/probe-event.h | 1 +
> tools/perf/util/probe-finder.c | 78 ++++++++++++++++++++++++++++++++++++----
> 3 files changed, 80 insertions(+), 8 deletions(-)
>
> diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
> index 19de8b7..05ca4a9 100644
> --- a/tools/perf/util/probe-event.c
> +++ b/tools/perf/util/probe-event.c
> @@ -740,6 +740,13 @@ static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg,
> buf += ret;
> buflen -= ret;
> }
> + /* Print argument type */
> + if (arg->type) {
> + ret = e_snprintf(buf, buflen, ":%s", arg->type);
> + if (ret <= 0)
> + return ret;
> + buf += ret;
> + }
>
> return buf - tmp;
> }
> @@ -848,6 +855,8 @@ void clear_kprobe_trace_event(struct kprobe_trace_event *tev)
> free(tev->args[i].name);
> if (tev->args[i].value)
> free(tev->args[i].value);
> + if (tev->args[i].type)
> + free(tev->args[i].type);

It follows the existing style but as kfree, free() also can cope with
NULL pointers, so unconditionally calling free is a shorter form, but
this can be fixed later, possibily in a big cleanup in all of
tools/perf.

- Arnaldo

2010-04-12 17:49:36

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH -tip v3 00/10] perf-probe updates - data-structure support improvements, etc.

Em Mon, Apr 12, 2010 at 01:16:46PM -0400, Masami Hiramatsu escreveu:
> Hi Ingo and Arnaldo,
>
> Here are several updates of perf-probe. This series improves
> data structure accessing. In this version, I added 'removing x*()'
> patches.

Ingo, I'm ok with this series, do you want me to pull them or will you?

- Arnaldo

2010-04-13 09:15:30

by Ingo Molnar

[permalink] [raw]
Subject: Re: [PATCH -tip v3 00/10] perf-probe updates - data-structure support improvements, etc.


* Arnaldo Carvalho de Melo <[email protected]> wrote:

> Em Mon, Apr 12, 2010 at 01:16:46PM -0400, Masami Hiramatsu escreveu:
> > Hi Ingo and Arnaldo,
> >
> > Here are several updates of perf-probe. This series improves data
> > structure accessing. In this version, I added 'removing x*()' patches.
>
> Ingo, I'm ok with this series, do you want me to pull them or will you?

Sure, feel free to pull it - you are doing many other tools/perf/ changes
which might interact.

Ingo

2010-04-15 07:28:28

by Masami Hiramatsu

[permalink] [raw]
Subject: [tip:perf/core] perf probe: Support argument name

Commit-ID: 48481938b02471d505296d7557ed296eb093d496
Gitweb: http://git.kernel.org/tip/48481938b02471d505296d7557ed296eb093d496
Author: Masami Hiramatsu <[email protected]>
AuthorDate: Mon, 12 Apr 2010 13:16:53 -0400
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Wed, 14 Apr 2010 17:26:04 -0300

perf probe: Support argument name

Set given names to event arguments. The syntax is same as kprobe-tracer,
you can add 'NAME=' right before each argument.

e.g.
./perf probe vfs_read foo=file

then, 'foo' is set to the argument name as below.

./perf probe -l
probe:vfs_read (on [email protected]/fs/read_write.c with foo)

Cc: Ingo Molnar <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Mike Galbraith <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
LKML-Reference: <[email protected]>
Signed-off-by: Masami Hiramatsu <[email protected]>
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/Documentation/perf-probe.txt | 10 ++++++++-
tools/perf/builtin-probe.c | 4 +-
tools/perf/util/probe-event.c | 34 +++++++++++++++++++++---------
tools/perf/util/probe-event.h | 1 +
tools/perf/util/probe-finder.c | 27 ++++++++++++++----------
5 files changed, 52 insertions(+), 24 deletions(-)

diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index bb671b3..e36ed4d 100644
--- a/tools/perf/Documentation/perf-probe.txt
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -79,7 +79,15 @@ Probe points are defined by following syntax.
'EVENT' specifies the name of new event, if omitted, it will be set the name of the probed function. Currently, event group name is set as 'probe'.
'FUNC' specifies a probed function name, and it may have one of the following options; '+OFFS' is the offset from function entry address in bytes, ':RLN' is the relative-line number from function entry line, and '%return' means that it probes function return. And ';PTN' means lazy matching pattern (see LAZY MATCHING). Note that ';PTN' must be the end of the probe point definition. In addition, '@SRC' specifies a source file which has that function.
It is also possible to specify a probe point by the source line number or lazy matching by using 'SRC:ALN' or 'SRC;PTN' syntax, where 'SRC' is the source file path, ':ALN' is the line number and ';PTN' is the lazy matching pattern.
-'ARG' specifies the arguments of this probe point. You can use the name of local variable, or kprobe-tracer argument format (e.g. $retval, %ax, etc).
+'ARG' specifies the arguments of this probe point, (see PROBE ARGUMENT).
+
+PROBE ARGUMENT
+--------------
+Each probe argument follows below syntax.
+
+ [NAME=]LOCALVAR|$retval|%REG|@SYMBOL
+
+'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), or kprobe-tracer argument format (e.g. $retval, %ax, etc).

LINE SYNTAX
-----------
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index bfc47ff..daf4668 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -142,9 +142,9 @@ static const struct option options[] = {
OPT_CALLBACK('a', "add", NULL,
#ifdef DWARF_SUPPORT
"[EVENT=]FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT"
- " [ARG ...]",
+ " [[NAME=]ARG ...]",
#else
- "[EVENT=]FUNC[+OFF|%return] [ARG ...]",
+ "[EVENT=]FUNC[+OFF|%return] [[NAME=]ARG ...]",
#endif
"probe point definition, where\n"
"\t\tGROUP:\tGroup name (optional)\n"
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 3fc0be7..ab6f53d 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -437,22 +437,28 @@ static void parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
/* Parse perf-probe event argument */
static void parse_perf_probe_arg(const char *str, struct perf_probe_arg *arg)
{
- const char *tmp;
+ char *tmp;
struct perf_probe_arg_field **fieldp;

pr_debug("parsing arg: %s into ", str);

+ tmp = strchr(str, '=');
+ if (tmp) {
+ arg->name = xstrndup(str, tmp - str);
+ str = tmp + 1;
+ }
+
tmp = strpbrk(str, "-.");
if (!is_c_varname(str) || !tmp) {
/* A variable, register, symbol or special value */
- arg->name = xstrdup(str);
- pr_debug("%s\n", arg->name);
+ arg->var = xstrdup(str);
+ pr_debug("%s\n", arg->var);
return;
}

/* Structure fields */
- arg->name = xstrndup(str, tmp - str);
- pr_debug("%s, ", arg->name);
+ arg->var = xstrndup(str, tmp - str);
+ pr_debug("%s, ", arg->var);
fieldp = &arg->field;

do {
@@ -497,7 +503,7 @@ void parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev)
pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs);
for (i = 0; i < pev->nargs; i++) {
parse_perf_probe_arg(argv[i + 1], &pev->args[i]);
- if (is_c_varname(pev->args[i].name) && pev->point.retprobe)
+ if (is_c_varname(pev->args[i].var) && pev->point.retprobe)
semantic_error("You can't specify local variable for"
" kretprobe");
}
@@ -514,7 +520,7 @@ bool perf_probe_event_need_dwarf(struct perf_probe_event *pev)
return true;

for (i = 0; i < pev->nargs; i++)
- if (is_c_varname(pev->args[i].name))
+ if (is_c_varname(pev->args[i].var))
return true;

return false;
@@ -575,7 +581,10 @@ int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len)
int ret;
char *tmp = buf;

- ret = e_snprintf(tmp, len, "%s", pa->name);
+ if (pa->name && pa->var)
+ ret = e_snprintf(tmp, len, "%s=%s", pa->name, pa->var);
+ else
+ ret = e_snprintf(tmp, len, "%s", pa->name ? pa->name : pa->var);
if (ret <= 0)
goto error;
tmp += ret;
@@ -803,6 +812,8 @@ void clear_perf_probe_event(struct perf_probe_event *pev)
for (i = 0; i < pev->nargs; i++) {
if (pev->args[i].name)
free(pev->args[i].name);
+ if (pev->args[i].var)
+ free(pev->args[i].var);
field = pev->args[i].field;
while (field) {
next = field->next;
@@ -1117,8 +1128,11 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev,
if (tev->nargs) {
tev->args = xzalloc(sizeof(struct kprobe_trace_arg)
* tev->nargs);
- for (i = 0; i < tev->nargs; i++)
- tev->args[i].value = xstrdup(pev->args[i].name);
+ for (i = 0; i < tev->nargs; i++) {
+ if (pev->args[i].name)
+ tev->args[i].name = xstrdup(pev->args[i].name);
+ tev->args[i].value = xstrdup(pev->args[i].var);
+ }
}

/* Currently just checking function name from symbol map */
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 9d99fc2..10411f5 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -55,6 +55,7 @@ struct perf_probe_arg_field {
/* Perf probe probing argument */
struct perf_probe_arg {
char *name; /* Argument name */
+ char *var; /* Variable name */
struct perf_probe_arg_field *field; /* Structure fields */
};

diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index a851377..105e95c 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -484,35 +484,40 @@ static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
convert_location(expr, pf);

if (pf->pvar->field)
- convert_variable_fields(vr_die, pf->pvar->name,
+ convert_variable_fields(vr_die, pf->pvar->var,
pf->pvar->field, &pf->tvar->ref);
/* *expr will be cached in libdw. Don't free it. */
return ;
error:
/* TODO: Support const_value */
die("Failed to find the location of %s at this address.\n"
- " Perhaps, it has been optimized out.", pf->pvar->name);
+ " Perhaps, it has been optimized out.", pf->pvar->var);
}

/* Find a variable in a subprogram die */
static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
{
Dwarf_Die vr_die;
- char buf[128];
+ char buf[32];

- /* TODO: Support struct members and arrays */
- if (!is_c_varname(pf->pvar->name)) {
+ /* TODO: Support arrays */
+ if (pf->pvar->name)
+ pf->tvar->name = xstrdup(pf->pvar->name);
+ else {
+ synthesize_perf_probe_arg(pf->pvar, buf, 32);
+ pf->tvar->name = xstrdup(buf);
+ }
+
+ if (!is_c_varname(pf->pvar->var)) {
/* Copy raw parameters */
- pf->tvar->value = xstrdup(pf->pvar->name);
+ pf->tvar->value = xstrdup(pf->pvar->var);
} else {
- synthesize_perf_probe_arg(pf->pvar, buf, 128);
- pf->tvar->name = xstrdup(buf);
pr_debug("Searching '%s' variable in context.\n",
- pf->pvar->name);
+ pf->pvar->var);
/* Search child die for local variables and parameters. */
- if (!die_find_variable(sp_die, pf->pvar->name, &vr_die))
+ if (!die_find_variable(sp_die, pf->pvar->var, &vr_die))
die("Failed to find '%s' in this function.",
- pf->pvar->name);
+ pf->pvar->var);
convert_variable(&vr_die, pf);
}
}

2010-04-15 07:28:50

by Masami Hiramatsu

[permalink] [raw]
Subject: [tip:perf/core] perf probe: Use the last field name as the argument name

Commit-ID: df0faf4be02996135bc3a06b4f34360449c78084
Gitweb: http://git.kernel.org/tip/df0faf4be02996135bc3a06b4f34360449c78084
Author: Masami Hiramatsu <[email protected]>
AuthorDate: Mon, 12 Apr 2010 13:17:00 -0400
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Wed, 14 Apr 2010 17:26:14 -0300

perf probe: Use the last field name as the argument name

Set the last field name to the argument name when the argument
is refering a data-structure member.

e.g.
./perf probe --add 'vfs_read file->f_mode'
Add new event:
probe:vfs_read (on vfs_read with f_mode=file->f_mode)

This probe records file->f_mode, but the argument name becomes "f_mode".

This enables perf-trace command to parse trace event format correctly.

Cc: Ingo Molnar <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Mike Galbraith <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
LKML-Reference: <[email protected]>
Signed-off-by: Masami Hiramatsu <[email protected]>
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/Documentation/perf-probe.txt | 2 +-
tools/perf/util/probe-event.c | 4 ++++
2 files changed, 5 insertions(+), 1 deletions(-)

diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index e36ed4d..441324f 100644
--- a/tools/perf/Documentation/perf-probe.txt
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -87,7 +87,7 @@ Each probe argument follows below syntax.

[NAME=]LOCALVAR|$retval|%REG|@SYMBOL

-'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), or kprobe-tracer argument format (e.g. $retval, %ax, etc).
+'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), or kprobe-tracer argument format (e.g. $retval, %ax, etc). Note that the name of this argument will be set as the last member name if you specify a local data structure member (e.g. field2 for 'var->field1.field2'.)

LINE SYNTAX
-----------
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index ab6f53d..19de8b7 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -481,6 +481,10 @@ static void parse_perf_probe_arg(const char *str, struct perf_probe_arg *arg)
} while (tmp);
(*fieldp)->name = xstrdup(str);
pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref);
+
+ /* If no name is specified, set the last field name */
+ if (!arg->name)
+ arg->name = xstrdup((*fieldp)->name);
}

/* Parse perf-probe event command */

2010-04-15 07:29:05

by Masami Hiramatsu

[permalink] [raw]
Subject: [tip:perf/core] tracing/kprobes: Support basic types on dynamic events

Commit-ID: 93ccae7a2227466a0d071fe52c51319f2f34c365
Gitweb: http://git.kernel.org/tip/93ccae7a2227466a0d071fe52c51319f2f34c365
Author: Masami Hiramatsu <[email protected]>
AuthorDate: Mon, 12 Apr 2010 13:17:08 -0400
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Wed, 14 Apr 2010 17:26:28 -0300

tracing/kprobes: Support basic types on dynamic events

Support basic types of integer (u8, u16, u32, u64, s8, s16, s32, s64) in
kprobe tracer. With this patch, users can specify above basic types on
each arguments after ':'. If omitted, the argument type is set as
unsigned long (u32 or u64, arch-dependent).

e.g.
echo 'p account_system_time+0 hardirq_offset=%si:s32' > kprobe_events

adds a probe recording hardirq_offset in signed-32bits value on the
entry of account_system_time.

Cc: Ingo Molnar <[email protected]>
Cc: Steven Rostedt <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Mike Galbraith <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
LKML-Reference: <[email protected]>
Signed-off-by: Masami Hiramatsu <[email protected]>
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
Documentation/trace/kprobetrace.txt | 4 +-
kernel/trace/trace.h | 16 +-
kernel/trace/trace_kprobe.c | 535 +++++++++++++++++++++--------------
3 files changed, 334 insertions(+), 221 deletions(-)

diff --git a/Documentation/trace/kprobetrace.txt b/Documentation/trace/kprobetrace.txt
index a9100b2..ec94748 100644
--- a/Documentation/trace/kprobetrace.txt
+++ b/Documentation/trace/kprobetrace.txt
@@ -40,7 +40,9 @@ Synopsis of kprobe_events
$stack : Fetch stack address.
$retval : Fetch return value.(*)
+|-offs(FETCHARG) : Fetch memory at FETCHARG +|- offs address.(**)
- NAME=FETCHARG: Set NAME as the argument name of FETCHARG.
+ NAME=FETCHARG : Set NAME as the argument name of FETCHARG.
+ FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types
+ (u8/u16/u32/u64/s8/s16/s32/s64) are supported.

(*) only for return probe.
(**) this is useful for fetching a field of data structures.
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index bec2c97..3ebdb6b 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -102,29 +102,17 @@ struct syscall_trace_exit {
long ret;
};

-struct kprobe_trace_entry {
+struct kprobe_trace_entry_head {
struct trace_entry ent;
unsigned long ip;
- int nargs;
- unsigned long args[];
};

-#define SIZEOF_KPROBE_TRACE_ENTRY(n) \
- (offsetof(struct kprobe_trace_entry, args) + \
- (sizeof(unsigned long) * (n)))
-
-struct kretprobe_trace_entry {
+struct kretprobe_trace_entry_head {
struct trace_entry ent;
unsigned long func;
unsigned long ret_ip;
- int nargs;
- unsigned long args[];
};

-#define SIZEOF_KRETPROBE_TRACE_ENTRY(n) \
- (offsetof(struct kretprobe_trace_entry, args) + \
- (sizeof(unsigned long) * (n)))
-
/*
* trace_flag_type is an enumeration that holds different
* states when a trace occurs. These are:
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index 1251e36..a751432 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -29,6 +29,8 @@
#include <linux/ctype.h>
#include <linux/ptrace.h>
#include <linux/perf_event.h>
+#include <linux/stringify.h>
+#include <asm/bitsperlong.h>

#include "trace.h"
#include "trace_output.h"
@@ -40,7 +42,6 @@

/* Reserved field names */
#define FIELD_STRING_IP "__probe_ip"
-#define FIELD_STRING_NARGS "__probe_nargs"
#define FIELD_STRING_RETIP "__probe_ret_ip"
#define FIELD_STRING_FUNC "__probe_func"

@@ -52,56 +53,102 @@ const char *reserved_field_names[] = {
"common_tgid",
"common_lock_depth",
FIELD_STRING_IP,
- FIELD_STRING_NARGS,
FIELD_STRING_RETIP,
FIELD_STRING_FUNC,
};

-struct fetch_func {
- unsigned long (*func)(struct pt_regs *, void *);
+/* Printing function type */
+typedef int (*print_type_func_t)(struct trace_seq *, const char *, void *);
+#define PRINT_TYPE_FUNC_NAME(type) print_type_##type
+#define PRINT_TYPE_FMT_NAME(type) print_type_format_##type
+
+/* Printing in basic type function template */
+#define DEFINE_BASIC_PRINT_TYPE_FUNC(type, fmt, cast) \
+static __kprobes int PRINT_TYPE_FUNC_NAME(type)(struct trace_seq *s, \
+ const char *name, void *data)\
+{ \
+ return trace_seq_printf(s, " %s=" fmt, name, (cast)*(type *)data);\
+} \
+static const char PRINT_TYPE_FMT_NAME(type)[] = fmt;
+
+DEFINE_BASIC_PRINT_TYPE_FUNC(u8, "%x", unsigned int)
+DEFINE_BASIC_PRINT_TYPE_FUNC(u16, "%x", unsigned int)
+DEFINE_BASIC_PRINT_TYPE_FUNC(u32, "%lx", unsigned long)
+DEFINE_BASIC_PRINT_TYPE_FUNC(u64, "%llx", unsigned long long)
+DEFINE_BASIC_PRINT_TYPE_FUNC(s8, "%d", int)
+DEFINE_BASIC_PRINT_TYPE_FUNC(s16, "%d", int)
+DEFINE_BASIC_PRINT_TYPE_FUNC(s32, "%ld", long)
+DEFINE_BASIC_PRINT_TYPE_FUNC(s64, "%lld", long long)
+
+/* Data fetch function type */
+typedef void (*fetch_func_t)(struct pt_regs *, void *, void *);
+
+struct fetch_param {
+ fetch_func_t fn;
void *data;
};

-static __kprobes unsigned long call_fetch(struct fetch_func *f,
- struct pt_regs *regs)
+static __kprobes void call_fetch(struct fetch_param *fprm,
+ struct pt_regs *regs, void *dest)
{
- return f->func(regs, f->data);
+ return fprm->fn(regs, fprm->data, dest);
}

-/* fetch handlers */
-static __kprobes unsigned long fetch_register(struct pt_regs *regs,
- void *offset)
-{
- return regs_get_register(regs, (unsigned int)((unsigned long)offset));
+#define FETCH_FUNC_NAME(kind, type) fetch_##kind##_##type
+/*
+ * Define macro for basic types - we don't need to define s* types, because
+ * we have to care only about bitwidth at recording time.
+ */
+#define DEFINE_BASIC_FETCH_FUNCS(kind) \
+DEFINE_FETCH_##kind(u8) \
+DEFINE_FETCH_##kind(u16) \
+DEFINE_FETCH_##kind(u32) \
+DEFINE_FETCH_##kind(u64)
+
+#define CHECK_BASIC_FETCH_FUNCS(kind, fn) \
+ ((FETCH_FUNC_NAME(kind, u8) == fn) || \
+ (FETCH_FUNC_NAME(kind, u16) == fn) || \
+ (FETCH_FUNC_NAME(kind, u32) == fn) || \
+ (FETCH_FUNC_NAME(kind, u64) == fn))
+
+/* Data fetch function templates */
+#define DEFINE_FETCH_reg(type) \
+static __kprobes void FETCH_FUNC_NAME(reg, type)(struct pt_regs *regs, \
+ void *offset, void *dest) \
+{ \
+ *(type *)dest = (type)regs_get_register(regs, \
+ (unsigned int)((unsigned long)offset)); \
}
-
-static __kprobes unsigned long fetch_stack(struct pt_regs *regs,
- void *num)
-{
- return regs_get_kernel_stack_nth(regs,
- (unsigned int)((unsigned long)num));
+DEFINE_BASIC_FETCH_FUNCS(reg)
+
+#define DEFINE_FETCH_stack(type) \
+static __kprobes void FETCH_FUNC_NAME(stack, type)(struct pt_regs *regs,\
+ void *offset, void *dest) \
+{ \
+ *(type *)dest = (type)regs_get_kernel_stack_nth(regs, \
+ (unsigned int)((unsigned long)offset)); \
}
+DEFINE_BASIC_FETCH_FUNCS(stack)

-static __kprobes unsigned long fetch_memory(struct pt_regs *regs, void *addr)
-{
- unsigned long retval;
-
- if (probe_kernel_address(addr, retval))
- return 0;
- return retval;
+#define DEFINE_FETCH_retval(type) \
+static __kprobes void FETCH_FUNC_NAME(retval, type)(struct pt_regs *regs,\
+ void *dummy, void *dest) \
+{ \
+ *(type *)dest = (type)regs_return_value(regs); \
}
-
-static __kprobes unsigned long fetch_retvalue(struct pt_regs *regs,
- void *dummy)
-{
- return regs_return_value(regs);
-}
-
-static __kprobes unsigned long fetch_stack_address(struct pt_regs *regs,
- void *dummy)
-{
- return kernel_stack_pointer(regs);
+DEFINE_BASIC_FETCH_FUNCS(retval)
+
+#define DEFINE_FETCH_memory(type) \
+static __kprobes void FETCH_FUNC_NAME(memory, type)(struct pt_regs *regs,\
+ void *addr, void *dest) \
+{ \
+ type retval; \
+ if (probe_kernel_address(addr, retval)) \
+ *(type *)dest = 0; \
+ else \
+ *(type *)dest = retval; \
}
+DEFINE_BASIC_FETCH_FUNCS(memory)

/* Memory fetching by symbol */
struct symbol_cache {
@@ -145,51 +192,126 @@ static struct symbol_cache *alloc_symbol_cache(const char *sym, long offset)
return sc;
}

-static __kprobes unsigned long fetch_symbol(struct pt_regs *regs, void *data)
-{
- struct symbol_cache *sc = data;
-
- if (sc->addr)
- return fetch_memory(regs, (void *)sc->addr);
- else
- return 0;
+#define DEFINE_FETCH_symbol(type) \
+static __kprobes void FETCH_FUNC_NAME(symbol, type)(struct pt_regs *regs,\
+ void *data, void *dest) \
+{ \
+ struct symbol_cache *sc = data; \
+ if (sc->addr) \
+ fetch_memory_##type(regs, (void *)sc->addr, dest); \
+ else \
+ *(type *)dest = 0; \
}
+DEFINE_BASIC_FETCH_FUNCS(symbol)

-/* Special indirect memory access interface */
-struct indirect_fetch_data {
- struct fetch_func orig;
+/* Dereference memory access function */
+struct deref_fetch_param {
+ struct fetch_param orig;
long offset;
};

-static __kprobes unsigned long fetch_indirect(struct pt_regs *regs, void *data)
-{
- struct indirect_fetch_data *ind = data;
- unsigned long addr;
-
- addr = call_fetch(&ind->orig, regs);
- if (addr) {
- addr += ind->offset;
- return fetch_memory(regs, (void *)addr);
- } else
- return 0;
+#define DEFINE_FETCH_deref(type) \
+static __kprobes void FETCH_FUNC_NAME(deref, type)(struct pt_regs *regs,\
+ void *data, void *dest) \
+{ \
+ struct deref_fetch_param *dprm = data; \
+ unsigned long addr; \
+ call_fetch(&dprm->orig, regs, &addr); \
+ if (addr) { \
+ addr += dprm->offset; \
+ fetch_memory_##type(regs, (void *)addr, dest); \
+ } else \
+ *(type *)dest = 0; \
}
+DEFINE_BASIC_FETCH_FUNCS(deref)

-static __kprobes void free_indirect_fetch_data(struct indirect_fetch_data *data)
+static __kprobes void free_deref_fetch_param(struct deref_fetch_param *data)
{
- if (data->orig.func == fetch_indirect)
- free_indirect_fetch_data(data->orig.data);
- else if (data->orig.func == fetch_symbol)
+ if (CHECK_BASIC_FETCH_FUNCS(deref, data->orig.fn))
+ free_deref_fetch_param(data->orig.data);
+ else if (CHECK_BASIC_FETCH_FUNCS(symbol, data->orig.fn))
free_symbol_cache(data->orig.data);
kfree(data);
}

+/* Default (unsigned long) fetch type */
+#define __DEFAULT_FETCH_TYPE(t) u##t
+#define _DEFAULT_FETCH_TYPE(t) __DEFAULT_FETCH_TYPE(t)
+#define DEFAULT_FETCH_TYPE _DEFAULT_FETCH_TYPE(BITS_PER_LONG)
+#define DEFAULT_FETCH_TYPE_STR __stringify(DEFAULT_FETCH_TYPE)
+
+#define ASSIGN_FETCH_FUNC(kind, type) \
+ .kind = FETCH_FUNC_NAME(kind, type)
+
+#define ASSIGN_FETCH_TYPE(ptype, ftype, sign) \
+ {.name = #ptype, \
+ .size = sizeof(ftype), \
+ .is_signed = sign, \
+ .print = PRINT_TYPE_FUNC_NAME(ptype), \
+ .fmt = PRINT_TYPE_FMT_NAME(ptype), \
+ASSIGN_FETCH_FUNC(reg, ftype), \
+ASSIGN_FETCH_FUNC(stack, ftype), \
+ASSIGN_FETCH_FUNC(retval, ftype), \
+ASSIGN_FETCH_FUNC(memory, ftype), \
+ASSIGN_FETCH_FUNC(symbol, ftype), \
+ASSIGN_FETCH_FUNC(deref, ftype), \
+ }
+
+/* Fetch type information table */
+static const struct fetch_type {
+ const char *name; /* Name of type */
+ size_t size; /* Byte size of type */
+ int is_signed; /* Signed flag */
+ print_type_func_t print; /* Print functions */
+ const char *fmt; /* Fromat string */
+ /* Fetch functions */
+ fetch_func_t reg;
+ fetch_func_t stack;
+ fetch_func_t retval;
+ fetch_func_t memory;
+ fetch_func_t symbol;
+ fetch_func_t deref;
+} fetch_type_table[] = {
+ ASSIGN_FETCH_TYPE(u8, u8, 0),
+ ASSIGN_FETCH_TYPE(u16, u16, 0),
+ ASSIGN_FETCH_TYPE(u32, u32, 0),
+ ASSIGN_FETCH_TYPE(u64, u64, 0),
+ ASSIGN_FETCH_TYPE(s8, u8, 1),
+ ASSIGN_FETCH_TYPE(s16, u16, 1),
+ ASSIGN_FETCH_TYPE(s32, u32, 1),
+ ASSIGN_FETCH_TYPE(s64, u64, 1),
+};
+
+static const struct fetch_type *find_fetch_type(const char *type)
+{
+ int i;
+
+ if (!type)
+ type = DEFAULT_FETCH_TYPE_STR;
+
+ for (i = 0; i < ARRAY_SIZE(fetch_type_table); i++)
+ if (strcmp(type, fetch_type_table[i].name) == 0)
+ return &fetch_type_table[i];
+ return NULL;
+}
+
+/* Special function : only accept unsigned long */
+static __kprobes void fetch_stack_address(struct pt_regs *regs,
+ void *dummy, void *dest)
+{
+ *(unsigned long *)dest = kernel_stack_pointer(regs);
+}
+
/**
* Kprobe event core functions
*/

struct probe_arg {
- struct fetch_func fetch;
- const char *name;
+ struct fetch_param fetch;
+ unsigned int offset; /* Offset from argument entry */
+ const char *name; /* Name of this argument */
+ const char *comm; /* Command of this argument */
+ const struct fetch_type *type; /* Type of this argument */
};

/* Flags for trace_probe */
@@ -204,6 +326,7 @@ struct trace_probe {
const char *symbol; /* symbol name */
struct ftrace_event_call call;
struct trace_event event;
+ ssize_t size; /* trace entry size */
unsigned int nr_args;
struct probe_arg args[];
};
@@ -212,6 +335,7 @@ struct trace_probe {
(offsetof(struct trace_probe, args) + \
(sizeof(struct probe_arg) * (n)))

+
static __kprobes int probe_is_return(struct trace_probe *tp)
{
return tp->rp.handler != NULL;
@@ -222,49 +346,6 @@ static __kprobes const char *probe_symbol(struct trace_probe *tp)
return tp->symbol ? tp->symbol : "unknown";
}

-static int probe_arg_string(char *buf, size_t n, struct fetch_func *ff)
-{
- int ret = -EINVAL;
-
- if (ff->func == fetch_register) {
- const char *name;
- name = regs_query_register_name((unsigned int)((long)ff->data));
- ret = snprintf(buf, n, "%%%s", name);
- } else if (ff->func == fetch_stack)
- ret = snprintf(buf, n, "$stack%lu", (unsigned long)ff->data);
- else if (ff->func == fetch_memory)
- ret = snprintf(buf, n, "@0x%p", ff->data);
- else if (ff->func == fetch_symbol) {
- struct symbol_cache *sc = ff->data;
- if (sc->offset)
- ret = snprintf(buf, n, "@%s%+ld", sc->symbol,
- sc->offset);
- else
- ret = snprintf(buf, n, "@%s", sc->symbol);
- } else if (ff->func == fetch_retvalue)
- ret = snprintf(buf, n, "$retval");
- else if (ff->func == fetch_stack_address)
- ret = snprintf(buf, n, "$stack");
- else if (ff->func == fetch_indirect) {
- struct indirect_fetch_data *id = ff->data;
- size_t l = 0;
- ret = snprintf(buf, n, "%+ld(", id->offset);
- if (ret >= n)
- goto end;
- l += ret;
- ret = probe_arg_string(buf + l, n - l, &id->orig);
- if (ret < 0)
- goto end;
- l += ret;
- ret = snprintf(buf + l, n - l, ")");
- ret += l;
- }
-end:
- if (ret >= n)
- return -ENOSPC;
- return ret;
-}
-
static int register_probe_event(struct trace_probe *tp);
static void unregister_probe_event(struct trace_probe *tp);

@@ -347,11 +428,12 @@ error:

static void free_probe_arg(struct probe_arg *arg)
{
- if (arg->fetch.func == fetch_symbol)
+ if (CHECK_BASIC_FETCH_FUNCS(deref, arg->fetch.fn))
+ free_deref_fetch_param(arg->fetch.data);
+ else if (CHECK_BASIC_FETCH_FUNCS(symbol, arg->fetch.fn))
free_symbol_cache(arg->fetch.data);
- else if (arg->fetch.func == fetch_indirect)
- free_indirect_fetch_data(arg->fetch.data);
kfree(arg->name);
+ kfree(arg->comm);
}

static void free_trace_probe(struct trace_probe *tp)
@@ -457,28 +539,30 @@ static int split_symbol_offset(char *symbol, unsigned long *offset)
#define PARAM_MAX_ARGS 16
#define PARAM_MAX_STACK (THREAD_SIZE / sizeof(unsigned long))

-static int parse_probe_vars(char *arg, struct fetch_func *ff, int is_return)
+static int parse_probe_vars(char *arg, const struct fetch_type *t,
+ struct fetch_param *f, int is_return)
{
int ret = 0;
unsigned long param;

if (strcmp(arg, "retval") == 0) {
- if (is_return) {
- ff->func = fetch_retvalue;
- ff->data = NULL;
- } else
+ if (is_return)
+ f->fn = t->retval;
+ else
ret = -EINVAL;
} else if (strncmp(arg, "stack", 5) == 0) {
if (arg[5] == '\0') {
- ff->func = fetch_stack_address;
- ff->data = NULL;
+ if (strcmp(t->name, DEFAULT_FETCH_TYPE_STR) == 0)
+ f->fn = fetch_stack_address;
+ else
+ ret = -EINVAL;
} else if (isdigit(arg[5])) {
ret = strict_strtoul(arg + 5, 10, &param);
if (ret || param > PARAM_MAX_STACK)
ret = -EINVAL;
else {
- ff->func = fetch_stack;
- ff->data = (void *)param;
+ f->fn = t->stack;
+ f->data = (void *)param;
}
} else
ret = -EINVAL;
@@ -488,7 +572,8 @@ static int parse_probe_vars(char *arg, struct fetch_func *ff, int is_return)
}

/* Recursive argument parser */
-static int __parse_probe_arg(char *arg, struct fetch_func *ff, int is_return)
+static int __parse_probe_arg(char *arg, const struct fetch_type *t,
+ struct fetch_param *f, int is_return)
{
int ret = 0;
unsigned long param;
@@ -497,13 +582,13 @@ static int __parse_probe_arg(char *arg, struct fetch_func *ff, int is_return)

switch (arg[0]) {
case '$':
- ret = parse_probe_vars(arg + 1, ff, is_return);
+ ret = parse_probe_vars(arg + 1, t, f, is_return);
break;
case '%': /* named register */
ret = regs_query_register_offset(arg + 1);
if (ret >= 0) {
- ff->func = fetch_register;
- ff->data = (void *)(unsigned long)ret;
+ f->fn = t->reg;
+ f->data = (void *)(unsigned long)ret;
ret = 0;
}
break;
@@ -512,26 +597,22 @@ static int __parse_probe_arg(char *arg, struct fetch_func *ff, int is_return)
ret = strict_strtoul(arg + 1, 0, &param);
if (ret)
break;
- ff->func = fetch_memory;
- ff->data = (void *)param;
+ f->fn = t->memory;
+ f->data = (void *)param;
} else {
ret = split_symbol_offset(arg + 1, &offset);
if (ret)
break;
- ff->data = alloc_symbol_cache(arg + 1, offset);
- if (ff->data)
- ff->func = fetch_symbol;
- else
- ret = -EINVAL;
+ f->data = alloc_symbol_cache(arg + 1, offset);
+ if (f->data)
+ f->fn = t->symbol;
}
break;
- case '+': /* indirect memory */
+ case '+': /* deref memory */
case '-':
tmp = strchr(arg, '(');
- if (!tmp) {
- ret = -EINVAL;
+ if (!tmp)
break;
- }
*tmp = '\0';
ret = strict_strtol(arg + 1, 0, &offset);
if (ret)
@@ -541,38 +622,58 @@ static int __parse_probe_arg(char *arg, struct fetch_func *ff, int is_return)
arg = tmp + 1;
tmp = strrchr(arg, ')');
if (tmp) {
- struct indirect_fetch_data *id;
+ struct deref_fetch_param *dprm;
+ const struct fetch_type *t2 = find_fetch_type(NULL);
*tmp = '\0';
- id = kzalloc(sizeof(struct indirect_fetch_data),
- GFP_KERNEL);
- if (!id)
+ dprm = kzalloc(sizeof(struct deref_fetch_param),
+ GFP_KERNEL);
+ if (!dprm)
return -ENOMEM;
- id->offset = offset;
- ret = __parse_probe_arg(arg, &id->orig, is_return);
+ dprm->offset = offset;
+ ret = __parse_probe_arg(arg, t2, &dprm->orig,
+ is_return);
if (ret)
- kfree(id);
+ kfree(dprm);
else {
- ff->func = fetch_indirect;
- ff->data = (void *)id;
+ f->fn = t->deref;
+ f->data = (void *)dprm;
}
- } else
- ret = -EINVAL;
+ }
break;
- default:
- /* TODO: support custom handler */
- ret = -EINVAL;
}
+ if (!ret && !f->fn)
+ ret = -EINVAL;
return ret;
}

/* String length checking wrapper */
-static int parse_probe_arg(char *arg, struct fetch_func *ff, int is_return)
+static int parse_probe_arg(char *arg, struct trace_probe *tp,
+ struct probe_arg *parg, int is_return)
{
+ const char *t;
+
if (strlen(arg) > MAX_ARGSTR_LEN) {
pr_info("Argument is too long.: %s\n", arg);
return -ENOSPC;
}
- return __parse_probe_arg(arg, ff, is_return);
+ parg->comm = kstrdup(arg, GFP_KERNEL);
+ if (!parg->comm) {
+ pr_info("Failed to allocate memory for command '%s'.\n", arg);
+ return -ENOMEM;
+ }
+ t = strchr(parg->comm, ':');
+ if (t) {
+ arg[t - parg->comm] = '\0';
+ t++;
+ }
+ parg->type = find_fetch_type(t);
+ if (!parg->type) {
+ pr_info("Unsupported type: %s\n", t);
+ return -EINVAL;
+ }
+ parg->offset = tp->size;
+ tp->size += parg->type->size;
+ return __parse_probe_arg(arg, parg->type, &parg->fetch, is_return);
}

/* Return 1 if name is reserved or already used by another argument */
@@ -602,15 +703,18 @@ static int create_trace_probe(int argc, char **argv)
* @ADDR : fetch memory at ADDR (ADDR should be in kernel)
* @SYM[+|-offs] : fetch memory at SYM +|- offs (SYM is a data symbol)
* %REG : fetch register REG
- * Indirect memory fetch:
+ * Dereferencing memory fetch:
* +|-offs(ARG) : fetch memory at ARG +|- offs address.
* Alias name of args:
* NAME=FETCHARG : set NAME as alias of FETCHARG.
+ * Type of args:
+ * FETCHARG:TYPE : use TYPE instead of unsigned long.
*/
struct trace_probe *tp;
int i, ret = 0;
int is_return = 0, is_delete = 0;
- char *symbol = NULL, *event = NULL, *arg = NULL, *group = NULL;
+ char *symbol = NULL, *event = NULL, *group = NULL;
+ char *arg, *tmp;
unsigned long offset = 0;
void *addr = NULL;
char buf[MAX_EVENT_NAME_LEN];
@@ -723,13 +827,6 @@ static int create_trace_probe(int argc, char **argv)
else
arg = argv[i];

- if (conflict_field_name(argv[i], tp->args, i)) {
- pr_info("Argument%d name '%s' conflicts with "
- "another field.\n", i, argv[i]);
- ret = -EINVAL;
- goto error;
- }
-
tp->args[i].name = kstrdup(argv[i], GFP_KERNEL);
if (!tp->args[i].name) {
pr_info("Failed to allocate argument%d name '%s'.\n",
@@ -737,9 +834,19 @@ static int create_trace_probe(int argc, char **argv)
ret = -ENOMEM;
goto error;
}
+ tmp = strchr(tp->args[i].name, ':');
+ if (tmp)
+ *tmp = '_'; /* convert : to _ */
+
+ if (conflict_field_name(tp->args[i].name, tp->args, i)) {
+ pr_info("Argument%d name '%s' conflicts with "
+ "another field.\n", i, argv[i]);
+ ret = -EINVAL;
+ goto error;
+ }

/* Parse fetch argument */
- ret = parse_probe_arg(arg, &tp->args[i].fetch, is_return);
+ ret = parse_probe_arg(arg, tp, &tp->args[i], is_return);
if (ret) {
pr_info("Parse error at argument%d. (%d)\n", i, ret);
kfree(tp->args[i].name);
@@ -794,8 +901,7 @@ static void probes_seq_stop(struct seq_file *m, void *v)
static int probes_seq_show(struct seq_file *m, void *v)
{
struct trace_probe *tp = v;
- int i, ret;
- char buf[MAX_ARGSTR_LEN + 1];
+ int i;

seq_printf(m, "%c", probe_is_return(tp) ? 'r' : 'p');
seq_printf(m, ":%s/%s", tp->call.system, tp->call.name);
@@ -807,15 +913,10 @@ static int probes_seq_show(struct seq_file *m, void *v)
else
seq_printf(m, " %s", probe_symbol(tp));

- for (i = 0; i < tp->nr_args; i++) {
- ret = probe_arg_string(buf, MAX_ARGSTR_LEN, &tp->args[i].fetch);
- if (ret < 0) {
- pr_warning("Argument%d decoding error(%d).\n", i, ret);
- return ret;
- }
- seq_printf(m, " %s=%s", tp->args[i].name, buf);
- }
+ for (i = 0; i < tp->nr_args; i++)
+ seq_printf(m, " %s=%s", tp->args[i].name, tp->args[i].comm);
seq_printf(m, "\n");
+
return 0;
}

@@ -945,9 +1046,10 @@ static const struct file_operations kprobe_profile_ops = {
static __kprobes void kprobe_trace_func(struct kprobe *kp, struct pt_regs *regs)
{
struct trace_probe *tp = container_of(kp, struct trace_probe, rp.kp);
- struct kprobe_trace_entry *entry;
+ struct kprobe_trace_entry_head *entry;
struct ring_buffer_event *event;
struct ring_buffer *buffer;
+ u8 *data;
int size, i, pc;
unsigned long irq_flags;
struct ftrace_event_call *call = &tp->call;
@@ -957,7 +1059,7 @@ static __kprobes void kprobe_trace_func(struct kprobe *kp, struct pt_regs *regs)
local_save_flags(irq_flags);
pc = preempt_count();

- size = SIZEOF_KPROBE_TRACE_ENTRY(tp->nr_args);
+ size = sizeof(*entry) + tp->size;

event = trace_current_buffer_lock_reserve(&buffer, call->id, size,
irq_flags, pc);
@@ -965,10 +1067,10 @@ static __kprobes void kprobe_trace_func(struct kprobe *kp, struct pt_regs *regs)
return;

entry = ring_buffer_event_data(event);
- entry->nargs = tp->nr_args;
entry->ip = (unsigned long)kp->addr;
+ data = (u8 *)&entry[1];
for (i = 0; i < tp->nr_args; i++)
- entry->args[i] = call_fetch(&tp->args[i].fetch, regs);
+ call_fetch(&tp->args[i].fetch, regs, data + tp->args[i].offset);

if (!filter_current_check_discard(buffer, call, entry, event))
trace_nowake_buffer_unlock_commit(buffer, event, irq_flags, pc);
@@ -979,9 +1081,10 @@ static __kprobes void kretprobe_trace_func(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct trace_probe *tp = container_of(ri->rp, struct trace_probe, rp);
- struct kretprobe_trace_entry *entry;
+ struct kretprobe_trace_entry_head *entry;
struct ring_buffer_event *event;
struct ring_buffer *buffer;
+ u8 *data;
int size, i, pc;
unsigned long irq_flags;
struct ftrace_event_call *call = &tp->call;
@@ -989,7 +1092,7 @@ static __kprobes void kretprobe_trace_func(struct kretprobe_instance *ri,
local_save_flags(irq_flags);
pc = preempt_count();

- size = SIZEOF_KRETPROBE_TRACE_ENTRY(tp->nr_args);
+ size = sizeof(*entry) + tp->size;

event = trace_current_buffer_lock_reserve(&buffer, call->id, size,
irq_flags, pc);
@@ -997,11 +1100,11 @@ static __kprobes void kretprobe_trace_func(struct kretprobe_instance *ri,
return;

entry = ring_buffer_event_data(event);
- entry->nargs = tp->nr_args;
entry->func = (unsigned long)tp->rp.kp.addr;
entry->ret_ip = (unsigned long)ri->ret_addr;
+ data = (u8 *)&entry[1];
for (i = 0; i < tp->nr_args; i++)
- entry->args[i] = call_fetch(&tp->args[i].fetch, regs);
+ call_fetch(&tp->args[i].fetch, regs, data + tp->args[i].offset);

if (!filter_current_check_discard(buffer, call, entry, event))
trace_nowake_buffer_unlock_commit(buffer, event, irq_flags, pc);
@@ -1011,13 +1114,14 @@ static __kprobes void kretprobe_trace_func(struct kretprobe_instance *ri,
enum print_line_t
print_kprobe_event(struct trace_iterator *iter, int flags)
{
- struct kprobe_trace_entry *field;
+ struct kprobe_trace_entry_head *field;
struct trace_seq *s = &iter->seq;
struct trace_event *event;
struct trace_probe *tp;
+ u8 *data;
int i;

- field = (struct kprobe_trace_entry *)iter->ent;
+ field = (struct kprobe_trace_entry_head *)iter->ent;
event = ftrace_find_event(field->ent.type);
tp = container_of(event, struct trace_probe, event);

@@ -1030,9 +1134,10 @@ print_kprobe_event(struct trace_iterator *iter, int flags)
if (!trace_seq_puts(s, ")"))
goto partial;

- for (i = 0; i < field->nargs; i++)
- if (!trace_seq_printf(s, " %s=%lx",
- tp->args[i].name, field->args[i]))
+ data = (u8 *)&field[1];
+ for (i = 0; i < tp->nr_args; i++)
+ if (!tp->args[i].type->print(s, tp->args[i].name,
+ data + tp->args[i].offset))
goto partial;

if (!trace_seq_puts(s, "\n"))
@@ -1046,13 +1151,14 @@ partial:
enum print_line_t
print_kretprobe_event(struct trace_iterator *iter, int flags)
{
- struct kretprobe_trace_entry *field;
+ struct kretprobe_trace_entry_head *field;
struct trace_seq *s = &iter->seq;
struct trace_event *event;
struct trace_probe *tp;
+ u8 *data;
int i;

- field = (struct kretprobe_trace_entry *)iter->ent;
+ field = (struct kretprobe_trace_entry_head *)iter->ent;
event = ftrace_find_event(field->ent.type);
tp = container_of(event, struct trace_probe, event);

@@ -1071,9 +1177,10 @@ print_kretprobe_event(struct trace_iterator *iter, int flags)
if (!trace_seq_puts(s, ")"))
goto partial;

- for (i = 0; i < field->nargs; i++)
- if (!trace_seq_printf(s, " %s=%lx",
- tp->args[i].name, field->args[i]))
+ data = (u8 *)&field[1];
+ for (i = 0; i < tp->nr_args; i++)
+ if (!tp->args[i].type->print(s, tp->args[i].name,
+ data + tp->args[i].offset))
goto partial;

if (!trace_seq_puts(s, "\n"))
@@ -1129,29 +1236,43 @@ static int probe_event_raw_init(struct ftrace_event_call *event_call)
static int kprobe_event_define_fields(struct ftrace_event_call *event_call)
{
int ret, i;
- struct kprobe_trace_entry field;
+ struct kprobe_trace_entry_head field;
struct trace_probe *tp = (struct trace_probe *)event_call->data;

DEFINE_FIELD(unsigned long, ip, FIELD_STRING_IP, 0);
- DEFINE_FIELD(int, nargs, FIELD_STRING_NARGS, 1);
/* Set argument names as fields */
- for (i = 0; i < tp->nr_args; i++)
- DEFINE_FIELD(unsigned long, args[i], tp->args[i].name, 0);
+ for (i = 0; i < tp->nr_args; i++) {
+ ret = trace_define_field(event_call, tp->args[i].type->name,
+ tp->args[i].name,
+ sizeof(field) + tp->args[i].offset,
+ tp->args[i].type->size,
+ tp->args[i].type->is_signed,
+ FILTER_OTHER);
+ if (ret)
+ return ret;
+ }
return 0;
}

static int kretprobe_event_define_fields(struct ftrace_event_call *event_call)
{
int ret, i;
- struct kretprobe_trace_entry field;
+ struct kretprobe_trace_entry_head field;
struct trace_probe *tp = (struct trace_probe *)event_call->data;

DEFINE_FIELD(unsigned long, func, FIELD_STRING_FUNC, 0);
DEFINE_FIELD(unsigned long, ret_ip, FIELD_STRING_RETIP, 0);
- DEFINE_FIELD(int, nargs, FIELD_STRING_NARGS, 1);
/* Set argument names as fields */
- for (i = 0; i < tp->nr_args; i++)
- DEFINE_FIELD(unsigned long, args[i], tp->args[i].name, 0);
+ for (i = 0; i < tp->nr_args; i++) {
+ ret = trace_define_field(event_call, tp->args[i].type->name,
+ tp->args[i].name,
+ sizeof(field) + tp->args[i].offset,
+ tp->args[i].type->size,
+ tp->args[i].type->is_signed,
+ FILTER_OTHER);
+ if (ret)
+ return ret;
+ }
return 0;
}

@@ -1176,8 +1297,8 @@ static int __set_print_fmt(struct trace_probe *tp, char *buf, int len)
pos += snprintf(buf + pos, LEN_OR_ZERO, "\"%s", fmt);

for (i = 0; i < tp->nr_args; i++) {
- pos += snprintf(buf + pos, LEN_OR_ZERO, " %s=%%lx",
- tp->args[i].name);
+ pos += snprintf(buf + pos, LEN_OR_ZERO, " %s=%s",
+ tp->args[i].name, tp->args[i].type->fmt);
}

pos += snprintf(buf + pos, LEN_OR_ZERO, "\", %s", arg);
@@ -1219,12 +1340,13 @@ static __kprobes void kprobe_perf_func(struct kprobe *kp,
{
struct trace_probe *tp = container_of(kp, struct trace_probe, rp.kp);
struct ftrace_event_call *call = &tp->call;
- struct kprobe_trace_entry *entry;
+ struct kprobe_trace_entry_head *entry;
+ u8 *data;
int size, __size, i;
unsigned long irq_flags;
int rctx;

- __size = SIZEOF_KPROBE_TRACE_ENTRY(tp->nr_args);
+ __size = sizeof(*entry) + tp->size;
size = ALIGN(__size + sizeof(u32), sizeof(u64));
size -= sizeof(u32);
if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE,
@@ -1235,10 +1357,10 @@ static __kprobes void kprobe_perf_func(struct kprobe *kp,
if (!entry)
return;

- entry->nargs = tp->nr_args;
entry->ip = (unsigned long)kp->addr;
+ data = (u8 *)&entry[1];
for (i = 0; i < tp->nr_args; i++)
- entry->args[i] = call_fetch(&tp->args[i].fetch, regs);
+ call_fetch(&tp->args[i].fetch, regs, data + tp->args[i].offset);

perf_trace_buf_submit(entry, size, rctx, entry->ip, 1, irq_flags, regs);
}
@@ -1249,12 +1371,13 @@ static __kprobes void kretprobe_perf_func(struct kretprobe_instance *ri,
{
struct trace_probe *tp = container_of(ri->rp, struct trace_probe, rp);
struct ftrace_event_call *call = &tp->call;
- struct kretprobe_trace_entry *entry;
+ struct kretprobe_trace_entry_head *entry;
+ u8 *data;
int size, __size, i;
unsigned long irq_flags;
int rctx;

- __size = SIZEOF_KRETPROBE_TRACE_ENTRY(tp->nr_args);
+ __size = sizeof(*entry) + tp->size;
size = ALIGN(__size + sizeof(u32), sizeof(u64));
size -= sizeof(u32);
if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE,
@@ -1265,11 +1388,11 @@ static __kprobes void kretprobe_perf_func(struct kretprobe_instance *ri,
if (!entry)
return;

- entry->nargs = tp->nr_args;
entry->func = (unsigned long)tp->rp.kp.addr;
entry->ret_ip = (unsigned long)ri->ret_addr;
+ data = (u8 *)&entry[1];
for (i = 0; i < tp->nr_args; i++)
- entry->args[i] = call_fetch(&tp->args[i].fetch, regs);
+ call_fetch(&tp->args[i].fetch, regs, data + tp->args[i].offset);

perf_trace_buf_submit(entry, size, rctx, entry->ret_ip, 1,
irq_flags, regs);

2010-04-15 07:29:14

by Masami Hiramatsu

[permalink] [raw]
Subject: [tip:perf/core] perf probe: Query basic types from debuginfo

Commit-ID: 4984912eb23113a4007940cd09c8351c0623ea5f
Gitweb: http://git.kernel.org/tip/4984912eb23113a4007940cd09c8351c0623ea5f
Author: Masami Hiramatsu <[email protected]>
AuthorDate: Mon, 12 Apr 2010 13:17:15 -0400
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Wed, 14 Apr 2010 17:27:56 -0300

perf probe: Query basic types from debuginfo

Query the basic type information (byte-size and signed-flag) from
debuginfo and pass that to kprobe-tracer. This is especially useful
for tracing the members of data structure, because each member has
different byte-size on the memory.

Cc: Ingo Molnar <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Mike Galbraith <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
LKML-Reference: <[email protected]>
Signed-off-by: Masami Hiramatsu <[email protected]>
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/util/probe-event.c | 9 +++++
tools/perf/util/probe-event.h | 1 +
tools/perf/util/probe-finder.c | 78 +++++++++++++++++++++++++++++++++++----
3 files changed, 80 insertions(+), 8 deletions(-)

diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 19de8b7..05ca4a9 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -740,6 +740,13 @@ static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg,
buf += ret;
buflen -= ret;
}
+ /* Print argument type */
+ if (arg->type) {
+ ret = e_snprintf(buf, buflen, ":%s", arg->type);
+ if (ret <= 0)
+ return ret;
+ buf += ret;
+ }

return buf - tmp;
}
@@ -848,6 +855,8 @@ void clear_kprobe_trace_event(struct kprobe_trace_event *tev)
free(tev->args[i].name);
if (tev->args[i].value)
free(tev->args[i].value);
+ if (tev->args[i].type)
+ free(tev->args[i].type);
ref = tev->args[i].ref;
while (ref) {
next = ref->next;
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 10411f5..a393a3f 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -23,6 +23,7 @@ struct kprobe_trace_arg_ref {
struct kprobe_trace_arg {
char *name; /* Argument name */
char *value; /* Base value */
+ char *type; /* Type name */
struct kprobe_trace_arg_ref *ref; /* Referencing offset */
};

diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 105e95c..ebeb413 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -84,6 +84,9 @@ const char *x86_64_regs_table[X86_64_MAX_REGS] = {
#define arch_regs_table x86_32_regs_table
#endif

+/* Kprobe tracer basic type is up to u64 */
+#define MAX_BASIC_TYPE_BITS 64
+
/* Return architecture dependent register string (for kprobe-tracer) */
static const char *get_arch_regstr(unsigned int n)
{
@@ -230,6 +233,31 @@ static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
return die_mem;
}

+static bool die_is_signed_type(Dwarf_Die *tp_die)
+{
+ Dwarf_Attribute attr;
+ Dwarf_Word ret;
+
+ if (dwarf_attr(tp_die, DW_AT_encoding, &attr) == NULL ||
+ dwarf_formudata(&attr, &ret) != 0)
+ return false;
+
+ return (ret == DW_ATE_signed_char || ret == DW_ATE_signed ||
+ ret == DW_ATE_signed_fixed);
+}
+
+static int die_get_byte_size(Dwarf_Die *tp_die)
+{
+ Dwarf_Attribute attr;
+ Dwarf_Word ret;
+
+ if (dwarf_attr(tp_die, DW_AT_byte_size, &attr) == NULL ||
+ dwarf_formudata(&attr, &ret) != 0)
+ return 0;
+
+ return (int)ret;
+}
+
/* Return values for die_find callbacks */
enum {
DIE_FIND_CB_FOUND = 0, /* End of Search */
@@ -406,13 +434,42 @@ static void convert_location(Dwarf_Op *op, struct probe_finder *pf)
}
}

+static void convert_variable_type(Dwarf_Die *vr_die,
+ struct kprobe_trace_arg *targ)
+{
+ Dwarf_Die type;
+ char buf[16];
+ int ret;
+
+ if (die_get_real_type(vr_die, &type) == NULL)
+ die("Failed to get a type information of %s.",
+ dwarf_diename(vr_die));
+
+ ret = die_get_byte_size(&type) * 8;
+ if (ret) {
+ /* Check the bitwidth */
+ if (ret > MAX_BASIC_TYPE_BITS) {
+ pr_warning(" Warning: %s exceeds max-bitwidth."
+ " Cut down to %d bits.\n",
+ dwarf_diename(&type), MAX_BASIC_TYPE_BITS);
+ ret = MAX_BASIC_TYPE_BITS;
+ }
+
+ ret = snprintf(buf, 16, "%c%d",
+ die_is_signed_type(&type) ? 's' : 'u', ret);
+ if (ret < 0 || ret >= 16)
+ die("Failed to convert variable type.");
+ targ->type = xstrdup(buf);
+ }
+}
+
static void convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
struct perf_probe_arg_field *field,
- struct kprobe_trace_arg_ref **ref_ptr)
+ struct kprobe_trace_arg_ref **ref_ptr,
+ Dwarf_Die *die_mem)
{
struct kprobe_trace_arg_ref *ref = *ref_ptr;
Dwarf_Attribute attr;
- Dwarf_Die member;
Dwarf_Die type;
Dwarf_Word offs;

@@ -450,26 +507,27 @@ static void convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
die("Structure on a register is not supported yet.");
}

- if (die_find_member(&type, field->name, &member) == NULL)
+ if (die_find_member(&type, field->name, die_mem) == NULL)
die("%s(tyep:%s) has no member %s.", varname,
dwarf_diename(&type), field->name);

/* Get the offset of the field */
- if (dwarf_attr(&member, DW_AT_data_member_location, &attr) == NULL ||
+ if (dwarf_attr(die_mem, DW_AT_data_member_location, &attr) == NULL ||
dwarf_formudata(&attr, &offs) != 0)
die("Failed to get the offset of %s.", field->name);
ref->offset += (long)offs;

/* Converting next field */
if (field->next)
- convert_variable_fields(&member, field->name, field->next,
- &ref);
+ convert_variable_fields(die_mem, field->name, field->next,
+ &ref, die_mem);
}

/* Show a variables in kprobe event format */
static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
{
Dwarf_Attribute attr;
+ Dwarf_Die die_mem;
Dwarf_Op *expr;
size_t nexpr;
int ret;
@@ -483,9 +541,13 @@ static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)

convert_location(expr, pf);

- if (pf->pvar->field)
+ if (pf->pvar->field) {
convert_variable_fields(vr_die, pf->pvar->var,
- pf->pvar->field, &pf->tvar->ref);
+ pf->pvar->field, &pf->tvar->ref,
+ &die_mem);
+ vr_die = &die_mem;
+ }
+ convert_variable_type(vr_die, pf->tvar);
/* *expr will be cached in libdw. Don't free it. */
return ;
error:

2010-04-15 07:29:43

by Masami Hiramatsu

[permalink] [raw]
Subject: [tip:perf/core] perf probe: Support basic type casting

Commit-ID: 11a1ca3554b377d2a8a318a3cbf8ce10a7a2a8e4
Gitweb: http://git.kernel.org/tip/11a1ca3554b377d2a8a318a3cbf8ce10a7a2a8e4
Author: Masami Hiramatsu <[email protected]>
AuthorDate: Mon, 12 Apr 2010 13:17:22 -0400
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Wed, 14 Apr 2010 17:28:09 -0300

perf probe: Support basic type casting

Add basic type casting for arguments to perf probe. This allows
users to specify the actual type of arguments. Of course, if
user sets invalid types, kprobe-tracer rejects that.

Cc: Ingo Molnar <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Mike Galbraith <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
LKML-Reference: <[email protected]>
Signed-off-by: Masami Hiramatsu <[email protected]>
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/Documentation/perf-probe.txt | 3 ++-
tools/perf/util/probe-event.c | 23 ++++++++++++++++++++++-
tools/perf/util/probe-event.h | 1 +
tools/perf/util/probe-finder.c | 10 ++++++++--
4 files changed, 33 insertions(+), 4 deletions(-)

diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index 441324f..63c25d3 100644
--- a/tools/perf/Documentation/perf-probe.txt
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -85,9 +85,10 @@ PROBE ARGUMENT
--------------
Each probe argument follows below syntax.

- [NAME=]LOCALVAR|$retval|%REG|@SYMBOL
+ [NAME=]LOCALVAR|$retval|%REG|@SYMBOL[:TYPE]

'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), or kprobe-tracer argument format (e.g. $retval, %ax, etc). Note that the name of this argument will be set as the last member name if you specify a local data structure member (e.g. field2 for 'var->field1.field2'.)
+'TYPE' casts the type of this argument (optional). If omitted, perf probe automatically set the type based on debuginfo.

LINE SYNTAX
-----------
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 05ca4a9..bef2805 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -435,7 +435,7 @@ static void parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
}

/* Parse perf-probe event argument */
-static void parse_perf_probe_arg(const char *str, struct perf_probe_arg *arg)
+static void parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
{
char *tmp;
struct perf_probe_arg_field **fieldp;
@@ -445,9 +445,17 @@ static void parse_perf_probe_arg(const char *str, struct perf_probe_arg *arg)
tmp = strchr(str, '=');
if (tmp) {
arg->name = xstrndup(str, tmp - str);
+ pr_debug("name:%s ", arg->name);
str = tmp + 1;
}

+ tmp = strchr(str, ':');
+ if (tmp) { /* Type setting */
+ *tmp = '\0';
+ arg->type = xstrdup(tmp + 1);
+ pr_debug("type:%s ", arg->type);
+ }
+
tmp = strpbrk(str, "-.");
if (!is_c_varname(str) || !tmp) {
/* A variable, register, symbol or special value */
@@ -603,6 +611,15 @@ int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len)
len -= ret;
field = field->next;
}
+
+ if (pa->type) {
+ ret = e_snprintf(tmp, len, ":%s", pa->type);
+ if (ret <= 0)
+ goto error;
+ tmp += ret;
+ len -= ret;
+ }
+
return tmp - buf;
error:
die("Failed to synthesize perf probe argument: %s", strerror(-ret));
@@ -825,6 +842,8 @@ void clear_perf_probe_event(struct perf_probe_event *pev)
free(pev->args[i].name);
if (pev->args[i].var)
free(pev->args[i].var);
+ if (pev->args[i].type)
+ free(pev->args[i].type);
field = pev->args[i].field;
while (field) {
next = field->next;
@@ -1145,6 +1164,8 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev,
if (pev->args[i].name)
tev->args[i].name = xstrdup(pev->args[i].name);
tev->args[i].value = xstrdup(pev->args[i].var);
+ if (pev->args[i].type)
+ tev->args[i].type = xstrdup(pev->args[i].type);
}
}

diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index a393a3f..ff2f26b 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -57,6 +57,7 @@ struct perf_probe_arg_field {
struct perf_probe_arg {
char *name; /* Argument name */
char *var; /* Variable name */
+ char *type; /* Type name */
struct perf_probe_arg_field *field; /* Structure fields */
};

diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index ebeb413..ab47673 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -547,7 +547,10 @@ static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
&die_mem);
vr_die = &die_mem;
}
- convert_variable_type(vr_die, pf->tvar);
+ if (pf->pvar->type)
+ pf->tvar->type = xstrdup(pf->pvar->type);
+ else
+ convert_variable_type(vr_die, pf->tvar);
/* *expr will be cached in libdw. Don't free it. */
return ;
error:
@@ -560,13 +563,16 @@ error:
static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
{
Dwarf_Die vr_die;
- char buf[32];
+ char buf[32], *ptr;

/* TODO: Support arrays */
if (pf->pvar->name)
pf->tvar->name = xstrdup(pf->pvar->name);
else {
synthesize_perf_probe_arg(pf->pvar, buf, 32);
+ ptr = strchr(buf, ':'); /* Change type separator to _ */
+ if (ptr)
+ *ptr = '_';
pf->tvar->name = xstrdup(buf);
}

2010-04-15 07:30:28

by Masami Hiramatsu

[permalink] [raw]
Subject: [tip:perf/core] perf probe: Remove die() from probe-event code

Commit-ID: 146a143948ed9e8b248c0ec59937f3e9e1bbc7e5
Gitweb: http://git.kernel.org/tip/146a143948ed9e8b248c0ec59937f3e9e1bbc7e5
Author: Masami Hiramatsu <[email protected]>
AuthorDate: Mon, 12 Apr 2010 13:17:42 -0400
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Wed, 14 Apr 2010 17:28:36 -0300

perf probe: Remove die() from probe-event code

Remove die() and DIE_IF() code from util/probe-event.c since
these 'sudden death' in utility functions make reusing it from
other code (especially tui/gui) difficult.

Cc: Ingo Molnar <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Mike Galbraith <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
LKML-Reference: <[email protected]>
Signed-off-by: Masami Hiramatsu <[email protected]>
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/builtin-probe.c | 70 ++++--
tools/perf/util/probe-event.c | 615 ++++++++++++++++++++++++++++-------------
tools/perf/util/probe-event.h | 24 +-
3 files changed, 480 insertions(+), 229 deletions(-)

diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index daf4668..64bc11a 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -59,23 +59,25 @@ static struct {


/* Parse an event definition. Note that any error must die. */
-static void parse_probe_event(const char *str)
+static int parse_probe_event(const char *str)
{
struct perf_probe_event *pev = &params.events[params.nevents];
+ int ret;

pr_debug("probe-definition(%d): %s\n", params.nevents, str);
if (++params.nevents == MAX_PROBES)
die("Too many probes (> %d) are specified.", MAX_PROBES);

/* Parse a perf-probe command into event */
- parse_perf_probe_command(str, pev);
-
+ ret = parse_perf_probe_command(str, pev);
pr_debug("%d arguments\n", pev->nargs);
+
+ return ret;
}

-static void parse_probe_event_argv(int argc, const char **argv)
+static int parse_probe_event_argv(int argc, const char **argv)
{
- int i, len;
+ int i, len, ret;
char *buf;

/* Bind up rest arguments */
@@ -86,16 +88,18 @@ static void parse_probe_event_argv(int argc, const char **argv)
len = 0;
for (i = 0; i < argc; i++)
len += sprintf(&buf[len], "%s ", argv[i]);
- parse_probe_event(buf);
+ ret = parse_probe_event(buf);
free(buf);
+ return ret;
}

static int opt_add_probe_event(const struct option *opt __used,
const char *str, int unset __used)
{
if (str)
- parse_probe_event(str);
- return 0;
+ return parse_probe_event(str);
+ else
+ return 0;
}

static int opt_del_probe_event(const struct option *opt __used,
@@ -113,11 +117,14 @@ static int opt_del_probe_event(const struct option *opt __used,
static int opt_show_lines(const struct option *opt __used,
const char *str, int unset __used)
{
+ int ret = 0;
+
if (str)
- parse_line_range_desc(str, &params.line_range);
+ ret = parse_line_range_desc(str, &params.line_range);
INIT_LIST_HEAD(&params.line_range.line_list);
params.show_lines = true;
- return 0;
+
+ return ret;
}
#endif

@@ -178,6 +185,8 @@ static const struct option options[] = {

int cmd_probe(int argc, const char **argv, const char *prefix __used)
{
+ int ret;
+
argc = parse_options(argc, argv, options, probe_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
if (argc > 0) {
@@ -185,7 +194,11 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
pr_warning(" Error: '-' is not supported.\n");
usage_with_options(probe_usage, options);
}
- parse_probe_event_argv(argc, argv);
+ ret = parse_probe_event_argv(argc, argv);
+ if (ret < 0) {
+ pr_err(" Error: Parse Error. (%d)\n", ret);
+ return ret;
+ }
}

if ((!params.nevents && !params.dellist && !params.list_events &&
@@ -197,16 +210,18 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)

if (params.list_events) {
if (params.nevents != 0 || params.dellist) {
- pr_warning(" Error: Don't use --list with"
- " --add/--del.\n");
+ pr_err(" Error: Don't use --list with --add/--del.\n");
usage_with_options(probe_usage, options);
}
if (params.show_lines) {
- pr_warning(" Error: Don't use --list with --line.\n");
+ pr_err(" Error: Don't use --list with --line.\n");
usage_with_options(probe_usage, options);
}
- show_perf_probe_events();
- return 0;
+ ret = show_perf_probe_events();
+ if (ret < 0)
+ pr_err(" Error: Failed to show event list. (%d)\n",
+ ret);
+ return ret;
}

#ifdef DWARF_SUPPORT
@@ -217,19 +232,30 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
usage_with_options(probe_usage, options);
}

- show_line_range(&params.line_range);
- return 0;
+ ret = show_line_range(&params.line_range);
+ if (ret < 0)
+ pr_err(" Error: Failed to show lines. (%d)\n", ret);
+ return ret;
}
#endif

if (params.dellist) {
- del_perf_probe_events(params.dellist);
+ ret = del_perf_probe_events(params.dellist);
strlist__delete(params.dellist);
- if (params.nevents == 0)
- return 0;
+ if (ret < 0) {
+ pr_err(" Error: Failed to delete events. (%d)\n", ret);
+ return ret;
+ }
}

- add_perf_probe_events(params.events, params.nevents, params.force_add);
+ if (params.nevents) {
+ ret = add_perf_probe_events(params.events, params.nevents,
+ params.force_add);
+ if (ret < 0) {
+ pr_err(" Error: Failed to add events. (%d)\n", ret);
+ return ret;
+ }
+ }
return 0;
}

diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 7893b32..bd68f7b 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -53,7 +53,7 @@

bool probe_event_dry_run; /* Dry run flag */

-#define semantic_error(msg ...) die("Semantic error :" msg)
+#define semantic_error(msg ...) pr_err("Semantic error :" msg)

/* If there is no space to write, returns -E2BIG. */
static int e_snprintf(char *str, size_t size, const char *format, ...)
@@ -76,19 +76,30 @@ static struct map_groups kmap_groups;
static struct map *kmaps[MAP__NR_TYPES];

/* Initialize symbol maps and path of vmlinux */
-static void init_vmlinux(void)
+static int init_vmlinux(void)
{
+ int ret;
+
symbol_conf.sort_by_name = true;
if (symbol_conf.vmlinux_name == NULL)
symbol_conf.try_vmlinux_path = true;
else
pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name);
- if (symbol__init() < 0)
- die("Failed to init symbol map.");
+ ret = symbol__init();
+ if (ret < 0) {
+ pr_debug("Failed to init symbol map.\n");
+ goto out;
+ }

map_groups__init(&kmap_groups);
- if (map_groups__create_kernel_maps(&kmap_groups, kmaps) < 0)
- die("Failed to create kernel maps.");
+ ret = map_groups__create_kernel_maps(&kmap_groups, kmaps);
+ if (ret < 0)
+ pr_debug("Failed to create kernel maps.\n");
+
+out:
+ if (ret < 0)
+ pr_warning("Failed to init vmlinux path.\n");
+ return ret;
}

#ifdef DWARF_SUPPORT
@@ -102,24 +113,32 @@ static int open_vmlinux(void)
return open(kmaps[MAP__FUNCTION]->dso->long_name, O_RDONLY);
}

-static void convert_to_perf_probe_point(struct kprobe_trace_point *tp,
- struct perf_probe_point *pp)
+/* Convert trace point to probe point with debuginfo */
+static int convert_to_perf_probe_point(struct kprobe_trace_point *tp,
+ struct perf_probe_point *pp)
{
struct symbol *sym;
- int fd, ret = 0;
+ int fd, ret = -ENOENT;

sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION],
tp->symbol, NULL);
if (sym) {
fd = open_vmlinux();
- ret = find_perf_probe_point(fd, sym->start + tp->offset, pp);
- close(fd);
+ if (fd >= 0) {
+ ret = find_perf_probe_point(fd,
+ sym->start + tp->offset, pp);
+ close(fd);
+ }
}
if (ret <= 0) {
+ pr_debug("Failed to find corresponding probes from "
+ "debuginfo. Use kprobe event information.\n");
pp->function = xstrdup(tp->symbol);
pp->offset = tp->offset;
}
pp->retprobe = tp->retprobe;
+
+ return 0;
}

/* Try to find perf_probe_event with debuginfo */
@@ -131,9 +150,10 @@ static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev,

fd = open_vmlinux();
if (fd < 0) {
- if (need_dwarf)
- die("Could not open debuginfo file.");
-
+ if (need_dwarf) {
+ pr_warning("Failed to open debuginfo file.\n");
+ return fd;
+ }
pr_debug("Could not open vmlinux. Try to use symbols.\n");
return 0;
}
@@ -142,30 +162,32 @@ static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev,
ntevs = find_kprobe_trace_events(fd, pev, tevs);
close(fd);

- if (ntevs > 0) /* Succeeded to find trace events */
+ if (ntevs > 0) { /* Succeeded to find trace events */
+ pr_debug("find %d kprobe_trace_events.\n", ntevs);
return ntevs;
+ }

- if (ntevs == 0) /* No error but failed to find probe point. */
- die("Probe point '%s' not found. - probe not added.",
- synthesize_perf_probe_point(&pev->point));
-
- /* Error path */
+ if (ntevs == 0) { /* No error but failed to find probe point. */
+ pr_warning("Probe point '%s' not found.\n",
+ synthesize_perf_probe_point(&pev->point));
+ return -ENOENT;
+ }
+ /* Error path : ntevs < 0 */
if (need_dwarf) {
if (ntevs == -EBADF)
pr_warning("No dwarf info found in the vmlinux - "
"please rebuild with CONFIG_DEBUG_INFO=y.\n");
- die("Failed to analyze debuginfo.");
+ return ntevs;
}
pr_debug("An error occurred in debuginfo analysis."
" Try to use symbols.\n");
return 0;
-
}

#define LINEBUF_SIZE 256
#define NR_ADDITIONAL_LINES 2

-static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num)
+static int show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num)
{
char buf[LINEBUF_SIZE];
const char *color = PERF_COLOR_BLUE;
@@ -190,19 +212,22 @@ static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num)
color_fprintf(stdout, color, "%s", buf);
}
}
- return;
+
+ return 0;
error:
if (feof(fp))
- die("Source file is shorter than expected.");
+ pr_warning("Source file is shorter than expected.\n");
else
- die("File read error: %s", strerror(errno));
+ pr_warning("File read error: %s\n", strerror(errno));
+
+ return -1;
}

/*
* Show line-range always requires debuginfo to find source file and
* line number.
*/
-void show_line_range(struct line_range *lr)
+int show_line_range(struct line_range *lr)
{
unsigned int l = 1;
struct line_node *ln;
@@ -210,14 +235,25 @@ void show_line_range(struct line_range *lr)
int fd, ret;

/* Search a line range */
- init_vmlinux();
+ ret = init_vmlinux();
+ if (ret < 0)
+ return ret;
+
fd = open_vmlinux();
- if (fd < 0)
- die("Could not open debuginfo file.");
+ if (fd < 0) {
+ pr_warning("Failed to open debuginfo file.\n");
+ return fd;
+ }
+
ret = find_line_range(fd, lr);
- if (ret <= 0)
- die("Source line is not found.\n");
close(fd);
+ if (ret == 0) {
+ pr_warning("Specified source line is not found.\n");
+ return -ENOENT;
+ } else if (ret < 0) {
+ pr_warning("Debuginfo analysis failed. (%d)\n", ret);
+ return ret;
+ }

setup_pager();

@@ -228,52 +264,68 @@ void show_line_range(struct line_range *lr)
fprintf(stdout, "<%s:%d>\n", lr->file, lr->start);

fp = fopen(lr->path, "r");
- if (fp == NULL)
- die("Failed to open %s: %s", lr->path, strerror(errno));
+ if (fp == NULL) {
+ pr_warning("Failed to open %s: %s\n", lr->path,
+ strerror(errno));
+ return -errno;
+ }
/* Skip to starting line number */
- while (l < lr->start)
- show_one_line(fp, l++, true, false);
+ while (l < lr->start && ret >= 0)
+ ret = show_one_line(fp, l++, true, false);
+ if (ret < 0)
+ goto end;

list_for_each_entry(ln, &lr->line_list, list) {
- while (ln->line > l)
- show_one_line(fp, (l++) - lr->offset, false, false);
- show_one_line(fp, (l++) - lr->offset, false, true);
+ while (ln->line > l && ret >= 0)
+ ret = show_one_line(fp, (l++) - lr->offset,
+ false, false);
+ if (ret >= 0)
+ ret = show_one_line(fp, (l++) - lr->offset,
+ false, true);
+ if (ret < 0)
+ goto end;
}

if (lr->end == INT_MAX)
lr->end = l + NR_ADDITIONAL_LINES;
- while (l < lr->end && !feof(fp))
- show_one_line(fp, (l++) - lr->offset, false, false);
-
+ while (l < lr->end && !feof(fp) && ret >= 0)
+ ret = show_one_line(fp, (l++) - lr->offset, false, false);
+end:
fclose(fp);
+ return ret;
}

#else /* !DWARF_SUPPORT */

-static void convert_to_perf_probe_point(struct kprobe_trace_point *tp,
+static int convert_to_perf_probe_point(struct kprobe_trace_point *tp,
struct perf_probe_point *pp)
{
pp->function = xstrdup(tp->symbol);
pp->offset = tp->offset;
pp->retprobe = tp->retprobe;
+
+ return 0;
}

static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev,
struct kprobe_trace_event **tevs __unused)
{
- if (perf_probe_event_need_dwarf(pev))
- die("Debuginfo-analysis is not supported");
+ if (perf_probe_event_need_dwarf(pev)) {
+ pr_warning("Debuginfo-analysis is not supported.\n");
+ return -ENOSYS;
+ }
return 0;
}

-void show_line_range(struct line_range *lr __unused)
+int show_line_range(struct line_range *lr __unused)
{
- die("Debuginfo-analysis is not supported");
+ pr_warning("Debuginfo-analysis is not supported.\n");
+ return -ENOSYS;
}

#endif

-void parse_line_range_desc(const char *arg, struct line_range *lr)
+int parse_line_range_desc(const char *arg, struct line_range *lr)
{
const char *ptr;
char *tmp;
@@ -293,12 +345,16 @@ void parse_line_range_desc(const char *arg, struct line_range *lr)
else
lr->end = 0;
pr_debug("Line range is %u to %u\n", lr->start, lr->end);
- if (lr->end && lr->start > lr->end)
+ if (lr->end && lr->start > lr->end) {
semantic_error("Start line must be smaller"
- " than end line.");
- if (*tmp != '\0')
- semantic_error("Tailing with invalid character '%d'.",
+ " than end line.\n");
+ return -EINVAL;
+ }
+ if (*tmp != '\0') {
+ semantic_error("Tailing with invalid character '%d'.\n",
*tmp);
+ return -EINVAL;
+ }
tmp = xstrndup(arg, (ptr - arg));
} else
tmp = xstrdup(arg);
@@ -307,6 +363,8 @@ void parse_line_range_desc(const char *arg, struct line_range *lr)
lr->file = tmp;
else
lr->function = tmp;
+
+ return 0;
}

/* Check the name is good for event/group */
@@ -322,7 +380,7 @@ static bool check_event_name(const char *name)
}

/* Parse probepoint definition. */
-static void parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
+static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
{
struct perf_probe_point *pp = &pev->point;
char *ptr, *tmp;
@@ -339,12 +397,15 @@ static void parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
if (ptr && *ptr == '=') { /* Event name */
*ptr = '\0';
tmp = ptr + 1;
- ptr = strchr(arg, ':');
- if (ptr) /* Group name is not supported yet. */
- semantic_error("Group name is not supported yet.");
- if (!check_event_name(arg))
+ if (strchr(arg, ':')) {
+ semantic_error("Group name is not supported yet.\n");
+ return -ENOTSUP;
+ }
+ if (!check_event_name(arg)) {
semantic_error("%s is bad for event name -it must "
- "follow C symbol-naming rule.", arg);
+ "follow C symbol-naming rule.\n", arg);
+ return -EINVAL;
+ }
pev->event = xstrdup(arg);
pev->group = NULL;
arg = tmp;
@@ -378,64 +439,89 @@ static void parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
switch (c) {
case ':': /* Line number */
pp->line = strtoul(arg, &tmp, 0);
- if (*tmp != '\0')
+ if (*tmp != '\0') {
semantic_error("There is non-digit char"
- " in line number.");
+ " in line number.\n");
+ return -EINVAL;
+ }
break;
case '+': /* Byte offset from a symbol */
pp->offset = strtoul(arg, &tmp, 0);
- if (*tmp != '\0')
+ if (*tmp != '\0') {
semantic_error("There is non-digit character"
- " in offset.");
+ " in offset.\n");
+ return -EINVAL;
+ }
break;
case '@': /* File name */
- if (pp->file)
- semantic_error("SRC@SRC is not allowed.");
+ if (pp->file) {
+ semantic_error("SRC@SRC is not allowed.\n");
+ return -EINVAL;
+ }
pp->file = xstrdup(arg);
break;
case '%': /* Probe places */
if (strcmp(arg, "return") == 0) {
pp->retprobe = 1;
- } else /* Others not supported yet */
- semantic_error("%%%s is not supported.", arg);
+ } else { /* Others not supported yet */
+ semantic_error("%%%s is not supported.\n", arg);
+ return -ENOTSUP;
+ }
break;
- default:
- DIE_IF("Program has a bug.");
+ default: /* Buggy case */
+ pr_err("This program has a bug at %s:%d.\n",
+ __FILE__, __LINE__);
+ return -ENOTSUP;
break;
}
}

/* Exclusion check */
- if (pp->lazy_line && pp->line)
+ if (pp->lazy_line && pp->line) {
semantic_error("Lazy pattern can't be used with line number.");
+ return -EINVAL;
+ }

- if (pp->lazy_line && pp->offset)
+ if (pp->lazy_line && pp->offset) {
semantic_error("Lazy pattern can't be used with offset.");
+ return -EINVAL;
+ }

- if (pp->line && pp->offset)
+ if (pp->line && pp->offset) {
semantic_error("Offset can't be used with line number.");
+ return -EINVAL;
+ }

- if (!pp->line && !pp->lazy_line && pp->file && !pp->function)
+ if (!pp->line && !pp->lazy_line && pp->file && !pp->function) {
semantic_error("File always requires line number or "
"lazy pattern.");
+ return -EINVAL;
+ }

- if (pp->offset && !pp->function)
+ if (pp->offset && !pp->function) {
semantic_error("Offset requires an entry function.");
+ return -EINVAL;
+ }

- if (pp->retprobe && !pp->function)
+ if (pp->retprobe && !pp->function) {
semantic_error("Return probe requires an entry function.");
+ return -EINVAL;
+ }

- if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe)
+ if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe) {
semantic_error("Offset/Line/Lazy pattern can't be used with "
"return probe.");
+ return -EINVAL;
+ }

pr_debug("symbol:%s file:%s line:%d offset:%lu return:%d lazy:%s\n",
pp->function, pp->file, pp->line, pp->offset, pp->retprobe,
pp->lazy_line);
+ return 0;
}

/* Parse perf-probe event argument */
-static void parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
+static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
{
char *tmp;
struct perf_probe_arg_field **fieldp;
@@ -461,7 +547,7 @@ static void parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
/* A variable, register, symbol or special value */
arg->var = xstrdup(str);
pr_debug("%s\n", arg->var);
- return;
+ return 0;
}

/* Structure fields */
@@ -477,8 +563,10 @@ static void parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
} else if (tmp[1] == '>') {
str = tmp + 2;
(*fieldp)->ref = true;
- } else
- semantic_error("Argument parse error: %s", str);
+ } else {
+ semantic_error("Argument parse error: %s\n", str);
+ return -EINVAL;
+ }

tmp = strpbrk(str, "-.");
if (tmp) {
@@ -493,34 +581,47 @@ static void parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
/* If no name is specified, set the last field name */
if (!arg->name)
arg->name = xstrdup((*fieldp)->name);
+
+ return 0;
}

/* Parse perf-probe event command */
-void parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev)
+int parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev)
{
char **argv;
- int argc, i;
+ int argc, i, ret = 0;

argv = argv_split(cmd, &argc);
- if (!argv)
- die("argv_split failed.");
- if (argc > MAX_PROBE_ARGS + 1)
- semantic_error("Too many arguments");
-
+ if (!argv) {
+ pr_debug("Failed to split arguments.\n");
+ return -ENOMEM;
+ }
+ if (argc - 1 > MAX_PROBE_ARGS) {
+ semantic_error("Too many probe arguments (%d).\n", argc - 1);
+ ret = -ERANGE;
+ goto out;
+ }
/* Parse probe point */
- parse_perf_probe_point(argv[0], pev);
+ ret = parse_perf_probe_point(argv[0], pev);
+ if (ret < 0)
+ goto out;

/* Copy arguments and ensure return probe has no C argument */
pev->nargs = argc - 1;
pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs);
- for (i = 0; i < pev->nargs; i++) {
- parse_perf_probe_arg(argv[i + 1], &pev->args[i]);
- if (is_c_varname(pev->args[i].var) && pev->point.retprobe)
+ for (i = 0; i < pev->nargs && ret >= 0; i++) {
+ ret = parse_perf_probe_arg(argv[i + 1], &pev->args[i]);
+ if (ret >= 0 &&
+ is_c_varname(pev->args[i].var) && pev->point.retprobe) {
semantic_error("You can't specify local variable for"
- " kretprobe");
+ " kretprobe.\n");
+ ret = -EINVAL;
+ }
}
-
+out:
argv_free(argv);
+
+ return ret;
}

/* Return true if this perf_probe_event requires debuginfo */
@@ -539,7 +640,7 @@ bool perf_probe_event_need_dwarf(struct perf_probe_event *pev)
}

/* Parse kprobe_events event into struct probe_point */
-void parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev)
+int parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev)
{
struct kprobe_trace_point *tp = &tev->point;
char pr;
@@ -549,17 +650,25 @@ void parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev)

pr_debug("Parsing kprobe_events: %s\n", cmd);
argv = argv_split(cmd, &argc);
- if (!argv)
- die("argv_split failed.");
- if (argc < 2)
- semantic_error("Too less arguments.");
+ if (!argv) {
+ pr_debug("Failed to split arguments.\n");
+ return -ENOMEM;
+ }
+ if (argc < 2) {
+ semantic_error("Too few probe arguments.\n");
+ ret = -ERANGE;
+ goto out;
+ }

/* Scan event and group name. */
ret = sscanf(argv[0], "%c:%a[^/ \t]/%a[^ \t]",
&pr, (float *)(void *)&tev->group,
(float *)(void *)&tev->event);
- if (ret != 3)
- semantic_error("Failed to parse event name: %s", argv[0]);
+ if (ret != 3) {
+ semantic_error("Failed to parse event name: %s\n", argv[0]);
+ ret = -EINVAL;
+ goto out;
+ }
pr_debug("Group:%s Event:%s probe:%c\n", tev->group, tev->event, pr);

tp->retprobe = (pr == 'r');
@@ -582,8 +691,10 @@ void parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev)
/* TODO: parse regs and offset */
tev->args[i].value = xstrdup(p);
}
-
+ ret = 0;
+out:
argv_free(argv);
+ return ret;
}

/* Compose only probe arg */
@@ -622,7 +733,9 @@ int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len)

return tmp - buf;
error:
- die("Failed to synthesize perf probe argument: %s", strerror(-ret));
+ pr_debug("Failed to synthesize perf probe argument: %s",
+ strerror(-ret));
+ return ret;
}

/* Compose only probe point (not argument) */
@@ -666,7 +779,10 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp)

return buf;
error:
- die("Failed to synthesize perf probe point: %s", strerror(-ret));
+ pr_debug("Failed to synthesize perf probe point: %s",
+ strerror(-ret));
+ free(buf);
+ return NULL;
}

#if 0
@@ -796,29 +912,37 @@ error:
return NULL;
}

-void convert_to_perf_probe_event(struct kprobe_trace_event *tev,
- struct perf_probe_event *pev)
+int convert_to_perf_probe_event(struct kprobe_trace_event *tev,
+ struct perf_probe_event *pev)
{
char buf[64];
- int i;
+ int i, ret;

/* Convert event/group name */
pev->event = xstrdup(tev->event);
pev->group = xstrdup(tev->group);

/* Convert trace_point to probe_point */
- convert_to_perf_probe_point(&tev->point, &pev->point);
+ ret = convert_to_perf_probe_point(&tev->point, &pev->point);
+ if (ret < 0)
+ return ret;

/* Convert trace_arg to probe_arg */
pev->nargs = tev->nargs;
pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs);
- for (i = 0; i < tev->nargs; i++)
+ for (i = 0; i < tev->nargs && ret >= 0; i++)
if (tev->args[i].name)
pev->args[i].name = xstrdup(tev->args[i].name);
else {
- synthesize_kprobe_trace_arg(&tev->args[i], buf, 64);
+ ret = synthesize_kprobe_trace_arg(&tev->args[i],
+ buf, 64);
pev->args[i].name = xstrdup(buf);
}
+
+ if (ret < 0)
+ clear_perf_probe_event(pev);
+
+ return ret;
}

void clear_perf_probe_event(struct perf_probe_event *pev)
@@ -894,21 +1018,20 @@ static int open_kprobe_events(bool readwrite)
int ret;

ret = e_snprintf(buf, PATH_MAX, "%s/../kprobe_events", debugfs_path);
- if (ret < 0)
- die("Failed to make kprobe_events path.");
-
- if (readwrite && !probe_event_dry_run)
- ret = open(buf, O_RDWR, O_APPEND);
- else
- ret = open(buf, O_RDONLY, 0);
+ if (ret >= 0) {
+ if (readwrite && !probe_event_dry_run)
+ ret = open(buf, O_RDWR, O_APPEND);
+ else
+ ret = open(buf, O_RDONLY, 0);
+ }

if (ret < 0) {
if (errno == ENOENT)
- die("kprobe_events file does not exist -"
- " please rebuild with CONFIG_KPROBE_EVENT.");
+ pr_warning("kprobe_events file does not exist - please"
+ " rebuild kernel with CONFIG_KPROBE_EVENT.\n");
else
- die("Could not open kprobe_events file: %s",
- strerror(errno));
+ pr_warning("Failed to open kprobe_events file: %s\n",
+ strerror(errno));
}
return ret;
}
@@ -934,8 +1057,11 @@ static struct strlist *get_kprobe_trace_command_rawlist(int fd)
if (p[idx] == '\n')
p[idx] = '\0';
ret = strlist__add(sl, buf);
- if (ret < 0)
- die("strlist__add failed: %s", strerror(-ret));
+ if (ret < 0) {
+ pr_debug("strlist__add failed: %s\n", strerror(-ret));
+ strlist__delete(sl);
+ return NULL;
+ }
}
fclose(fp);

@@ -943,7 +1069,7 @@ static struct strlist *get_kprobe_trace_command_rawlist(int fd)
}

/* Show an event */
-static void show_perf_probe_event(struct perf_probe_event *pev)
+static int show_perf_probe_event(struct perf_probe_event *pev)
{
int i, ret;
char buf[128];
@@ -951,52 +1077,71 @@ static void show_perf_probe_event(struct perf_probe_event *pev)

/* Synthesize only event probe point */
place = synthesize_perf_probe_point(&pev->point);
+ if (!place)
+ return -EINVAL;

ret = e_snprintf(buf, 128, "%s:%s", pev->group, pev->event);
if (ret < 0)
- die("Failed to copy event: %s", strerror(-ret));
+ return ret;
+
printf(" %-20s (on %s", buf, place);

if (pev->nargs > 0) {
printf(" with");
for (i = 0; i < pev->nargs; i++) {
- synthesize_perf_probe_arg(&pev->args[i], buf, 128);
+ ret = synthesize_perf_probe_arg(&pev->args[i],
+ buf, 128);
+ if (ret < 0)
+ break;
printf(" %s", buf);
}
}
printf(")\n");
free(place);
+ return ret;
}

/* List up current perf-probe events */
-void show_perf_probe_events(void)
+int show_perf_probe_events(void)
{
- int fd;
+ int fd, ret;
struct kprobe_trace_event tev;
struct perf_probe_event pev;
struct strlist *rawlist;
struct str_node *ent;

setup_pager();
- init_vmlinux();
+ ret = init_vmlinux();
+ if (ret < 0)
+ return ret;

memset(&tev, 0, sizeof(tev));
memset(&pev, 0, sizeof(pev));

fd = open_kprobe_events(false);
+ if (fd < 0)
+ return fd;
+
rawlist = get_kprobe_trace_command_rawlist(fd);
close(fd);
+ if (!rawlist)
+ return -ENOENT;

strlist__for_each(ent, rawlist) {
- parse_kprobe_trace_command(ent->s, &tev);
- convert_to_perf_probe_event(&tev, &pev);
- /* Show an event */
- show_perf_probe_event(&pev);
+ ret = parse_kprobe_trace_command(ent->s, &tev);
+ if (ret >= 0) {
+ ret = convert_to_perf_probe_event(&tev, &pev);
+ if (ret >= 0)
+ ret = show_perf_probe_event(&pev);
+ }
clear_perf_probe_event(&pev);
clear_kprobe_trace_event(&tev);
+ if (ret < 0)
+ break;
}
-
strlist__delete(rawlist);
+
+ return ret;
}

/* Get current perf-probe event names */
@@ -1006,88 +1151,118 @@ static struct strlist *get_kprobe_trace_event_names(int fd, bool include_group)
struct strlist *sl, *rawlist;
struct str_node *ent;
struct kprobe_trace_event tev;
+ int ret = 0;

memset(&tev, 0, sizeof(tev));

rawlist = get_kprobe_trace_command_rawlist(fd);
sl = strlist__new(true, NULL);
strlist__for_each(ent, rawlist) {
- parse_kprobe_trace_command(ent->s, &tev);
+ ret = parse_kprobe_trace_command(ent->s, &tev);
+ if (ret < 0)
+ break;
if (include_group) {
- if (e_snprintf(buf, 128, "%s:%s", tev.group,
- tev.event) < 0)
- die("Failed to copy group:event name.");
- strlist__add(sl, buf);
+ ret = e_snprintf(buf, 128, "%s:%s", tev.group,
+ tev.event);
+ if (ret >= 0)
+ ret = strlist__add(sl, buf);
} else
- strlist__add(sl, tev.event);
+ ret = strlist__add(sl, tev.event);
clear_kprobe_trace_event(&tev);
+ if (ret < 0)
+ break;
}
-
strlist__delete(rawlist);

+ if (ret < 0) {
+ strlist__delete(sl);
+ return NULL;
+ }
return sl;
}

-static void write_kprobe_trace_event(int fd, struct kprobe_trace_event *tev)
+static int write_kprobe_trace_event(int fd, struct kprobe_trace_event *tev)
{
int ret;
char *buf = synthesize_kprobe_trace_command(tev);

+ if (!buf) {
+ pr_debug("Failed to synthesize kprobe trace event.\n");
+ return -EINVAL;
+ }
+
pr_debug("Writing event: %s\n", buf);
if (!probe_event_dry_run) {
ret = write(fd, buf, strlen(buf));
if (ret <= 0)
- die("Failed to write event: %s", strerror(errno));
+ pr_warning("Failed to write event: %s\n",
+ strerror(errno));
}
free(buf);
+ return ret;
}

-static void get_new_event_name(char *buf, size_t len, const char *base,
- struct strlist *namelist, bool allow_suffix)
+static int get_new_event_name(char *buf, size_t len, const char *base,
+ struct strlist *namelist, bool allow_suffix)
{
int i, ret;

/* Try no suffix */
ret = e_snprintf(buf, len, "%s", base);
- if (ret < 0)
- die("snprintf() failed: %s", strerror(-ret));
+ if (ret < 0) {
+ pr_debug("snprintf() failed: %s\n", strerror(-ret));
+ return ret;
+ }
if (!strlist__has_entry(namelist, buf))
- return;
+ return 0;

if (!allow_suffix) {
pr_warning("Error: event \"%s\" already exists. "
"(Use -f to force duplicates.)\n", base);
- die("Can't add new event.");
+ return -EEXIST;
}

/* Try to add suffix */
for (i = 1; i < MAX_EVENT_INDEX; i++) {
ret = e_snprintf(buf, len, "%s_%d", base, i);
- if (ret < 0)
- die("snprintf() failed: %s", strerror(-ret));
+ if (ret < 0) {
+ pr_debug("snprintf() failed: %s\n", strerror(-ret));
+ return ret;
+ }
if (!strlist__has_entry(namelist, buf))
break;
}
- if (i == MAX_EVENT_INDEX)
- die("Too many events are on the same function.");
+ if (i == MAX_EVENT_INDEX) {
+ pr_warning("Too many events are on the same function.\n");
+ ret = -ERANGE;
+ }
+
+ return ret;
}

-static void __add_kprobe_trace_events(struct perf_probe_event *pev,
- struct kprobe_trace_event *tevs,
- int ntevs, bool allow_suffix)
+static int __add_kprobe_trace_events(struct perf_probe_event *pev,
+ struct kprobe_trace_event *tevs,
+ int ntevs, bool allow_suffix)
{
- int i, fd;
+ int i, fd, ret;
struct kprobe_trace_event *tev = NULL;
char buf[64];
const char *event, *group;
struct strlist *namelist;

fd = open_kprobe_events(true);
+ if (fd < 0)
+ return fd;
/* Get current event names */
namelist = get_kprobe_trace_event_names(fd, false);
+ if (!namelist) {
+ pr_debug("Failed to get current event list.\n");
+ return -EIO;
+ }

+ ret = 0;
printf("Add new event%s\n", (ntevs > 1) ? "s:" : ":");
- for (i = 0; i < ntevs; i++) {
+ for (i = 0; i < ntevs && ret >= 0; i++) {
tev = &tevs[i];
if (pev->event)
event = pev->event;
@@ -1102,12 +1277,17 @@ static void __add_kprobe_trace_events(struct perf_probe_event *pev,
group = PERFPROBE_GROUP;

/* Get an unused new event name */
- get_new_event_name(buf, 64, event, namelist, allow_suffix);
+ ret = get_new_event_name(buf, 64, event,
+ namelist, allow_suffix);
+ if (ret < 0)
+ break;
event = buf;

tev->event = xstrdup(event);
tev->group = xstrdup(group);
- write_kprobe_trace_event(fd, tev);
+ ret = write_kprobe_trace_event(fd, tev);
+ if (ret < 0)
+ break;
/* Add added event name to namelist */
strlist__add(namelist, event);

@@ -1129,12 +1309,17 @@ static void __add_kprobe_trace_events(struct perf_probe_event *pev,
*/
allow_suffix = true;
}
- /* Show how to use the event. */
- printf("\nYou can now use it on all perf tools, such as:\n\n");
- printf("\tperf record -e %s:%s -a sleep 1\n\n", tev->group, tev->event);
+
+ if (ret >= 0) {
+ /* Show how to use the event. */
+ printf("\nYou can now use it on all perf tools, such as:\n\n");
+ printf("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group,
+ tev->event);
+ }

strlist__delete(namelist);
close(fd);
+ return ret;
}

static int convert_to_kprobe_trace_events(struct perf_probe_event *pev,
@@ -1146,7 +1331,7 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev,

/* Convert perf_probe_event with debuginfo */
ntevs = try_to_find_kprobe_trace_events(pev, tevs);
- if (ntevs > 0)
+ if (ntevs != 0)
return ntevs;

/* Allocate trace event buffer */
@@ -1172,10 +1357,11 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev,
/* Currently just checking function name from symbol map */
sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION],
tev->point.symbol, NULL);
- if (!sym)
- die("Kernel symbol \'%s\' not found - probe not added.",
- tev->point.symbol);
-
+ if (!sym) {
+ pr_warning("Kernel symbol \'%s\' not found.\n",
+ tev->point.symbol);
+ return -ENOENT;
+ }
return ntevs;
}

@@ -1185,93 +1371,128 @@ struct __event_package {
int ntevs;
};

-void add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
- bool force_add)
+int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
+ bool force_add)
{
- int i;
+ int i, j, ret;
struct __event_package *pkgs;

pkgs = xzalloc(sizeof(struct __event_package) * npevs);

/* Init vmlinux path */
- init_vmlinux();
+ ret = init_vmlinux();
+ if (ret < 0)
+ return ret;

/* Loop 1: convert all events */
for (i = 0; i < npevs; i++) {
pkgs[i].pev = &pevs[i];
/* Convert with or without debuginfo */
- pkgs[i].ntevs = convert_to_kprobe_trace_events(pkgs[i].pev,
- &pkgs[i].tevs);
+ ret = convert_to_kprobe_trace_events(pkgs[i].pev,
+ &pkgs[i].tevs);
+ if (ret < 0)
+ goto end;
+ pkgs[i].ntevs = ret;
}

/* Loop 2: add all events */
+ for (i = 0; i < npevs && ret >= 0; i++)
+ ret = __add_kprobe_trace_events(pkgs[i].pev, pkgs[i].tevs,
+ pkgs[i].ntevs, force_add);
+end:
+ /* Loop 3: cleanup trace events */
for (i = 0; i < npevs; i++)
- __add_kprobe_trace_events(pkgs[i].pev, pkgs[i].tevs,
- pkgs[i].ntevs, force_add);
- /* TODO: cleanup all trace events? */
+ for (j = 0; j < pkgs[i].ntevs; j++)
+ clear_kprobe_trace_event(&pkgs[i].tevs[j]);
+
+ return ret;
}

-static void __del_trace_kprobe_event(int fd, struct str_node *ent)
+static int __del_trace_kprobe_event(int fd, struct str_node *ent)
{
char *p;
char buf[128];
int ret;

/* Convert from perf-probe event to trace-kprobe event */
- if (e_snprintf(buf, 128, "-:%s", ent->s) < 0)
- die("Failed to copy event.");
+ ret = e_snprintf(buf, 128, "-:%s", ent->s);
+ if (ret < 0)
+ goto error;
+
p = strchr(buf + 2, ':');
- if (!p)
- die("Internal error: %s should have ':' but not.", ent->s);
+ if (!p) {
+ pr_debug("Internal error: %s should have ':' but not.\n",
+ ent->s);
+ ret = -ENOTSUP;
+ goto error;
+ }
*p = '/';

pr_debug("Writing event: %s\n", buf);
ret = write(fd, buf, strlen(buf));
- if (ret <= 0)
- die("Failed to write event: %s", strerror(errno));
+ if (ret < 0)
+ goto error;
+
printf("Remove event: %s\n", ent->s);
+ return 0;
+error:
+ pr_warning("Failed to delete event: %s\n", strerror(-ret));
+ return ret;
}

-static void del_trace_kprobe_event(int fd, const char *group,
- const char *event, struct strlist *namelist)
+static int del_trace_kprobe_event(int fd, const char *group,
+ const char *event, struct strlist *namelist)
{
char buf[128];
struct str_node *ent, *n;
- int found = 0;
+ int found = 0, ret = 0;

- if (e_snprintf(buf, 128, "%s:%s", group, event) < 0)
- die("Failed to copy event.");
+ ret = e_snprintf(buf, 128, "%s:%s", group, event);
+ if (ret < 0) {
+ pr_err("Failed to copy event.");
+ return ret;
+ }

if (strpbrk(buf, "*?")) { /* Glob-exp */
strlist__for_each_safe(ent, n, namelist)
if (strglobmatch(ent->s, buf)) {
found++;
- __del_trace_kprobe_event(fd, ent);
+ ret = __del_trace_kprobe_event(fd, ent);
+ if (ret < 0)
+ break;
strlist__remove(namelist, ent);
}
} else {
ent = strlist__find(namelist, buf);
if (ent) {
found++;
- __del_trace_kprobe_event(fd, ent);
- strlist__remove(namelist, ent);
+ ret = __del_trace_kprobe_event(fd, ent);
+ if (ret >= 0)
+ strlist__remove(namelist, ent);
}
}
- if (found == 0)
- pr_info("Info: event \"%s\" does not exist, could not remove it.\n", buf);
+ if (found == 0 && ret >= 0)
+ pr_info("Info: Event \"%s\" does not exist.\n", buf);
+
+ return ret;
}

-void del_perf_probe_events(struct strlist *dellist)
+int del_perf_probe_events(struct strlist *dellist)
{
- int fd;
+ int fd, ret = 0;
const char *group, *event;
char *p, *str;
struct str_node *ent;
struct strlist *namelist;

fd = open_kprobe_events(true);
+ if (fd < 0)
+ return fd;
+
/* Get current event names */
namelist = get_kprobe_trace_event_names(fd, true);
+ if (namelist == NULL)
+ return -EINVAL;

strlist__for_each(ent, dellist) {
str = xstrdup(ent->s);
@@ -1286,10 +1507,14 @@ void del_perf_probe_events(struct strlist *dellist)
event = str;
}
pr_debug("Group: %s, Event: %s\n", group, event);
- del_trace_kprobe_event(fd, group, event, namelist);
+ ret = del_trace_kprobe_event(fd, group, event, namelist);
free(str);
+ if (ret < 0)
+ break;
}
strlist__delete(namelist);
close(fd);
+
+ return ret;
}

diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index ff2f26b..ab54929 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -89,10 +89,10 @@ struct line_range {
};

/* Command string to events */
-extern void parse_perf_probe_command(const char *cmd,
- struct perf_probe_event *pev);
-extern void parse_kprobe_trace_command(const char *cmd,
- struct kprobe_trace_event *tev);
+extern int parse_perf_probe_command(const char *cmd,
+ struct perf_probe_event *pev);
+extern int parse_kprobe_trace_command(const char *cmd,
+ struct kprobe_trace_event *tev);

/* Events to command string */
extern char *synthesize_perf_probe_command(struct perf_probe_event *pev);
@@ -104,22 +104,22 @@ extern int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf,
extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev);

/* Convert from kprobe_trace_event to perf_probe_event */
-extern void convert_to_perf_probe_event(struct kprobe_trace_event *tev,
- struct perf_probe_event *pev);
+extern int convert_to_perf_probe_event(struct kprobe_trace_event *tev,
+ struct perf_probe_event *pev);

/* Release event contents */
extern void clear_perf_probe_event(struct perf_probe_event *pev);
extern void clear_kprobe_trace_event(struct kprobe_trace_event *tev);

/* Command string to line-range */
-extern void parse_line_range_desc(const char *cmd, struct line_range *lr);
+extern int parse_line_range_desc(const char *cmd, struct line_range *lr);


-extern void add_perf_probe_events(struct perf_probe_event *pevs, int ntevs,
- bool force_add);
-extern void del_perf_probe_events(struct strlist *dellist);
-extern void show_perf_probe_events(void);
-extern void show_line_range(struct line_range *lr);
+extern int add_perf_probe_events(struct perf_probe_event *pevs, int ntevs,
+ 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);


/* Maximum index number of event-name postfix */

2010-04-15 07:30:53

by Masami Hiramatsu

[permalink] [raw]
Subject: [tip:perf/core] perf probe: Remove die() from probe-finder code

Commit-ID: b55a87ade3839c33ab94768a0b5955734073f987
Gitweb: http://git.kernel.org/tip/b55a87ade3839c33ab94768a0b5955734073f987
Author: Masami Hiramatsu <[email protected]>
AuthorDate: Mon, 12 Apr 2010 13:17:35 -0400
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Wed, 14 Apr 2010 17:28:30 -0300

perf probe: Remove die() from probe-finder code

Remove die() and DIE_IF() code from util/probe-finder.c since
these 'sudden death' in utility functions make reusing it from
other code (especially tui/gui) difficult.

Cc: Ingo Molnar <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Mike Galbraith <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
LKML-Reference: <[email protected]>
Signed-off-by: Masami Hiramatsu <[email protected]>
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/util/probe-event.c | 4 +-
tools/perf/util/probe-finder.c | 517 +++++++++++++++++++++++++---------------
2 files changed, 322 insertions(+), 199 deletions(-)

diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index bef2805..7893b32 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -151,10 +151,10 @@ static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev,

/* Error path */
if (need_dwarf) {
- if (ntevs == -ENOENT)
+ if (ntevs == -EBADF)
pr_warning("No dwarf info found in the vmlinux - "
"please rebuild with CONFIG_DEBUG_INFO=y.\n");
- die("Could not analyze debuginfo.");
+ die("Failed to analyze debuginfo.");
}
pr_debug("An error occurred in debuginfo analysis."
" Try to use symbols.\n");
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 1f45285..54daa91 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -196,19 +196,7 @@ static bool die_compare_name(Dwarf_Die *dw_die, const char *tname)
{
const char *name;
name = dwarf_diename(dw_die);
- DIE_IF(name == NULL);
- return strcmp(tname, name);
-}
-
-/* Get entry pc(or low pc, 1st entry of ranges) of the die */
-static Dwarf_Addr die_get_entrypc(Dwarf_Die *dw_die)
-{
- Dwarf_Addr epc;
- int ret;
-
- ret = dwarf_entrypc(dw_die, &epc);
- DIE_IF(ret == -1);
- return epc;
+ return name ? strcmp(tname, name) : -1;
}

/* Get type die, but skip qualifiers and typedef */
@@ -390,7 +378,7 @@ static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
*/

/* Show a location */
-static void convert_location(Dwarf_Op *op, struct probe_finder *pf)
+static int convert_location(Dwarf_Op *op, struct probe_finder *pf)
{
unsigned int regn;
Dwarf_Word offs = 0;
@@ -400,8 +388,11 @@ static void convert_location(Dwarf_Op *op, struct probe_finder *pf)

/* If this is based on frame buffer, set the offset */
if (op->atom == DW_OP_fbreg) {
- if (pf->fb_ops == NULL)
- die("The attribute of frame base is not supported.\n");
+ if (pf->fb_ops == NULL) {
+ pr_warning("The attribute of frame base is not "
+ "supported.\n");
+ return -ENOTSUP;
+ }
ref = true;
offs = op->number;
op = &pf->fb_ops[0];
@@ -419,50 +410,63 @@ static void convert_location(Dwarf_Op *op, struct probe_finder *pf)
ref = true;
} else if (op->atom == DW_OP_regx) {
regn = op->number;
- } else
- die("DW_OP %d is not supported.", op->atom);
+ } else {
+ pr_warning("DW_OP %x is not supported.\n", op->atom);
+ return -ENOTSUP;
+ }

regs = get_arch_regstr(regn);
- if (!regs)
- die("%u exceeds max register number.", regn);
+ if (!regs) {
+ pr_warning("%u exceeds max register number.\n", regn);
+ return -ERANGE;
+ }

tvar->value = xstrdup(regs);
if (ref) {
tvar->ref = xzalloc(sizeof(struct kprobe_trace_arg_ref));
tvar->ref->offset = (long)offs;
}
+ return 0;
}

-static void convert_variable_type(Dwarf_Die *vr_die,
- struct kprobe_trace_arg *targ)
+static int convert_variable_type(Dwarf_Die *vr_die,
+ struct kprobe_trace_arg *targ)
{
Dwarf_Die type;
char buf[16];
int ret;

- if (die_get_real_type(vr_die, &type) == NULL)
- die("Failed to get a type information of %s.",
- dwarf_diename(vr_die));
+ if (die_get_real_type(vr_die, &type) == NULL) {
+ pr_warning("Failed to get a type information of %s.\n",
+ dwarf_diename(vr_die));
+ return -ENOENT;
+ }

ret = die_get_byte_size(&type) * 8;
if (ret) {
/* Check the bitwidth */
if (ret > MAX_BASIC_TYPE_BITS) {
- pr_warning(" Warning: %s exceeds max-bitwidth."
- " Cut down to %d bits.\n",
- dwarf_diename(&type), MAX_BASIC_TYPE_BITS);
+ pr_info("%s exceeds max-bitwidth."
+ " Cut down to %d bits.\n",
+ dwarf_diename(&type), MAX_BASIC_TYPE_BITS);
ret = MAX_BASIC_TYPE_BITS;
}

ret = snprintf(buf, 16, "%c%d",
die_is_signed_type(&type) ? 's' : 'u', ret);
- if (ret < 0 || ret >= 16)
- die("Failed to convert variable type.");
+ if (ret < 0 || ret >= 16) {
+ if (ret >= 16)
+ ret = -E2BIG;
+ pr_warning("Failed to convert variable type: %s\n",
+ strerror(-ret));
+ return ret;
+ }
targ->type = xstrdup(buf);
}
+ return 0;
}

-static void convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
+static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
struct perf_probe_arg_field *field,
struct kprobe_trace_arg_ref **ref_ptr,
Dwarf_Die *die_mem)
@@ -473,21 +477,28 @@ static void convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
Dwarf_Word offs;

pr_debug("converting %s in %s\n", field->name, varname);
- if (die_get_real_type(vr_die, &type) == NULL)
- die("Failed to get a type information of %s.", varname);
+ if (die_get_real_type(vr_die, &type) == NULL) {
+ pr_warning("Failed to get the type of %s.\n", varname);
+ return -ENOENT;
+ }

/* Check the pointer and dereference */
if (dwarf_tag(&type) == DW_TAG_pointer_type) {
- if (!field->ref)
- die("Semantic error: %s must be referred by '->'",
- field->name);
+ if (!field->ref) {
+ pr_err("Semantic error: %s must be referred by '->'\n",
+ field->name);
+ return -EINVAL;
+ }
/* Get the type pointed by this pointer */
- if (die_get_real_type(&type, &type) == NULL)
- die("Failed to get a type information of %s.", varname);
-
+ if (die_get_real_type(&type, &type) == NULL) {
+ pr_warning("Failed to get the type of %s.\n", varname);
+ return -ENOENT;
+ }
/* Verify it is a data structure */
- if (dwarf_tag(&type) != DW_TAG_structure_type)
- die("%s is not a data structure.", varname);
+ if (dwarf_tag(&type) != DW_TAG_structure_type) {
+ pr_warning("%s is not a data structure.\n", varname);
+ return -EINVAL;
+ }

ref = xzalloc(sizeof(struct kprobe_trace_arg_ref));
if (*ref_ptr)
@@ -496,34 +507,46 @@ static void convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
*ref_ptr = ref;
} else {
/* Verify it is a data structure */
- if (dwarf_tag(&type) != DW_TAG_structure_type)
- die("%s is not a data structure.", varname);
-
- if (field->ref)
- die("Semantic error: %s must be referred by '.'",
- field->name);
- if (!ref)
- die("Structure on a register is not supported yet.");
+ if (dwarf_tag(&type) != DW_TAG_structure_type) {
+ pr_warning("%s is not a data structure.\n", varname);
+ return -EINVAL;
+ }
+ if (field->ref) {
+ pr_err("Semantic error: %s must be referred by '.'\n",
+ field->name);
+ return -EINVAL;
+ }
+ if (!ref) {
+ pr_warning("Structure on a register is not "
+ "supported yet.\n");
+ return -ENOTSUP;
+ }
}

- if (die_find_member(&type, field->name, die_mem) == NULL)
- die("%s(tyep:%s) has no member %s.", varname,
- dwarf_diename(&type), field->name);
+ if (die_find_member(&type, field->name, die_mem) == NULL) {
+ pr_warning("%s(tyep:%s) has no member %s.\n", varname,
+ dwarf_diename(&type), field->name);
+ return -EINVAL;
+ }

/* Get the offset of the field */
if (dwarf_attr(die_mem, DW_AT_data_member_location, &attr) == NULL ||
- dwarf_formudata(&attr, &offs) != 0)
- die("Failed to get the offset of %s.", field->name);
+ dwarf_formudata(&attr, &offs) != 0) {
+ pr_warning("Failed to get the offset of %s.\n", field->name);
+ return -ENOENT;
+ }
ref->offset += (long)offs;

/* Converting next field */
if (field->next)
- convert_variable_fields(die_mem, field->name, field->next,
- &ref, die_mem);
+ return convert_variable_fields(die_mem, field->name,
+ field->next, &ref, die_mem);
+ else
+ return 0;
}

/* Show a variables in kprobe event format */
-static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
+static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
{
Dwarf_Attribute attr;
Dwarf_Die die_mem;
@@ -538,28 +561,30 @@ static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
if (ret <= 0 || nexpr == 0)
goto error;

- convert_location(expr, pf);
-
- if (pf->pvar->field) {
- convert_variable_fields(vr_die, pf->pvar->var,
- pf->pvar->field, &pf->tvar->ref,
- &die_mem);
+ ret = convert_location(expr, pf);
+ if (ret == 0 && pf->pvar->field) {
+ ret = convert_variable_fields(vr_die, pf->pvar->var,
+ pf->pvar->field, &pf->tvar->ref,
+ &die_mem);
vr_die = &die_mem;
}
- if (pf->pvar->type)
- pf->tvar->type = xstrdup(pf->pvar->type);
- else
- convert_variable_type(vr_die, pf->tvar);
+ if (ret == 0) {
+ if (pf->pvar->type)
+ pf->tvar->type = xstrdup(pf->pvar->type);
+ else
+ ret = convert_variable_type(vr_die, pf->tvar);
+ }
/* *expr will be cached in libdw. Don't free it. */
- return ;
+ return ret;
error:
/* TODO: Support const_value */
- die("Failed to find the location of %s at this address.\n"
- " Perhaps, it has been optimized out.", pf->pvar->var);
+ 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;
}

/* Find a variable in a subprogram die */
-static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
+static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
{
Dwarf_Die vr_die;
char buf[32], *ptr;
@@ -578,19 +603,22 @@ static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
if (!is_c_varname(pf->pvar->var)) {
/* Copy raw parameters */
pf->tvar->value = xstrdup(pf->pvar->var);
- } else {
- 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))
- die("Failed to find '%s' in this function.",
- pf->pvar->var);
- convert_variable(&vr_die, pf);
+ return 0;
}
+
+ 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)) {
+ pr_warning("Failed to find '%s' in this function.\n",
+ pf->pvar->var);
+ return -ENOENT;
+ }
+ return convert_variable(&vr_die, pf);
}

/* Show a probe point to output buffer */
-static void convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
+static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
{
struct kprobe_trace_event *tev;
Dwarf_Addr eaddr;
@@ -600,22 +628,31 @@ static void convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
Dwarf_Attribute fb_attr;
size_t nops;

- if (pf->ntevs == MAX_PROBES)
- die("Too many( > %d) probe point found.\n", MAX_PROBES);
+ if (pf->ntevs == MAX_PROBES) {
+ pr_warning("Too many( > %d) probe point found.\n", MAX_PROBES);
+ 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)
- die("Probe point is not found in subprograms.");
+ 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);
if (name) {
- dwarf_entrypc(sp_die, &eaddr);
+ if (dwarf_entrypc(sp_die, &eaddr) != 0) {
+ pr_warning("Failed to get entry pc of %s\n",
+ dwarf_diename(sp_die));
+ return -ENOENT;
+ }
tev->point.symbol = xstrdup(name);
tev->point.offset = (unsigned long)(pf->addr - eaddr);
} else
@@ -633,9 +670,12 @@ static void convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
} else if (nops == 1 && pf->fb_ops[0].atom == DW_OP_call_frame_cfa &&
pf->cfi != NULL) {
Dwarf_Frame *frame;
- ret = dwarf_cfi_addrframe(pf->cfi, pf->addr, &frame);
- DIE_IF(ret != 0);
- dwarf_frame_cfa(frame, &pf->fb_ops, &nops);
+ if (dwarf_cfi_addrframe(pf->cfi, pf->addr, &frame) != 0 ||
+ dwarf_frame_cfa(frame, &pf->fb_ops, &nops) != 0) {
+ pr_warning("Failed to get CFA on 0x%jx\n",
+ (uintmax_t)pf->addr);
+ return -ENOENT;
+ }
}

/* Find each argument */
@@ -644,45 +684,53 @@ static void convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
for (i = 0; i < pf->pev->nargs; i++) {
pf->pvar = &pf->pev->args[i];
pf->tvar = &tev->args[i];
- find_variable(sp_die, pf);
+ ret = find_variable(sp_die, pf);
+ if (ret != 0)
+ return ret;
}

/* *pf->fb_ops will be cached in libdw. Don't free it. */
pf->fb_ops = NULL;
+ return 0;
}

/* Find probe point from its line number */
-static void find_probe_point_by_line(struct probe_finder *pf)
+static int find_probe_point_by_line(struct probe_finder *pf)
{
Dwarf_Lines *lines;
Dwarf_Line *line;
size_t nlines, i;
Dwarf_Addr addr;
int lineno;
- int ret;
+ int ret = 0;

- ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines);
- DIE_IF(ret != 0);
+ if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) {
+ pr_warning("No source lines found in this CU.\n");
+ return -ENOENT;
+ }

- for (i = 0; i < nlines; i++) {
+ for (i = 0; i < nlines && ret == 0; i++) {
line = dwarf_onesrcline(lines, i);
- dwarf_lineno(line, &lineno);
- if (lineno != pf->lno)
+ if (dwarf_lineno(line, &lineno) != 0 ||
+ lineno != pf->lno)
continue;

/* TODO: Get fileno from line, but how? */
if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0)
continue;

- ret = dwarf_lineaddr(line, &addr);
- DIE_IF(ret != 0);
+ if (dwarf_lineaddr(line, &addr) != 0) {
+ pr_warning("Failed to get the address of the line.\n");
+ return -ENOENT;
+ }
pr_debug("Probe line found: line[%d]:%d addr:0x%jx\n",
(int)i, lineno, (uintmax_t)addr);
pf->addr = addr;

- convert_probe_point(NULL, pf);
+ ret = convert_probe_point(NULL, pf);
/* Continuing, because target line might be inlined. */
}
+ return ret;
}

/* Find lines which match lazy pattern */
@@ -690,15 +738,27 @@ static int find_lazy_match_lines(struct list_head *head,
const char *fname, const char *pat)
{
char *fbuf, *p1, *p2;
- int fd, line, nlines = 0;
+ int fd, ret, line, nlines = 0;
struct stat st;

fd = open(fname, O_RDONLY);
- if (fd < 0)
- die("failed to open %s", fname);
- DIE_IF(fstat(fd, &st) < 0);
+ if (fd < 0) {
+ pr_warning("Failed to open %s: %s\n", fname, strerror(-fd));
+ return fd;
+ }
+
+ ret = fstat(fd, &st);
+ if (ret < 0) {
+ pr_warning("Failed to get the size of %s: %s\n",
+ fname, strerror(errno));
+ return ret;
+ }
fbuf = xmalloc(st.st_size + 2);
- DIE_IF(read(fd, fbuf, st.st_size) < 0);
+ ret = read(fd, fbuf, st.st_size);
+ if (ret < 0) {
+ pr_warning("Failed to read %s: %s\n", fname, strerror(errno));
+ return ret;
+ }
close(fd);
fbuf[st.st_size] = '\n'; /* Dummy line */
fbuf[st.st_size + 1] = '\0';
@@ -718,7 +778,7 @@ static int find_lazy_match_lines(struct list_head *head,
}

/* Find probe points from lazy pattern */
-static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
+static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
{
Dwarf_Lines *lines;
Dwarf_Line *line;
@@ -726,31 +786,40 @@ static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
Dwarf_Addr addr;
Dwarf_Die die_mem;
int lineno;
- int ret;
+ int ret = 0;

if (list_empty(&pf->lcache)) {
/* Matching lazy line pattern */
ret = find_lazy_match_lines(&pf->lcache, pf->fname,
pf->pev->point.lazy_line);
- if (ret <= 0)
- die("No matched lines found in %s.", pf->fname);
+ if (ret == 0) {
+ pr_debug("No matched lines found in %s.\n", pf->fname);
+ return 0;
+ } else if (ret < 0)
+ return ret;
}

- ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines);
- DIE_IF(ret != 0);
- for (i = 0; i < nlines; i++) {
+ if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) {
+ pr_warning("No source lines found in this CU.\n");
+ return -ENOENT;
+ }
+
+ for (i = 0; i < nlines && ret >= 0; i++) {
line = dwarf_onesrcline(lines, i);

- dwarf_lineno(line, &lineno);
- if (!line_list__has_line(&pf->lcache, lineno))
+ if (dwarf_lineno(line, &lineno) != 0 ||
+ !line_list__has_line(&pf->lcache, lineno))
continue;

/* TODO: Get fileno from line, but how? */
if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0)
continue;

- ret = dwarf_lineaddr(line, &addr);
- DIE_IF(ret != 0);
+ if (dwarf_lineaddr(line, &addr) != 0) {
+ pr_debug("Failed to get the address of line %d.\n",
+ lineno);
+ continue;
+ }
if (sp_die) {
/* Address filtering 1: does sp_die include addr? */
if (!dwarf_haspc(sp_die, addr))
@@ -764,27 +833,42 @@ static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
(int)i, lineno, (unsigned long long)addr);
pf->addr = addr;

- convert_probe_point(sp_die, pf);
+ ret = convert_probe_point(sp_die, pf);
/* Continuing, because target line might be inlined. */
}
/* TODO: deallocate lines, but how? */
+ return ret;
}

+/* Callback parameter with return value */
+struct dwarf_callback_param {
+ void *data;
+ int retval;
+};
+
static int probe_point_inline_cb(Dwarf_Die *in_die, void *data)
{
- struct probe_finder *pf = (struct probe_finder *)data;
+ struct dwarf_callback_param *param = data;
+ struct probe_finder *pf = param->data;
struct perf_probe_point *pp = &pf->pev->point;
+ Dwarf_Addr addr;

if (pp->lazy_line)
- find_probe_point_lazy(in_die, pf);
+ param->retval = find_probe_point_lazy(in_die, pf);
else {
/* Get probe address */
- pf->addr = die_get_entrypc(in_die);
+ if (dwarf_entrypc(in_die, &addr) != 0) {
+ pr_warning("Failed to get entry pc of %s.\n",
+ dwarf_diename(in_die));
+ param->retval = -ENOENT;
+ return DWARF_CB_ABORT;
+ }
+ pf->addr = addr;
pf->addr += pp->offset;
pr_debug("found inline addr: 0x%jx\n",
(uintmax_t)pf->addr);

- convert_probe_point(in_die, pf);
+ param->retval = convert_probe_point(in_die, pf);
}

return DWARF_CB_OK;
@@ -793,39 +877,53 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data)
/* Search function from function name */
static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
{
- struct probe_finder *pf = (struct probe_finder *)data;
+ struct dwarf_callback_param *param = data;
+ struct probe_finder *pf = param->data;
struct perf_probe_point *pp = &pf->pev->point;

/* Check tag and diename */
if (dwarf_tag(sp_die) != DW_TAG_subprogram ||
die_compare_name(sp_die, pp->function) != 0)
- return 0;
+ return DWARF_CB_OK;

pf->fname = dwarf_decl_file(sp_die);
if (pp->line) { /* Function relative line */
dwarf_decl_line(sp_die, &pf->lno);
pf->lno += pp->line;
- find_probe_point_by_line(pf);
+ param->retval = find_probe_point_by_line(pf);
} else if (!dwarf_func_inline(sp_die)) {
/* Real function */
if (pp->lazy_line)
- find_probe_point_lazy(sp_die, pf);
+ param->retval = find_probe_point_lazy(sp_die, pf);
else {
- pf->addr = die_get_entrypc(sp_die);
+ if (dwarf_entrypc(sp_die, &pf->addr) != 0) {
+ pr_warning("Failed to get entry pc of %s.\n",
+ dwarf_diename(sp_die));
+ param->retval = -ENOENT;
+ return DWARF_CB_ABORT;
+ }
pf->addr += pp->offset;
/* TODO: Check the address in this function */
- convert_probe_point(sp_die, pf);
+ param->retval = convert_probe_point(sp_die, pf);
}
- } else
+ } else {
+ struct dwarf_callback_param _param = {.data = (void *)pf,
+ .retval = 0};
/* Inlined function: search instances */
- dwarf_func_inline_instances(sp_die, probe_point_inline_cb, pf);
+ dwarf_func_inline_instances(sp_die, probe_point_inline_cb,
+ &_param);
+ param->retval = _param.retval;
+ }

- return 1; /* Exit; no same symbol in this CU. */
+ return DWARF_CB_ABORT; /* Exit; no same symbol in this CU. */
}

-static void find_probe_point_by_func(struct probe_finder *pf)
+static int find_probe_point_by_func(struct probe_finder *pf)
{
- dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, pf, 0);
+ struct dwarf_callback_param _param = {.data = (void *)pf,
+ .retval = 0};
+ dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, &_param, 0);
+ return _param.retval;
}

/* Find kprobe_trace_events specified by perf_probe_event from debuginfo */
@@ -838,14 +936,18 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev,
size_t cuhl;
Dwarf_Die *diep;
Dwarf *dbg;
+ int ret = 0;

pf.tevs = xzalloc(sizeof(struct kprobe_trace_event) * MAX_PROBES);
*tevs = pf.tevs;
pf.ntevs = 0;

dbg = dwarf_begin(fd, DWARF_C_READ);
- if (!dbg)
- return -ENOENT;
+ if (!dbg) {
+ pr_warning("No dwarf info found in the vmlinux - "
+ "please rebuild with CONFIG_DEBUG_INFO=y.\n");
+ return -EBADF;
+ }

/* Get the call frame information from this dwarf */
pf.cfi = dwarf_getcfi(dbg);
@@ -853,7 +955,8 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev,
off = 0;
line_list__init(&pf.lcache);
/* Loop on CUs (Compilation Unit) */
- while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL)) {
+ 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);
if (!diep)
@@ -867,12 +970,12 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev,

if (!pp->file || pf.fname) {
if (pp->function)
- find_probe_point_by_func(&pf);
+ ret = find_probe_point_by_func(&pf);
else if (pp->lazy_line)
- find_probe_point_lazy(NULL, &pf);
+ ret = find_probe_point_lazy(NULL, &pf);
else {
pf.lno = pp->line;
- find_probe_point_by_line(&pf);
+ ret = find_probe_point_by_line(&pf);
}
}
off = noff;
@@ -880,7 +983,7 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev,
line_list__free(&pf.lcache);
dwarf_end(dbg);

- return pf.ntevs;
+ return (ret < 0) ? ret : pf.ntevs;
}

/* Reverse search */
@@ -893,10 +996,11 @@ int find_perf_probe_point(int fd, unsigned long addr,
Dwarf_Addr laddr, eaddr;
const char *tmp;
int lineno, ret = 0;
+ bool found = false;

dbg = dwarf_begin(fd, DWARF_C_READ);
if (!dbg)
- return -ENOENT;
+ return -EBADF;

/* Find cu die */
if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr, &cudie)) {
@@ -907,82 +1011,87 @@ int find_perf_probe_point(int fd, unsigned long addr,
/* Find a corresponding line */
line = dwarf_getsrc_die(&cudie, (Dwarf_Addr)addr);
if (line) {
- dwarf_lineaddr(line, &laddr);
- if ((Dwarf_Addr)addr == laddr) {
- dwarf_lineno(line, &lineno);
- ppt->line = lineno;
-
+ if (dwarf_lineaddr(line, &laddr) == 0 &&
+ (Dwarf_Addr)addr == laddr &&
+ dwarf_lineno(line, &lineno) == 0) {
tmp = dwarf_linesrc(line, NULL, NULL);
- DIE_IF(!tmp);
- ppt->file = xstrdup(tmp);
- ret = 1;
+ if (tmp) {
+ ppt->line = lineno;
+ ppt->file = xstrdup(tmp);
+ found = true;
+ }
}
}

/* Find a corresponding function */
if (die_find_real_subprogram(&cudie, (Dwarf_Addr)addr, &spdie)) {
tmp = dwarf_diename(&spdie);
- if (!tmp)
+ if (!tmp || dwarf_entrypc(&spdie, &eaddr) != 0)
goto end;

- dwarf_entrypc(&spdie, &eaddr);
- if (!lineno) {
- /* We don't have a line number, let's use offset */
- ppt->function = xstrdup(tmp);
- ppt->offset = addr - (unsigned long)eaddr;
- ret = 1;
- goto end;
- }
- if (die_find_inlinefunc(&spdie, (Dwarf_Addr)addr, &indie)) {
- /* addr in an inline function */
- tmp = dwarf_diename(&indie);
- if (!tmp)
- goto end;
- dwarf_decl_line(&indie, &lineno);
- } else {
- if (eaddr == addr) /* No offset: function entry */
- lineno = ppt->line;
- else
- dwarf_decl_line(&spdie, &lineno);
+ if (ppt->line) {
+ if (die_find_inlinefunc(&spdie, (Dwarf_Addr)addr,
+ &indie)) {
+ /* addr in an inline function */
+ tmp = dwarf_diename(&indie);
+ if (!tmp)
+ goto end;
+ ret = dwarf_decl_line(&indie, &lineno);
+ } else {
+ if (eaddr == addr) { /* Function entry */
+ lineno = ppt->line;
+ ret = 0;
+ } else
+ ret = dwarf_decl_line(&spdie, &lineno);
+ }
+ if (ret == 0) {
+ /* Make a relative line number */
+ ppt->line -= lineno;
+ goto found;
+ }
}
+ /* We don't have a line number, let's use offset */
+ ppt->offset = addr - (unsigned long)eaddr;
+found:
ppt->function = xstrdup(tmp);
- ppt->line -= lineno; /* Make a relative line number */
+ found = true;
}

end:
dwarf_end(dbg);
+ if (ret >= 0)
+ ret = found ? 1 : 0;
return ret;
}


/* Find line range from its line number */
-static void find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf)
+static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf)
{
Dwarf_Lines *lines;
Dwarf_Line *line;
size_t nlines, i;
Dwarf_Addr addr;
int lineno;
- int ret;
const char *src;
Dwarf_Die die_mem;

line_list__init(&lf->lr->line_list);
- ret = dwarf_getsrclines(&lf->cu_die, &lines, &nlines);
- DIE_IF(ret != 0);
+ if (dwarf_getsrclines(&lf->cu_die, &lines, &nlines) != 0) {
+ pr_warning("No source lines found in this CU.\n");
+ return -ENOENT;
+ }

for (i = 0; i < nlines; i++) {
line = dwarf_onesrcline(lines, i);
- ret = dwarf_lineno(line, &lineno);
- DIE_IF(ret != 0);
- if (lf->lno_s > lineno || lf->lno_e < lineno)
+ if (dwarf_lineno(line, &lineno) != 0 ||
+ (lf->lno_s > lineno || lf->lno_e < lineno))
continue;

if (sp_die) {
/* Address filtering 1: does sp_die include addr? */
- ret = dwarf_lineaddr(line, &addr);
- DIE_IF(ret != 0);
- if (!dwarf_haspc(sp_die, addr))
+ if (dwarf_lineaddr(line, &addr) != 0 ||
+ !dwarf_haspc(sp_die, addr))
continue;

/* Address filtering 2: No child include addr? */
@@ -1007,18 +1116,22 @@ static void find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf)
free(lf->lr->path);
lf->lr->path = NULL;
}
+ return lf->found;
}

static int line_range_inline_cb(Dwarf_Die *in_die, void *data)
{
- find_line_range_by_line(in_die, (struct line_finder *)data);
+ struct dwarf_callback_param *param = data;
+
+ param->retval = find_line_range_by_line(in_die, param->data);
return DWARF_CB_ABORT; /* No need to find other instances */
}

/* Search function from function name */
static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
{
- struct line_finder *lf = (struct line_finder *)data;
+ struct dwarf_callback_param *param = data;
+ struct line_finder *lf = param->data;
struct line_range *lr = lf->lr;

if (dwarf_tag(sp_die) == DW_TAG_subprogram &&
@@ -1033,38 +1146,47 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
lf->lno_e = lr->offset + lr->end;
lr->start = lf->lno_s;
lr->end = lf->lno_e;
- if (dwarf_func_inline(sp_die))
+ if (dwarf_func_inline(sp_die)) {
+ struct dwarf_callback_param _param;
+ _param.data = (void *)lf;
+ _param.retval = 0;
dwarf_func_inline_instances(sp_die,
- line_range_inline_cb, lf);
- else
- find_line_range_by_line(sp_die, lf);
- return 1;
+ line_range_inline_cb,
+ &_param);
+ param->retval = _param.retval;
+ } else
+ param->retval = find_line_range_by_line(sp_die, lf);
+ return DWARF_CB_ABORT;
}
- return 0;
+ return DWARF_CB_OK;
}

-static void find_line_range_by_func(struct line_finder *lf)
+static int find_line_range_by_func(struct line_finder *lf)
{
- dwarf_getfuncs(&lf->cu_die, line_range_search_cb, lf, 0);
+ struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0};
+ dwarf_getfuncs(&lf->cu_die, line_range_search_cb, &param, 0);
+ return param.retval;
}

int find_line_range(int fd, struct line_range *lr)
{
struct line_finder lf = {.lr = lr, .found = 0};
- int ret;
+ int ret = 0;
Dwarf_Off off = 0, noff;
size_t cuhl;
Dwarf_Die *diep;
Dwarf *dbg;

dbg = dwarf_begin(fd, DWARF_C_READ);
- if (!dbg)
- return -ENOENT;
+ if (!dbg) {
+ pr_warning("No dwarf info found in the vmlinux - "
+ "please rebuild with CONFIG_DEBUG_INFO=y.\n");
+ return -EBADF;
+ }

/* Loop on CUs (Compilation Unit) */
- while (!lf.found) {
- ret = dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL);
- if (ret != 0)
+ while (!lf.found && ret >= 0) {
+ if (dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) != 0)
break;

/* Get the DIE(Debugging Information Entry) of this CU */
@@ -1080,20 +1202,21 @@ int find_line_range(int fd, struct line_range *lr)

if (!lr->file || lf.fname) {
if (lr->function)
- find_line_range_by_func(&lf);
+ ret = find_line_range_by_func(&lf);
else {
lf.lno_s = lr->start;
if (!lr->end)
lf.lno_e = INT_MAX;
else
lf.lno_e = lr->end;
- find_line_range_by_line(NULL, &lf);
+ ret = find_line_range_by_line(NULL, &lf);
}
}
off = noff;
}
pr_debug("path: %lx\n", (unsigned long)lr->path);
dwarf_end(dbg);
- return lf.found;
+
+ return (ret < 0) ? ret : lf.found;
}

2010-04-15 07:30:41

by Masami Hiramatsu

[permalink] [raw]
Subject: [tip:perf/core] perf probe: Remove xzalloc() from util/probe-{event, finder}.c

Commit-ID: e334016f1d7250a6523b3a44ccecfe23af6e2f57
Gitweb: http://git.kernel.org/tip/e334016f1d7250a6523b3a44ccecfe23af6e2f57
Author: Masami Hiramatsu <[email protected]>
AuthorDate: Mon, 12 Apr 2010 13:17:49 -0400
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Wed, 14 Apr 2010 17:28:45 -0300

perf probe: Remove xzalloc() from util/probe-{event, finder}.c

Remove all xzalloc() calls from util/probe-{event,finder}.c since
it may cause 'sudden death' in utility functions and it makes
reusing it from other code difficult.

Cc: Ingo Molnar <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Mike Galbraith <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
LKML-Reference: <[email protected]>
Signed-off-by: Masami Hiramatsu <[email protected]>
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/util/probe-event.c | 69 +++++++++++++++++++++++++++++----------
tools/perf/util/probe-finder.c | 25 ++++++++++----
2 files changed, 69 insertions(+), 25 deletions(-)

diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index bd68f7b..aacbf73 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -556,7 +556,9 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
fieldp = &arg->field;

do {
- *fieldp = xzalloc(sizeof(struct perf_probe_arg_field));
+ *fieldp = zalloc(sizeof(struct perf_probe_arg_field));
+ if (*fieldp == NULL)
+ return -ENOMEM;
if (*tmp == '.') {
str = tmp + 1;
(*fieldp)->ref = false;
@@ -608,7 +610,11 @@ int parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev)

/* Copy arguments and ensure return probe has no C argument */
pev->nargs = argc - 1;
- pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs);
+ pev->args = zalloc(sizeof(struct perf_probe_arg) * pev->nargs);
+ if (pev->args == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
for (i = 0; i < pev->nargs && ret >= 0; i++) {
ret = parse_perf_probe_arg(argv[i + 1], &pev->args[i]);
if (ret >= 0 &&
@@ -680,7 +686,11 @@ int parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev)
tp->offset = 0;

tev->nargs = argc - 2;
- tev->args = xzalloc(sizeof(struct kprobe_trace_arg) * tev->nargs);
+ tev->args = zalloc(sizeof(struct kprobe_trace_arg) * tev->nargs);
+ if (tev->args == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
for (i = 0; i < tev->nargs; i++) {
p = strchr(argv[i + 2], '=');
if (p) /* We don't need which register is assigned. */
@@ -745,7 +755,11 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp)
char offs[32] = "", line[32] = "", file[32] = "";
int ret, len;

- buf = xzalloc(MAX_CMDLEN);
+ buf = zalloc(MAX_CMDLEN);
+ if (buf == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
if (pp->offset) {
ret = e_snprintf(offs, 32, "+%lu", pp->offset);
if (ret <= 0)
@@ -781,7 +795,8 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp)
error:
pr_debug("Failed to synthesize perf probe point: %s",
strerror(-ret));
- free(buf);
+ if (buf)
+ free(buf);
return NULL;
}

@@ -890,7 +905,10 @@ char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev)
char *buf;
int i, len, ret;

- buf = xzalloc(MAX_CMDLEN);
+ buf = zalloc(MAX_CMDLEN);
+ if (buf == NULL)
+ return NULL;
+
len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s+%lu",
tp->retprobe ? 'r' : 'p',
tev->group, tev->event,
@@ -929,7 +947,9 @@ int convert_to_perf_probe_event(struct kprobe_trace_event *tev,

/* Convert trace_arg to probe_arg */
pev->nargs = tev->nargs;
- pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs);
+ pev->args = zalloc(sizeof(struct perf_probe_arg) * pev->nargs);
+ if (pev->args == NULL)
+ return -ENOMEM;
for (i = 0; i < tev->nargs && ret >= 0; i++)
if (tev->args[i].name)
pev->args[i].name = xstrdup(tev->args[i].name);
@@ -1326,25 +1346,31 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev,
struct kprobe_trace_event **tevs)
{
struct symbol *sym;
- int ntevs = 0, i;
+ int ret = 0, i;
struct kprobe_trace_event *tev;

/* Convert perf_probe_event with debuginfo */
- ntevs = try_to_find_kprobe_trace_events(pev, tevs);
- if (ntevs != 0)
- return ntevs;
+ ret = try_to_find_kprobe_trace_events(pev, tevs);
+ if (ret != 0)
+ return ret;

/* Allocate trace event buffer */
- ntevs = 1;
- tev = *tevs = xzalloc(sizeof(struct kprobe_trace_event));
+ tev = *tevs = zalloc(sizeof(struct kprobe_trace_event));
+ if (tev == NULL)
+ return -ENOMEM;

/* Copy parameters */
tev->point.symbol = xstrdup(pev->point.function);
tev->point.offset = pev->point.offset;
tev->nargs = pev->nargs;
if (tev->nargs) {
- tev->args = xzalloc(sizeof(struct kprobe_trace_arg)
- * tev->nargs);
+ tev->args = zalloc(sizeof(struct kprobe_trace_arg)
+ * tev->nargs);
+ if (tev->args == NULL) {
+ free(tev);
+ *tevs = NULL;
+ return -ENOMEM;
+ }
for (i = 0; i < tev->nargs; i++) {
if (pev->args[i].name)
tev->args[i].name = xstrdup(pev->args[i].name);
@@ -1360,9 +1386,14 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev,
if (!sym) {
pr_warning("Kernel symbol \'%s\' not found.\n",
tev->point.symbol);
+ clear_kprobe_trace_event(tev);
+ free(tev);
+ *tevs = NULL;
return -ENOENT;
- }
- return ntevs;
+ } else
+ ret = 1;
+
+ return ret;
}

struct __event_package {
@@ -1377,7 +1408,9 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
int i, j, ret;
struct __event_package *pkgs;

- pkgs = xzalloc(sizeof(struct __event_package) * npevs);
+ pkgs = zalloc(sizeof(struct __event_package) * npevs);
+ if (pkgs == NULL)
+ return -ENOMEM;

/* Init vmlinux path */
ret = init_vmlinux();
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 54daa91..ce1ac82 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -111,7 +111,7 @@ static int strtailcmp(const char *s1, const char *s2)
/* Line number list operations */

/* Add a line to line number list */
-static void line_list__add_line(struct list_head *head, unsigned int line)
+static int line_list__add_line(struct list_head *head, unsigned int line)
{
struct line_node *ln;
struct list_head *p;
@@ -122,16 +122,19 @@ static void line_list__add_line(struct list_head *head, unsigned int line)
p = &ln->list;
goto found;
} else if (ln->line == line) /* Already exist */
- return ;
+ return 1;
}
/* List is empty, or the smallest entry */
p = head;
found:
pr_debug("line list: add a line %u\n", line);
- ln = xzalloc(sizeof(struct line_node));
+ ln = zalloc(sizeof(struct line_node));
+ if (ln == NULL)
+ return -ENOMEM;
ln->line = line;
INIT_LIST_HEAD(&ln->list);
list_add(&ln->list, p);
+ return 0;
}

/* Check if the line in line number list */
@@ -423,7 +426,9 @@ static int convert_location(Dwarf_Op *op, struct probe_finder *pf)

tvar->value = xstrdup(regs);
if (ref) {
- tvar->ref = xzalloc(sizeof(struct kprobe_trace_arg_ref));
+ tvar->ref = zalloc(sizeof(struct kprobe_trace_arg_ref));
+ if (tvar->ref == NULL)
+ return -ENOMEM;
tvar->ref->offset = (long)offs;
}
return 0;
@@ -500,7 +505,9 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
return -EINVAL;
}

- ref = xzalloc(sizeof(struct kprobe_trace_arg_ref));
+ ref = zalloc(sizeof(struct kprobe_trace_arg_ref));
+ if (ref == NULL)
+ return -ENOMEM;
if (*ref_ptr)
(*ref_ptr)->next = ref;
else
@@ -680,7 +687,9 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)

/* Find each argument */
tev->nargs = pf->pev->nargs;
- tev->args = xzalloc(sizeof(struct kprobe_trace_arg) * tev->nargs);
+ tev->args = zalloc(sizeof(struct kprobe_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];
@@ -938,7 +947,9 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev,
Dwarf *dbg;
int ret = 0;

- pf.tevs = xzalloc(sizeof(struct kprobe_trace_event) * MAX_PROBES);
+ pf.tevs = zalloc(sizeof(struct kprobe_trace_event) * MAX_PROBES);
+ if (pf.tevs == NULL)
+ return -ENOMEM;
*tevs = pf.tevs;
pf.ntevs = 0;

2010-04-15 07:31:00

by Masami Hiramatsu

[permalink] [raw]
Subject: [tip:perf/core] perf probe: Remove xstrdup()/xstrndup() from util/probe-{event, finder}.c

Commit-ID: 02b95dadc8a1d2c302513e5fa24c492380d26e93
Gitweb: http://git.kernel.org/tip/02b95dadc8a1d2c302513e5fa24c492380d26e93
Author: Masami Hiramatsu <[email protected]>
AuthorDate: Mon, 12 Apr 2010 13:17:56 -0400
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Wed, 14 Apr 2010 17:28:52 -0300

perf probe: Remove xstrdup()/xstrndup() from util/probe-{event, finder}.c

Remove all xstr*dup() calls from util/probe-{event,finder}.c since
it may cause 'sudden death' in utility functions and it makes
reusing it from other code difficult.

Cc: Ingo Molnar <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Mike Galbraith <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
LKML-Reference: <[email protected]>
Signed-off-by: Masami Hiramatsu <[email protected]>
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/util/probe-event.c | 159 ++++++++++++++++++++++++++++------------
tools/perf/util/probe-finder.c | 58 +++++++++++----
2 files changed, 156 insertions(+), 61 deletions(-)

diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index aacbf73..ca108b2 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -133,7 +133,9 @@ static int convert_to_perf_probe_point(struct kprobe_trace_point *tp,
if (ret <= 0) {
pr_debug("Failed to find corresponding probes from "
"debuginfo. Use kprobe event information.\n");
- pp->function = xstrdup(tp->symbol);
+ pp->function = strdup(tp->symbol);
+ if (pp->function == NULL)
+ return -ENOMEM;
pp->offset = tp->offset;
}
pp->retprobe = tp->retprobe;
@@ -300,7 +302,9 @@ end:
static int convert_to_perf_probe_point(struct kprobe_trace_point *tp,
struct perf_probe_point *pp)
{
- pp->function = xstrdup(tp->symbol);
+ pp->function = strdup(tp->symbol);
+ if (pp->function == NULL)
+ return -ENOMEM;
pp->offset = tp->offset;
pp->retprobe = tp->retprobe;

@@ -355,9 +359,12 @@ int parse_line_range_desc(const char *arg, struct line_range *lr)
*tmp);
return -EINVAL;
}
- tmp = xstrndup(arg, (ptr - arg));
+ tmp = strndup(arg, (ptr - arg));
} else
- tmp = xstrdup(arg);
+ tmp = strdup(arg);
+
+ if (tmp == NULL)
+ return -ENOMEM;

if (strchr(tmp, '.'))
lr->file = tmp;
@@ -406,7 +413,9 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
"follow C symbol-naming rule.\n", arg);
return -EINVAL;
}
- pev->event = xstrdup(arg);
+ pev->event = strdup(arg);
+ if (pev->event == NULL)
+ return -ENOMEM;
pev->group = NULL;
arg = tmp;
}
@@ -417,18 +426,24 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
*ptr++ = '\0';
}

+ tmp = strdup(arg);
+ if (tmp == NULL)
+ return -ENOMEM;
+
/* Check arg is function or file and copy it */
- if (strchr(arg, '.')) /* File */
- pp->file = xstrdup(arg);
+ if (strchr(tmp, '.')) /* File */
+ pp->file = tmp;
else /* Function */
- pp->function = xstrdup(arg);
+ pp->function = tmp;

/* Parse other options */
while (ptr) {
arg = ptr;
c = nc;
if (c == ';') { /* Lazy pattern must be the last part */
- pp->lazy_line = xstrdup(arg);
+ pp->lazy_line = strdup(arg);
+ if (pp->lazy_line == NULL)
+ return -ENOMEM;
break;
}
ptr = strpbrk(arg, ";:+@%");
@@ -458,7 +473,9 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
semantic_error("SRC@SRC is not allowed.\n");
return -EINVAL;
}
- pp->file = xstrdup(arg);
+ pp->file = strdup(arg);
+ if (pp->file == NULL)
+ return -ENOMEM;
break;
case '%': /* Probe places */
if (strcmp(arg, "return") == 0) {
@@ -530,7 +547,9 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)

tmp = strchr(str, '=');
if (tmp) {
- arg->name = xstrndup(str, tmp - str);
+ arg->name = strndup(str, tmp - str);
+ if (arg->name == NULL)
+ return -ENOMEM;
pr_debug("name:%s ", arg->name);
str = tmp + 1;
}
@@ -538,20 +557,26 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
tmp = strchr(str, ':');
if (tmp) { /* Type setting */
*tmp = '\0';
- arg->type = xstrdup(tmp + 1);
+ arg->type = strdup(tmp + 1);
+ if (arg->type == NULL)
+ return -ENOMEM;
pr_debug("type:%s ", arg->type);
}

tmp = strpbrk(str, "-.");
if (!is_c_varname(str) || !tmp) {
/* A variable, register, symbol or special value */
- arg->var = xstrdup(str);
+ arg->var = strdup(str);
+ if (arg->var == NULL)
+ return -ENOMEM;
pr_debug("%s\n", arg->var);
return 0;
}

/* Structure fields */
- arg->var = xstrndup(str, tmp - str);
+ arg->var = strndup(str, tmp - str);
+ if (arg->var == NULL)
+ return -ENOMEM;
pr_debug("%s, ", arg->var);
fieldp = &arg->field;

@@ -572,18 +597,24 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)

tmp = strpbrk(str, "-.");
if (tmp) {
- (*fieldp)->name = xstrndup(str, tmp - str);
+ (*fieldp)->name = strndup(str, tmp - str);
+ if ((*fieldp)->name == NULL)
+ return -ENOMEM;
pr_debug("%s(%d), ", (*fieldp)->name, (*fieldp)->ref);
fieldp = &(*fieldp)->next;
}
} while (tmp);
- (*fieldp)->name = xstrdup(str);
+ (*fieldp)->name = strdup(str);
+ if ((*fieldp)->name == NULL)
+ return -ENOMEM;
pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref);

/* If no name is specified, set the last field name */
- if (!arg->name)
- arg->name = xstrdup((*fieldp)->name);
-
+ if (!arg->name) {
+ arg->name = strdup((*fieldp)->name);
+ if (arg->name == NULL)
+ return -ENOMEM;
+ }
return 0;
}

@@ -697,9 +728,13 @@ int parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev)
*p++ = '\0';
else
p = argv[i + 2];
- tev->args[i].name = xstrdup(argv[i + 2]);
+ tev->args[i].name = strdup(argv[i + 2]);
/* TODO: parse regs and offset */
- tev->args[i].value = xstrdup(p);
+ tev->args[i].value = strdup(p);
+ if (tev->args[i].name == NULL || tev->args[i].value == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
}
ret = 0;
out:
@@ -933,12 +968,14 @@ error:
int convert_to_perf_probe_event(struct kprobe_trace_event *tev,
struct perf_probe_event *pev)
{
- char buf[64];
+ char buf[64] = "";
int i, ret;

/* Convert event/group name */
- pev->event = xstrdup(tev->event);
- pev->group = xstrdup(tev->group);
+ pev->event = strdup(tev->event);
+ pev->group = strdup(tev->group);
+ if (pev->event == NULL || pev->group == NULL)
+ return -ENOMEM;

/* Convert trace_point to probe_point */
ret = convert_to_perf_probe_point(&tev->point, &pev->point);
@@ -950,14 +987,17 @@ int convert_to_perf_probe_event(struct kprobe_trace_event *tev,
pev->args = zalloc(sizeof(struct perf_probe_arg) * pev->nargs);
if (pev->args == NULL)
return -ENOMEM;
- for (i = 0; i < tev->nargs && ret >= 0; i++)
+ for (i = 0; i < tev->nargs && ret >= 0; i++) {
if (tev->args[i].name)
- pev->args[i].name = xstrdup(tev->args[i].name);
+ pev->args[i].name = strdup(tev->args[i].name);
else {
ret = synthesize_kprobe_trace_arg(&tev->args[i],
buf, 64);
- pev->args[i].name = xstrdup(buf);
+ pev->args[i].name = strdup(buf);
}
+ if (pev->args[i].name == NULL && ret >= 0)
+ ret = -ENOMEM;
+ }

if (ret < 0)
clear_perf_probe_event(pev);
@@ -1282,7 +1322,7 @@ static int __add_kprobe_trace_events(struct perf_probe_event *pev,

ret = 0;
printf("Add new event%s\n", (ntevs > 1) ? "s:" : ":");
- for (i = 0; i < ntevs && ret >= 0; i++) {
+ for (i = 0; i < ntevs; i++) {
tev = &tevs[i];
if (pev->event)
event = pev->event;
@@ -1303,8 +1343,12 @@ static int __add_kprobe_trace_events(struct perf_probe_event *pev,
break;
event = buf;

- tev->event = xstrdup(event);
- tev->group = xstrdup(group);
+ tev->event = strdup(event);
+ tev->group = strdup(group);
+ if (tev->event == NULL || tev->group == NULL) {
+ ret = -ENOMEM;
+ break;
+ }
ret = write_kprobe_trace_event(fd, tev);
if (ret < 0)
break;
@@ -1360,23 +1404,40 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev,
return -ENOMEM;

/* Copy parameters */
- tev->point.symbol = xstrdup(pev->point.function);
+ tev->point.symbol = strdup(pev->point.function);
+ if (tev->point.symbol == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
tev->point.offset = pev->point.offset;
tev->nargs = pev->nargs;
if (tev->nargs) {
tev->args = zalloc(sizeof(struct kprobe_trace_arg)
* tev->nargs);
if (tev->args == NULL) {
- free(tev);
- *tevs = NULL;
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto error;
}
for (i = 0; i < tev->nargs; i++) {
- if (pev->args[i].name)
- tev->args[i].name = xstrdup(pev->args[i].name);
- tev->args[i].value = xstrdup(pev->args[i].var);
- if (pev->args[i].type)
- tev->args[i].type = xstrdup(pev->args[i].type);
+ if (pev->args[i].name) {
+ tev->args[i].name = strdup(pev->args[i].name);
+ if (tev->args[i].name == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ }
+ tev->args[i].value = strdup(pev->args[i].var);
+ if (tev->args[i].value == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ if (pev->args[i].type) {
+ tev->args[i].type = strdup(pev->args[i].type);
+ if (tev->args[i].type == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ }
}
}

@@ -1386,13 +1447,15 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev,
if (!sym) {
pr_warning("Kernel symbol \'%s\' not found.\n",
tev->point.symbol);
- clear_kprobe_trace_event(tev);
- free(tev);
- *tevs = NULL;
- return -ENOENT;
- } else
- ret = 1;
+ ret = -ENOENT;
+ goto error;
+ }

+ return 1;
+error:
+ clear_kprobe_trace_event(tev);
+ free(tev);
+ *tevs = NULL;
return ret;
}

@@ -1528,7 +1591,11 @@ int del_perf_probe_events(struct strlist *dellist)
return -EINVAL;

strlist__for_each(ent, dellist) {
- str = xstrdup(ent->s);
+ str = strdup(ent->s);
+ if (str == NULL) {
+ ret = -ENOMEM;
+ break;
+ }
pr_debug("Parsing: %s\n", str);
p = strchr(str, ':');
if (p) {
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index ce1ac82..e443e69 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -424,7 +424,10 @@ static int convert_location(Dwarf_Op *op, struct probe_finder *pf)
return -ERANGE;
}

- tvar->value = xstrdup(regs);
+ tvar->value = strdup(regs);
+ if (tvar->value == NULL)
+ return -ENOMEM;
+
if (ref) {
tvar->ref = zalloc(sizeof(struct kprobe_trace_arg_ref));
if (tvar->ref == NULL)
@@ -466,7 +469,9 @@ static int convert_variable_type(Dwarf_Die *vr_die,
strerror(-ret));
return ret;
}
- targ->type = xstrdup(buf);
+ targ->type = strdup(buf);
+ if (targ->type == NULL)
+ return -ENOMEM;
}
return 0;
}
@@ -576,9 +581,11 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
vr_die = &die_mem;
}
if (ret == 0) {
- if (pf->pvar->type)
- pf->tvar->type = xstrdup(pf->pvar->type);
- else
+ if (pf->pvar->type) {
+ pf->tvar->type = strdup(pf->pvar->type);
+ if (pf->tvar->type == NULL)
+ ret = -ENOMEM;
+ } else
ret = convert_variable_type(vr_die, pf->tvar);
}
/* *expr will be cached in libdw. Don't free it. */
@@ -595,22 +602,30 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
{
Dwarf_Die vr_die;
char buf[32], *ptr;
+ int ret;

/* TODO: Support arrays */
if (pf->pvar->name)
- pf->tvar->name = xstrdup(pf->pvar->name);
+ pf->tvar->name = strdup(pf->pvar->name);
else {
- synthesize_perf_probe_arg(pf->pvar, buf, 32);
+ ret = synthesize_perf_probe_arg(pf->pvar, buf, 32);
+ if (ret < 0)
+ return ret;
ptr = strchr(buf, ':'); /* Change type separator to _ */
if (ptr)
*ptr = '_';
- pf->tvar->name = xstrdup(buf);
+ pf->tvar->name = strdup(buf);
}
+ if (pf->tvar->name == NULL)
+ return -ENOMEM;

if (!is_c_varname(pf->pvar->var)) {
/* Copy raw parameters */
- pf->tvar->value = xstrdup(pf->pvar->var);
- return 0;
+ pf->tvar->value = strdup(pf->pvar->var);
+ if (pf->tvar->value == NULL)
+ return -ENOMEM;
+ else
+ return 0;
}

pr_debug("Searching '%s' variable in context.\n",
@@ -660,7 +675,9 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
dwarf_diename(sp_die));
return -ENOENT;
}
- tev->point.symbol = xstrdup(name);
+ tev->point.symbol = strdup(name);
+ if (tev->point.symbol == NULL)
+ return -ENOMEM;
tev->point.offset = (unsigned long)(pf->addr - eaddr);
} else
/* This function has no name. */
@@ -1028,7 +1045,11 @@ int find_perf_probe_point(int fd, unsigned long addr,
tmp = dwarf_linesrc(line, NULL, NULL);
if (tmp) {
ppt->line = lineno;
- ppt->file = xstrdup(tmp);
+ ppt->file = strdup(tmp);
+ if (ppt->file == NULL) {
+ ret = -ENOMEM;
+ goto end;
+ }
found = true;
}
}
@@ -1064,7 +1085,11 @@ int find_perf_probe_point(int fd, unsigned long addr,
/* We don't have a line number, let's use offset */
ppt->offset = addr - (unsigned long)eaddr;
found:
- ppt->function = xstrdup(tmp);
+ ppt->function = strdup(tmp);
+ if (ppt->function == NULL) {
+ ret = -ENOMEM;
+ goto end;
+ }
found = true;
}

@@ -1116,8 +1141,11 @@ static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf)
continue;

/* Copy real path */
- if (!lf->lr->path)
- lf->lr->path = xstrdup(src);
+ if (!lf->lr->path) {
+ lf->lr->path = strdup(src);
+ if (lf->lr->path == NULL)
+ return -ENOMEM;
+ }
line_list__add_line(&lf->lr->line_list, (unsigned int)lineno);
}
/* Update status */

2010-04-15 07:32:55

by Masami Hiramatsu

[permalink] [raw]
Subject: [tip:perf/core] perf probe: Support DW_OP_call_frame_cfa in debuginfo

Commit-ID: a34a985499895a46a4dacff727d0fbc63cdc75e8
Gitweb: http://git.kernel.org/tip/a34a985499895a46a4dacff727d0fbc63cdc75e8
Author: Masami Hiramatsu <[email protected]>
AuthorDate: Mon, 12 Apr 2010 13:17:29 -0400
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Wed, 14 Apr 2010 17:28:20 -0300

perf probe: Support DW_OP_call_frame_cfa in debuginfo

When building kernel without CONFIG_FRAME_POINTER, gcc uses
CFA (canonical frame address) for frame base. With this patch,
perf probe just gets CFI (call frame information) from debuginfo
and search corresponding CFA from the CFI. IOW, this allows
perf probe works correctly on the kernel without CONFIG_FRAME_POINTER.

<Before>
./perf probe -fn sched_slice:12 lw.weight
Fatal: DW_OP 156 is not supported.
(^^^ DW_OP_call_frame_cfa)

<After>
./perf probe -fn sched_slice:12 lw.weight
Add new event:
probe:sched_slice (on sched_slice:12 with weight=lw.weight)

Cc: Ingo Molnar <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Mike Galbraith <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
LKML-Reference: <[email protected]>
Signed-off-by: Masami Hiramatsu <[email protected]>
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/util/probe-finder.c | 14 +++++++++++---
tools/perf/util/probe-finder.h | 1 +
2 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index ab47673..1f45285 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -398,7 +398,6 @@ static void convert_location(Dwarf_Op *op, struct probe_finder *pf)
const char *regs;
struct kprobe_trace_arg *tvar = pf->tvar;

- /* TODO: support CFA */
/* If this is based on frame buffer, set the offset */
if (op->atom == DW_OP_fbreg) {
if (pf->fb_ops == NULL)
@@ -629,11 +628,17 @@ static void convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
/* Get the frame base attribute/ops */
dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr);
ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1);
- if (ret <= 0 || nops == 0)
+ if (ret <= 0 || nops == 0) {
pf->fb_ops = NULL;
+ } else if (nops == 1 && pf->fb_ops[0].atom == DW_OP_call_frame_cfa &&
+ pf->cfi != NULL) {
+ Dwarf_Frame *frame;
+ ret = dwarf_cfi_addrframe(pf->cfi, pf->addr, &frame);
+ DIE_IF(ret != 0);
+ dwarf_frame_cfa(frame, &pf->fb_ops, &nops);
+ }

/* Find each argument */
- /* TODO: use dwarf_cfi_addrframe */
tev->nargs = pf->pev->nargs;
tev->args = xzalloc(sizeof(struct kprobe_trace_arg) * tev->nargs);
for (i = 0; i < pf->pev->nargs; i++) {
@@ -842,6 +847,9 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev,
if (!dbg)
return -ENOENT;

+ /* Get the call frame information from this dwarf */
+ pf.cfi = dwarf_getcfi(dbg);
+
off = 0;
line_list__init(&pf.lcache);
/* Loop on CUs (Compilation Unit) */
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
index 2a27132..310ce89 100644
--- a/tools/perf/util/probe-finder.h
+++ b/tools/perf/util/probe-finder.h
@@ -42,6 +42,7 @@ struct probe_finder {
struct list_head lcache; /* Line cache for lazy match */

/* For variable searching */
+ Dwarf_CFI *cfi; /* Call Frame Information */
Dwarf_Op *fb_ops; /* Frame base attribute */
struct perf_probe_arg *pvar; /* Current target variable */
struct kprobe_trace_arg *tvar; /* Current result variable */