summaryrefslogtreecommitdiffstats
path: root/tools/perf/util/probe-finder.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util/probe-finder.c')
-rw-r--r--tools/perf/util/probe-finder.c248
1 files changed, 190 insertions, 58 deletions
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index d964cb199c6..840f1aabbb7 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -37,6 +37,7 @@
#include "event.h"
#include "debug.h"
#include "util.h"
+#include "symbol.h"
#include "probe-finder.h"
/* Kprobe tracer basic type is up to u64 */
@@ -143,12 +144,21 @@ static const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname)
return src;
}
+/* Get DW_AT_comp_dir (should be NULL with older gcc) */
+static const char *cu_get_comp_dir(Dwarf_Die *cu_die)
+{
+ Dwarf_Attribute attr;
+ if (dwarf_attr(cu_die, DW_AT_comp_dir, &attr) == NULL)
+ return NULL;
+ return dwarf_formstring(&attr);
+}
+
/* Compare diename and tname */
static bool die_compare_name(Dwarf_Die *dw_die, const char *tname)
{
const char *name;
name = dwarf_diename(dw_die);
- return name ? strcmp(tname, name) : -1;
+ return name ? (strcmp(tname, name) == 0) : false;
}
/* Get type die, but skip qualifiers and typedef */
@@ -319,7 +329,7 @@ static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data)
tag = dwarf_tag(die_mem);
if ((tag == DW_TAG_formal_parameter ||
tag == DW_TAG_variable) &&
- (die_compare_name(die_mem, name) == 0))
+ die_compare_name(die_mem, name))
return DIE_FIND_CB_FOUND;
return DIE_FIND_CB_CONTINUE;
@@ -338,7 +348,7 @@ static int __die_find_member_cb(Dwarf_Die *die_mem, void *data)
const char *name = data;
if ((dwarf_tag(die_mem) == DW_TAG_member) &&
- (die_compare_name(die_mem, name) == 0))
+ die_compare_name(die_mem, name))
return DIE_FIND_CB_FOUND;
return DIE_FIND_CB_SIBLING;
@@ -356,14 +366,50 @@ static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
* Probe finder related functions
*/
+static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs)
+{
+ struct probe_trace_arg_ref *ref;
+ ref = zalloc(sizeof(struct probe_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;
+ struct probe_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) {
@@ -405,27 +451,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 probe_trace_arg *tvar,
+ const char *cast)
{
+ struct probe_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 probe_trace_arg_ref));
+ if (*ref_ptr == NULL) {
+ pr_warning("Out of memory error\n");
+ return -ENOMEM;
+ }
+ }
+ if (!die_compare_name(&type, "char") &&
+ !die_compare_name(&type, "unsigned char")) {
+ 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 */
@@ -445,8 +536,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;
@@ -454,22 +545,50 @@ static int convert_variable_type(Dwarf_Die *vr_die,
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 probe_trace_arg_ref **ref_ptr,
Dwarf_Die *die_mem)
{
- struct kprobe_trace_arg_ref *ref = *ref_ptr;
+ struct probe_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 probe_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);
@@ -486,7 +605,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
return -EINVAL;
}
- ref = zalloc(sizeof(struct kprobe_trace_arg_ref));
+ ref = zalloc(sizeof(struct probe_trace_arg_ref));
if (ref == NULL)
return -ENOMEM;
if (*ref_ptr)
@@ -495,10 +614,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);
@@ -525,6 +649,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,
@@ -536,51 +661,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 {
@@ -607,18 +713,32 @@ 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 */
static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
{
- struct kprobe_trace_event *tev;
+ struct probe_trace_event *tev;
Dwarf_Addr eaddr;
Dwarf_Die die_mem;
const char *name;
@@ -683,7 +803,7 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
/* Find each argument */
tev->nargs = pf->pev->nargs;
- tev->args = zalloc(sizeof(struct kprobe_trace_arg) * tev->nargs);
+ tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
if (tev->args == NULL)
return -ENOMEM;
for (i = 0; i < pf->pev->nargs; i++) {
@@ -897,7 +1017,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
/* Check tag and diename */
if (dwarf_tag(sp_die) != DW_TAG_subprogram ||
- die_compare_name(sp_die, pp->function) != 0)
+ !die_compare_name(sp_die, pp->function))
return DWARF_CB_OK;
pf->fname = dwarf_decl_file(sp_die);
@@ -940,9 +1060,9 @@ static int find_probe_point_by_func(struct probe_finder *pf)
return _param.retval;
}
-/* Find kprobe_trace_events specified by perf_probe_event from debuginfo */
-int find_kprobe_trace_events(int fd, struct perf_probe_event *pev,
- struct kprobe_trace_event **tevs, int max_tevs)
+/* Find probe_trace_events specified by perf_probe_event from debuginfo */
+int find_probe_trace_events(int fd, struct perf_probe_event *pev,
+ struct probe_trace_event **tevs, int max_tevs)
{
struct probe_finder pf = {.pev = pev, .max_tevs = max_tevs};
struct perf_probe_point *pp = &pev->point;
@@ -952,7 +1072,7 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev,
Dwarf *dbg;
int ret = 0;
- pf.tevs = zalloc(sizeof(struct kprobe_trace_event) * max_tevs);
+ pf.tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs);
if (pf.tevs == NULL)
return -ENOMEM;
*tevs = pf.tevs;
@@ -1096,7 +1216,7 @@ end:
static int line_range_add_line(const char *src, unsigned int lineno,
struct line_range *lr)
{
- /* Copy real path */
+ /* Copy source path */
if (!lr->path) {
lr->path = strdup(src);
if (lr->path == NULL)
@@ -1220,7 +1340,7 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
struct line_range *lr = lf->lr;
if (dwarf_tag(sp_die) == DW_TAG_subprogram &&
- die_compare_name(sp_die, lr->function) == 0) {
+ die_compare_name(sp_die, lr->function)) {
lf->fname = dwarf_decl_file(sp_die);
dwarf_decl_line(sp_die, &lr->offset);
pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset);
@@ -1263,6 +1383,7 @@ int find_line_range(int fd, struct line_range *lr)
size_t cuhl;
Dwarf_Die *diep;
Dwarf *dbg;
+ const char *comp_dir;
dbg = dwarf_begin(fd, DWARF_C_READ);
if (!dbg) {
@@ -1298,7 +1419,18 @@ int find_line_range(int fd, struct line_range *lr)
}
off = noff;
}
- pr_debug("path: %lx\n", (unsigned long)lr->path);
+
+ /* Store comp_dir */
+ if (lf.found) {
+ comp_dir = cu_get_comp_dir(&lf.cu_die);
+ if (comp_dir) {
+ lr->comp_dir = strdup(comp_dir);
+ if (!lr->comp_dir)
+ ret = -ENOMEM;
+ }
+ }
+
+ pr_debug("path: %s\n", lr->path);
dwarf_end(dbg);
return (ret < 0) ? ret : lf.found;