Commit b5a80b7e authored by Ingo Molnar's avatar Ingo Molnar

Merge branch 'perf' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux-2.6 into perf/core

parents 84b13fd5 f6c903f5
......@@ -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.
......
......@@ -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:
......
......@@ -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 @@ static struct trace_probe *alloc_trace_probe(const char *group,
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 @@ print_kprobe_event(struct trace_iterator *iter, int flags)
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);
......
......@@ -79,7 +79,16 @@ 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[: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
-----------
......
......@@ -40,7 +40,6 @@
#include "util/debug.h"
#include "util/debugfs.h"
#include "util/parse-options.h"
#include "util/parse-events.h" /* For debugfs_path */
#include "util/probe-finder.h"
#include "util/probe-event.h"
......@@ -59,23 +58,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 +87,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 +116,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
......@@ -142,9 +148,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"
......@@ -178,6 +184,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,28 +193,31 @@ 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 &&
!params.show_lines))
usage_with_options(probe_usage, options);
if (debugfs_valid_mountpoint(debugfs_path) < 0)
die("Failed to find debugfs path.");
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 +228,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;
}
......@@ -68,7 +68,7 @@ hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
int64_t cmp = 0;
list_for_each_entry(se, &hist_entry__sort_list, list) {
cmp = se->cmp(left, right);
cmp = se->se_cmp(left, right);
if (cmp)
break;
}
......@@ -85,7 +85,7 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
list_for_each_entry(se, &hist_entry__sort_list, list) {
int64_t (*f)(struct hist_entry *, struct hist_entry *);
f = se->collapse ?: se->cmp;
f = se->se_collapse ?: se->se_cmp;
cmp = f(left, right);
if (cmp)
......@@ -536,8 +536,8 @@ int hist_entry__snprintf(struct hist_entry *self,
continue;
ret += snprintf(s + ret, size - ret, "%s", sep ?: " ");
ret += se->snprintf(self, s + ret, size - ret,
se->width ? *se->width : 0);
ret += se->se_snprintf(self, s + ret, size - ret,
se->se_width ? *se->se_width : 0);
}
return ret;
......@@ -564,7 +564,7 @@ static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp,
if (sort__first_dimension == SORT_COMM) {
struct sort_entry *se = list_first_entry(&hist_entry__sort_list,
typeof(*se), list);
left_margin = se->width ? *se->width : 0;
left_margin = se->se_width ? *se->se_width : 0;
left_margin -= thread__comm_len(self->thread);
}
......@@ -615,22 +615,22 @@ size_t perf_session__fprintf_hists(struct rb_root *hists,
if (se->elide)
continue;
if (sep) {
fprintf(fp, "%c%s", *sep, se->header);
fprintf(fp, "%c%s", *sep, se->se_header);
continue;
}
width = strlen(se->header);
if (se->width) {
width = strlen(se->se_header);
if (se->se_width) {
if (symbol_conf.col_width_list_str) {
if (col_width) {
*se->width = atoi(col_width);
*se->se_width = atoi(col_width);
col_width = strchr(col_width, ',');
if (col_width)
++col_width;
}
}
width = *se->width = max(*se->width, width);
width = *se->se_width = max(*se->se_width, width);
}
fprintf(fp, " %*s", width, se->header);
fprintf(fp, " %*s", width, se->se_header);
}
fprintf(fp, "\n");
......@@ -652,10 +652,10 @@ size_t perf_session__fprintf_hists(struct rb_root *hists,
continue;
fprintf(fp, " ");
if (se->width)
width = *se->width;
if (se->se_width)
width = *se->se_width;
else
width = strlen(se->header);
width = strlen(se->se_header);
for (i = 0; i < width; i++)
fprintf(fp, ".");
}
......
......@@ -42,8 +42,8 @@
#include "color.h"
#include "symbol.h"
#include "thread.h"
#include "debugfs.h"
#include "trace-event.h" /* For __unused */
#include "parse-events.h" /* For debugfs_path */
#include "probe-event.h"
#include "probe-finder.h"
......@@ -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,34 @@ 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) {
pp->function = xstrdup(tp->symbol);
pr_debug("Failed to find corresponding probes from "
"debuginfo. Use kprobe event information.\n");
pp->function = strdup(tp->symbol);
if (pp->function == NULL)
return -ENOMEM;
pp->offset = tp->offset;
}
pp->retprobe = tp->retprobe;
return 0;
}
/* Try to find perf_probe_event with debuginfo */
......@@ -131,9 +152,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 +164,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 == -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.");
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, int l, bool skip, bool show_num)
{
char buf[LINEBUF_SIZE];
const char *color = PERF_COLOR_BLUE;
......@@ -174,7 +198,7 @@ static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num)
goto error;
if (!skip) {
if (show_num)
fprintf(stdout, "%7u %s", l, buf);
fprintf(stdout, "%7d %s", l, buf);
else
color_fprintf(stdout, color, " %s", buf);
}
......@@ -190,34 +214,48 @@ 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;
int l = 1;
struct line_node *ln;
FILE *fp;
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 +266,70 @@ 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->function = strdup(tp->symbol);
if (pp->function == NULL)
return -ENOMEM;
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;
......@@ -284,29 +340,45 @@ void parse_line_range_desc(const char *arg, struct line_range *lr)
*/
ptr = strchr(arg, ':');
if (ptr) {
lr->start = (unsigned int)strtoul(ptr + 1, &tmp, 0);
if (*tmp == '+')
lr->end = lr->start + (unsigned int)strtoul(tmp + 1,
&tmp, 0);
else if (*tmp == '-')
lr->end = (unsigned int)strtoul(tmp + 1, &tmp, 0);
lr->start = (int)strtoul(ptr + 1, &tmp, 0);
if (*tmp == '+') {
lr->end = lr->start + (int)strtoul(tmp + 1, &tmp, 0);
lr->end--; /*
* Adjust the number of lines here.
* If the number of lines == 1, the
* the end of line should be equal to
* the start of line.
*/
} else if (*tmp == '-')
lr->end = (int)strtoul(tmp + 1, &tmp, 0);
else
lr->end = 0;
pr_debug("Line range is %u to %u\n", lr->start, lr->end);
if (lr->end && lr->start > lr->end)
lr->end = INT_MAX;
pr_debug("Line range is %d to %d\n", lr->start, lr->end);
if (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);
tmp = xstrndup(arg, (ptr - arg));
} else
tmp = xstrdup(arg);
return -EINVAL;
}
tmp = strndup(arg, (ptr - arg));
} else {
tmp = strdup(arg);
lr->end = INT_MAX;
}
if (tmp == NULL)
return -ENOMEM;
if (strchr(tmp, '.'))
lr->file = tmp;
else
lr->function = tmp;
return 0;
}
/* Check the name is good for event/group */
......@@ -322,7 +394,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,13 +411,18 @@ 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);
pev->event = xstrdup(arg);
"follow C symbol-naming rule.\n", arg);
return -EINVAL;
}
pev->event = strdup(arg);
if (pev->event == NULL)
return -ENOMEM;
pev->group = NULL;
arg = tmp;
}
......@@ -356,18 +433,24 @@ static void 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, ";:+@%");
......@@ -378,131 +461,211 @@ 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.");
pp->file = xstrdup(arg);
if (pp->file) {
semantic_error("SRC@SRC is not allowed.\n");
return -EINVAL;
}
pp->file = strdup(arg);
if (pp->file == NULL)
return -ENOMEM;
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(const char *str, struct perf_probe_arg *arg)
static int parse_perf_probe_arg(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 = strndup(str, tmp - str);
if (arg->name == NULL)
return -ENOMEM;
pr_debug("name:%s ", arg->name);
str = tmp + 1;
}
tmp = strchr(str, ':');
if (tmp) { /* Type setting */
*tmp = '\0';
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->name = xstrdup(str);
pr_debug("%s\n", arg->name);
return;
arg->var = strdup(str);
if (arg->var == NULL)
return -ENOMEM;
pr_debug("%s\n", arg->var);
return 0;
}
/* Structure fields */
arg->name = xstrndup(str, tmp - str);
pr_debug("%s, ", arg->name);
arg->var = strndup(str, tmp - str);
if (arg->var == NULL)
return -ENOMEM;
pr_debug("%s, ", arg->var);
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;
} 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) {
(*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 = strdup((*fieldp)->name);
if (arg->name == NULL)
return -ENOMEM;
}
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].name) && pev->point.retprobe)
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 &&
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 */
......@@ -514,14 +677,14 @@ 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;
}
/* 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;
......@@ -531,17 +694,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');
......@@ -553,19 +724,29 @@ void 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. */
*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:
argv_free(argv);
return ret;
}
/* Compose only probe arg */
......@@ -575,7 +756,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;
......@@ -590,9 +774,20 @@ 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));
pr_debug("Failed to synthesize perf probe argument: %s",
strerror(-ret));
return ret;
}
/* Compose only probe point (not argument) */
......@@ -602,7 +797,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)
......@@ -614,12 +813,12 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp)
goto error;
}
if (pp->file) {
len = strlen(pp->file) - 32;
len = strlen(pp->file) - 31;
if (len < 0)
len = 0;
tmp = strchr(pp->file + len, '/');
if (!tmp)
tmp = pp->file + len - 1;
tmp = pp->file + len;
ret = e_snprintf(file, 32, "@%s", tmp + 1);
if (ret <= 0)
goto error;
......@@ -636,7 +835,11 @@ 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));
if (buf)
free(buf);
return NULL;
}
#if 0
......@@ -727,6 +930,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;
}
......@@ -737,7 +947,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,
......@@ -759,29 +972,44 @@ char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev)
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;
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 */
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++)
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);
pev->args[i].name = strdup(tev->args[i].name);
else {
synthesize_kprobe_trace_arg(&tev->args[i], buf, 64);
pev->args[i].name = xstrdup(buf);
ret = synthesize_kprobe_trace_arg(&tev->args[i],
buf, 64);
pev->args[i].name = strdup(buf);
}
if (pev->args[i].name == NULL && ret >= 0)
ret = -ENOMEM;
}
if (ret < 0)
clear_perf_probe_event(pev);
return ret;
}
void clear_perf_probe_event(struct perf_probe_event *pev)
......@@ -803,6 +1031,10 @@ 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);
if (pev->args[i].type)
free(pev->args[i].type);
field = pev->args[i].field;
while (field) {
next = field->next;
......@@ -833,6 +1065,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;
......@@ -848,24 +1082,31 @@ void clear_kprobe_trace_event(struct kprobe_trace_event *tev)
static int open_kprobe_events(bool readwrite)
{
char buf[PATH_MAX];
const char *__debugfs;
int ret;
ret = e_snprintf(buf, PATH_MAX, "%s/../kprobe_events", debugfs_path);
if (ret < 0)
die("Failed to make kprobe_events path.");
__debugfs = debugfs_find_mountpoint();
if (__debugfs == NULL) {
pr_warning("Debugfs is not mounted.\n");
return -ENOENT;
}
if (readwrite && !probe_event_dry_run)
ret = open(buf, O_RDWR, O_APPEND);
else
ret = open(buf, O_RDONLY, 0);
ret = e_snprintf(buf, PATH_MAX, "%stracing/kprobe_events", __debugfs);
if (ret >= 0) {
pr_debug("Opening %s write=%d\n", buf, readwrite);
if (readwrite && !probe_event_dry_run)
ret = open(buf, O_RDWR, O_APPEND);
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;
}
......@@ -891,8 +1132,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);
......@@ -900,7 +1144,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];
......@@ -908,52 +1152,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 */
......@@ -963,86 +1226,116 @@ 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++) {
tev = &tevs[i];
......@@ -1059,12 +1352,21 @@ 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);
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;
/* Add added event name to namelist */
strlist__add(namelist, event);
......@@ -1086,49 +1388,90 @@ 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,
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.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 = xzalloc(sizeof(struct kprobe_trace_arg)
* tev->nargs);
for (i = 0; i < tev->nargs; i++)
tev->args[i].value = xstrdup(pev->args[i].name);
tev->args = zalloc(sizeof(struct kprobe_trace_arg)
* tev->nargs);
if (tev->args == NULL) {
ret = -ENOMEM;
goto error;
}
for (i = 0; i < tev->nargs; i++) {
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;
}
}
}
}
/* 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);
ret = -ENOENT;
goto error;
}
return ntevs;
return 1;
error:
clear_kprobe_trace_event(tev);
free(tev);
*tevs = NULL;
return ret;
}
struct __event_package {
......@@ -1137,96 +1480,137 @@ 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);
pkgs = zalloc(sizeof(struct __event_package) * npevs);
if (pkgs == NULL)
return -ENOMEM;
/* 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);
str = strdup(ent->s);
if (str == NULL) {
ret = -ENOMEM;
break;
}
pr_debug("Parsing: %s\n", str);
p = strchr(str, ':');
if (p) {
......@@ -1238,10 +1622,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;
}
......@@ -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 */
};
......@@ -55,6 +56,8 @@ struct perf_probe_arg_field {
/* Perf probe probing argument */
struct perf_probe_arg {
char *name; /* Argument name */
char *var; /* Variable name */
char *type; /* Type name */
struct perf_probe_arg_field *field; /* Structure fields */
};
......@@ -71,25 +74,25 @@ struct perf_probe_event {
/* Line number container */
struct line_node {
struct list_head list;
unsigned int line;
int line;
};
/* Line range */
struct line_range {
char *file; /* File name */
char *function; /* Function name */
unsigned int start; /* Start line number */
unsigned int end; /* End line number */
int start; /* Start line number */
int end; /* End line number */
int offset; /* Start line offset */
char *path; /* Real path name */
struct list_head line_list; /* Visible lines */
};
/* Command string to events */
extern void parse_perf_probe_command(const char *cmd,
struct perf_probe_event *pev);
extern void parse_kprobe_trace_command(const char *cmd,
struct kprobe_trace_event *tev);
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);
......@@ -101,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 */
......
......@@ -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)
{
......@@ -108,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, int line)
{
struct line_node *ln;
struct list_head *p;
......@@ -119,20 +122,23 @@ 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 */
static int line_list__has_line(struct list_head *head, unsigned int line)
static int line_list__has_line(struct list_head *head, int line)
{
struct line_node *ln;
......@@ -193,19 +199,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 */
......@@ -230,6 +224,58 @@ 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;
}
/* Get data_member_location offset */
static int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs)
{
Dwarf_Attribute attr;
Dwarf_Op *expr;
size_t nexpr;
int ret;
if (dwarf_attr(mb_die, DW_AT_data_member_location, &attr) == NULL)
return -ENOENT;
if (dwarf_formudata(&attr, offs) != 0) {
/* DW_AT_data_member_location should be DW_OP_plus_uconst */
ret = dwarf_getlocation(&attr, &expr, &nexpr);
if (ret < 0 || nexpr == 0)
return -ENOENT;
if (expr[0].atom != DW_OP_plus_uconst || nexpr != 1) {
pr_debug("Unable to get offset:Unexpected OP %x (%zd)\n",
expr[0].atom, nexpr);
return -ENOTSUP;
}
*offs = (Dwarf_Word)expr[0].number;
}
return 0;
}
/* Return values for die_find callbacks */
enum {
DIE_FIND_CB_FOUND = 0, /* End of Search */
......@@ -362,7 +408,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;
......@@ -370,11 +416,13 @@ 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)
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];
......@@ -392,84 +440,155 @@ 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 = strdup(regs);
if (tvar->value == NULL)
return -ENOMEM;
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;
}
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) {
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_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) {
if (ret >= 16)
ret = -E2BIG;
pr_warning("Failed to convert variable type: %s\n",
strerror(-ret));
return ret;
}
targ->type = strdup(buf);
if (targ->type == NULL)
return -ENOMEM;
}
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)
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;
int ret;
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));
ref = zalloc(sizeof(struct kprobe_trace_arg_ref));
if (ref == NULL)
return -ENOMEM;
if (*ref_ptr)
(*ref_ptr)->next = ref;
else
*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, &member) == 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(&member, DW_AT_data_member_location, &attr) == NULL ||
dwarf_formudata(&attr, &offs) != 0)
die("Failed to get the offset of %s.", field->name);
ret = die_get_data_member_location(die_mem, &offs);
if (ret < 0) {
pr_warning("Failed to get the offset of %s.\n", field->name);
return ret;
}
ref->offset += (long)offs;
/* Converting next field */
if (field->next)
convert_variable_fields(&member, field->name, field->next,
&ref);
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;
Dwarf_Op *expr;
size_t nexpr;
int ret;
......@@ -481,44 +600,74 @@ 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->name,
pf->pvar->field, &pf->tvar->ref);
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 (ret == 0) {
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. */
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->name);
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[128];
char buf[32], *ptr;
int ret;
/* TODO: Support arrays */
if (pf->pvar->name)
pf->tvar->name = strdup(pf->pvar->name);
else {
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 = strdup(buf);
}
if (pf->tvar->name == NULL)
return -ENOMEM;
/* TODO: Support struct members and arrays */
if (!is_c_varname(pf->pvar->name)) {
if (!is_c_varname(pf->pvar->var)) {
/* Copy raw parameters */
pf->tvar->value = xstrdup(pf->pvar->name);
} 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);
/* Search child die for local variables and parameters. */
if (!die_find_variable(sp_die, pf->pvar->name, &vr_die))
die("Failed to find '%s' in this function.",
pf->pvar->name);
convert_variable(&vr_die, pf);
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",
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;
......@@ -528,23 +677,34 @@ 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);
tev->point.symbol = xstrdup(name);
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 = strdup(name);
if (tev->point.symbol == NULL)
return -ENOMEM;
tev->point.offset = (unsigned long)(pf->addr - eaddr);
} else
/* This function has no name. */
......@@ -556,55 +716,74 @@ 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;
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 */
/* TODO: use dwarf_cfi_addrframe */
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];
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 */
......@@ -612,15 +791,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';
......@@ -640,7 +831,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;
......@@ -648,31 +839,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))
......@@ -686,27 +886,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;
......@@ -715,39 +930,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 */
......@@ -760,19 +989,29 @@ 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);
pf.tevs = zalloc(sizeof(struct kprobe_trace_event) * MAX_PROBES);
if (pf.tevs == NULL)
return -ENOMEM;
*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);
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)
......@@ -786,12 +1025,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;
......@@ -799,7 +1038,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 */
......@@ -812,10 +1051,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)) {
......@@ -826,82 +1066,135 @@ 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 = strdup(tmp);
if (ppt->file == NULL) {
ret = -ENOMEM;
goto end;
}
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 (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;
}
}
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);
/* We don't have a line number, let's use offset */
ppt->offset = addr - (unsigned long)eaddr;
found:
ppt->function = strdup(tmp);
if (ppt->function == NULL) {
ret = -ENOMEM;
goto end;
}
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;
}
/* Add a line and store the src path */
static int line_range_add_line(const char *src, unsigned int lineno,
struct line_range *lr)
{
/* Copy real path */
if (!lr->path) {
lr->path = strdup(src);
if (lr->path == NULL)
return -ENOMEM;
}
return line_list__add_line(&lr->line_list, lineno);
}
/* Search function declaration lines */
static int line_range_funcdecl_cb(Dwarf_Die *sp_die, void *data)
{
struct dwarf_callback_param *param = data;
struct line_finder *lf = param->data;
const char *src;
int lineno;
src = dwarf_decl_file(sp_die);
if (src && strtailcmp(src, lf->fname) != 0)
return DWARF_CB_OK;
if (dwarf_decl_line(sp_die, &lineno) != 0 ||
(lf->lno_s > lineno || lf->lno_e < lineno))
return DWARF_CB_OK;
param->retval = line_range_add_line(src, lineno, lf->lr);
return DWARF_CB_OK;
}
static int find_line_range_func_decl_lines(struct line_finder *lf)
{
struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0};
dwarf_getfuncs(&lf->cu_die, line_range_funcdecl_cb, &param, 0);
return param.retval;
}
/* 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;
int lineno, ret = 0;
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;
}
/* Search probable lines on lines list */
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? */
......@@ -914,30 +1207,49 @@ static void find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf)
if (strtailcmp(src, lf->fname) != 0)
continue;
/* Copy real path */
if (!lf->lr->path)
lf->lr->path = xstrdup(src);
line_list__add_line(&lf->lr->line_list, (unsigned int)lineno);
ret = line_range_add_line(src, lineno, lf->lr);
if (ret < 0)
return ret;
}
/*
* Dwarf lines doesn't include function declarations. We have to
* check functions list or given function.
*/
if (sp_die) {
src = dwarf_decl_file(sp_die);
if (src && dwarf_decl_line(sp_die, &lineno) == 0 &&
(lf->lno_s <= lineno && lf->lno_e >= lineno))
ret = line_range_add_line(src, lineno, lf->lr);
} else
ret = find_line_range_func_decl_lines(lf);
/* Update status */
if (!list_empty(&lf->lr->line_list))
lf->found = 1;
if (ret >= 0)
if (!list_empty(&lf->lr->line_list))
ret = lf->found = 1;
else
ret = 0; /* Lines are not found */
else {
free(lf->lr->path);
lf->lr->path = NULL;
}
return ret;
}
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 &&
......@@ -946,44 +1258,55 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
dwarf_decl_line(sp_die, &lr->offset);
pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset);
lf->lno_s = lr->offset + lr->start;
if (!lr->end)
if (lf->lno_s < 0) /* Overflow */
lf->lno_s = INT_MAX;
lf->lno_e = lr->offset + lr->end;
if (lf->lno_e < 0) /* Overflow */
lf->lno_e = INT_MAX;
else
lf->lno_e = lr->offset + lr->end;
pr_debug("New line range: %d to %d\n", lf->lno_s, lf->lno_e);
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 */
......@@ -999,20 +1322,18 @@ 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);
lf.lno_e = lr->end;
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;
}
......@@ -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 */
......
......@@ -30,38 +30,38 @@ static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf,
size_t size, unsigned int width);
struct sort_entry sort_thread = {
.header = "Command: Pid",
.cmp = sort__thread_cmp,
.snprintf = hist_entry__thread_snprintf,
.width = &threads__col_width,
.se_header = "Command: Pid",
.se_cmp = sort__thread_cmp,
.se_snprintf = hist_entry__thread_snprintf,
.se_width = &threads__col_width,
};
struct sort_entry sort_comm = {
.header = "Command",
.cmp = sort__comm_cmp,
.collapse = sort__comm_collapse,
.snprintf = hist_entry__comm_snprintf,
.width = &comms__col_width,
.se_header = "Command",
.se_cmp = sort__comm_cmp,
.se_collapse = sort__comm_collapse,
.se_snprintf = hist_entry__comm_snprintf,
.se_width = &comms__col_width,
};
struct sort_entry sort_dso = {
.header = "Shared Object",
.cmp = sort__dso_cmp,
.snprintf = hist_entry__dso_snprintf,
.width = &dsos__col_width,
.se_header = "Shared Object",
.se_cmp = sort__dso_cmp,
.se_snprintf = hist_entry__dso_snprintf,
.se_width = &dsos__col_width,
};
struct sort_entry sort_sym = {
.header = "Symbol",
.cmp = sort__sym_cmp,
.snprintf = hist_entry__sym_snprintf,
.se_header = "Symbol",
.se_cmp = sort__sym_cmp,
.se_snprintf = hist_entry__sym_snprintf,
};
struct sort_entry sort_parent = {
.header = "Parent symbol",
.cmp = sort__parent_cmp,
.snprintf = hist_entry__parent_snprintf,
.width = &parent_symbol__col_width,
.se_header = "Parent symbol",
.se_cmp = sort__parent_cmp,
.se_snprintf = hist_entry__parent_snprintf,
.se_width = &parent_symbol__col_width,
};
struct sort_dimension {
......@@ -255,7 +255,7 @@ int sort_dimension__add(const char *tok)
if (strncasecmp(tok, sd->name, strlen(tok)))
continue;
if (sd->entry->collapse)
if (sd->entry->se_collapse)
sort__need_collapse = 1;
if (sd->entry == &sort_parent) {
......
......@@ -78,13 +78,13 @@ enum sort_type {
struct sort_entry {
struct list_head list;
const char *header;
const char *se_header;
int64_t (*cmp)(struct hist_entry *, struct hist_entry *);
int64_t (*collapse)(struct hist_entry *, struct hist_entry *);
int (*snprintf)(struct hist_entry *self, char *bf, size_t size,
unsigned int width);
unsigned int *width;
int64_t (*se_cmp)(struct hist_entry *, struct hist_entry *);
int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *);
int (*se_snprintf)(struct hist_entry *self, char *bf, size_t size,
unsigned int width);
unsigned int *se_width;
bool elide;
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment