|
@@ -116,6 +116,101 @@ static void line_list__free(struct list_head *head)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/* Dwarf FL wrappers */
|
|
|
|
+
|
|
|
|
+static int __linux_kernel_find_elf(Dwfl_Module *mod,
|
|
|
|
+ void **userdata,
|
|
|
|
+ const char *module_name,
|
|
|
|
+ Dwarf_Addr base,
|
|
|
|
+ char **file_name, Elf **elfp)
|
|
|
|
+{
|
|
|
|
+ int fd;
|
|
|
|
+ const char *path = kernel_get_module_path(module_name);
|
|
|
|
+
|
|
|
|
+ if (path) {
|
|
|
|
+ fd = open(path, O_RDONLY);
|
|
|
|
+ if (fd >= 0) {
|
|
|
|
+ *file_name = strdup(path);
|
|
|
|
+ return fd;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ /* If failed, try to call standard method */
|
|
|
|
+ return dwfl_linux_kernel_find_elf(mod, userdata, module_name, base,
|
|
|
|
+ file_name, elfp);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static char *debuginfo_path; /* Currently dummy */
|
|
|
|
+
|
|
|
|
+static const Dwfl_Callbacks offline_callbacks = {
|
|
|
|
+ .find_debuginfo = dwfl_standard_find_debuginfo,
|
|
|
|
+ .debuginfo_path = &debuginfo_path,
|
|
|
|
+
|
|
|
|
+ .section_address = dwfl_offline_section_address,
|
|
|
|
+
|
|
|
|
+ /* We use this table for core files too. */
|
|
|
|
+ .find_elf = dwfl_build_id_find_elf,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const Dwfl_Callbacks kernel_callbacks = {
|
|
|
|
+ .find_debuginfo = dwfl_standard_find_debuginfo,
|
|
|
|
+ .debuginfo_path = &debuginfo_path,
|
|
|
|
+
|
|
|
|
+ .find_elf = __linux_kernel_find_elf,
|
|
|
|
+ .section_address = dwfl_linux_kernel_module_section_address,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/* Get a Dwarf from offline image */
|
|
|
|
+static Dwarf *dwfl_init_offline_dwarf(int fd, Dwfl **dwflp, Dwarf_Addr *bias)
|
|
|
|
+{
|
|
|
|
+ Dwfl_Module *mod;
|
|
|
|
+ Dwarf *dbg = NULL;
|
|
|
|
+
|
|
|
|
+ if (!dwflp)
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
|
|
+ *dwflp = dwfl_begin(&offline_callbacks);
|
|
|
|
+ if (!*dwflp)
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
|
|
+ mod = dwfl_report_offline(*dwflp, "", "", fd);
|
|
|
|
+ if (!mod)
|
|
|
|
+ goto error;
|
|
|
|
+
|
|
|
|
+ dbg = dwfl_module_getdwarf(mod, bias);
|
|
|
|
+ if (!dbg) {
|
|
|
|
+error:
|
|
|
|
+ dwfl_end(*dwflp);
|
|
|
|
+ *dwflp = NULL;
|
|
|
|
+ }
|
|
|
|
+ return dbg;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* Get a Dwarf from live kernel image */
|
|
|
|
+static Dwarf *dwfl_init_live_kernel_dwarf(Dwarf_Addr addr, Dwfl **dwflp,
|
|
|
|
+ Dwarf_Addr *bias)
|
|
|
|
+{
|
|
|
|
+ Dwarf *dbg;
|
|
|
|
+
|
|
|
|
+ if (!dwflp)
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
|
|
+ *dwflp = dwfl_begin(&kernel_callbacks);
|
|
|
|
+ if (!*dwflp)
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
|
|
+ /* Load the kernel dwarves: Don't care the result here */
|
|
|
|
+ dwfl_linux_kernel_report_kernel(*dwflp);
|
|
|
|
+ dwfl_linux_kernel_report_modules(*dwflp);
|
|
|
|
+
|
|
|
|
+ dbg = dwfl_addrdwarf(*dwflp, addr, bias);
|
|
|
|
+ /* Here, check whether we could get a real dwarf */
|
|
|
|
+ if (!dbg) {
|
|
|
|
+ dwfl_end(*dwflp);
|
|
|
|
+ *dwflp = NULL;
|
|
|
|
+ }
|
|
|
|
+ return dbg;
|
|
|
|
+}
|
|
|
|
+
|
|
/* Dwarf wrappers */
|
|
/* Dwarf wrappers */
|
|
|
|
|
|
/* Find the realpath of the target file. */
|
|
/* Find the realpath of the target file. */
|
|
@@ -160,26 +255,44 @@ static bool die_compare_name(Dwarf_Die *dw_die, const char *tname)
|
|
return name ? (strcmp(tname, name) == 0) : false;
|
|
return name ? (strcmp(tname, name) == 0) : false;
|
|
}
|
|
}
|
|
|
|
|
|
-/* Get type die, but skip qualifiers and typedef */
|
|
|
|
-static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
|
|
|
|
|
|
+/* Get type die */
|
|
|
|
+static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
|
|
{
|
|
{
|
|
Dwarf_Attribute attr;
|
|
Dwarf_Attribute attr;
|
|
|
|
+
|
|
|
|
+ if (dwarf_attr_integrate(vr_die, DW_AT_type, &attr) &&
|
|
|
|
+ dwarf_formref_die(&attr, die_mem))
|
|
|
|
+ return die_mem;
|
|
|
|
+ else
|
|
|
|
+ return NULL;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* Get a type die, but skip qualifiers */
|
|
|
|
+static Dwarf_Die *__die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
|
|
|
|
+{
|
|
int tag;
|
|
int tag;
|
|
|
|
|
|
do {
|
|
do {
|
|
- if (dwarf_attr(vr_die, DW_AT_type, &attr) == NULL ||
|
|
|
|
- dwarf_formref_die(&attr, die_mem) == NULL)
|
|
|
|
- return NULL;
|
|
|
|
-
|
|
|
|
- tag = dwarf_tag(die_mem);
|
|
|
|
- vr_die = die_mem;
|
|
|
|
|
|
+ vr_die = die_get_type(vr_die, die_mem);
|
|
|
|
+ if (!vr_die)
|
|
|
|
+ break;
|
|
|
|
+ tag = dwarf_tag(vr_die);
|
|
} while (tag == DW_TAG_const_type ||
|
|
} while (tag == DW_TAG_const_type ||
|
|
tag == DW_TAG_restrict_type ||
|
|
tag == DW_TAG_restrict_type ||
|
|
tag == DW_TAG_volatile_type ||
|
|
tag == DW_TAG_volatile_type ||
|
|
- tag == DW_TAG_shared_type ||
|
|
|
|
- tag == DW_TAG_typedef);
|
|
|
|
|
|
+ tag == DW_TAG_shared_type);
|
|
|
|
+
|
|
|
|
+ return vr_die;
|
|
|
|
+}
|
|
|
|
|
|
- return die_mem;
|
|
|
|
|
|
+/* Get a type die, but skip qualifiers and typedef */
|
|
|
|
+static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
|
|
|
|
+{
|
|
|
|
+ do {
|
|
|
|
+ vr_die = __die_get_real_type(vr_die, die_mem);
|
|
|
|
+ } while (vr_die && dwarf_tag(vr_die) == DW_TAG_typedef);
|
|
|
|
+
|
|
|
|
+ return vr_die;
|
|
}
|
|
}
|
|
|
|
|
|
static bool die_is_signed_type(Dwarf_Die *tp_die)
|
|
static bool die_is_signed_type(Dwarf_Die *tp_die)
|
|
@@ -320,25 +433,35 @@ static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr,
|
|
return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem);
|
|
return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+struct __find_variable_param {
|
|
|
|
+ const char *name;
|
|
|
|
+ Dwarf_Addr addr;
|
|
|
|
+};
|
|
|
|
+
|
|
static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data)
|
|
static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data)
|
|
{
|
|
{
|
|
- const char *name = data;
|
|
|
|
|
|
+ struct __find_variable_param *fvp = data;
|
|
int tag;
|
|
int tag;
|
|
|
|
|
|
tag = dwarf_tag(die_mem);
|
|
tag = dwarf_tag(die_mem);
|
|
if ((tag == DW_TAG_formal_parameter ||
|
|
if ((tag == DW_TAG_formal_parameter ||
|
|
tag == DW_TAG_variable) &&
|
|
tag == DW_TAG_variable) &&
|
|
- die_compare_name(die_mem, name))
|
|
|
|
|
|
+ die_compare_name(die_mem, fvp->name))
|
|
return DIE_FIND_CB_FOUND;
|
|
return DIE_FIND_CB_FOUND;
|
|
|
|
|
|
- return DIE_FIND_CB_CONTINUE;
|
|
|
|
|
|
+ if (dwarf_haspc(die_mem, fvp->addr))
|
|
|
|
+ return DIE_FIND_CB_CONTINUE;
|
|
|
|
+ else
|
|
|
|
+ return DIE_FIND_CB_SIBLING;
|
|
}
|
|
}
|
|
|
|
|
|
-/* Find a variable called 'name' */
|
|
|
|
-static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name,
|
|
|
|
- Dwarf_Die *die_mem)
|
|
|
|
|
|
+/* Find a variable called 'name' at given address */
|
|
|
|
+static Dwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name,
|
|
|
|
+ Dwarf_Addr addr, Dwarf_Die *die_mem)
|
|
{
|
|
{
|
|
- return die_find_child(sp_die, __die_find_variable_cb, (void *)name,
|
|
|
|
|
|
+ struct __find_variable_param fvp = { .name = name, .addr = addr};
|
|
|
|
+
|
|
|
|
+ return die_find_child(sp_die, __die_find_variable_cb, (void *)&fvp,
|
|
die_mem);
|
|
die_mem);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -361,6 +484,60 @@ static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
|
|
die_mem);
|
|
die_mem);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/* Get the name of given variable DIE */
|
|
|
|
+static int die_get_typename(Dwarf_Die *vr_die, char *buf, int len)
|
|
|
|
+{
|
|
|
|
+ Dwarf_Die type;
|
|
|
|
+ int tag, ret, ret2;
|
|
|
|
+ const char *tmp = "";
|
|
|
|
+
|
|
|
|
+ if (__die_get_real_type(vr_die, &type) == NULL)
|
|
|
|
+ return -ENOENT;
|
|
|
|
+
|
|
|
|
+ tag = dwarf_tag(&type);
|
|
|
|
+ if (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)
|
|
|
|
+ tmp = "*";
|
|
|
|
+ else if (tag == DW_TAG_subroutine_type) {
|
|
|
|
+ /* Function pointer */
|
|
|
|
+ ret = snprintf(buf, len, "(function_type)");
|
|
|
|
+ return (ret >= len) ? -E2BIG : ret;
|
|
|
|
+ } else {
|
|
|
|
+ if (!dwarf_diename(&type))
|
|
|
|
+ return -ENOENT;
|
|
|
|
+ if (tag == DW_TAG_union_type)
|
|
|
|
+ tmp = "union ";
|
|
|
|
+ else if (tag == DW_TAG_structure_type)
|
|
|
|
+ tmp = "struct ";
|
|
|
|
+ /* Write a base name */
|
|
|
|
+ ret = snprintf(buf, len, "%s%s", tmp, dwarf_diename(&type));
|
|
|
|
+ return (ret >= len) ? -E2BIG : ret;
|
|
|
|
+ }
|
|
|
|
+ ret = die_get_typename(&type, buf, len);
|
|
|
|
+ if (ret > 0) {
|
|
|
|
+ ret2 = snprintf(buf + ret, len - ret, "%s", tmp);
|
|
|
|
+ ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
|
|
|
|
+ }
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* Get the name and type of given variable DIE, stored as "type\tname" */
|
|
|
|
+static int die_get_varname(Dwarf_Die *vr_die, char *buf, int len)
|
|
|
|
+{
|
|
|
|
+ int ret, ret2;
|
|
|
|
+
|
|
|
|
+ ret = die_get_typename(vr_die, buf, len);
|
|
|
|
+ if (ret < 0) {
|
|
|
|
+ pr_debug("Failed to get type, make it unknown.\n");
|
|
|
|
+ ret = snprintf(buf, len, "(unknown_type)");
|
|
|
|
+ }
|
|
|
|
+ if (ret > 0) {
|
|
|
|
+ ret2 = snprintf(buf + ret, len - ret, "\t%s",
|
|
|
|
+ dwarf_diename(vr_die));
|
|
|
|
+ ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
|
|
|
|
+ }
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Probe finder related functions
|
|
* Probe finder related functions
|
|
*/
|
|
*/
|
|
@@ -374,8 +551,13 @@ static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs)
|
|
return ref;
|
|
return ref;
|
|
}
|
|
}
|
|
|
|
|
|
-/* Show a location */
|
|
|
|
-static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
|
|
|
|
|
|
+/*
|
|
|
|
+ * Convert a location into trace_arg.
|
|
|
|
+ * If tvar == NULL, this just checks variable can be converted.
|
|
|
|
+ */
|
|
|
|
+static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr,
|
|
|
|
+ Dwarf_Op *fb_ops,
|
|
|
|
+ struct probe_trace_arg *tvar)
|
|
{
|
|
{
|
|
Dwarf_Attribute attr;
|
|
Dwarf_Attribute attr;
|
|
Dwarf_Op *op;
|
|
Dwarf_Op *op;
|
|
@@ -384,20 +566,23 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
|
|
Dwarf_Word offs = 0;
|
|
Dwarf_Word offs = 0;
|
|
bool ref = false;
|
|
bool ref = false;
|
|
const char *regs;
|
|
const char *regs;
|
|
- struct probe_trace_arg *tvar = pf->tvar;
|
|
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
|
|
+ if (dwarf_attr(vr_die, DW_AT_external, &attr) != NULL)
|
|
|
|
+ goto static_var;
|
|
|
|
+
|
|
/* TODO: handle more than 1 exprs */
|
|
/* TODO: handle more than 1 exprs */
|
|
if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL ||
|
|
if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL ||
|
|
- dwarf_getlocation_addr(&attr, pf->addr, &op, &nops, 1) <= 0 ||
|
|
|
|
|
|
+ dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0 ||
|
|
nops == 0) {
|
|
nops == 0) {
|
|
/* TODO: Support const_value */
|
|
/* 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;
|
|
return -ENOENT;
|
|
}
|
|
}
|
|
|
|
|
|
if (op->atom == DW_OP_addr) {
|
|
if (op->atom == DW_OP_addr) {
|
|
|
|
+static_var:
|
|
|
|
+ if (!tvar)
|
|
|
|
+ return 0;
|
|
/* Static variables on memory (not stack), make @varname */
|
|
/* Static variables on memory (not stack), make @varname */
|
|
ret = strlen(dwarf_diename(vr_die));
|
|
ret = strlen(dwarf_diename(vr_die));
|
|
tvar->value = zalloc(ret + 2);
|
|
tvar->value = zalloc(ret + 2);
|
|
@@ -412,14 +597,11 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
|
|
|
|
|
|
/* If this is based on frame buffer, set the offset */
|
|
/* If this is based on frame buffer, set the offset */
|
|
if (op->atom == DW_OP_fbreg) {
|
|
if (op->atom == DW_OP_fbreg) {
|
|
- if (pf->fb_ops == NULL) {
|
|
|
|
- pr_warning("The attribute of frame base is not "
|
|
|
|
- "supported.\n");
|
|
|
|
|
|
+ if (fb_ops == NULL)
|
|
return -ENOTSUP;
|
|
return -ENOTSUP;
|
|
- }
|
|
|
|
ref = true;
|
|
ref = true;
|
|
offs = op->number;
|
|
offs = op->number;
|
|
- op = &pf->fb_ops[0];
|
|
|
|
|
|
+ op = &fb_ops[0];
|
|
}
|
|
}
|
|
|
|
|
|
if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) {
|
|
if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) {
|
|
@@ -435,13 +617,18 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
|
|
} else if (op->atom == DW_OP_regx) {
|
|
} else if (op->atom == DW_OP_regx) {
|
|
regn = op->number;
|
|
regn = op->number;
|
|
} else {
|
|
} else {
|
|
- pr_warning("DW_OP %x is not supported.\n", op->atom);
|
|
|
|
|
|
+ pr_debug("DW_OP %x is not supported.\n", op->atom);
|
|
return -ENOTSUP;
|
|
return -ENOTSUP;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (!tvar)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
regs = get_arch_regstr(regn);
|
|
regs = get_arch_regstr(regn);
|
|
if (!regs) {
|
|
if (!regs) {
|
|
- pr_warning("Mapping for DWARF register number %u missing on this architecture.", regn);
|
|
|
|
|
|
+ /* This should be a bug in DWARF or this tool */
|
|
|
|
+ pr_warning("Mapping for DWARF register number %u "
|
|
|
|
+ "missing on this architecture.", regn);
|
|
return -ERANGE;
|
|
return -ERANGE;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -666,8 +853,14 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
|
|
pr_debug("Converting variable %s into trace event.\n",
|
|
pr_debug("Converting variable %s into trace event.\n",
|
|
dwarf_diename(vr_die));
|
|
dwarf_diename(vr_die));
|
|
|
|
|
|
- ret = convert_variable_location(vr_die, pf);
|
|
|
|
- if (ret == 0 && pf->pvar->field) {
|
|
|
|
|
|
+ ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops,
|
|
|
|
+ pf->tvar);
|
|
|
|
+ if (ret == -ENOENT)
|
|
|
|
+ pr_err("Failed to find the location of %s at this address.\n"
|
|
|
|
+ " Perhaps, it has been optimized out.\n", pf->pvar->var);
|
|
|
|
+ else if (ret == -ENOTSUP)
|
|
|
|
+ pr_err("Sorry, we don't support this variable location yet.\n");
|
|
|
|
+ else if (pf->pvar->field) {
|
|
ret = convert_variable_fields(vr_die, pf->pvar->var,
|
|
ret = convert_variable_fields(vr_die, pf->pvar->var,
|
|
pf->pvar->field, &pf->tvar->ref,
|
|
pf->pvar->field, &pf->tvar->ref,
|
|
&die_mem);
|
|
&die_mem);
|
|
@@ -722,56 +915,39 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
|
|
pr_debug("Searching '%s' variable in context.\n",
|
|
pr_debug("Searching '%s' variable in context.\n",
|
|
pf->pvar->var);
|
|
pf->pvar->var);
|
|
/* Search child die for local variables and parameters. */
|
|
/* Search child die for local variables and parameters. */
|
|
- if (die_find_variable(sp_die, pf->pvar->var, &vr_die))
|
|
|
|
|
|
+ if (die_find_variable_at(sp_die, pf->pvar->var, pf->addr, &vr_die))
|
|
ret = convert_variable(&vr_die, pf);
|
|
ret = convert_variable(&vr_die, pf);
|
|
else {
|
|
else {
|
|
/* Search upper class */
|
|
/* Search upper class */
|
|
nscopes = dwarf_getscopes_die(sp_die, &scopes);
|
|
nscopes = dwarf_getscopes_die(sp_die, &scopes);
|
|
- if (nscopes > 0) {
|
|
|
|
- ret = dwarf_getscopevar(scopes, nscopes, pf->pvar->var,
|
|
|
|
- 0, NULL, 0, 0, &vr_die);
|
|
|
|
- if (ret >= 0)
|
|
|
|
|
|
+ while (nscopes-- > 1) {
|
|
|
|
+ pr_debug("Searching variables in %s\n",
|
|
|
|
+ dwarf_diename(&scopes[nscopes]));
|
|
|
|
+ /* We should check this scope, so give dummy address */
|
|
|
|
+ if (die_find_variable_at(&scopes[nscopes],
|
|
|
|
+ pf->pvar->var, 0,
|
|
|
|
+ &vr_die)) {
|
|
ret = convert_variable(&vr_die, pf);
|
|
ret = convert_variable(&vr_die, pf);
|
|
- else
|
|
|
|
- ret = -ENOENT;
|
|
|
|
|
|
+ goto found;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (scopes)
|
|
free(scopes);
|
|
free(scopes);
|
|
- } else
|
|
|
|
- ret = -ENOENT;
|
|
|
|
|
|
+ ret = -ENOENT;
|
|
}
|
|
}
|
|
|
|
+found:
|
|
if (ret < 0)
|
|
if (ret < 0)
|
|
pr_warning("Failed to find '%s' in this function.\n",
|
|
pr_warning("Failed to find '%s' in this function.\n",
|
|
pf->pvar->var);
|
|
pf->pvar->var);
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
-/* Show a probe point to output buffer */
|
|
|
|
-static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
|
|
|
|
|
|
+/* Convert subprogram DIE to trace point */
|
|
|
|
+static int convert_to_trace_point(Dwarf_Die *sp_die, Dwarf_Addr paddr,
|
|
|
|
+ bool retprobe, struct probe_trace_point *tp)
|
|
{
|
|
{
|
|
- struct probe_trace_event *tev;
|
|
|
|
Dwarf_Addr eaddr;
|
|
Dwarf_Addr eaddr;
|
|
- Dwarf_Die die_mem;
|
|
|
|
const char *name;
|
|
const char *name;
|
|
- int ret, i;
|
|
|
|
- Dwarf_Attribute fb_attr;
|
|
|
|
- size_t nops;
|
|
|
|
-
|
|
|
|
- if (pf->ntevs == pf->max_tevs) {
|
|
|
|
- pr_warning("Too many( > %d) probe point found.\n",
|
|
|
|
- pf->max_tevs);
|
|
|
|
- return -ERANGE;
|
|
|
|
- }
|
|
|
|
- tev = &pf->tevs[pf->ntevs++];
|
|
|
|
-
|
|
|
|
- /* If no real subprogram, find a real one */
|
|
|
|
- if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) {
|
|
|
|
- sp_die = die_find_real_subprogram(&pf->cu_die,
|
|
|
|
- pf->addr, &die_mem);
|
|
|
|
- if (!sp_die) {
|
|
|
|
- pr_warning("Failed to find probe point in any "
|
|
|
|
- "functions.\n");
|
|
|
|
- return -ENOENT;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
|
|
/* Copy the name of probe point */
|
|
/* Copy the name of probe point */
|
|
name = dwarf_diename(sp_die);
|
|
name = dwarf_diename(sp_die);
|
|
@@ -781,26 +957,45 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
|
|
dwarf_diename(sp_die));
|
|
dwarf_diename(sp_die));
|
|
return -ENOENT;
|
|
return -ENOENT;
|
|
}
|
|
}
|
|
- tev->point.symbol = strdup(name);
|
|
|
|
- if (tev->point.symbol == NULL)
|
|
|
|
|
|
+ tp->symbol = strdup(name);
|
|
|
|
+ if (tp->symbol == NULL)
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
- tev->point.offset = (unsigned long)(pf->addr - eaddr);
|
|
|
|
|
|
+ tp->offset = (unsigned long)(paddr - eaddr);
|
|
} else
|
|
} else
|
|
/* This function has no name. */
|
|
/* This function has no name. */
|
|
- tev->point.offset = (unsigned long)pf->addr;
|
|
|
|
|
|
+ tp->offset = (unsigned long)paddr;
|
|
|
|
|
|
/* Return probe must be on the head of a subprogram */
|
|
/* Return probe must be on the head of a subprogram */
|
|
- if (pf->pev->point.retprobe) {
|
|
|
|
- if (tev->point.offset != 0) {
|
|
|
|
|
|
+ if (retprobe) {
|
|
|
|
+ if (eaddr != paddr) {
|
|
pr_warning("Return probe must be on the head of"
|
|
pr_warning("Return probe must be on the head of"
|
|
" a real function\n");
|
|
" a real function\n");
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
- tev->point.retprobe = true;
|
|
|
|
|
|
+ tp->retprobe = true;
|
|
}
|
|
}
|
|
|
|
|
|
- pr_debug("Probe point found: %s+%lu\n", tev->point.symbol,
|
|
|
|
- tev->point.offset);
|
|
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* Call probe_finder callback with real subprogram DIE */
|
|
|
|
+static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf)
|
|
|
|
+{
|
|
|
|
+ Dwarf_Die die_mem;
|
|
|
|
+ Dwarf_Attribute fb_attr;
|
|
|
|
+ size_t nops;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ /* If no real subprogram, find a real one */
|
|
|
|
+ if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) {
|
|
|
|
+ sp_die = die_find_real_subprogram(&pf->cu_die,
|
|
|
|
+ pf->addr, &die_mem);
|
|
|
|
+ if (!sp_die) {
|
|
|
|
+ pr_warning("Failed to find probe point in any "
|
|
|
|
+ "functions.\n");
|
|
|
|
+ return -ENOENT;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
|
|
/* Get the frame base attribute/ops */
|
|
/* Get the frame base attribute/ops */
|
|
dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr);
|
|
dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr);
|
|
@@ -820,22 +1015,13 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
|
|
#endif
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
- /* Find each argument */
|
|
|
|
- tev->nargs = pf->pev->nargs;
|
|
|
|
- tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
|
|
|
|
- if (tev->args == NULL)
|
|
|
|
- return -ENOMEM;
|
|
|
|
- for (i = 0; i < pf->pev->nargs; i++) {
|
|
|
|
- pf->pvar = &pf->pev->args[i];
|
|
|
|
- pf->tvar = &tev->args[i];
|
|
|
|
- ret = find_variable(sp_die, pf);
|
|
|
|
- if (ret != 0)
|
|
|
|
- return ret;
|
|
|
|
- }
|
|
|
|
|
|
+ /* Call finder's callback handler */
|
|
|
|
+ ret = pf->callback(sp_die, pf);
|
|
|
|
|
|
/* *pf->fb_ops will be cached in libdw. Don't free it. */
|
|
/* *pf->fb_ops will be cached in libdw. Don't free it. */
|
|
pf->fb_ops = NULL;
|
|
pf->fb_ops = NULL;
|
|
- return 0;
|
|
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
}
|
|
}
|
|
|
|
|
|
/* Find probe point from its line number */
|
|
/* Find probe point from its line number */
|
|
@@ -871,7 +1057,7 @@ static int find_probe_point_by_line(struct probe_finder *pf)
|
|
(int)i, lineno, (uintmax_t)addr);
|
|
(int)i, lineno, (uintmax_t)addr);
|
|
pf->addr = addr;
|
|
pf->addr = addr;
|
|
|
|
|
|
- ret = convert_probe_point(NULL, pf);
|
|
|
|
|
|
+ ret = call_probe_finder(NULL, pf);
|
|
/* Continuing, because target line might be inlined. */
|
|
/* Continuing, because target line might be inlined. */
|
|
}
|
|
}
|
|
return ret;
|
|
return ret;
|
|
@@ -984,7 +1170,7 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
|
|
(int)i, lineno, (unsigned long long)addr);
|
|
(int)i, lineno, (unsigned long long)addr);
|
|
pf->addr = addr;
|
|
pf->addr = addr;
|
|
|
|
|
|
- ret = convert_probe_point(sp_die, pf);
|
|
|
|
|
|
+ ret = call_probe_finder(sp_die, pf);
|
|
/* Continuing, because target line might be inlined. */
|
|
/* Continuing, because target line might be inlined. */
|
|
}
|
|
}
|
|
/* TODO: deallocate lines, but how? */
|
|
/* TODO: deallocate lines, but how? */
|
|
@@ -1019,7 +1205,7 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data)
|
|
pr_debug("found inline addr: 0x%jx\n",
|
|
pr_debug("found inline addr: 0x%jx\n",
|
|
(uintmax_t)pf->addr);
|
|
(uintmax_t)pf->addr);
|
|
|
|
|
|
- param->retval = convert_probe_point(in_die, pf);
|
|
|
|
|
|
+ param->retval = call_probe_finder(in_die, pf);
|
|
if (param->retval < 0)
|
|
if (param->retval < 0)
|
|
return DWARF_CB_ABORT;
|
|
return DWARF_CB_ABORT;
|
|
}
|
|
}
|
|
@@ -1057,7 +1243,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
|
|
}
|
|
}
|
|
pf->addr += pp->offset;
|
|
pf->addr += pp->offset;
|
|
/* TODO: Check the address in this function */
|
|
/* TODO: Check the address in this function */
|
|
- param->retval = convert_probe_point(sp_die, pf);
|
|
|
|
|
|
+ param->retval = call_probe_finder(sp_die, pf);
|
|
}
|
|
}
|
|
} else {
|
|
} else {
|
|
struct dwarf_callback_param _param = {.data = (void *)pf,
|
|
struct dwarf_callback_param _param = {.data = (void *)pf,
|
|
@@ -1079,90 +1265,276 @@ static int find_probe_point_by_func(struct probe_finder *pf)
|
|
return _param.retval;
|
|
return _param.retval;
|
|
}
|
|
}
|
|
|
|
|
|
-/* Find probe_trace_events specified by perf_probe_event from debuginfo */
|
|
|
|
-int find_probe_trace_events(int fd, struct perf_probe_event *pev,
|
|
|
|
- struct probe_trace_event **tevs, int max_tevs)
|
|
|
|
|
|
+/* Find probe points from debuginfo */
|
|
|
|
+static int find_probes(int fd, struct probe_finder *pf)
|
|
{
|
|
{
|
|
- struct probe_finder pf = {.pev = pev, .max_tevs = max_tevs};
|
|
|
|
- struct perf_probe_point *pp = &pev->point;
|
|
|
|
|
|
+ struct perf_probe_point *pp = &pf->pev->point;
|
|
Dwarf_Off off, noff;
|
|
Dwarf_Off off, noff;
|
|
size_t cuhl;
|
|
size_t cuhl;
|
|
Dwarf_Die *diep;
|
|
Dwarf_Die *diep;
|
|
- Dwarf *dbg;
|
|
|
|
|
|
+ Dwarf *dbg = NULL;
|
|
|
|
+ Dwfl *dwfl;
|
|
|
|
+ Dwarf_Addr bias; /* Currently ignored */
|
|
int ret = 0;
|
|
int ret = 0;
|
|
|
|
|
|
- pf.tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs);
|
|
|
|
- if (pf.tevs == NULL)
|
|
|
|
- return -ENOMEM;
|
|
|
|
- *tevs = pf.tevs;
|
|
|
|
- pf.ntevs = 0;
|
|
|
|
-
|
|
|
|
- dbg = dwarf_begin(fd, DWARF_C_READ);
|
|
|
|
|
|
+ dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias);
|
|
if (!dbg) {
|
|
if (!dbg) {
|
|
pr_warning("No dwarf info found in the vmlinux - "
|
|
pr_warning("No dwarf info found in the vmlinux - "
|
|
"please rebuild with CONFIG_DEBUG_INFO=y.\n");
|
|
"please rebuild with CONFIG_DEBUG_INFO=y.\n");
|
|
- free(pf.tevs);
|
|
|
|
- *tevs = NULL;
|
|
|
|
return -EBADF;
|
|
return -EBADF;
|
|
}
|
|
}
|
|
|
|
|
|
#if _ELFUTILS_PREREQ(0, 142)
|
|
#if _ELFUTILS_PREREQ(0, 142)
|
|
/* Get the call frame information from this dwarf */
|
|
/* Get the call frame information from this dwarf */
|
|
- pf.cfi = dwarf_getcfi(dbg);
|
|
|
|
|
|
+ pf->cfi = dwarf_getcfi(dbg);
|
|
#endif
|
|
#endif
|
|
|
|
|
|
off = 0;
|
|
off = 0;
|
|
- line_list__init(&pf.lcache);
|
|
|
|
|
|
+ line_list__init(&pf->lcache);
|
|
/* Loop on CUs (Compilation Unit) */
|
|
/* 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) {
|
|
ret >= 0) {
|
|
/* Get the DIE(Debugging Information Entry) of this CU */
|
|
/* Get the DIE(Debugging Information Entry) of this CU */
|
|
- diep = dwarf_offdie(dbg, off + cuhl, &pf.cu_die);
|
|
|
|
|
|
+ diep = dwarf_offdie(dbg, off + cuhl, &pf->cu_die);
|
|
if (!diep)
|
|
if (!diep)
|
|
continue;
|
|
continue;
|
|
|
|
|
|
/* Check if target file is included. */
|
|
/* Check if target file is included. */
|
|
if (pp->file)
|
|
if (pp->file)
|
|
- pf.fname = cu_find_realpath(&pf.cu_die, pp->file);
|
|
|
|
|
|
+ pf->fname = cu_find_realpath(&pf->cu_die, pp->file);
|
|
else
|
|
else
|
|
- pf.fname = NULL;
|
|
|
|
|
|
+ pf->fname = NULL;
|
|
|
|
|
|
- if (!pp->file || pf.fname) {
|
|
|
|
|
|
+ if (!pp->file || pf->fname) {
|
|
if (pp->function)
|
|
if (pp->function)
|
|
- ret = find_probe_point_by_func(&pf);
|
|
|
|
|
|
+ ret = find_probe_point_by_func(pf);
|
|
else if (pp->lazy_line)
|
|
else if (pp->lazy_line)
|
|
- ret = find_probe_point_lazy(NULL, &pf);
|
|
|
|
|
|
+ ret = find_probe_point_lazy(NULL, pf);
|
|
else {
|
|
else {
|
|
- pf.lno = pp->line;
|
|
|
|
- ret = find_probe_point_by_line(&pf);
|
|
|
|
|
|
+ pf->lno = pp->line;
|
|
|
|
+ ret = find_probe_point_by_line(pf);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
off = noff;
|
|
off = noff;
|
|
}
|
|
}
|
|
- line_list__free(&pf.lcache);
|
|
|
|
- dwarf_end(dbg);
|
|
|
|
|
|
+ line_list__free(&pf->lcache);
|
|
|
|
+ if (dwfl)
|
|
|
|
+ dwfl_end(dwfl);
|
|
|
|
|
|
- return (ret < 0) ? ret : pf.ntevs;
|
|
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* Add a found probe point into trace event list */
|
|
|
|
+static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf)
|
|
|
|
+{
|
|
|
|
+ struct trace_event_finder *tf =
|
|
|
|
+ container_of(pf, struct trace_event_finder, pf);
|
|
|
|
+ struct probe_trace_event *tev;
|
|
|
|
+ int ret, i;
|
|
|
|
+
|
|
|
|
+ /* Check number of tevs */
|
|
|
|
+ if (tf->ntevs == tf->max_tevs) {
|
|
|
|
+ pr_warning("Too many( > %d) probe point found.\n",
|
|
|
|
+ tf->max_tevs);
|
|
|
|
+ return -ERANGE;
|
|
|
|
+ }
|
|
|
|
+ tev = &tf->tevs[tf->ntevs++];
|
|
|
|
+
|
|
|
|
+ ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe,
|
|
|
|
+ &tev->point);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ pr_debug("Probe point found: %s+%lu\n", tev->point.symbol,
|
|
|
|
+ tev->point.offset);
|
|
|
|
+
|
|
|
|
+ /* Find each argument */
|
|
|
|
+ tev->nargs = pf->pev->nargs;
|
|
|
|
+ tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
|
|
|
|
+ if (tev->args == NULL)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+ for (i = 0; i < pf->pev->nargs; i++) {
|
|
|
|
+ pf->pvar = &pf->pev->args[i];
|
|
|
|
+ pf->tvar = &tev->args[i];
|
|
|
|
+ ret = find_variable(sp_die, pf);
|
|
|
|
+ if (ret != 0)
|
|
|
|
+ return ret;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* Find probe_trace_events specified by perf_probe_event from debuginfo */
|
|
|
|
+int find_probe_trace_events(int fd, struct perf_probe_event *pev,
|
|
|
|
+ struct probe_trace_event **tevs, int max_tevs)
|
|
|
|
+{
|
|
|
|
+ struct trace_event_finder tf = {
|
|
|
|
+ .pf = {.pev = pev, .callback = add_probe_trace_event},
|
|
|
|
+ .max_tevs = max_tevs};
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ /* Allocate result tevs array */
|
|
|
|
+ *tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs);
|
|
|
|
+ if (*tevs == NULL)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ tf.tevs = *tevs;
|
|
|
|
+ tf.ntevs = 0;
|
|
|
|
+
|
|
|
|
+ ret = find_probes(fd, &tf.pf);
|
|
|
|
+ if (ret < 0) {
|
|
|
|
+ free(*tevs);
|
|
|
|
+ *tevs = NULL;
|
|
|
|
+ return ret;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return (ret < 0) ? ret : tf.ntevs;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+#define MAX_VAR_LEN 64
|
|
|
|
+
|
|
|
|
+/* Collect available variables in this scope */
|
|
|
|
+static int collect_variables_cb(Dwarf_Die *die_mem, void *data)
|
|
|
|
+{
|
|
|
|
+ struct available_var_finder *af = data;
|
|
|
|
+ struct variable_list *vl;
|
|
|
|
+ char buf[MAX_VAR_LEN];
|
|
|
|
+ int tag, ret;
|
|
|
|
+
|
|
|
|
+ vl = &af->vls[af->nvls - 1];
|
|
|
|
+
|
|
|
|
+ tag = dwarf_tag(die_mem);
|
|
|
|
+ if (tag == DW_TAG_formal_parameter ||
|
|
|
|
+ tag == DW_TAG_variable) {
|
|
|
|
+ ret = convert_variable_location(die_mem, af->pf.addr,
|
|
|
|
+ af->pf.fb_ops, NULL);
|
|
|
|
+ if (ret == 0) {
|
|
|
|
+ ret = die_get_varname(die_mem, buf, MAX_VAR_LEN);
|
|
|
|
+ pr_debug2("Add new var: %s\n", buf);
|
|
|
|
+ if (ret > 0)
|
|
|
|
+ strlist__add(vl->vars, buf);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (af->child && dwarf_haspc(die_mem, af->pf.addr))
|
|
|
|
+ return DIE_FIND_CB_CONTINUE;
|
|
|
|
+ else
|
|
|
|
+ return DIE_FIND_CB_SIBLING;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* Add a found vars into available variables list */
|
|
|
|
+static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf)
|
|
|
|
+{
|
|
|
|
+ struct available_var_finder *af =
|
|
|
|
+ container_of(pf, struct available_var_finder, pf);
|
|
|
|
+ struct variable_list *vl;
|
|
|
|
+ Dwarf_Die die_mem, *scopes = NULL;
|
|
|
|
+ int ret, nscopes;
|
|
|
|
+
|
|
|
|
+ /* Check number of tevs */
|
|
|
|
+ if (af->nvls == af->max_vls) {
|
|
|
|
+ pr_warning("Too many( > %d) probe point found.\n", af->max_vls);
|
|
|
|
+ return -ERANGE;
|
|
|
|
+ }
|
|
|
|
+ vl = &af->vls[af->nvls++];
|
|
|
|
+
|
|
|
|
+ ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe,
|
|
|
|
+ &vl->point);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ pr_debug("Probe point found: %s+%lu\n", vl->point.symbol,
|
|
|
|
+ vl->point.offset);
|
|
|
|
+
|
|
|
|
+ /* Find local variables */
|
|
|
|
+ vl->vars = strlist__new(true, NULL);
|
|
|
|
+ if (vl->vars == NULL)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+ af->child = true;
|
|
|
|
+ die_find_child(sp_die, collect_variables_cb, (void *)af, &die_mem);
|
|
|
|
+
|
|
|
|
+ /* Find external variables */
|
|
|
|
+ if (!af->externs)
|
|
|
|
+ goto out;
|
|
|
|
+ /* Don't need to search child DIE for externs. */
|
|
|
|
+ af->child = false;
|
|
|
|
+ nscopes = dwarf_getscopes_die(sp_die, &scopes);
|
|
|
|
+ while (nscopes-- > 1)
|
|
|
|
+ die_find_child(&scopes[nscopes], collect_variables_cb,
|
|
|
|
+ (void *)af, &die_mem);
|
|
|
|
+ if (scopes)
|
|
|
|
+ free(scopes);
|
|
|
|
+
|
|
|
|
+out:
|
|
|
|
+ if (strlist__empty(vl->vars)) {
|
|
|
|
+ strlist__delete(vl->vars);
|
|
|
|
+ vl->vars = NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* Find available variables at given probe point */
|
|
|
|
+int find_available_vars_at(int fd, struct perf_probe_event *pev,
|
|
|
|
+ struct variable_list **vls, int max_vls,
|
|
|
|
+ bool externs)
|
|
|
|
+{
|
|
|
|
+ struct available_var_finder af = {
|
|
|
|
+ .pf = {.pev = pev, .callback = add_available_vars},
|
|
|
|
+ .max_vls = max_vls, .externs = externs};
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ /* Allocate result vls array */
|
|
|
|
+ *vls = zalloc(sizeof(struct variable_list) * max_vls);
|
|
|
|
+ if (*vls == NULL)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ af.vls = *vls;
|
|
|
|
+ af.nvls = 0;
|
|
|
|
+
|
|
|
|
+ ret = find_probes(fd, &af.pf);
|
|
|
|
+ if (ret < 0) {
|
|
|
|
+ /* Free vlist for error */
|
|
|
|
+ while (af.nvls--) {
|
|
|
|
+ if (af.vls[af.nvls].point.symbol)
|
|
|
|
+ free(af.vls[af.nvls].point.symbol);
|
|
|
|
+ if (af.vls[af.nvls].vars)
|
|
|
|
+ strlist__delete(af.vls[af.nvls].vars);
|
|
|
|
+ }
|
|
|
|
+ free(af.vls);
|
|
|
|
+ *vls = NULL;
|
|
|
|
+ return ret;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return (ret < 0) ? ret : af.nvls;
|
|
}
|
|
}
|
|
|
|
|
|
/* Reverse search */
|
|
/* Reverse search */
|
|
-int find_perf_probe_point(int fd, unsigned long addr,
|
|
|
|
- struct perf_probe_point *ppt)
|
|
|
|
|
|
+int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt)
|
|
{
|
|
{
|
|
Dwarf_Die cudie, spdie, indie;
|
|
Dwarf_Die cudie, spdie, indie;
|
|
- Dwarf *dbg;
|
|
|
|
|
|
+ Dwarf *dbg = NULL;
|
|
|
|
+ Dwfl *dwfl = NULL;
|
|
Dwarf_Line *line;
|
|
Dwarf_Line *line;
|
|
- Dwarf_Addr laddr, eaddr;
|
|
|
|
|
|
+ Dwarf_Addr laddr, eaddr, bias = 0;
|
|
const char *tmp;
|
|
const char *tmp;
|
|
int lineno, ret = 0;
|
|
int lineno, ret = 0;
|
|
bool found = false;
|
|
bool found = false;
|
|
|
|
|
|
- dbg = dwarf_begin(fd, DWARF_C_READ);
|
|
|
|
- if (!dbg)
|
|
|
|
- return -EBADF;
|
|
|
|
|
|
+ /* Open the live linux kernel */
|
|
|
|
+ dbg = dwfl_init_live_kernel_dwarf(addr, &dwfl, &bias);
|
|
|
|
+ if (!dbg) {
|
|
|
|
+ pr_warning("No dwarf info found in the vmlinux - "
|
|
|
|
+ "please rebuild with CONFIG_DEBUG_INFO=y.\n");
|
|
|
|
+ ret = -EINVAL;
|
|
|
|
+ goto end;
|
|
|
|
+ }
|
|
|
|
|
|
|
|
+ /* Adjust address with bias */
|
|
|
|
+ addr += bias;
|
|
/* Find cu die */
|
|
/* Find cu die */
|
|
- if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr, &cudie)) {
|
|
|
|
|
|
+ if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr - bias, &cudie)) {
|
|
|
|
+ pr_warning("No CU DIE is found at %lx\n", addr);
|
|
ret = -EINVAL;
|
|
ret = -EINVAL;
|
|
goto end;
|
|
goto end;
|
|
}
|
|
}
|
|
@@ -1225,7 +1597,8 @@ found:
|
|
}
|
|
}
|
|
|
|
|
|
end:
|
|
end:
|
|
- dwarf_end(dbg);
|
|
|
|
|
|
+ if (dwfl)
|
|
|
|
+ dwfl_end(dwfl);
|
|
if (ret >= 0)
|
|
if (ret >= 0)
|
|
ret = found ? 1 : 0;
|
|
ret = found ? 1 : 0;
|
|
return ret;
|
|
return ret;
|
|
@@ -1358,6 +1731,9 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
|
|
struct line_finder *lf = param->data;
|
|
struct line_finder *lf = param->data;
|
|
struct line_range *lr = lf->lr;
|
|
struct line_range *lr = lf->lr;
|
|
|
|
|
|
|
|
+ pr_debug("find (%llx) %s\n",
|
|
|
|
+ (unsigned long long)dwarf_dieoffset(sp_die),
|
|
|
|
+ dwarf_diename(sp_die));
|
|
if (dwarf_tag(sp_die) == DW_TAG_subprogram &&
|
|
if (dwarf_tag(sp_die) == DW_TAG_subprogram &&
|
|
die_compare_name(sp_die, lr->function)) {
|
|
die_compare_name(sp_die, lr->function)) {
|
|
lf->fname = dwarf_decl_file(sp_die);
|
|
lf->fname = dwarf_decl_file(sp_die);
|
|
@@ -1401,10 +1777,12 @@ int find_line_range(int fd, struct line_range *lr)
|
|
Dwarf_Off off = 0, noff;
|
|
Dwarf_Off off = 0, noff;
|
|
size_t cuhl;
|
|
size_t cuhl;
|
|
Dwarf_Die *diep;
|
|
Dwarf_Die *diep;
|
|
- Dwarf *dbg;
|
|
|
|
|
|
+ Dwarf *dbg = NULL;
|
|
|
|
+ Dwfl *dwfl;
|
|
|
|
+ Dwarf_Addr bias; /* Currently ignored */
|
|
const char *comp_dir;
|
|
const char *comp_dir;
|
|
|
|
|
|
- dbg = dwarf_begin(fd, DWARF_C_READ);
|
|
|
|
|
|
+ dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias);
|
|
if (!dbg) {
|
|
if (!dbg) {
|
|
pr_warning("No dwarf info found in the vmlinux - "
|
|
pr_warning("No dwarf info found in the vmlinux - "
|
|
"please rebuild with CONFIG_DEBUG_INFO=y.\n");
|
|
"please rebuild with CONFIG_DEBUG_INFO=y.\n");
|
|
@@ -1450,8 +1828,7 @@ int find_line_range(int fd, struct line_range *lr)
|
|
}
|
|
}
|
|
|
|
|
|
pr_debug("path: %s\n", lr->path);
|
|
pr_debug("path: %s\n", lr->path);
|
|
- dwarf_end(dbg);
|
|
|
|
-
|
|
|
|
|
|
+ dwfl_end(dwfl);
|
|
return (ret < 0) ? ret : lf.found;
|
|
return (ret < 0) ? ret : lf.found;
|
|
}
|
|
}
|
|
|
|
|