aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/util
diff options
context:
space:
mode:
authorIngo Molnar <mingo@elte.hu>2010-07-06 09:00:32 +0200
committerIngo Molnar <mingo@elte.hu>2010-07-06 09:00:32 +0200
commit8bd0e1be25c43a9fbd33a8de06449088879d527a (patch)
tree8f24493dfd45df467faa69e2ecef67b8321932ef /tools/perf/util
parentperf, x86: P4 PMU -- redesign cache events (diff)
parentperf probe: Support static and global variables (diff)
downloadlinux-dev-8bd0e1be25c43a9fbd33a8de06449088879d527a.tar.xz
linux-dev-8bd0e1be25c43a9fbd33a8de06449088879d527a.zip
Merge branch 'perf/core' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux-2.6 into perf/core
Diffstat (limited to 'tools/perf/util')
-rw-r--r--tools/perf/util/probe-event.c71
-rw-r--r--tools/perf/util/probe-event.h1
-rw-r--r--tools/perf/util/probe-finder.c192
3 files changed, 203 insertions, 61 deletions
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 914c67095d96..09cf5465e10a 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -557,7 +557,7 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
/* Parse perf-probe event argument */
static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
{
- char *tmp;
+ char *tmp, *goodname;
struct perf_probe_arg_field **fieldp;
pr_debug("parsing arg: %s into ", str);
@@ -580,7 +580,7 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
pr_debug("type:%s ", arg->type);
}
- tmp = strpbrk(str, "-.");
+ tmp = strpbrk(str, "-.[");
if (!is_c_varname(str) || !tmp) {
/* A variable, register, symbol or special value */
arg->var = strdup(str);
@@ -590,10 +590,11 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
return 0;
}
- /* Structure fields */
+ /* Structure fields or array element */
arg->var = strndup(str, tmp - str);
if (arg->var == NULL)
return -ENOMEM;
+ goodname = arg->var;
pr_debug("%s, ", arg->var);
fieldp = &arg->field;
@@ -601,22 +602,38 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
*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;
+ if (*tmp == '[') { /* Array */
+ str = tmp;
+ (*fieldp)->index = strtol(str + 1, &tmp, 0);
(*fieldp)->ref = true;
- } else {
- semantic_error("Argument parse error: %s\n", str);
- return -EINVAL;
+ if (*tmp != ']' || tmp == str + 1) {
+ semantic_error("Array index must be a"
+ " number.\n");
+ return -EINVAL;
+ }
+ tmp++;
+ if (*tmp == '\0')
+ tmp = NULL;
+ } else { /* Structure */
+ 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\n",
+ str);
+ return -EINVAL;
+ }
+ tmp = strpbrk(str, "-.[");
}
-
- tmp = strpbrk(str, "-.");
if (tmp) {
(*fieldp)->name = strndup(str, tmp - str);
if ((*fieldp)->name == NULL)
return -ENOMEM;
+ if (*str != '[')
+ goodname = (*fieldp)->name;
pr_debug("%s(%d), ", (*fieldp)->name, (*fieldp)->ref);
fieldp = &(*fieldp)->next;
}
@@ -624,11 +641,13 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
(*fieldp)->name = strdup(str);
if ((*fieldp)->name == NULL)
return -ENOMEM;
+ if (*str != '[')
+ goodname = (*fieldp)->name;
pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref);
- /* If no name is specified, set the last field name */
+ /* If no name is specified, set the last field name (not array index)*/
if (!arg->name) {
- arg->name = strdup((*fieldp)->name);
+ arg->name = strdup(goodname);
if (arg->name == NULL)
return -ENOMEM;
}
@@ -776,8 +795,11 @@ int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len)
len -= ret;
while (field) {
- ret = e_snprintf(tmp, len, "%s%s", field->ref ? "->" : ".",
- field->name);
+ if (field->name[0] == '[')
+ ret = e_snprintf(tmp, len, "%s", field->name);
+ else
+ ret = e_snprintf(tmp, len, "%s%s",
+ field->ref ? "->" : ".", field->name);
if (ret <= 0)
goto error;
tmp += ret;
@@ -904,6 +926,7 @@ out:
static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg,
char *buf, size_t buflen)
{
+ struct kprobe_trace_arg_ref *ref = arg->ref;
int ret, depth = 0;
char *tmp = buf;
@@ -917,16 +940,24 @@ static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg,
buf += ret;
buflen -= ret;
+ /* Special case: @XXX */
+ if (arg->value[0] == '@' && arg->ref)
+ ref = ref->next;
+
/* Dereferencing arguments */
- if (arg->ref) {
- depth = __synthesize_kprobe_trace_arg_ref(arg->ref, &buf,
+ if (ref) {
+ depth = __synthesize_kprobe_trace_arg_ref(ref, &buf,
&buflen, 1);
if (depth < 0)
return depth;
}
/* Print argument value */
- ret = e_snprintf(buf, buflen, "%s", arg->value);
+ if (arg->value[0] == '@' && arg->ref)
+ ret = e_snprintf(buf, buflen, "%s%+ld", arg->value,
+ arg->ref->offset);
+ else
+ ret = e_snprintf(buf, buflen, "%s", arg->value);
if (ret < 0)
return ret;
buf += ret;
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index e9db1a214ca4..bc06d3e8bafa 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -50,6 +50,7 @@ struct perf_probe_point {
struct perf_probe_arg_field {
struct perf_probe_arg_field *next; /* Next field */
char *name; /* Name of the field */
+ long index; /* Array index number */
bool ref; /* Referencing flag */
};
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index baf665383498..3e64e1fa1051 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -406,14 +406,50 @@ static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
* Probe finder related functions
*/
+static struct kprobe_trace_arg_ref *alloc_trace_arg_ref(long offs)
+{
+ struct kprobe_trace_arg_ref *ref;
+ ref = zalloc(sizeof(struct kprobe_trace_arg_ref));
+ if (ref != NULL)
+ ref->offset = offs;
+ return ref;
+}
+
/* Show a location */
-static int convert_location(Dwarf_Op *op, struct probe_finder *pf)
+static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
{
+ Dwarf_Attribute attr;
+ Dwarf_Op *op;
+ size_t nops;
unsigned int regn;
Dwarf_Word offs = 0;
bool ref = false;
const char *regs;
struct kprobe_trace_arg *tvar = pf->tvar;
+ int ret;
+
+ /* TODO: handle more than 1 exprs */
+ if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL ||
+ dwarf_getlocation_addr(&attr, pf->addr, &op, &nops, 1) <= 0 ||
+ nops == 0) {
+ /* TODO: Support const_value */
+ pr_err("Failed to find the location of %s at this address.\n"
+ " Perhaps, it has been optimized out.\n", pf->pvar->var);
+ return -ENOENT;
+ }
+
+ if (op->atom == DW_OP_addr) {
+ /* Static variables on memory (not stack), make @varname */
+ ret = strlen(dwarf_diename(vr_die));
+ tvar->value = zalloc(ret + 2);
+ if (tvar->value == NULL)
+ return -ENOMEM;
+ snprintf(tvar->value, ret + 2, "@%s", dwarf_diename(vr_die));
+ tvar->ref = alloc_trace_arg_ref((long)offs);
+ if (tvar->ref == NULL)
+ return -ENOMEM;
+ return 0;
+ }
/* If this is based on frame buffer, set the offset */
if (op->atom == DW_OP_fbreg) {
@@ -455,27 +491,72 @@ static int convert_location(Dwarf_Op *op, struct probe_finder *pf)
return -ENOMEM;
if (ref) {
- tvar->ref = zalloc(sizeof(struct kprobe_trace_arg_ref));
+ tvar->ref = alloc_trace_arg_ref((long)offs);
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)
+ struct kprobe_trace_arg *tvar,
+ const char *cast)
{
+ struct kprobe_trace_arg_ref **ref_ptr = &tvar->ref;
Dwarf_Die type;
char buf[16];
int ret;
+ /* TODO: check all types */
+ if (cast && strcmp(cast, "string") != 0) {
+ /* Non string type is OK */
+ tvar->type = strdup(cast);
+ return (tvar->type == NULL) ? -ENOMEM : 0;
+ }
+
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;
}
+ pr_debug("%s type is %s.\n",
+ dwarf_diename(vr_die), dwarf_diename(&type));
+
+ if (cast && strcmp(cast, "string") == 0) { /* String type */
+ ret = dwarf_tag(&type);
+ if (ret != DW_TAG_pointer_type &&
+ ret != DW_TAG_array_type) {
+ pr_warning("Failed to cast into string: "
+ "%s(%s) is not a pointer nor array.",
+ dwarf_diename(vr_die), dwarf_diename(&type));
+ return -EINVAL;
+ }
+ if (ret == DW_TAG_pointer_type) {
+ if (die_get_real_type(&type, &type) == NULL) {
+ pr_warning("Failed to get a type information.");
+ return -ENOENT;
+ }
+ while (*ref_ptr)
+ ref_ptr = &(*ref_ptr)->next;
+ /* Add new reference with offset +0 */
+ *ref_ptr = zalloc(sizeof(struct kprobe_trace_arg_ref));
+ if (*ref_ptr == NULL) {
+ pr_warning("Out of memory error\n");
+ return -ENOMEM;
+ }
+ }
+ if (die_compare_name(&type, "char") != 0 &&
+ die_compare_name(&type, "unsigned char") != 0) {
+ pr_warning("Failed to cast into string: "
+ "%s is not (unsigned) char *.",
+ dwarf_diename(vr_die));
+ return -EINVAL;
+ }
+ tvar->type = strdup(cast);
+ return (tvar->type == NULL) ? -ENOMEM : 0;
+ }
+
ret = die_get_byte_size(&type) * 8;
if (ret) {
/* Check the bitwidth */
@@ -495,8 +576,8 @@ static int convert_variable_type(Dwarf_Die *vr_die,
strerror(-ret));
return ret;
}
- targ->type = strdup(buf);
- if (targ->type == NULL)
+ tvar->type = strdup(buf);
+ if (tvar->type == NULL)
return -ENOMEM;
}
return 0;
@@ -510,16 +591,44 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
struct kprobe_trace_arg_ref *ref = *ref_ptr;
Dwarf_Die type;
Dwarf_Word offs;
- int ret;
+ int ret, tag;
pr_debug("converting %s in %s\n", field->name, 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) {
+ pr_debug2("Var real type: (%x)\n", (unsigned)dwarf_dieoffset(&type));
+ tag = dwarf_tag(&type);
+
+ if (field->name[0] == '[' &&
+ (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)) {
+ if (field->next)
+ /* Save original type for next field */
+ memcpy(die_mem, &type, sizeof(*die_mem));
+ /* Get the type of this array */
+ if (die_get_real_type(&type, &type) == NULL) {
+ pr_warning("Failed to get the type of %s.\n", varname);
+ return -ENOENT;
+ }
+ pr_debug2("Array real type: (%x)\n",
+ (unsigned)dwarf_dieoffset(&type));
+ if (tag == DW_TAG_pointer_type) {
+ ref = zalloc(sizeof(struct kprobe_trace_arg_ref));
+ if (ref == NULL)
+ return -ENOMEM;
+ if (*ref_ptr)
+ (*ref_ptr)->next = ref;
+ else
+ *ref_ptr = ref;
+ }
+ ref->offset += die_get_byte_size(&type) * field->index;
+ if (!field->next)
+ /* Save vr_die for converting types */
+ memcpy(die_mem, vr_die, sizeof(*die_mem));
+ goto next;
+ } else if (tag == DW_TAG_pointer_type) {
+ /* Check the pointer and dereference */
if (!field->ref) {
pr_err("Semantic error: %s must be referred by '->'\n",
field->name);
@@ -545,10 +654,15 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
*ref_ptr = ref;
} else {
/* Verify it is a data structure */
- if (dwarf_tag(&type) != DW_TAG_structure_type) {
+ if (tag != DW_TAG_structure_type) {
pr_warning("%s is not a data structure.\n", varname);
return -EINVAL;
}
+ if (field->name[0] == '[') {
+ pr_err("Semantic error: %s is not a pointor nor array.",
+ varname);
+ return -EINVAL;
+ }
if (field->ref) {
pr_err("Semantic error: %s must be referred by '.'\n",
field->name);
@@ -575,6 +689,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
}
ref->offset += (long)offs;
+next:
/* Converting next field */
if (field->next)
return convert_variable_fields(die_mem, field->name,
@@ -586,51 +701,32 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
/* Show a variables in kprobe event format */
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;
- if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL)
- goto error;
- /* TODO: handle more than 1 exprs */
- ret = dwarf_getlocation_addr(&attr, pf->addr, &expr, &nexpr, 1);
- if (ret <= 0 || nexpr == 0)
- goto error;
+ pr_debug("Converting variable %s into trace event.\n",
+ dwarf_diename(vr_die));
- ret = convert_location(expr, pf);
+ ret = convert_variable_location(vr_die, 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);
- }
+ if (ret == 0)
+ ret = convert_variable_type(vr_die, pf->tvar, pf->pvar->type);
/* *expr will be cached in libdw. Don't free it. */
return ret;
-error:
- /* TODO: Support const_value */
- pr_err("Failed to find the location of %s at this address.\n"
- " Perhaps, it has been optimized out.\n", pf->pvar->var);
- return -ENOENT;
}
/* Find a variable in a subprogram die */
static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
{
- Dwarf_Die vr_die;
+ Dwarf_Die vr_die, *scopes;
char buf[32], *ptr;
- int ret;
+ int ret, nscopes;
- /* TODO: Support arrays */
if (pf->pvar->name)
pf->tvar->name = strdup(pf->pvar->name);
else {
@@ -657,12 +753,26 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
pr_debug("Searching '%s' variable in context.\n",
pf->pvar->var);
/* Search child die for local variables and parameters. */
- if (!die_find_variable(sp_die, pf->pvar->var, &vr_die)) {
+ if (die_find_variable(sp_die, pf->pvar->var, &vr_die))
+ ret = convert_variable(&vr_die, pf);
+ else {
+ /* Search upper class */
+ nscopes = dwarf_getscopes_die(sp_die, &scopes);
+ if (nscopes > 0) {
+ ret = dwarf_getscopevar(scopes, nscopes, pf->pvar->var,
+ 0, NULL, 0, 0, &vr_die);
+ if (ret >= 0)
+ ret = convert_variable(&vr_die, pf);
+ else
+ ret = -ENOENT;
+ free(scopes);
+ } else
+ ret = -ENOENT;
+ }
+ if (ret < 0)
pr_warning("Failed to find '%s' in this function.\n",
pf->pvar->var);
- return -ENOENT;
- }
- return convert_variable(&vr_die, pf);
+ return ret;
}
/* Show a probe point to output buffer */