static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) { Dwarf_Die die_mem; int ret; pr_debug("Converting variable %s into trace event.\n", dwarf_diename(vr_die)); 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, pf->pvar->field, &pf->tvar->ref, &die_mem); vr_die = &die_mem; } if (ret == 0) ret = convert_variable_type(vr_die, pf->tvar, pf->pvar->type); return ret; }
/* 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; 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 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; }
static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, struct perf_probe_arg_field *field, struct probe_trace_arg_ref **ref_ptr, Dwarf_Die *die_mem) { struct probe_trace_arg_ref *ref = *ref_ptr; Dwarf_Die type; Dwarf_Word offs; 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; } 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) memcpy(die_mem, &type, sizeof(*die_mem)); 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 += dwarf_bytesize(&type) * field->index; if (!field->next) memcpy(die_mem, vr_die, sizeof(*die_mem)); goto next; } else if (tag == DW_TAG_pointer_type) { if (!field->ref) { pr_err("Semantic error: %s must be referred by '->'\n", field->name); return -EINVAL; } if (die_get_real_type(&type, &type) == NULL) { pr_warning("Failed to get the type of %s.\n", varname); return -ENOENT; } if (dwarf_tag(&type) != DW_TAG_structure_type) { pr_warning("%s is not a data structure.\n", varname); return -EINVAL; } ref = zalloc(sizeof(struct probe_trace_arg_ref)); if (ref == NULL) return -ENOMEM; if (*ref_ptr) (*ref_ptr)->next = ref; else *ref_ptr = ref; } else { 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.\n", varname); return -EINVAL; } if (field->ref) { pr_err("Semantic error: %s must be referred by '.'\n", field->name); return -EINVAL; } if (!ref) { pr_warning("Structure on a register is not " "supported yet.\n"); return -ENOTSUP; } } if (die_find_member(&type, field->name, die_mem) == NULL) { pr_warning("%s(tyep:%s) has no member %s.\n", varname, dwarf_diename(&type), field->name); return -EINVAL; } 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; next: if (field->next) return convert_variable_fields(die_mem, field->name, field->next, &ref, die_mem); else return 0; }
static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, struct perf_probe_arg_field *field, struct probe_trace_arg_ref **ref_ptr, Dwarf_Die *die_mem) { struct probe_trace_arg_ref *ref = *ref_ptr; Dwarf_Die type; Dwarf_Word offs; 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; } 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 += dwarf_bytesize(&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); return -EINVAL; } /* Get the type pointed by this pointer */ 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 */ tag = dwarf_tag(&type); if (tag != DW_TAG_structure_type && tag != DW_TAG_union_type) { pr_warning("%s is not a data structure nor an union.\n", varname); return -EINVAL; } ref = zalloc(sizeof(struct probe_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 (tag != DW_TAG_structure_type && tag != DW_TAG_union_type) { pr_warning("%s is not a data structure nor an union.\n", varname); return -EINVAL; } if (field->name[0] == '[') { pr_err("Semantic error: %s is not a pointer" " nor array.\n", varname); return -EINVAL; } /* While prcessing unnamed field, we don't care about this */ if (field->ref && dwarf_diename(vr_die)) { pr_err("Semantic error: %s must be referred by '.'\n", field->name); return -EINVAL; } if (!ref) { pr_warning("Structure on a register is not " "supported yet.\n"); return -ENOTSUP; } } if (die_find_member(&type, field->name, die_mem) == NULL) { pr_warning("%s(type:%s) has no member %s.\n", varname, dwarf_diename(&type), field->name); return -EINVAL; } /* Get the offset of the field */ if (tag == DW_TAG_union_type) { offs = 0; } else { 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; /* If this member is unnamed, we need to reuse this field */ if (!dwarf_diename(die_mem)) return convert_variable_fields(die_mem, varname, field, &ref, die_mem); next: /* Converting next field */ if (field->next) return convert_variable_fields(die_mem, field->name, field->next, &ref, die_mem); else return 0; }
static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, struct perf_probe_arg_field *field, struct kprobe_trace_arg_ref **ref_ptr, Dwarf_Die *die_mem) { struct kprobe_trace_arg_ref *ref = *ref_ptr; 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) { 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) { 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) { 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) { pr_warning("%s is not a data structure.\n", varname); return -EINVAL; } 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) { pr_warning("%s is not a data structure.\n", varname); return -EINVAL; } if (field->ref) { pr_err("Semantic error: %s must be referred by '.'\n", field->name); return -EINVAL; } if (!ref) { pr_warning("Structure on a register is not " "supported yet.\n"); return -ENOTSUP; } } if (die_find_member(&type, field->name, die_mem) == NULL) { pr_warning("%s(tyep:%s) has no member %s.\n", varname, dwarf_diename(&type), field->name); return -EINVAL; } /* Get the offset of the field */ 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) return convert_variable_fields(die_mem, field->name, field->next, &ref, die_mem); else return 0; }