162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * probe-finder.c : C expression to kprobe event converter 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Written by Masami Hiramatsu <mhiramat@redhat.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <inttypes.h> 962306a36Sopenharmony_ci#include <sys/utsname.h> 1062306a36Sopenharmony_ci#include <sys/types.h> 1162306a36Sopenharmony_ci#include <sys/stat.h> 1262306a36Sopenharmony_ci#include <fcntl.h> 1362306a36Sopenharmony_ci#include <errno.h> 1462306a36Sopenharmony_ci#include <stdio.h> 1562306a36Sopenharmony_ci#include <unistd.h> 1662306a36Sopenharmony_ci#include <stdlib.h> 1762306a36Sopenharmony_ci#include <string.h> 1862306a36Sopenharmony_ci#include <stdarg.h> 1962306a36Sopenharmony_ci#include <dwarf-regs.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <linux/bitops.h> 2262306a36Sopenharmony_ci#include <linux/zalloc.h> 2362306a36Sopenharmony_ci#include "event.h" 2462306a36Sopenharmony_ci#include "dso.h" 2562306a36Sopenharmony_ci#include "debug.h" 2662306a36Sopenharmony_ci#include "intlist.h" 2762306a36Sopenharmony_ci#include "strbuf.h" 2862306a36Sopenharmony_ci#include "strlist.h" 2962306a36Sopenharmony_ci#include "symbol.h" 3062306a36Sopenharmony_ci#include "probe-finder.h" 3162306a36Sopenharmony_ci#include "probe-file.h" 3262306a36Sopenharmony_ci#include "string2.h" 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#ifdef HAVE_DEBUGINFOD_SUPPORT 3562306a36Sopenharmony_ci#include <elfutils/debuginfod.h> 3662306a36Sopenharmony_ci#endif 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* Kprobe tracer basic type is up to u64 */ 3962306a36Sopenharmony_ci#define MAX_BASIC_TYPE_BITS 64 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* Dwarf FL wrappers */ 4262306a36Sopenharmony_cistatic char *debuginfo_path; /* Currently dummy */ 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic const Dwfl_Callbacks offline_callbacks = { 4562306a36Sopenharmony_ci .find_debuginfo = dwfl_standard_find_debuginfo, 4662306a36Sopenharmony_ci .debuginfo_path = &debuginfo_path, 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci .section_address = dwfl_offline_section_address, 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci /* We use this table for core files too. */ 5162306a36Sopenharmony_ci .find_elf = dwfl_build_id_find_elf, 5262306a36Sopenharmony_ci}; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* Get a Dwarf from offline image */ 5562306a36Sopenharmony_cistatic int debuginfo__init_offline_dwarf(struct debuginfo *dbg, 5662306a36Sopenharmony_ci const char *path) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci GElf_Addr dummy; 5962306a36Sopenharmony_ci int fd; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci fd = open(path, O_RDONLY); 6262306a36Sopenharmony_ci if (fd < 0) 6362306a36Sopenharmony_ci return fd; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci dbg->dwfl = dwfl_begin(&offline_callbacks); 6662306a36Sopenharmony_ci if (!dbg->dwfl) 6762306a36Sopenharmony_ci goto error; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci dwfl_report_begin(dbg->dwfl); 7062306a36Sopenharmony_ci dbg->mod = dwfl_report_offline(dbg->dwfl, "", "", fd); 7162306a36Sopenharmony_ci if (!dbg->mod) 7262306a36Sopenharmony_ci goto error; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci dbg->dbg = dwfl_module_getdwarf(dbg->mod, &dbg->bias); 7562306a36Sopenharmony_ci if (!dbg->dbg) 7662306a36Sopenharmony_ci goto error; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci dwfl_module_build_id(dbg->mod, &dbg->build_id, &dummy); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci dwfl_report_end(dbg->dwfl, NULL, NULL); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci return 0; 8362306a36Sopenharmony_cierror: 8462306a36Sopenharmony_ci if (dbg->dwfl) 8562306a36Sopenharmony_ci dwfl_end(dbg->dwfl); 8662306a36Sopenharmony_ci else 8762306a36Sopenharmony_ci close(fd); 8862306a36Sopenharmony_ci memset(dbg, 0, sizeof(*dbg)); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci return -ENOENT; 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic struct debuginfo *__debuginfo__new(const char *path) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci struct debuginfo *dbg = zalloc(sizeof(*dbg)); 9662306a36Sopenharmony_ci if (!dbg) 9762306a36Sopenharmony_ci return NULL; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (debuginfo__init_offline_dwarf(dbg, path) < 0) 10062306a36Sopenharmony_ci zfree(&dbg); 10162306a36Sopenharmony_ci if (dbg) 10262306a36Sopenharmony_ci pr_debug("Open Debuginfo file: %s\n", path); 10362306a36Sopenharmony_ci return dbg; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cienum dso_binary_type distro_dwarf_types[] = { 10762306a36Sopenharmony_ci DSO_BINARY_TYPE__FEDORA_DEBUGINFO, 10862306a36Sopenharmony_ci DSO_BINARY_TYPE__UBUNTU_DEBUGINFO, 10962306a36Sopenharmony_ci DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, 11062306a36Sopenharmony_ci DSO_BINARY_TYPE__BUILDID_DEBUGINFO, 11162306a36Sopenharmony_ci DSO_BINARY_TYPE__MIXEDUP_UBUNTU_DEBUGINFO, 11262306a36Sopenharmony_ci DSO_BINARY_TYPE__NOT_FOUND, 11362306a36Sopenharmony_ci}; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistruct debuginfo *debuginfo__new(const char *path) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci enum dso_binary_type *type; 11862306a36Sopenharmony_ci char buf[PATH_MAX], nil = '\0'; 11962306a36Sopenharmony_ci struct dso *dso; 12062306a36Sopenharmony_ci struct debuginfo *dinfo = NULL; 12162306a36Sopenharmony_ci struct build_id bid; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* Try to open distro debuginfo files */ 12462306a36Sopenharmony_ci dso = dso__new(path); 12562306a36Sopenharmony_ci if (!dso) 12662306a36Sopenharmony_ci goto out; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* Set the build id for DSO_BINARY_TYPE__BUILDID_DEBUGINFO */ 12962306a36Sopenharmony_ci if (is_regular_file(path) && filename__read_build_id(path, &bid) > 0) 13062306a36Sopenharmony_ci dso__set_build_id(dso, &bid); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci for (type = distro_dwarf_types; 13362306a36Sopenharmony_ci !dinfo && *type != DSO_BINARY_TYPE__NOT_FOUND; 13462306a36Sopenharmony_ci type++) { 13562306a36Sopenharmony_ci if (dso__read_binary_type_filename(dso, *type, &nil, 13662306a36Sopenharmony_ci buf, PATH_MAX) < 0) 13762306a36Sopenharmony_ci continue; 13862306a36Sopenharmony_ci dinfo = __debuginfo__new(buf); 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci dso__put(dso); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ciout: 14362306a36Sopenharmony_ci /* if failed to open all distro debuginfo, open given binary */ 14462306a36Sopenharmony_ci return dinfo ? : __debuginfo__new(path); 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_civoid debuginfo__delete(struct debuginfo *dbg) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci if (dbg) { 15062306a36Sopenharmony_ci if (dbg->dwfl) 15162306a36Sopenharmony_ci dwfl_end(dbg->dwfl); 15262306a36Sopenharmony_ci free(dbg); 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci/* 15762306a36Sopenharmony_ci * Probe finder related functions 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct probe_trace_arg_ref *ref; 16362306a36Sopenharmony_ci ref = zalloc(sizeof(struct probe_trace_arg_ref)); 16462306a36Sopenharmony_ci if (ref != NULL) 16562306a36Sopenharmony_ci ref->offset = offs; 16662306a36Sopenharmony_ci return ref; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci/* 17062306a36Sopenharmony_ci * Convert a location into trace_arg. 17162306a36Sopenharmony_ci * If tvar == NULL, this just checks variable can be converted. 17262306a36Sopenharmony_ci * If fentry == true and vr_die is a parameter, do heuristic search 17362306a36Sopenharmony_ci * for the location fuzzed by function entry mcount. 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_cistatic int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, 17662306a36Sopenharmony_ci Dwarf_Op *fb_ops, Dwarf_Die *sp_die, 17762306a36Sopenharmony_ci unsigned int machine, 17862306a36Sopenharmony_ci struct probe_trace_arg *tvar) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci Dwarf_Attribute attr; 18162306a36Sopenharmony_ci Dwarf_Addr tmp = 0; 18262306a36Sopenharmony_ci Dwarf_Op *op; 18362306a36Sopenharmony_ci size_t nops; 18462306a36Sopenharmony_ci unsigned int regn; 18562306a36Sopenharmony_ci Dwarf_Word offs = 0; 18662306a36Sopenharmony_ci bool ref = false; 18762306a36Sopenharmony_ci const char *regs; 18862306a36Sopenharmony_ci int ret, ret2 = 0; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (dwarf_attr(vr_die, DW_AT_external, &attr) != NULL) 19162306a36Sopenharmony_ci goto static_var; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* Constant value */ 19462306a36Sopenharmony_ci if (dwarf_attr(vr_die, DW_AT_const_value, &attr) && 19562306a36Sopenharmony_ci immediate_value_is_supported()) { 19662306a36Sopenharmony_ci Dwarf_Sword snum; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (!tvar) 19962306a36Sopenharmony_ci return 0; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci dwarf_formsdata(&attr, &snum); 20262306a36Sopenharmony_ci ret = asprintf(&tvar->value, "\\%ld", (long)snum); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci return ret < 0 ? -ENOMEM : 0; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci /* TODO: handle more than 1 exprs */ 20862306a36Sopenharmony_ci if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL) 20962306a36Sopenharmony_ci return -EINVAL; /* Broken DIE ? */ 21062306a36Sopenharmony_ci if (dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0) { 21162306a36Sopenharmony_ci ret = dwarf_entrypc(sp_die, &tmp); 21262306a36Sopenharmony_ci if (ret) 21362306a36Sopenharmony_ci return -ENOENT; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (probe_conf.show_location_range && 21662306a36Sopenharmony_ci (dwarf_tag(vr_die) == DW_TAG_variable)) { 21762306a36Sopenharmony_ci ret2 = -ERANGE; 21862306a36Sopenharmony_ci } else if (addr != tmp || 21962306a36Sopenharmony_ci dwarf_tag(vr_die) != DW_TAG_formal_parameter) { 22062306a36Sopenharmony_ci return -ENOENT; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci ret = dwarf_highpc(sp_die, &tmp); 22462306a36Sopenharmony_ci if (ret) 22562306a36Sopenharmony_ci return -ENOENT; 22662306a36Sopenharmony_ci /* 22762306a36Sopenharmony_ci * This is fuzzed by fentry mcount. We try to find the 22862306a36Sopenharmony_ci * parameter location at the earliest address. 22962306a36Sopenharmony_ci */ 23062306a36Sopenharmony_ci for (addr += 1; addr <= tmp; addr++) { 23162306a36Sopenharmony_ci if (dwarf_getlocation_addr(&attr, addr, &op, 23262306a36Sopenharmony_ci &nops, 1) > 0) 23362306a36Sopenharmony_ci goto found; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci return -ENOENT; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_cifound: 23862306a36Sopenharmony_ci if (nops == 0) 23962306a36Sopenharmony_ci /* TODO: Support const_value */ 24062306a36Sopenharmony_ci return -ENOENT; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (op->atom == DW_OP_addr) { 24362306a36Sopenharmony_cistatic_var: 24462306a36Sopenharmony_ci if (!tvar) 24562306a36Sopenharmony_ci return ret2; 24662306a36Sopenharmony_ci /* Static variables on memory (not stack), make @varname */ 24762306a36Sopenharmony_ci ret = strlen(dwarf_diename(vr_die)); 24862306a36Sopenharmony_ci tvar->value = zalloc(ret + 2); 24962306a36Sopenharmony_ci if (tvar->value == NULL) 25062306a36Sopenharmony_ci return -ENOMEM; 25162306a36Sopenharmony_ci snprintf(tvar->value, ret + 2, "@%s", dwarf_diename(vr_die)); 25262306a36Sopenharmony_ci tvar->ref = alloc_trace_arg_ref((long)offs); 25362306a36Sopenharmony_ci if (tvar->ref == NULL) 25462306a36Sopenharmony_ci return -ENOMEM; 25562306a36Sopenharmony_ci return ret2; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* If this is based on frame buffer, set the offset */ 25962306a36Sopenharmony_ci if (op->atom == DW_OP_fbreg) { 26062306a36Sopenharmony_ci if (fb_ops == NULL) 26162306a36Sopenharmony_ci return -ENOTSUP; 26262306a36Sopenharmony_ci ref = true; 26362306a36Sopenharmony_ci offs = op->number; 26462306a36Sopenharmony_ci op = &fb_ops[0]; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) { 26862306a36Sopenharmony_ci regn = op->atom - DW_OP_breg0; 26962306a36Sopenharmony_ci offs += op->number; 27062306a36Sopenharmony_ci ref = true; 27162306a36Sopenharmony_ci } else if (op->atom >= DW_OP_reg0 && op->atom <= DW_OP_reg31) { 27262306a36Sopenharmony_ci regn = op->atom - DW_OP_reg0; 27362306a36Sopenharmony_ci } else if (op->atom == DW_OP_bregx) { 27462306a36Sopenharmony_ci regn = op->number; 27562306a36Sopenharmony_ci offs += op->number2; 27662306a36Sopenharmony_ci ref = true; 27762306a36Sopenharmony_ci } else if (op->atom == DW_OP_regx) { 27862306a36Sopenharmony_ci regn = op->number; 27962306a36Sopenharmony_ci } else { 28062306a36Sopenharmony_ci pr_debug("DW_OP %x is not supported.\n", op->atom); 28162306a36Sopenharmony_ci return -ENOTSUP; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci if (!tvar) 28562306a36Sopenharmony_ci return ret2; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci regs = get_dwarf_regstr(regn, machine); 28862306a36Sopenharmony_ci if (!regs) { 28962306a36Sopenharmony_ci /* This should be a bug in DWARF or this tool */ 29062306a36Sopenharmony_ci pr_warning("Mapping for the register number %u " 29162306a36Sopenharmony_ci "missing on this architecture.\n", regn); 29262306a36Sopenharmony_ci return -ENOTSUP; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci tvar->value = strdup(regs); 29662306a36Sopenharmony_ci if (tvar->value == NULL) 29762306a36Sopenharmony_ci return -ENOMEM; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (ref) { 30062306a36Sopenharmony_ci tvar->ref = alloc_trace_arg_ref((long)offs); 30162306a36Sopenharmony_ci if (tvar->ref == NULL) 30262306a36Sopenharmony_ci return -ENOMEM; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci return ret2; 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci#define BYTES_TO_BITS(nb) ((nb) * BITS_PER_LONG / sizeof(long)) 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic int convert_variable_type(Dwarf_Die *vr_die, 31062306a36Sopenharmony_ci struct probe_trace_arg *tvar, 31162306a36Sopenharmony_ci const char *cast, bool user_access) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci struct probe_trace_arg_ref **ref_ptr = &tvar->ref; 31462306a36Sopenharmony_ci Dwarf_Die type; 31562306a36Sopenharmony_ci char buf[16]; 31662306a36Sopenharmony_ci char sbuf[STRERR_BUFSIZE]; 31762306a36Sopenharmony_ci int bsize, boffs, total; 31862306a36Sopenharmony_ci int ret; 31962306a36Sopenharmony_ci char prefix; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci /* TODO: check all types */ 32262306a36Sopenharmony_ci if (cast && strcmp(cast, "string") != 0 && strcmp(cast, "ustring") && 32362306a36Sopenharmony_ci strcmp(cast, "x") != 0 && 32462306a36Sopenharmony_ci strcmp(cast, "s") != 0 && strcmp(cast, "u") != 0) { 32562306a36Sopenharmony_ci /* Non string type is OK */ 32662306a36Sopenharmony_ci /* and respect signedness/hexadecimal cast */ 32762306a36Sopenharmony_ci tvar->type = strdup(cast); 32862306a36Sopenharmony_ci return (tvar->type == NULL) ? -ENOMEM : 0; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci bsize = dwarf_bitsize(vr_die); 33262306a36Sopenharmony_ci if (bsize > 0) { 33362306a36Sopenharmony_ci /* This is a bitfield */ 33462306a36Sopenharmony_ci boffs = dwarf_bitoffset(vr_die); 33562306a36Sopenharmony_ci total = dwarf_bytesize(vr_die); 33662306a36Sopenharmony_ci if (boffs < 0 || total < 0) 33762306a36Sopenharmony_ci return -ENOENT; 33862306a36Sopenharmony_ci ret = snprintf(buf, 16, "b%d@%d/%zd", bsize, boffs, 33962306a36Sopenharmony_ci BYTES_TO_BITS(total)); 34062306a36Sopenharmony_ci goto formatted; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (die_get_real_type(vr_die, &type) == NULL) { 34462306a36Sopenharmony_ci pr_warning("Failed to get a type information of %s.\n", 34562306a36Sopenharmony_ci dwarf_diename(vr_die)); 34662306a36Sopenharmony_ci return -ENOENT; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci pr_debug("%s type is %s.\n", 35062306a36Sopenharmony_ci dwarf_diename(vr_die), dwarf_diename(&type)); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci if (cast && (!strcmp(cast, "string") || !strcmp(cast, "ustring"))) { 35362306a36Sopenharmony_ci /* String type */ 35462306a36Sopenharmony_ci ret = dwarf_tag(&type); 35562306a36Sopenharmony_ci if (ret != DW_TAG_pointer_type && 35662306a36Sopenharmony_ci ret != DW_TAG_array_type) { 35762306a36Sopenharmony_ci pr_warning("Failed to cast into string: " 35862306a36Sopenharmony_ci "%s(%s) is not a pointer nor array.\n", 35962306a36Sopenharmony_ci dwarf_diename(vr_die), dwarf_diename(&type)); 36062306a36Sopenharmony_ci return -EINVAL; 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci if (die_get_real_type(&type, &type) == NULL) { 36362306a36Sopenharmony_ci pr_warning("Failed to get a type" 36462306a36Sopenharmony_ci " information.\n"); 36562306a36Sopenharmony_ci return -ENOENT; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci if (ret == DW_TAG_pointer_type) { 36862306a36Sopenharmony_ci while (*ref_ptr) 36962306a36Sopenharmony_ci ref_ptr = &(*ref_ptr)->next; 37062306a36Sopenharmony_ci /* Add new reference with offset +0 */ 37162306a36Sopenharmony_ci *ref_ptr = zalloc(sizeof(struct probe_trace_arg_ref)); 37262306a36Sopenharmony_ci if (*ref_ptr == NULL) { 37362306a36Sopenharmony_ci pr_warning("Out of memory error\n"); 37462306a36Sopenharmony_ci return -ENOMEM; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci (*ref_ptr)->user_access = user_access; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci if (!die_compare_name(&type, "char") && 37962306a36Sopenharmony_ci !die_compare_name(&type, "unsigned char")) { 38062306a36Sopenharmony_ci pr_warning("Failed to cast into string: " 38162306a36Sopenharmony_ci "%s is not (unsigned) char *.\n", 38262306a36Sopenharmony_ci dwarf_diename(vr_die)); 38362306a36Sopenharmony_ci return -EINVAL; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci tvar->type = strdup(cast); 38662306a36Sopenharmony_ci return (tvar->type == NULL) ? -ENOMEM : 0; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (cast && (strcmp(cast, "u") == 0)) 39062306a36Sopenharmony_ci prefix = 'u'; 39162306a36Sopenharmony_ci else if (cast && (strcmp(cast, "s") == 0)) 39262306a36Sopenharmony_ci prefix = 's'; 39362306a36Sopenharmony_ci else if (cast && (strcmp(cast, "x") == 0) && 39462306a36Sopenharmony_ci probe_type_is_available(PROBE_TYPE_X)) 39562306a36Sopenharmony_ci prefix = 'x'; 39662306a36Sopenharmony_ci else 39762306a36Sopenharmony_ci prefix = die_is_signed_type(&type) ? 's' : 39862306a36Sopenharmony_ci probe_type_is_available(PROBE_TYPE_X) ? 'x' : 'u'; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci ret = dwarf_bytesize(&type); 40162306a36Sopenharmony_ci if (ret <= 0) 40262306a36Sopenharmony_ci /* No size ... try to use default type */ 40362306a36Sopenharmony_ci return 0; 40462306a36Sopenharmony_ci ret = BYTES_TO_BITS(ret); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci /* Check the bitwidth */ 40762306a36Sopenharmony_ci if (ret > MAX_BASIC_TYPE_BITS) { 40862306a36Sopenharmony_ci pr_info("%s exceeds max-bitwidth. Cut down to %d bits.\n", 40962306a36Sopenharmony_ci dwarf_diename(&type), MAX_BASIC_TYPE_BITS); 41062306a36Sopenharmony_ci ret = MAX_BASIC_TYPE_BITS; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci ret = snprintf(buf, 16, "%c%d", prefix, ret); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ciformatted: 41562306a36Sopenharmony_ci if (ret < 0 || ret >= 16) { 41662306a36Sopenharmony_ci if (ret >= 16) 41762306a36Sopenharmony_ci ret = -E2BIG; 41862306a36Sopenharmony_ci pr_warning("Failed to convert variable type: %s\n", 41962306a36Sopenharmony_ci str_error_r(-ret, sbuf, sizeof(sbuf))); 42062306a36Sopenharmony_ci return ret; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci tvar->type = strdup(buf); 42362306a36Sopenharmony_ci if (tvar->type == NULL) 42462306a36Sopenharmony_ci return -ENOMEM; 42562306a36Sopenharmony_ci return 0; 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_cistatic int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, 42962306a36Sopenharmony_ci struct perf_probe_arg_field *field, 43062306a36Sopenharmony_ci struct probe_trace_arg_ref **ref_ptr, 43162306a36Sopenharmony_ci Dwarf_Die *die_mem, bool user_access) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci struct probe_trace_arg_ref *ref = *ref_ptr; 43462306a36Sopenharmony_ci Dwarf_Die type; 43562306a36Sopenharmony_ci Dwarf_Word offs; 43662306a36Sopenharmony_ci int ret, tag; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci pr_debug("converting %s in %s\n", field->name, varname); 43962306a36Sopenharmony_ci if (die_get_real_type(vr_die, &type) == NULL) { 44062306a36Sopenharmony_ci pr_warning("Failed to get the type of %s.\n", varname); 44162306a36Sopenharmony_ci return -ENOENT; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci pr_debug2("Var real type: %s (%x)\n", dwarf_diename(&type), 44462306a36Sopenharmony_ci (unsigned)dwarf_dieoffset(&type)); 44562306a36Sopenharmony_ci tag = dwarf_tag(&type); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if (field->name[0] == '[' && 44862306a36Sopenharmony_ci (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)) { 44962306a36Sopenharmony_ci /* Save original type for next field or type */ 45062306a36Sopenharmony_ci memcpy(die_mem, &type, sizeof(*die_mem)); 45162306a36Sopenharmony_ci /* Get the type of this array */ 45262306a36Sopenharmony_ci if (die_get_real_type(&type, &type) == NULL) { 45362306a36Sopenharmony_ci pr_warning("Failed to get the type of %s.\n", varname); 45462306a36Sopenharmony_ci return -ENOENT; 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci pr_debug2("Array real type: %s (%x)\n", dwarf_diename(&type), 45762306a36Sopenharmony_ci (unsigned)dwarf_dieoffset(&type)); 45862306a36Sopenharmony_ci if (tag == DW_TAG_pointer_type) { 45962306a36Sopenharmony_ci ref = zalloc(sizeof(struct probe_trace_arg_ref)); 46062306a36Sopenharmony_ci if (ref == NULL) 46162306a36Sopenharmony_ci return -ENOMEM; 46262306a36Sopenharmony_ci if (*ref_ptr) 46362306a36Sopenharmony_ci (*ref_ptr)->next = ref; 46462306a36Sopenharmony_ci else 46562306a36Sopenharmony_ci *ref_ptr = ref; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci ref->offset += dwarf_bytesize(&type) * field->index; 46862306a36Sopenharmony_ci ref->user_access = user_access; 46962306a36Sopenharmony_ci goto next; 47062306a36Sopenharmony_ci } else if (tag == DW_TAG_pointer_type) { 47162306a36Sopenharmony_ci /* Check the pointer and dereference */ 47262306a36Sopenharmony_ci if (!field->ref) { 47362306a36Sopenharmony_ci pr_err("Semantic error: %s must be referred by '->'\n", 47462306a36Sopenharmony_ci field->name); 47562306a36Sopenharmony_ci return -EINVAL; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci /* Get the type pointed by this pointer */ 47862306a36Sopenharmony_ci if (die_get_real_type(&type, &type) == NULL) { 47962306a36Sopenharmony_ci pr_warning("Failed to get the type of %s.\n", varname); 48062306a36Sopenharmony_ci return -ENOENT; 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci /* Verify it is a data structure */ 48362306a36Sopenharmony_ci tag = dwarf_tag(&type); 48462306a36Sopenharmony_ci if (tag != DW_TAG_structure_type && tag != DW_TAG_union_type) { 48562306a36Sopenharmony_ci pr_warning("%s is not a data structure nor a union.\n", 48662306a36Sopenharmony_ci varname); 48762306a36Sopenharmony_ci return -EINVAL; 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci ref = zalloc(sizeof(struct probe_trace_arg_ref)); 49162306a36Sopenharmony_ci if (ref == NULL) 49262306a36Sopenharmony_ci return -ENOMEM; 49362306a36Sopenharmony_ci if (*ref_ptr) 49462306a36Sopenharmony_ci (*ref_ptr)->next = ref; 49562306a36Sopenharmony_ci else 49662306a36Sopenharmony_ci *ref_ptr = ref; 49762306a36Sopenharmony_ci } else { 49862306a36Sopenharmony_ci /* Verify it is a data structure */ 49962306a36Sopenharmony_ci if (tag != DW_TAG_structure_type && tag != DW_TAG_union_type) { 50062306a36Sopenharmony_ci pr_warning("%s is not a data structure nor a union.\n", 50162306a36Sopenharmony_ci varname); 50262306a36Sopenharmony_ci return -EINVAL; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci if (field->name[0] == '[') { 50562306a36Sopenharmony_ci pr_err("Semantic error: %s is not a pointer" 50662306a36Sopenharmony_ci " nor array.\n", varname); 50762306a36Sopenharmony_ci return -EINVAL; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci /* While processing unnamed field, we don't care about this */ 51062306a36Sopenharmony_ci if (field->ref && dwarf_diename(vr_die)) { 51162306a36Sopenharmony_ci pr_err("Semantic error: %s must be referred by '.'\n", 51262306a36Sopenharmony_ci field->name); 51362306a36Sopenharmony_ci return -EINVAL; 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci if (!ref) { 51662306a36Sopenharmony_ci pr_warning("Structure on a register is not " 51762306a36Sopenharmony_ci "supported yet.\n"); 51862306a36Sopenharmony_ci return -ENOTSUP; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (die_find_member(&type, field->name, die_mem) == NULL) { 52362306a36Sopenharmony_ci pr_warning("%s(type:%s) has no member %s.\n", varname, 52462306a36Sopenharmony_ci dwarf_diename(&type), field->name); 52562306a36Sopenharmony_ci return -EINVAL; 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci /* Get the offset of the field */ 52962306a36Sopenharmony_ci if (tag == DW_TAG_union_type) { 53062306a36Sopenharmony_ci offs = 0; 53162306a36Sopenharmony_ci } else { 53262306a36Sopenharmony_ci ret = die_get_data_member_location(die_mem, &offs); 53362306a36Sopenharmony_ci if (ret < 0) { 53462306a36Sopenharmony_ci pr_warning("Failed to get the offset of %s.\n", 53562306a36Sopenharmony_ci field->name); 53662306a36Sopenharmony_ci return ret; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci ref->offset += (long)offs; 54062306a36Sopenharmony_ci ref->user_access = user_access; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci /* If this member is unnamed, we need to reuse this field */ 54362306a36Sopenharmony_ci if (!dwarf_diename(die_mem)) 54462306a36Sopenharmony_ci return convert_variable_fields(die_mem, varname, field, 54562306a36Sopenharmony_ci &ref, die_mem, user_access); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cinext: 54862306a36Sopenharmony_ci /* Converting next field */ 54962306a36Sopenharmony_ci if (field->next) 55062306a36Sopenharmony_ci return convert_variable_fields(die_mem, field->name, 55162306a36Sopenharmony_ci field->next, &ref, die_mem, user_access); 55262306a36Sopenharmony_ci else 55362306a36Sopenharmony_ci return 0; 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cistatic void print_var_not_found(const char *varname) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci pr_err("Failed to find the location of the '%s' variable at this address.\n" 55962306a36Sopenharmony_ci " Perhaps it has been optimized out.\n" 56062306a36Sopenharmony_ci " Use -V with the --range option to show '%s' location range.\n", 56162306a36Sopenharmony_ci varname, varname); 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci/* Show a variables in kprobe event format */ 56562306a36Sopenharmony_cistatic int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci Dwarf_Die die_mem; 56862306a36Sopenharmony_ci int ret; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci pr_debug("Converting variable %s into trace event.\n", 57162306a36Sopenharmony_ci dwarf_diename(vr_die)); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops, 57462306a36Sopenharmony_ci &pf->sp_die, pf->machine, pf->tvar); 57562306a36Sopenharmony_ci if (ret == -ENOENT && pf->skip_empty_arg) 57662306a36Sopenharmony_ci /* This can be found in other place. skip it */ 57762306a36Sopenharmony_ci return 0; 57862306a36Sopenharmony_ci if (ret == -ENOENT || ret == -EINVAL) { 57962306a36Sopenharmony_ci print_var_not_found(pf->pvar->var); 58062306a36Sopenharmony_ci } else if (ret == -ENOTSUP) 58162306a36Sopenharmony_ci pr_err("Sorry, we don't support this variable location yet.\n"); 58262306a36Sopenharmony_ci else if (ret == 0 && pf->pvar->field) { 58362306a36Sopenharmony_ci ret = convert_variable_fields(vr_die, pf->pvar->var, 58462306a36Sopenharmony_ci pf->pvar->field, &pf->tvar->ref, 58562306a36Sopenharmony_ci &die_mem, pf->pvar->user_access); 58662306a36Sopenharmony_ci vr_die = &die_mem; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci if (ret == 0) 58962306a36Sopenharmony_ci ret = convert_variable_type(vr_die, pf->tvar, pf->pvar->type, 59062306a36Sopenharmony_ci pf->pvar->user_access); 59162306a36Sopenharmony_ci /* *expr will be cached in libdw. Don't free it. */ 59262306a36Sopenharmony_ci return ret; 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci/* Find a variable in a scope DIE */ 59662306a36Sopenharmony_cistatic int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf) 59762306a36Sopenharmony_ci{ 59862306a36Sopenharmony_ci Dwarf_Die vr_die; 59962306a36Sopenharmony_ci char *buf, *ptr; 60062306a36Sopenharmony_ci int ret = 0; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci /* Copy raw parameters */ 60362306a36Sopenharmony_ci if (!is_c_varname(pf->pvar->var)) 60462306a36Sopenharmony_ci return copy_to_probe_trace_arg(pf->tvar, pf->pvar); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci if (pf->pvar->name) 60762306a36Sopenharmony_ci pf->tvar->name = strdup(pf->pvar->name); 60862306a36Sopenharmony_ci else { 60962306a36Sopenharmony_ci buf = synthesize_perf_probe_arg(pf->pvar); 61062306a36Sopenharmony_ci if (!buf) 61162306a36Sopenharmony_ci return -ENOMEM; 61262306a36Sopenharmony_ci ptr = strchr(buf, ':'); /* Change type separator to _ */ 61362306a36Sopenharmony_ci if (ptr) 61462306a36Sopenharmony_ci *ptr = '_'; 61562306a36Sopenharmony_ci pf->tvar->name = buf; 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci if (pf->tvar->name == NULL) 61862306a36Sopenharmony_ci return -ENOMEM; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci pr_debug("Searching '%s' variable in context.\n", pf->pvar->var); 62162306a36Sopenharmony_ci /* Search child die for local variables and parameters. */ 62262306a36Sopenharmony_ci if (!die_find_variable_at(sc_die, pf->pvar->var, pf->addr, &vr_die)) { 62362306a36Sopenharmony_ci /* Search again in global variables */ 62462306a36Sopenharmony_ci if (!die_find_variable_at(&pf->cu_die, pf->pvar->var, 62562306a36Sopenharmony_ci 0, &vr_die)) { 62662306a36Sopenharmony_ci if (pf->skip_empty_arg) 62762306a36Sopenharmony_ci return 0; 62862306a36Sopenharmony_ci pr_warning("Failed to find '%s' in this function.\n", 62962306a36Sopenharmony_ci pf->pvar->var); 63062306a36Sopenharmony_ci ret = -ENOENT; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci if (ret >= 0) 63462306a36Sopenharmony_ci ret = convert_variable(&vr_die, pf); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci return ret; 63762306a36Sopenharmony_ci} 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci/* Convert subprogram DIE to trace point */ 64062306a36Sopenharmony_cistatic int convert_to_trace_point(Dwarf_Die *sp_die, Dwfl_Module *mod, 64162306a36Sopenharmony_ci Dwarf_Addr paddr, bool retprobe, 64262306a36Sopenharmony_ci const char *function, 64362306a36Sopenharmony_ci struct probe_trace_point *tp) 64462306a36Sopenharmony_ci{ 64562306a36Sopenharmony_ci Dwarf_Addr eaddr; 64662306a36Sopenharmony_ci GElf_Sym sym; 64762306a36Sopenharmony_ci const char *symbol; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci /* Verify the address is correct */ 65062306a36Sopenharmony_ci if (!dwarf_haspc(sp_die, paddr)) { 65162306a36Sopenharmony_ci pr_warning("Specified offset is out of %s\n", 65262306a36Sopenharmony_ci dwarf_diename(sp_die)); 65362306a36Sopenharmony_ci return -EINVAL; 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci if (dwarf_entrypc(sp_die, &eaddr) == 0) { 65762306a36Sopenharmony_ci /* If the DIE has entrypc, use it. */ 65862306a36Sopenharmony_ci symbol = dwarf_diename(sp_die); 65962306a36Sopenharmony_ci } else { 66062306a36Sopenharmony_ci /* Try to get actual symbol name and address from symtab */ 66162306a36Sopenharmony_ci symbol = dwfl_module_addrsym(mod, paddr, &sym, NULL); 66262306a36Sopenharmony_ci eaddr = sym.st_value; 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci if (!symbol) { 66562306a36Sopenharmony_ci pr_warning("Failed to find symbol at 0x%lx\n", 66662306a36Sopenharmony_ci (unsigned long)paddr); 66762306a36Sopenharmony_ci return -ENOENT; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci tp->offset = (unsigned long)(paddr - eaddr); 67162306a36Sopenharmony_ci tp->address = paddr; 67262306a36Sopenharmony_ci tp->symbol = strdup(symbol); 67362306a36Sopenharmony_ci if (!tp->symbol) 67462306a36Sopenharmony_ci return -ENOMEM; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci /* Return probe must be on the head of a subprogram */ 67762306a36Sopenharmony_ci if (retprobe) { 67862306a36Sopenharmony_ci if (eaddr != paddr) { 67962306a36Sopenharmony_ci pr_warning("Failed to find \"%s%%return\",\n" 68062306a36Sopenharmony_ci " because %s is an inlined function and" 68162306a36Sopenharmony_ci " has no return point.\n", function, 68262306a36Sopenharmony_ci function); 68362306a36Sopenharmony_ci return -EINVAL; 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci tp->retprobe = true; 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci return 0; 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci/* Call probe_finder callback with scope DIE */ 69262306a36Sopenharmony_cistatic int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci Dwarf_Attribute fb_attr; 69562306a36Sopenharmony_ci Dwarf_Frame *frame = NULL; 69662306a36Sopenharmony_ci size_t nops; 69762306a36Sopenharmony_ci int ret; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci if (!sc_die) { 70062306a36Sopenharmony_ci pr_err("Caller must pass a scope DIE. Program error.\n"); 70162306a36Sopenharmony_ci return -EINVAL; 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci /* If not a real subprogram, find a real one */ 70562306a36Sopenharmony_ci if (!die_is_func_def(sc_die)) { 70662306a36Sopenharmony_ci if (!die_find_realfunc(&pf->cu_die, pf->addr, &pf->sp_die)) { 70762306a36Sopenharmony_ci if (die_find_tailfunc(&pf->cu_die, pf->addr, &pf->sp_die)) { 70862306a36Sopenharmony_ci pr_warning("Ignoring tail call from %s\n", 70962306a36Sopenharmony_ci dwarf_diename(&pf->sp_die)); 71062306a36Sopenharmony_ci return 0; 71162306a36Sopenharmony_ci } else { 71262306a36Sopenharmony_ci pr_warning("Failed to find probe point in any " 71362306a36Sopenharmony_ci "functions.\n"); 71462306a36Sopenharmony_ci return -ENOENT; 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci } else 71862306a36Sopenharmony_ci memcpy(&pf->sp_die, sc_die, sizeof(Dwarf_Die)); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci /* Get the frame base attribute/ops from subprogram */ 72162306a36Sopenharmony_ci dwarf_attr(&pf->sp_die, DW_AT_frame_base, &fb_attr); 72262306a36Sopenharmony_ci ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); 72362306a36Sopenharmony_ci if (ret <= 0 || nops == 0) { 72462306a36Sopenharmony_ci pf->fb_ops = NULL; 72562306a36Sopenharmony_ci#if _ELFUTILS_PREREQ(0, 142) 72662306a36Sopenharmony_ci } else if (nops == 1 && pf->fb_ops[0].atom == DW_OP_call_frame_cfa && 72762306a36Sopenharmony_ci (pf->cfi_eh != NULL || pf->cfi_dbg != NULL)) { 72862306a36Sopenharmony_ci if ((dwarf_cfi_addrframe(pf->cfi_eh, pf->addr, &frame) != 0 && 72962306a36Sopenharmony_ci (dwarf_cfi_addrframe(pf->cfi_dbg, pf->addr, &frame) != 0)) || 73062306a36Sopenharmony_ci dwarf_frame_cfa(frame, &pf->fb_ops, &nops) != 0) { 73162306a36Sopenharmony_ci pr_warning("Failed to get call frame on 0x%jx\n", 73262306a36Sopenharmony_ci (uintmax_t)pf->addr); 73362306a36Sopenharmony_ci free(frame); 73462306a36Sopenharmony_ci return -ENOENT; 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci#endif 73762306a36Sopenharmony_ci } 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci /* Call finder's callback handler */ 74062306a36Sopenharmony_ci ret = pf->callback(sc_die, pf); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci /* Since *pf->fb_ops can be a part of frame. we should free it here. */ 74362306a36Sopenharmony_ci free(frame); 74462306a36Sopenharmony_ci pf->fb_ops = NULL; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci return ret; 74762306a36Sopenharmony_ci} 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_cistruct find_scope_param { 75062306a36Sopenharmony_ci const char *function; 75162306a36Sopenharmony_ci const char *file; 75262306a36Sopenharmony_ci int line; 75362306a36Sopenharmony_ci int diff; 75462306a36Sopenharmony_ci Dwarf_Die *die_mem; 75562306a36Sopenharmony_ci bool found; 75662306a36Sopenharmony_ci}; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_cistatic int find_best_scope_cb(Dwarf_Die *fn_die, void *data) 75962306a36Sopenharmony_ci{ 76062306a36Sopenharmony_ci struct find_scope_param *fsp = data; 76162306a36Sopenharmony_ci const char *file; 76262306a36Sopenharmony_ci int lno; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci /* Skip if declared file name does not match */ 76562306a36Sopenharmony_ci if (fsp->file) { 76662306a36Sopenharmony_ci file = die_get_decl_file(fn_die); 76762306a36Sopenharmony_ci if (!file || strcmp(fsp->file, file) != 0) 76862306a36Sopenharmony_ci return 0; 76962306a36Sopenharmony_ci } 77062306a36Sopenharmony_ci /* If the function name is given, that's what user expects */ 77162306a36Sopenharmony_ci if (fsp->function) { 77262306a36Sopenharmony_ci if (die_match_name(fn_die, fsp->function)) { 77362306a36Sopenharmony_ci memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die)); 77462306a36Sopenharmony_ci fsp->found = true; 77562306a36Sopenharmony_ci return 1; 77662306a36Sopenharmony_ci } 77762306a36Sopenharmony_ci } else { 77862306a36Sopenharmony_ci /* With the line number, find the nearest declared DIE */ 77962306a36Sopenharmony_ci dwarf_decl_line(fn_die, &lno); 78062306a36Sopenharmony_ci if (lno < fsp->line && fsp->diff > fsp->line - lno) { 78162306a36Sopenharmony_ci /* Keep a candidate and continue */ 78262306a36Sopenharmony_ci fsp->diff = fsp->line - lno; 78362306a36Sopenharmony_ci memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die)); 78462306a36Sopenharmony_ci fsp->found = true; 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci return 0; 78862306a36Sopenharmony_ci} 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci/* Return innermost DIE */ 79162306a36Sopenharmony_cistatic int find_inner_scope_cb(Dwarf_Die *fn_die, void *data) 79262306a36Sopenharmony_ci{ 79362306a36Sopenharmony_ci struct find_scope_param *fsp = data; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die)); 79662306a36Sopenharmony_ci fsp->found = true; 79762306a36Sopenharmony_ci return 1; 79862306a36Sopenharmony_ci} 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci/* Find an appropriate scope fits to given conditions */ 80162306a36Sopenharmony_cistatic Dwarf_Die *find_best_scope(struct probe_finder *pf, Dwarf_Die *die_mem) 80262306a36Sopenharmony_ci{ 80362306a36Sopenharmony_ci struct find_scope_param fsp = { 80462306a36Sopenharmony_ci .function = pf->pev->point.function, 80562306a36Sopenharmony_ci .file = pf->fname, 80662306a36Sopenharmony_ci .line = pf->lno, 80762306a36Sopenharmony_ci .diff = INT_MAX, 80862306a36Sopenharmony_ci .die_mem = die_mem, 80962306a36Sopenharmony_ci .found = false, 81062306a36Sopenharmony_ci }; 81162306a36Sopenharmony_ci int ret; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci ret = cu_walk_functions_at(&pf->cu_die, pf->addr, find_best_scope_cb, 81462306a36Sopenharmony_ci &fsp); 81562306a36Sopenharmony_ci if (!ret && !fsp.found) 81662306a36Sopenharmony_ci cu_walk_functions_at(&pf->cu_die, pf->addr, 81762306a36Sopenharmony_ci find_inner_scope_cb, &fsp); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci return fsp.found ? die_mem : NULL; 82062306a36Sopenharmony_ci} 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_cistatic int verify_representive_line(struct probe_finder *pf, const char *fname, 82362306a36Sopenharmony_ci int lineno, Dwarf_Addr addr) 82462306a36Sopenharmony_ci{ 82562306a36Sopenharmony_ci const char *__fname, *__func = NULL; 82662306a36Sopenharmony_ci Dwarf_Die die_mem; 82762306a36Sopenharmony_ci int __lineno; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci /* Verify line number and address by reverse search */ 83062306a36Sopenharmony_ci if (cu_find_lineinfo(&pf->cu_die, addr, &__fname, &__lineno) < 0) 83162306a36Sopenharmony_ci return 0; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci pr_debug2("Reversed line: %s:%d\n", __fname, __lineno); 83462306a36Sopenharmony_ci if (strcmp(fname, __fname) || lineno == __lineno) 83562306a36Sopenharmony_ci return 0; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci pr_warning("This line is sharing the address with other lines.\n"); 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci if (pf->pev->point.function) { 84062306a36Sopenharmony_ci /* Find best match function name and lines */ 84162306a36Sopenharmony_ci pf->addr = addr; 84262306a36Sopenharmony_ci if (find_best_scope(pf, &die_mem) 84362306a36Sopenharmony_ci && die_match_name(&die_mem, pf->pev->point.function) 84462306a36Sopenharmony_ci && dwarf_decl_line(&die_mem, &lineno) == 0) { 84562306a36Sopenharmony_ci __func = dwarf_diename(&die_mem); 84662306a36Sopenharmony_ci __lineno -= lineno; 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci } 84962306a36Sopenharmony_ci pr_warning("Please try to probe at %s:%d instead.\n", 85062306a36Sopenharmony_ci __func ? : __fname, __lineno); 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci return -ENOENT; 85362306a36Sopenharmony_ci} 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_cistatic int probe_point_line_walker(const char *fname, int lineno, 85662306a36Sopenharmony_ci Dwarf_Addr addr, void *data) 85762306a36Sopenharmony_ci{ 85862306a36Sopenharmony_ci struct probe_finder *pf = data; 85962306a36Sopenharmony_ci Dwarf_Die *sc_die, die_mem; 86062306a36Sopenharmony_ci int ret; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0) 86362306a36Sopenharmony_ci return 0; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci if (verify_representive_line(pf, fname, lineno, addr)) 86662306a36Sopenharmony_ci return -ENOENT; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci pf->addr = addr; 86962306a36Sopenharmony_ci sc_die = find_best_scope(pf, &die_mem); 87062306a36Sopenharmony_ci if (!sc_die) { 87162306a36Sopenharmony_ci pr_warning("Failed to find scope of probe point.\n"); 87262306a36Sopenharmony_ci return -ENOENT; 87362306a36Sopenharmony_ci } 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci ret = call_probe_finder(sc_die, pf); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci /* Continue if no error, because the line will be in inline function */ 87862306a36Sopenharmony_ci return ret < 0 ? ret : 0; 87962306a36Sopenharmony_ci} 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci/* Find probe point from its line number */ 88262306a36Sopenharmony_cistatic int find_probe_point_by_line(struct probe_finder *pf) 88362306a36Sopenharmony_ci{ 88462306a36Sopenharmony_ci return die_walk_lines(&pf->cu_die, probe_point_line_walker, pf); 88562306a36Sopenharmony_ci} 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci/* Find lines which match lazy pattern */ 88862306a36Sopenharmony_cistatic int find_lazy_match_lines(struct intlist *list, 88962306a36Sopenharmony_ci const char *fname, const char *pat) 89062306a36Sopenharmony_ci{ 89162306a36Sopenharmony_ci FILE *fp; 89262306a36Sopenharmony_ci char *line = NULL; 89362306a36Sopenharmony_ci size_t line_len; 89462306a36Sopenharmony_ci ssize_t len; 89562306a36Sopenharmony_ci int count = 0, linenum = 1; 89662306a36Sopenharmony_ci char sbuf[STRERR_BUFSIZE]; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci fp = fopen(fname, "r"); 89962306a36Sopenharmony_ci if (!fp) { 90062306a36Sopenharmony_ci pr_warning("Failed to open %s: %s\n", fname, 90162306a36Sopenharmony_ci str_error_r(errno, sbuf, sizeof(sbuf))); 90262306a36Sopenharmony_ci return -errno; 90362306a36Sopenharmony_ci } 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci while ((len = getline(&line, &line_len, fp)) > 0) { 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci if (line[len - 1] == '\n') 90862306a36Sopenharmony_ci line[len - 1] = '\0'; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci if (strlazymatch(line, pat)) { 91162306a36Sopenharmony_ci intlist__add(list, linenum); 91262306a36Sopenharmony_ci count++; 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci linenum++; 91562306a36Sopenharmony_ci } 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci if (ferror(fp)) 91862306a36Sopenharmony_ci count = -errno; 91962306a36Sopenharmony_ci free(line); 92062306a36Sopenharmony_ci fclose(fp); 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci if (count == 0) 92362306a36Sopenharmony_ci pr_debug("No matched lines found in %s.\n", fname); 92462306a36Sopenharmony_ci return count; 92562306a36Sopenharmony_ci} 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_cistatic int probe_point_lazy_walker(const char *fname, int lineno, 92862306a36Sopenharmony_ci Dwarf_Addr addr, void *data) 92962306a36Sopenharmony_ci{ 93062306a36Sopenharmony_ci struct probe_finder *pf = data; 93162306a36Sopenharmony_ci Dwarf_Die *sc_die, die_mem; 93262306a36Sopenharmony_ci int ret; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci if (!intlist__has_entry(pf->lcache, lineno) || 93562306a36Sopenharmony_ci strtailcmp(fname, pf->fname) != 0) 93662306a36Sopenharmony_ci return 0; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci pr_debug("Probe line found: line:%d addr:0x%llx\n", 93962306a36Sopenharmony_ci lineno, (unsigned long long)addr); 94062306a36Sopenharmony_ci pf->addr = addr; 94162306a36Sopenharmony_ci pf->lno = lineno; 94262306a36Sopenharmony_ci sc_die = find_best_scope(pf, &die_mem); 94362306a36Sopenharmony_ci if (!sc_die) { 94462306a36Sopenharmony_ci pr_warning("Failed to find scope of probe point.\n"); 94562306a36Sopenharmony_ci return -ENOENT; 94662306a36Sopenharmony_ci } 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci ret = call_probe_finder(sc_die, pf); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci /* 95162306a36Sopenharmony_ci * Continue if no error, because the lazy pattern will match 95262306a36Sopenharmony_ci * to other lines 95362306a36Sopenharmony_ci */ 95462306a36Sopenharmony_ci return ret < 0 ? ret : 0; 95562306a36Sopenharmony_ci} 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci/* Find probe points from lazy pattern */ 95862306a36Sopenharmony_cistatic int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) 95962306a36Sopenharmony_ci{ 96062306a36Sopenharmony_ci struct build_id bid; 96162306a36Sopenharmony_ci char sbuild_id[SBUILD_ID_SIZE] = ""; 96262306a36Sopenharmony_ci int ret = 0; 96362306a36Sopenharmony_ci char *fpath; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci if (intlist__empty(pf->lcache)) { 96662306a36Sopenharmony_ci const char *comp_dir; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci comp_dir = cu_get_comp_dir(&pf->cu_die); 96962306a36Sopenharmony_ci if (pf->dbg->build_id) { 97062306a36Sopenharmony_ci build_id__init(&bid, pf->dbg->build_id, BUILD_ID_SIZE); 97162306a36Sopenharmony_ci build_id__sprintf(&bid, sbuild_id); 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci ret = find_source_path(pf->fname, sbuild_id, comp_dir, &fpath); 97462306a36Sopenharmony_ci if (ret < 0) { 97562306a36Sopenharmony_ci pr_warning("Failed to find source file path.\n"); 97662306a36Sopenharmony_ci return ret; 97762306a36Sopenharmony_ci } 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci /* Matching lazy line pattern */ 98062306a36Sopenharmony_ci ret = find_lazy_match_lines(pf->lcache, fpath, 98162306a36Sopenharmony_ci pf->pev->point.lazy_line); 98262306a36Sopenharmony_ci free(fpath); 98362306a36Sopenharmony_ci if (ret <= 0) 98462306a36Sopenharmony_ci return ret; 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci return die_walk_lines(sp_die, probe_point_lazy_walker, pf); 98862306a36Sopenharmony_ci} 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_cistatic void skip_prologue(Dwarf_Die *sp_die, struct probe_finder *pf) 99162306a36Sopenharmony_ci{ 99262306a36Sopenharmony_ci struct perf_probe_point *pp = &pf->pev->point; 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci /* Not uprobe? */ 99562306a36Sopenharmony_ci if (!pf->pev->uprobes) 99662306a36Sopenharmony_ci return; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci /* Compiled with optimization? */ 99962306a36Sopenharmony_ci if (die_is_optimized_target(&pf->cu_die)) 100062306a36Sopenharmony_ci return; 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci /* Don't know entrypc? */ 100362306a36Sopenharmony_ci if (!pf->addr) 100462306a36Sopenharmony_ci return; 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci /* Only FUNC and FUNC@SRC are eligible. */ 100762306a36Sopenharmony_ci if (!pp->function || pp->line || pp->retprobe || pp->lazy_line || 100862306a36Sopenharmony_ci pp->offset || pp->abs_address) 100962306a36Sopenharmony_ci return; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci /* Not interested in func parameter? */ 101262306a36Sopenharmony_ci if (!perf_probe_with_var(pf->pev)) 101362306a36Sopenharmony_ci return; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci pr_info("Target program is compiled without optimization. Skipping prologue.\n" 101662306a36Sopenharmony_ci "Probe on address 0x%" PRIx64 " to force probing at the function entry.\n\n", 101762306a36Sopenharmony_ci pf->addr); 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci die_skip_prologue(sp_die, &pf->cu_die, &pf->addr); 102062306a36Sopenharmony_ci} 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_cistatic int probe_point_inline_cb(Dwarf_Die *in_die, void *data) 102362306a36Sopenharmony_ci{ 102462306a36Sopenharmony_ci struct probe_finder *pf = data; 102562306a36Sopenharmony_ci struct perf_probe_point *pp = &pf->pev->point; 102662306a36Sopenharmony_ci Dwarf_Addr addr; 102762306a36Sopenharmony_ci int ret; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci if (pp->lazy_line) 103062306a36Sopenharmony_ci ret = find_probe_point_lazy(in_die, pf); 103162306a36Sopenharmony_ci else { 103262306a36Sopenharmony_ci /* Get probe address */ 103362306a36Sopenharmony_ci if (die_entrypc(in_die, &addr) != 0) { 103462306a36Sopenharmony_ci pr_warning("Failed to get entry address of %s.\n", 103562306a36Sopenharmony_ci dwarf_diename(in_die)); 103662306a36Sopenharmony_ci return -ENOENT; 103762306a36Sopenharmony_ci } 103862306a36Sopenharmony_ci if (addr == 0) { 103962306a36Sopenharmony_ci pr_debug("%s has no valid entry address. skipped.\n", 104062306a36Sopenharmony_ci dwarf_diename(in_die)); 104162306a36Sopenharmony_ci return -ENOENT; 104262306a36Sopenharmony_ci } 104362306a36Sopenharmony_ci pf->addr = addr; 104462306a36Sopenharmony_ci pf->addr += pp->offset; 104562306a36Sopenharmony_ci pr_debug("found inline addr: 0x%jx\n", 104662306a36Sopenharmony_ci (uintmax_t)pf->addr); 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci ret = call_probe_finder(in_die, pf); 104962306a36Sopenharmony_ci } 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci return ret; 105262306a36Sopenharmony_ci} 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci/* Callback parameter with return value for libdw */ 105562306a36Sopenharmony_cistruct dwarf_callback_param { 105662306a36Sopenharmony_ci void *data; 105762306a36Sopenharmony_ci int retval; 105862306a36Sopenharmony_ci}; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci/* Search function from function name */ 106162306a36Sopenharmony_cistatic int probe_point_search_cb(Dwarf_Die *sp_die, void *data) 106262306a36Sopenharmony_ci{ 106362306a36Sopenharmony_ci struct dwarf_callback_param *param = data; 106462306a36Sopenharmony_ci struct probe_finder *pf = param->data; 106562306a36Sopenharmony_ci struct perf_probe_point *pp = &pf->pev->point; 106662306a36Sopenharmony_ci const char *fname; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci /* Check tag and diename */ 106962306a36Sopenharmony_ci if (!die_is_func_def(sp_die) || 107062306a36Sopenharmony_ci !die_match_name(sp_die, pp->function)) 107162306a36Sopenharmony_ci return DWARF_CB_OK; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci /* Check declared file */ 107462306a36Sopenharmony_ci fname = die_get_decl_file(sp_die); 107562306a36Sopenharmony_ci if (!fname) { 107662306a36Sopenharmony_ci pr_warning("A function DIE doesn't have decl_line. Maybe broken DWARF?\n"); 107762306a36Sopenharmony_ci return DWARF_CB_OK; 107862306a36Sopenharmony_ci } 107962306a36Sopenharmony_ci if (pp->file && fname && strtailcmp(pp->file, fname)) 108062306a36Sopenharmony_ci return DWARF_CB_OK; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci pr_debug("Matched function: %s [%lx]\n", dwarf_diename(sp_die), 108362306a36Sopenharmony_ci (unsigned long)dwarf_dieoffset(sp_die)); 108462306a36Sopenharmony_ci pf->fname = fname; 108562306a36Sopenharmony_ci if (pp->line) { /* Function relative line */ 108662306a36Sopenharmony_ci dwarf_decl_line(sp_die, &pf->lno); 108762306a36Sopenharmony_ci pf->lno += pp->line; 108862306a36Sopenharmony_ci param->retval = find_probe_point_by_line(pf); 108962306a36Sopenharmony_ci } else if (die_is_func_instance(sp_die)) { 109062306a36Sopenharmony_ci /* Instances always have the entry address */ 109162306a36Sopenharmony_ci die_entrypc(sp_die, &pf->addr); 109262306a36Sopenharmony_ci /* But in some case the entry address is 0 */ 109362306a36Sopenharmony_ci if (pf->addr == 0) { 109462306a36Sopenharmony_ci pr_debug("%s has no entry PC. Skipped\n", 109562306a36Sopenharmony_ci dwarf_diename(sp_die)); 109662306a36Sopenharmony_ci param->retval = 0; 109762306a36Sopenharmony_ci /* Real function */ 109862306a36Sopenharmony_ci } else if (pp->lazy_line) 109962306a36Sopenharmony_ci param->retval = find_probe_point_lazy(sp_die, pf); 110062306a36Sopenharmony_ci else { 110162306a36Sopenharmony_ci skip_prologue(sp_die, pf); 110262306a36Sopenharmony_ci pf->addr += pp->offset; 110362306a36Sopenharmony_ci /* TODO: Check the address in this function */ 110462306a36Sopenharmony_ci param->retval = call_probe_finder(sp_die, pf); 110562306a36Sopenharmony_ci } 110662306a36Sopenharmony_ci } else if (!probe_conf.no_inlines) { 110762306a36Sopenharmony_ci /* Inlined function: search instances */ 110862306a36Sopenharmony_ci param->retval = die_walk_instances(sp_die, 110962306a36Sopenharmony_ci probe_point_inline_cb, (void *)pf); 111062306a36Sopenharmony_ci /* This could be a non-existed inline definition */ 111162306a36Sopenharmony_ci if (param->retval == -ENOENT) 111262306a36Sopenharmony_ci param->retval = 0; 111362306a36Sopenharmony_ci } 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci /* We need to find other candidates */ 111662306a36Sopenharmony_ci if (strisglob(pp->function) && param->retval >= 0) { 111762306a36Sopenharmony_ci param->retval = 0; /* We have to clear the result */ 111862306a36Sopenharmony_ci return DWARF_CB_OK; 111962306a36Sopenharmony_ci } 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci return DWARF_CB_ABORT; /* Exit; no same symbol in this CU. */ 112262306a36Sopenharmony_ci} 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_cistatic int find_probe_point_by_func(struct probe_finder *pf) 112562306a36Sopenharmony_ci{ 112662306a36Sopenharmony_ci struct dwarf_callback_param _param = {.data = (void *)pf, 112762306a36Sopenharmony_ci .retval = 0}; 112862306a36Sopenharmony_ci dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, &_param, 0); 112962306a36Sopenharmony_ci return _param.retval; 113062306a36Sopenharmony_ci} 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_cistruct pubname_callback_param { 113362306a36Sopenharmony_ci char *function; 113462306a36Sopenharmony_ci char *file; 113562306a36Sopenharmony_ci Dwarf_Die *cu_die; 113662306a36Sopenharmony_ci Dwarf_Die *sp_die; 113762306a36Sopenharmony_ci int found; 113862306a36Sopenharmony_ci}; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_cistatic int pubname_search_cb(Dwarf *dbg, Dwarf_Global *gl, void *data) 114162306a36Sopenharmony_ci{ 114262306a36Sopenharmony_ci struct pubname_callback_param *param = data; 114362306a36Sopenharmony_ci const char *fname; 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci if (dwarf_offdie(dbg, gl->die_offset, param->sp_die)) { 114662306a36Sopenharmony_ci if (dwarf_tag(param->sp_die) != DW_TAG_subprogram) 114762306a36Sopenharmony_ci return DWARF_CB_OK; 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci if (die_match_name(param->sp_die, param->function)) { 115062306a36Sopenharmony_ci if (!dwarf_offdie(dbg, gl->cu_offset, param->cu_die)) 115162306a36Sopenharmony_ci return DWARF_CB_OK; 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci if (param->file) { 115462306a36Sopenharmony_ci fname = die_get_decl_file(param->sp_die); 115562306a36Sopenharmony_ci if (!fname || strtailcmp(param->file, fname)) 115662306a36Sopenharmony_ci return DWARF_CB_OK; 115762306a36Sopenharmony_ci } 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci param->found = 1; 116062306a36Sopenharmony_ci return DWARF_CB_ABORT; 116162306a36Sopenharmony_ci } 116262306a36Sopenharmony_ci } 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci return DWARF_CB_OK; 116562306a36Sopenharmony_ci} 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_cistatic int debuginfo__find_probe_location(struct debuginfo *dbg, 116862306a36Sopenharmony_ci struct probe_finder *pf) 116962306a36Sopenharmony_ci{ 117062306a36Sopenharmony_ci struct perf_probe_point *pp = &pf->pev->point; 117162306a36Sopenharmony_ci Dwarf_Off off, noff; 117262306a36Sopenharmony_ci size_t cuhl; 117362306a36Sopenharmony_ci Dwarf_Die *diep; 117462306a36Sopenharmony_ci int ret = 0; 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci off = 0; 117762306a36Sopenharmony_ci pf->lcache = intlist__new(NULL); 117862306a36Sopenharmony_ci if (!pf->lcache) 117962306a36Sopenharmony_ci return -ENOMEM; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci /* Fastpath: lookup by function name from .debug_pubnames section */ 118262306a36Sopenharmony_ci if (pp->function && !strisglob(pp->function)) { 118362306a36Sopenharmony_ci struct pubname_callback_param pubname_param = { 118462306a36Sopenharmony_ci .function = pp->function, 118562306a36Sopenharmony_ci .file = pp->file, 118662306a36Sopenharmony_ci .cu_die = &pf->cu_die, 118762306a36Sopenharmony_ci .sp_die = &pf->sp_die, 118862306a36Sopenharmony_ci .found = 0, 118962306a36Sopenharmony_ci }; 119062306a36Sopenharmony_ci struct dwarf_callback_param probe_param = { 119162306a36Sopenharmony_ci .data = pf, 119262306a36Sopenharmony_ci }; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci dwarf_getpubnames(dbg->dbg, pubname_search_cb, 119562306a36Sopenharmony_ci &pubname_param, 0); 119662306a36Sopenharmony_ci if (pubname_param.found) { 119762306a36Sopenharmony_ci ret = probe_point_search_cb(&pf->sp_die, &probe_param); 119862306a36Sopenharmony_ci if (ret) 119962306a36Sopenharmony_ci goto found; 120062306a36Sopenharmony_ci } 120162306a36Sopenharmony_ci } 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci /* Loop on CUs (Compilation Unit) */ 120462306a36Sopenharmony_ci while (!dwarf_nextcu(dbg->dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { 120562306a36Sopenharmony_ci /* Get the DIE(Debugging Information Entry) of this CU */ 120662306a36Sopenharmony_ci diep = dwarf_offdie(dbg->dbg, off + cuhl, &pf->cu_die); 120762306a36Sopenharmony_ci if (!diep) { 120862306a36Sopenharmony_ci off = noff; 120962306a36Sopenharmony_ci continue; 121062306a36Sopenharmony_ci } 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci /* Check if target file is included. */ 121362306a36Sopenharmony_ci if (pp->file) 121462306a36Sopenharmony_ci pf->fname = cu_find_realpath(&pf->cu_die, pp->file); 121562306a36Sopenharmony_ci else 121662306a36Sopenharmony_ci pf->fname = NULL; 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci if (!pp->file || pf->fname) { 121962306a36Sopenharmony_ci if (pp->function) 122062306a36Sopenharmony_ci ret = find_probe_point_by_func(pf); 122162306a36Sopenharmony_ci else if (pp->lazy_line) 122262306a36Sopenharmony_ci ret = find_probe_point_lazy(&pf->cu_die, pf); 122362306a36Sopenharmony_ci else { 122462306a36Sopenharmony_ci pf->lno = pp->line; 122562306a36Sopenharmony_ci ret = find_probe_point_by_line(pf); 122662306a36Sopenharmony_ci } 122762306a36Sopenharmony_ci if (ret < 0) 122862306a36Sopenharmony_ci break; 122962306a36Sopenharmony_ci } 123062306a36Sopenharmony_ci off = noff; 123162306a36Sopenharmony_ci } 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_cifound: 123462306a36Sopenharmony_ci intlist__delete(pf->lcache); 123562306a36Sopenharmony_ci pf->lcache = NULL; 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci return ret; 123862306a36Sopenharmony_ci} 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci/* Find probe points from debuginfo */ 124162306a36Sopenharmony_cistatic int debuginfo__find_probes(struct debuginfo *dbg, 124262306a36Sopenharmony_ci struct probe_finder *pf) 124362306a36Sopenharmony_ci{ 124462306a36Sopenharmony_ci int ret = 0; 124562306a36Sopenharmony_ci Elf *elf; 124662306a36Sopenharmony_ci GElf_Ehdr ehdr; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci if (pf->cfi_eh || pf->cfi_dbg) 124962306a36Sopenharmony_ci return debuginfo__find_probe_location(dbg, pf); 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci /* Get the call frame information from this dwarf */ 125262306a36Sopenharmony_ci elf = dwarf_getelf(dbg->dbg); 125362306a36Sopenharmony_ci if (elf == NULL) 125462306a36Sopenharmony_ci return -EINVAL; 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci if (gelf_getehdr(elf, &ehdr) == NULL) 125762306a36Sopenharmony_ci return -EINVAL; 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci pf->machine = ehdr.e_machine; 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci#if _ELFUTILS_PREREQ(0, 142) 126262306a36Sopenharmony_ci do { 126362306a36Sopenharmony_ci GElf_Shdr shdr; 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci if (elf_section_by_name(elf, &ehdr, &shdr, ".eh_frame", NULL) && 126662306a36Sopenharmony_ci shdr.sh_type == SHT_PROGBITS) 126762306a36Sopenharmony_ci pf->cfi_eh = dwarf_getcfi_elf(elf); 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci pf->cfi_dbg = dwarf_getcfi(dbg->dbg); 127062306a36Sopenharmony_ci } while (0); 127162306a36Sopenharmony_ci#endif 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci ret = debuginfo__find_probe_location(dbg, pf); 127462306a36Sopenharmony_ci return ret; 127562306a36Sopenharmony_ci} 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_cistruct local_vars_finder { 127862306a36Sopenharmony_ci struct probe_finder *pf; 127962306a36Sopenharmony_ci struct perf_probe_arg *args; 128062306a36Sopenharmony_ci bool vars; 128162306a36Sopenharmony_ci int max_args; 128262306a36Sopenharmony_ci int nargs; 128362306a36Sopenharmony_ci int ret; 128462306a36Sopenharmony_ci}; 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci/* Collect available variables in this scope */ 128762306a36Sopenharmony_cistatic int copy_variables_cb(Dwarf_Die *die_mem, void *data) 128862306a36Sopenharmony_ci{ 128962306a36Sopenharmony_ci struct local_vars_finder *vf = data; 129062306a36Sopenharmony_ci struct probe_finder *pf = vf->pf; 129162306a36Sopenharmony_ci int tag; 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci tag = dwarf_tag(die_mem); 129462306a36Sopenharmony_ci if (tag == DW_TAG_formal_parameter || 129562306a36Sopenharmony_ci (tag == DW_TAG_variable && vf->vars)) { 129662306a36Sopenharmony_ci if (convert_variable_location(die_mem, vf->pf->addr, 129762306a36Sopenharmony_ci vf->pf->fb_ops, &pf->sp_die, 129862306a36Sopenharmony_ci pf->machine, NULL) == 0) { 129962306a36Sopenharmony_ci vf->args[vf->nargs].var = (char *)dwarf_diename(die_mem); 130062306a36Sopenharmony_ci if (vf->args[vf->nargs].var == NULL) { 130162306a36Sopenharmony_ci vf->ret = -ENOMEM; 130262306a36Sopenharmony_ci return DIE_FIND_CB_END; 130362306a36Sopenharmony_ci } 130462306a36Sopenharmony_ci pr_debug(" %s", vf->args[vf->nargs].var); 130562306a36Sopenharmony_ci vf->nargs++; 130662306a36Sopenharmony_ci } 130762306a36Sopenharmony_ci } 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci if (dwarf_haspc(die_mem, vf->pf->addr)) 131062306a36Sopenharmony_ci return DIE_FIND_CB_CONTINUE; 131162306a36Sopenharmony_ci else 131262306a36Sopenharmony_ci return DIE_FIND_CB_SIBLING; 131362306a36Sopenharmony_ci} 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_cistatic int expand_probe_args(Dwarf_Die *sc_die, struct probe_finder *pf, 131662306a36Sopenharmony_ci struct perf_probe_arg *args) 131762306a36Sopenharmony_ci{ 131862306a36Sopenharmony_ci Dwarf_Die die_mem; 131962306a36Sopenharmony_ci int i; 132062306a36Sopenharmony_ci int n = 0; 132162306a36Sopenharmony_ci struct local_vars_finder vf = {.pf = pf, .args = args, .vars = false, 132262306a36Sopenharmony_ci .max_args = MAX_PROBE_ARGS, .ret = 0}; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci for (i = 0; i < pf->pev->nargs; i++) { 132562306a36Sopenharmony_ci /* var never be NULL */ 132662306a36Sopenharmony_ci if (strcmp(pf->pev->args[i].var, PROBE_ARG_VARS) == 0) 132762306a36Sopenharmony_ci vf.vars = true; 132862306a36Sopenharmony_ci else if (strcmp(pf->pev->args[i].var, PROBE_ARG_PARAMS) != 0) { 132962306a36Sopenharmony_ci /* Copy normal argument */ 133062306a36Sopenharmony_ci args[n] = pf->pev->args[i]; 133162306a36Sopenharmony_ci n++; 133262306a36Sopenharmony_ci continue; 133362306a36Sopenharmony_ci } 133462306a36Sopenharmony_ci pr_debug("Expanding %s into:", pf->pev->args[i].var); 133562306a36Sopenharmony_ci vf.nargs = n; 133662306a36Sopenharmony_ci /* Special local variables */ 133762306a36Sopenharmony_ci die_find_child(sc_die, copy_variables_cb, (void *)&vf, 133862306a36Sopenharmony_ci &die_mem); 133962306a36Sopenharmony_ci pr_debug(" (%d)\n", vf.nargs - n); 134062306a36Sopenharmony_ci if (vf.ret < 0) 134162306a36Sopenharmony_ci return vf.ret; 134262306a36Sopenharmony_ci n = vf.nargs; 134362306a36Sopenharmony_ci } 134462306a36Sopenharmony_ci return n; 134562306a36Sopenharmony_ci} 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_cistatic bool trace_event_finder_overlap(struct trace_event_finder *tf) 134862306a36Sopenharmony_ci{ 134962306a36Sopenharmony_ci int i; 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci for (i = 0; i < tf->ntevs; i++) { 135262306a36Sopenharmony_ci if (tf->pf.addr == tf->tevs[i].point.address) 135362306a36Sopenharmony_ci return true; 135462306a36Sopenharmony_ci } 135562306a36Sopenharmony_ci return false; 135662306a36Sopenharmony_ci} 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci/* Add a found probe point into trace event list */ 135962306a36Sopenharmony_cistatic int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) 136062306a36Sopenharmony_ci{ 136162306a36Sopenharmony_ci struct trace_event_finder *tf = 136262306a36Sopenharmony_ci container_of(pf, struct trace_event_finder, pf); 136362306a36Sopenharmony_ci struct perf_probe_point *pp = &pf->pev->point; 136462306a36Sopenharmony_ci struct probe_trace_event *tev; 136562306a36Sopenharmony_ci struct perf_probe_arg *args = NULL; 136662306a36Sopenharmony_ci int ret, i; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci /* 136962306a36Sopenharmony_ci * For some reason (e.g. different column assigned to same address) 137062306a36Sopenharmony_ci * This callback can be called with the address which already passed. 137162306a36Sopenharmony_ci * Ignore it first. 137262306a36Sopenharmony_ci */ 137362306a36Sopenharmony_ci if (trace_event_finder_overlap(tf)) 137462306a36Sopenharmony_ci return 0; 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci /* Check number of tevs */ 137762306a36Sopenharmony_ci if (tf->ntevs == tf->max_tevs) { 137862306a36Sopenharmony_ci pr_warning("Too many( > %d) probe point found.\n", 137962306a36Sopenharmony_ci tf->max_tevs); 138062306a36Sopenharmony_ci return -ERANGE; 138162306a36Sopenharmony_ci } 138262306a36Sopenharmony_ci tev = &tf->tevs[tf->ntevs++]; 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci /* Trace point should be converted from subprogram DIE */ 138562306a36Sopenharmony_ci ret = convert_to_trace_point(&pf->sp_die, tf->mod, pf->addr, 138662306a36Sopenharmony_ci pp->retprobe, pp->function, &tev->point); 138762306a36Sopenharmony_ci if (ret < 0) 138862306a36Sopenharmony_ci goto end; 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci tev->point.realname = strdup(dwarf_diename(sc_die)); 139162306a36Sopenharmony_ci if (!tev->point.realname) { 139262306a36Sopenharmony_ci ret = -ENOMEM; 139362306a36Sopenharmony_ci goto end; 139462306a36Sopenharmony_ci } 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci pr_debug("Probe point found: %s+%lu\n", tev->point.symbol, 139762306a36Sopenharmony_ci tev->point.offset); 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_ci /* Expand special probe argument if exist */ 140062306a36Sopenharmony_ci args = zalloc(sizeof(struct perf_probe_arg) * MAX_PROBE_ARGS); 140162306a36Sopenharmony_ci if (args == NULL) { 140262306a36Sopenharmony_ci ret = -ENOMEM; 140362306a36Sopenharmony_ci goto end; 140462306a36Sopenharmony_ci } 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci ret = expand_probe_args(sc_die, pf, args); 140762306a36Sopenharmony_ci if (ret < 0) 140862306a36Sopenharmony_ci goto end; 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci tev->nargs = ret; 141162306a36Sopenharmony_ci tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); 141262306a36Sopenharmony_ci if (tev->args == NULL) { 141362306a36Sopenharmony_ci ret = -ENOMEM; 141462306a36Sopenharmony_ci goto end; 141562306a36Sopenharmony_ci } 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci /* Find each argument */ 141862306a36Sopenharmony_ci for (i = 0; i < tev->nargs; i++) { 141962306a36Sopenharmony_ci pf->pvar = &args[i]; 142062306a36Sopenharmony_ci pf->tvar = &tev->args[i]; 142162306a36Sopenharmony_ci /* Variable should be found from scope DIE */ 142262306a36Sopenharmony_ci ret = find_variable(sc_die, pf); 142362306a36Sopenharmony_ci if (ret != 0) 142462306a36Sopenharmony_ci break; 142562306a36Sopenharmony_ci } 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ciend: 142862306a36Sopenharmony_ci if (ret) { 142962306a36Sopenharmony_ci clear_probe_trace_event(tev); 143062306a36Sopenharmony_ci tf->ntevs--; 143162306a36Sopenharmony_ci } 143262306a36Sopenharmony_ci free(args); 143362306a36Sopenharmony_ci return ret; 143462306a36Sopenharmony_ci} 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_cistatic int fill_empty_trace_arg(struct perf_probe_event *pev, 143762306a36Sopenharmony_ci struct probe_trace_event *tevs, int ntevs) 143862306a36Sopenharmony_ci{ 143962306a36Sopenharmony_ci char **valp; 144062306a36Sopenharmony_ci char *type; 144162306a36Sopenharmony_ci int i, j, ret; 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci if (!ntevs) 144462306a36Sopenharmony_ci return -ENOENT; 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci for (i = 0; i < pev->nargs; i++) { 144762306a36Sopenharmony_ci type = NULL; 144862306a36Sopenharmony_ci for (j = 0; j < ntevs; j++) { 144962306a36Sopenharmony_ci if (tevs[j].args[i].value) { 145062306a36Sopenharmony_ci type = tevs[j].args[i].type; 145162306a36Sopenharmony_ci break; 145262306a36Sopenharmony_ci } 145362306a36Sopenharmony_ci } 145462306a36Sopenharmony_ci if (j == ntevs) { 145562306a36Sopenharmony_ci print_var_not_found(pev->args[i].var); 145662306a36Sopenharmony_ci return -ENOENT; 145762306a36Sopenharmony_ci } 145862306a36Sopenharmony_ci for (j = 0; j < ntevs; j++) { 145962306a36Sopenharmony_ci valp = &tevs[j].args[i].value; 146062306a36Sopenharmony_ci if (*valp) 146162306a36Sopenharmony_ci continue; 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci ret = asprintf(valp, "\\%lx", probe_conf.magic_num); 146462306a36Sopenharmony_ci if (ret < 0) 146562306a36Sopenharmony_ci return -ENOMEM; 146662306a36Sopenharmony_ci /* Note that type can be NULL */ 146762306a36Sopenharmony_ci if (type) { 146862306a36Sopenharmony_ci tevs[j].args[i].type = strdup(type); 146962306a36Sopenharmony_ci if (!tevs[j].args[i].type) 147062306a36Sopenharmony_ci return -ENOMEM; 147162306a36Sopenharmony_ci } 147262306a36Sopenharmony_ci } 147362306a36Sopenharmony_ci } 147462306a36Sopenharmony_ci return 0; 147562306a36Sopenharmony_ci} 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci/* Find probe_trace_events specified by perf_probe_event from debuginfo */ 147862306a36Sopenharmony_ciint debuginfo__find_trace_events(struct debuginfo *dbg, 147962306a36Sopenharmony_ci struct perf_probe_event *pev, 148062306a36Sopenharmony_ci struct probe_trace_event **tevs) 148162306a36Sopenharmony_ci{ 148262306a36Sopenharmony_ci struct trace_event_finder tf = { 148362306a36Sopenharmony_ci .pf = {.pev = pev, .dbg = dbg, .callback = add_probe_trace_event}, 148462306a36Sopenharmony_ci .max_tevs = probe_conf.max_probes, .mod = dbg->mod}; 148562306a36Sopenharmony_ci int ret, i; 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci /* Allocate result tevs array */ 148862306a36Sopenharmony_ci *tevs = zalloc(sizeof(struct probe_trace_event) * tf.max_tevs); 148962306a36Sopenharmony_ci if (*tevs == NULL) 149062306a36Sopenharmony_ci return -ENOMEM; 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci tf.tevs = *tevs; 149362306a36Sopenharmony_ci tf.ntevs = 0; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci if (pev->nargs != 0 && immediate_value_is_supported()) 149662306a36Sopenharmony_ci tf.pf.skip_empty_arg = true; 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci ret = debuginfo__find_probes(dbg, &tf.pf); 149962306a36Sopenharmony_ci if (ret >= 0 && tf.pf.skip_empty_arg) 150062306a36Sopenharmony_ci ret = fill_empty_trace_arg(pev, tf.tevs, tf.ntevs); 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci if (ret < 0 || tf.ntevs == 0) { 150362306a36Sopenharmony_ci for (i = 0; i < tf.ntevs; i++) 150462306a36Sopenharmony_ci clear_probe_trace_event(&tf.tevs[i]); 150562306a36Sopenharmony_ci zfree(tevs); 150662306a36Sopenharmony_ci return ret; 150762306a36Sopenharmony_ci } 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci return (ret < 0) ? ret : tf.ntevs; 151062306a36Sopenharmony_ci} 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci/* Collect available variables in this scope */ 151362306a36Sopenharmony_cistatic int collect_variables_cb(Dwarf_Die *die_mem, void *data) 151462306a36Sopenharmony_ci{ 151562306a36Sopenharmony_ci struct available_var_finder *af = data; 151662306a36Sopenharmony_ci struct variable_list *vl; 151762306a36Sopenharmony_ci struct strbuf buf = STRBUF_INIT; 151862306a36Sopenharmony_ci int tag, ret; 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci vl = &af->vls[af->nvls - 1]; 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci tag = dwarf_tag(die_mem); 152362306a36Sopenharmony_ci if (tag == DW_TAG_formal_parameter || 152462306a36Sopenharmony_ci tag == DW_TAG_variable) { 152562306a36Sopenharmony_ci ret = convert_variable_location(die_mem, af->pf.addr, 152662306a36Sopenharmony_ci af->pf.fb_ops, &af->pf.sp_die, 152762306a36Sopenharmony_ci af->pf.machine, NULL); 152862306a36Sopenharmony_ci if (ret == 0 || ret == -ERANGE) { 152962306a36Sopenharmony_ci int ret2; 153062306a36Sopenharmony_ci bool externs = !af->child; 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci if (strbuf_init(&buf, 64) < 0) 153362306a36Sopenharmony_ci goto error; 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci if (probe_conf.show_location_range) { 153662306a36Sopenharmony_ci if (!externs) 153762306a36Sopenharmony_ci ret2 = strbuf_add(&buf, 153862306a36Sopenharmony_ci ret ? "[INV]\t" : "[VAL]\t", 6); 153962306a36Sopenharmony_ci else 154062306a36Sopenharmony_ci ret2 = strbuf_add(&buf, "[EXT]\t", 6); 154162306a36Sopenharmony_ci if (ret2) 154262306a36Sopenharmony_ci goto error; 154362306a36Sopenharmony_ci } 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci ret2 = die_get_varname(die_mem, &buf); 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci if (!ret2 && probe_conf.show_location_range && 154862306a36Sopenharmony_ci !externs) { 154962306a36Sopenharmony_ci if (strbuf_addch(&buf, '\t') < 0) 155062306a36Sopenharmony_ci goto error; 155162306a36Sopenharmony_ci ret2 = die_get_var_range(&af->pf.sp_die, 155262306a36Sopenharmony_ci die_mem, &buf); 155362306a36Sopenharmony_ci } 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci pr_debug("Add new var: %s\n", buf.buf); 155662306a36Sopenharmony_ci if (ret2 == 0) { 155762306a36Sopenharmony_ci strlist__add(vl->vars, 155862306a36Sopenharmony_ci strbuf_detach(&buf, NULL)); 155962306a36Sopenharmony_ci } 156062306a36Sopenharmony_ci strbuf_release(&buf); 156162306a36Sopenharmony_ci } 156262306a36Sopenharmony_ci } 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci if (af->child && dwarf_haspc(die_mem, af->pf.addr)) 156562306a36Sopenharmony_ci return DIE_FIND_CB_CONTINUE; 156662306a36Sopenharmony_ci else 156762306a36Sopenharmony_ci return DIE_FIND_CB_SIBLING; 156862306a36Sopenharmony_cierror: 156962306a36Sopenharmony_ci strbuf_release(&buf); 157062306a36Sopenharmony_ci pr_debug("Error in strbuf\n"); 157162306a36Sopenharmony_ci return DIE_FIND_CB_END; 157262306a36Sopenharmony_ci} 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_cistatic bool available_var_finder_overlap(struct available_var_finder *af) 157562306a36Sopenharmony_ci{ 157662306a36Sopenharmony_ci int i; 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_ci for (i = 0; i < af->nvls; i++) { 157962306a36Sopenharmony_ci if (af->pf.addr == af->vls[i].point.address) 158062306a36Sopenharmony_ci return true; 158162306a36Sopenharmony_ci } 158262306a36Sopenharmony_ci return false; 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci} 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci/* Add a found vars into available variables list */ 158762306a36Sopenharmony_cistatic int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf) 158862306a36Sopenharmony_ci{ 158962306a36Sopenharmony_ci struct available_var_finder *af = 159062306a36Sopenharmony_ci container_of(pf, struct available_var_finder, pf); 159162306a36Sopenharmony_ci struct perf_probe_point *pp = &pf->pev->point; 159262306a36Sopenharmony_ci struct variable_list *vl; 159362306a36Sopenharmony_ci Dwarf_Die die_mem; 159462306a36Sopenharmony_ci int ret; 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci /* 159762306a36Sopenharmony_ci * For some reason (e.g. different column assigned to same address), 159862306a36Sopenharmony_ci * this callback can be called with the address which already passed. 159962306a36Sopenharmony_ci * Ignore it first. 160062306a36Sopenharmony_ci */ 160162306a36Sopenharmony_ci if (available_var_finder_overlap(af)) 160262306a36Sopenharmony_ci return 0; 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci /* Check number of tevs */ 160562306a36Sopenharmony_ci if (af->nvls == af->max_vls) { 160662306a36Sopenharmony_ci pr_warning("Too many( > %d) probe point found.\n", af->max_vls); 160762306a36Sopenharmony_ci return -ERANGE; 160862306a36Sopenharmony_ci } 160962306a36Sopenharmony_ci vl = &af->vls[af->nvls++]; 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci /* Trace point should be converted from subprogram DIE */ 161262306a36Sopenharmony_ci ret = convert_to_trace_point(&pf->sp_die, af->mod, pf->addr, 161362306a36Sopenharmony_ci pp->retprobe, pp->function, &vl->point); 161462306a36Sopenharmony_ci if (ret < 0) 161562306a36Sopenharmony_ci return ret; 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci pr_debug("Probe point found: %s+%lu\n", vl->point.symbol, 161862306a36Sopenharmony_ci vl->point.offset); 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci /* Find local variables */ 162162306a36Sopenharmony_ci vl->vars = strlist__new(NULL, NULL); 162262306a36Sopenharmony_ci if (vl->vars == NULL) 162362306a36Sopenharmony_ci return -ENOMEM; 162462306a36Sopenharmony_ci af->child = true; 162562306a36Sopenharmony_ci die_find_child(sc_die, collect_variables_cb, (void *)af, &die_mem); 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci /* Find external variables */ 162862306a36Sopenharmony_ci if (!probe_conf.show_ext_vars) 162962306a36Sopenharmony_ci goto out; 163062306a36Sopenharmony_ci /* Don't need to search child DIE for external vars. */ 163162306a36Sopenharmony_ci af->child = false; 163262306a36Sopenharmony_ci die_find_child(&pf->cu_die, collect_variables_cb, (void *)af, &die_mem); 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ciout: 163562306a36Sopenharmony_ci if (strlist__empty(vl->vars)) { 163662306a36Sopenharmony_ci strlist__delete(vl->vars); 163762306a36Sopenharmony_ci vl->vars = NULL; 163862306a36Sopenharmony_ci } 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_ci return ret; 164162306a36Sopenharmony_ci} 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci/* 164462306a36Sopenharmony_ci * Find available variables at given probe point 164562306a36Sopenharmony_ci * Return the number of found probe points. Return 0 if there is no 164662306a36Sopenharmony_ci * matched probe point. Return <0 if an error occurs. 164762306a36Sopenharmony_ci */ 164862306a36Sopenharmony_ciint debuginfo__find_available_vars_at(struct debuginfo *dbg, 164962306a36Sopenharmony_ci struct perf_probe_event *pev, 165062306a36Sopenharmony_ci struct variable_list **vls) 165162306a36Sopenharmony_ci{ 165262306a36Sopenharmony_ci struct available_var_finder af = { 165362306a36Sopenharmony_ci .pf = {.pev = pev, .dbg = dbg, .callback = add_available_vars}, 165462306a36Sopenharmony_ci .mod = dbg->mod, 165562306a36Sopenharmony_ci .max_vls = probe_conf.max_probes}; 165662306a36Sopenharmony_ci int ret; 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci /* Allocate result vls array */ 165962306a36Sopenharmony_ci *vls = zalloc(sizeof(struct variable_list) * af.max_vls); 166062306a36Sopenharmony_ci if (*vls == NULL) 166162306a36Sopenharmony_ci return -ENOMEM; 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci af.vls = *vls; 166462306a36Sopenharmony_ci af.nvls = 0; 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ci ret = debuginfo__find_probes(dbg, &af.pf); 166762306a36Sopenharmony_ci if (ret < 0) { 166862306a36Sopenharmony_ci /* Free vlist for error */ 166962306a36Sopenharmony_ci while (af.nvls--) { 167062306a36Sopenharmony_ci zfree(&af.vls[af.nvls].point.symbol); 167162306a36Sopenharmony_ci strlist__delete(af.vls[af.nvls].vars); 167262306a36Sopenharmony_ci } 167362306a36Sopenharmony_ci zfree(vls); 167462306a36Sopenharmony_ci return ret; 167562306a36Sopenharmony_ci } 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci return (ret < 0) ? ret : af.nvls; 167862306a36Sopenharmony_ci} 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ci/* For the kernel module, we need a special code to get a DIE */ 168162306a36Sopenharmony_ciint debuginfo__get_text_offset(struct debuginfo *dbg, Dwarf_Addr *offs, 168262306a36Sopenharmony_ci bool adjust_offset) 168362306a36Sopenharmony_ci{ 168462306a36Sopenharmony_ci int n, i; 168562306a36Sopenharmony_ci Elf32_Word shndx; 168662306a36Sopenharmony_ci Elf_Scn *scn; 168762306a36Sopenharmony_ci Elf *elf; 168862306a36Sopenharmony_ci GElf_Shdr mem, *shdr; 168962306a36Sopenharmony_ci const char *p; 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ci elf = dwfl_module_getelf(dbg->mod, &dbg->bias); 169262306a36Sopenharmony_ci if (!elf) 169362306a36Sopenharmony_ci return -EINVAL; 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci /* Get the number of relocations */ 169662306a36Sopenharmony_ci n = dwfl_module_relocations(dbg->mod); 169762306a36Sopenharmony_ci if (n < 0) 169862306a36Sopenharmony_ci return -ENOENT; 169962306a36Sopenharmony_ci /* Search the relocation related .text section */ 170062306a36Sopenharmony_ci for (i = 0; i < n; i++) { 170162306a36Sopenharmony_ci p = dwfl_module_relocation_info(dbg->mod, i, &shndx); 170262306a36Sopenharmony_ci if (strcmp(p, ".text") == 0) { 170362306a36Sopenharmony_ci /* OK, get the section header */ 170462306a36Sopenharmony_ci scn = elf_getscn(elf, shndx); 170562306a36Sopenharmony_ci if (!scn) 170662306a36Sopenharmony_ci return -ENOENT; 170762306a36Sopenharmony_ci shdr = gelf_getshdr(scn, &mem); 170862306a36Sopenharmony_ci if (!shdr) 170962306a36Sopenharmony_ci return -ENOENT; 171062306a36Sopenharmony_ci *offs = shdr->sh_addr; 171162306a36Sopenharmony_ci if (adjust_offset) 171262306a36Sopenharmony_ci *offs -= shdr->sh_offset; 171362306a36Sopenharmony_ci } 171462306a36Sopenharmony_ci } 171562306a36Sopenharmony_ci return 0; 171662306a36Sopenharmony_ci} 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_ci/* Reverse search */ 171962306a36Sopenharmony_ciint debuginfo__find_probe_point(struct debuginfo *dbg, u64 addr, 172062306a36Sopenharmony_ci struct perf_probe_point *ppt) 172162306a36Sopenharmony_ci{ 172262306a36Sopenharmony_ci Dwarf_Die cudie, spdie, indie; 172362306a36Sopenharmony_ci Dwarf_Addr _addr = 0, baseaddr = 0; 172462306a36Sopenharmony_ci const char *fname = NULL, *func = NULL, *basefunc = NULL, *tmp; 172562306a36Sopenharmony_ci int baseline = 0, lineno = 0, ret = 0; 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ci /* We always need to relocate the address for aranges */ 172862306a36Sopenharmony_ci if (debuginfo__get_text_offset(dbg, &baseaddr, false) == 0) 172962306a36Sopenharmony_ci addr += baseaddr; 173062306a36Sopenharmony_ci /* Find cu die */ 173162306a36Sopenharmony_ci if (!dwarf_addrdie(dbg->dbg, (Dwarf_Addr)addr, &cudie)) { 173262306a36Sopenharmony_ci pr_warning("Failed to find debug information for address %#" PRIx64 "\n", 173362306a36Sopenharmony_ci addr); 173462306a36Sopenharmony_ci ret = -EINVAL; 173562306a36Sopenharmony_ci goto end; 173662306a36Sopenharmony_ci } 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_ci /* Find a corresponding line (filename and lineno) */ 173962306a36Sopenharmony_ci cu_find_lineinfo(&cudie, (Dwarf_Addr)addr, &fname, &lineno); 174062306a36Sopenharmony_ci /* Don't care whether it failed or not */ 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_ci /* Find a corresponding function (name, baseline and baseaddr) */ 174362306a36Sopenharmony_ci if (die_find_realfunc(&cudie, (Dwarf_Addr)addr, &spdie)) { 174462306a36Sopenharmony_ci /* Get function entry information */ 174562306a36Sopenharmony_ci func = basefunc = dwarf_diename(&spdie); 174662306a36Sopenharmony_ci if (!func || 174762306a36Sopenharmony_ci die_entrypc(&spdie, &baseaddr) != 0 || 174862306a36Sopenharmony_ci dwarf_decl_line(&spdie, &baseline) != 0) { 174962306a36Sopenharmony_ci lineno = 0; 175062306a36Sopenharmony_ci goto post; 175162306a36Sopenharmony_ci } 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci fname = die_get_decl_file(&spdie); 175462306a36Sopenharmony_ci if (addr == baseaddr) { 175562306a36Sopenharmony_ci /* Function entry - Relative line number is 0 */ 175662306a36Sopenharmony_ci lineno = baseline; 175762306a36Sopenharmony_ci goto post; 175862306a36Sopenharmony_ci } 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_ci /* Track down the inline functions step by step */ 176162306a36Sopenharmony_ci while (die_find_top_inlinefunc(&spdie, (Dwarf_Addr)addr, 176262306a36Sopenharmony_ci &indie)) { 176362306a36Sopenharmony_ci /* There is an inline function */ 176462306a36Sopenharmony_ci if (die_entrypc(&indie, &_addr) == 0 && 176562306a36Sopenharmony_ci _addr == addr) { 176662306a36Sopenharmony_ci /* 176762306a36Sopenharmony_ci * addr is at an inline function entry. 176862306a36Sopenharmony_ci * In this case, lineno should be the call-site 176962306a36Sopenharmony_ci * line number. (overwrite lineinfo) 177062306a36Sopenharmony_ci */ 177162306a36Sopenharmony_ci lineno = die_get_call_lineno(&indie); 177262306a36Sopenharmony_ci fname = die_get_call_file(&indie); 177362306a36Sopenharmony_ci break; 177462306a36Sopenharmony_ci } else { 177562306a36Sopenharmony_ci /* 177662306a36Sopenharmony_ci * addr is in an inline function body. 177762306a36Sopenharmony_ci * Since lineno points one of the lines 177862306a36Sopenharmony_ci * of the inline function, baseline should 177962306a36Sopenharmony_ci * be the entry line of the inline function. 178062306a36Sopenharmony_ci */ 178162306a36Sopenharmony_ci tmp = dwarf_diename(&indie); 178262306a36Sopenharmony_ci if (!tmp || 178362306a36Sopenharmony_ci dwarf_decl_line(&indie, &baseline) != 0) 178462306a36Sopenharmony_ci break; 178562306a36Sopenharmony_ci func = tmp; 178662306a36Sopenharmony_ci spdie = indie; 178762306a36Sopenharmony_ci } 178862306a36Sopenharmony_ci } 178962306a36Sopenharmony_ci /* Verify the lineno and baseline are in a same file */ 179062306a36Sopenharmony_ci tmp = die_get_decl_file(&spdie); 179162306a36Sopenharmony_ci if (!tmp || (fname && strcmp(tmp, fname) != 0)) 179262306a36Sopenharmony_ci lineno = 0; 179362306a36Sopenharmony_ci } 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_cipost: 179662306a36Sopenharmony_ci /* Make a relative line number or an offset */ 179762306a36Sopenharmony_ci if (lineno) 179862306a36Sopenharmony_ci ppt->line = lineno - baseline; 179962306a36Sopenharmony_ci else if (basefunc) { 180062306a36Sopenharmony_ci ppt->offset = addr - baseaddr; 180162306a36Sopenharmony_ci func = basefunc; 180262306a36Sopenharmony_ci } 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_ci /* Duplicate strings */ 180562306a36Sopenharmony_ci if (func) { 180662306a36Sopenharmony_ci ppt->function = strdup(func); 180762306a36Sopenharmony_ci if (ppt->function == NULL) { 180862306a36Sopenharmony_ci ret = -ENOMEM; 180962306a36Sopenharmony_ci goto end; 181062306a36Sopenharmony_ci } 181162306a36Sopenharmony_ci } 181262306a36Sopenharmony_ci if (fname) { 181362306a36Sopenharmony_ci ppt->file = strdup(fname); 181462306a36Sopenharmony_ci if (ppt->file == NULL) { 181562306a36Sopenharmony_ci zfree(&ppt->function); 181662306a36Sopenharmony_ci ret = -ENOMEM; 181762306a36Sopenharmony_ci goto end; 181862306a36Sopenharmony_ci } 181962306a36Sopenharmony_ci } 182062306a36Sopenharmony_ciend: 182162306a36Sopenharmony_ci if (ret == 0 && (fname || func)) 182262306a36Sopenharmony_ci ret = 1; /* Found a point */ 182362306a36Sopenharmony_ci return ret; 182462306a36Sopenharmony_ci} 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci/* Add a line and store the src path */ 182762306a36Sopenharmony_cistatic int line_range_add_line(const char *src, unsigned int lineno, 182862306a36Sopenharmony_ci struct line_range *lr) 182962306a36Sopenharmony_ci{ 183062306a36Sopenharmony_ci /* Copy source path */ 183162306a36Sopenharmony_ci if (!lr->path) { 183262306a36Sopenharmony_ci lr->path = strdup(src); 183362306a36Sopenharmony_ci if (lr->path == NULL) 183462306a36Sopenharmony_ci return -ENOMEM; 183562306a36Sopenharmony_ci } 183662306a36Sopenharmony_ci return intlist__add(lr->line_list, lineno); 183762306a36Sopenharmony_ci} 183862306a36Sopenharmony_ci 183962306a36Sopenharmony_cistatic int line_range_walk_cb(const char *fname, int lineno, 184062306a36Sopenharmony_ci Dwarf_Addr addr, void *data) 184162306a36Sopenharmony_ci{ 184262306a36Sopenharmony_ci struct line_finder *lf = data; 184362306a36Sopenharmony_ci const char *__fname; 184462306a36Sopenharmony_ci int __lineno; 184562306a36Sopenharmony_ci int err; 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_ci if ((strtailcmp(fname, lf->fname) != 0) || 184862306a36Sopenharmony_ci (lf->lno_s > lineno || lf->lno_e < lineno)) 184962306a36Sopenharmony_ci return 0; 185062306a36Sopenharmony_ci 185162306a36Sopenharmony_ci /* Make sure this line can be reversible */ 185262306a36Sopenharmony_ci if (cu_find_lineinfo(&lf->cu_die, addr, &__fname, &__lineno) > 0 185362306a36Sopenharmony_ci && (lineno != __lineno || strcmp(fname, __fname))) 185462306a36Sopenharmony_ci return 0; 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci err = line_range_add_line(fname, lineno, lf->lr); 185762306a36Sopenharmony_ci if (err < 0 && err != -EEXIST) 185862306a36Sopenharmony_ci return err; 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci return 0; 186162306a36Sopenharmony_ci} 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_ci/* Find line range from its line number */ 186462306a36Sopenharmony_cistatic int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) 186562306a36Sopenharmony_ci{ 186662306a36Sopenharmony_ci int ret; 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_ci ret = die_walk_lines(sp_die ?: &lf->cu_die, line_range_walk_cb, lf); 186962306a36Sopenharmony_ci 187062306a36Sopenharmony_ci /* Update status */ 187162306a36Sopenharmony_ci if (ret >= 0) 187262306a36Sopenharmony_ci if (!intlist__empty(lf->lr->line_list)) 187362306a36Sopenharmony_ci ret = lf->found = 1; 187462306a36Sopenharmony_ci else 187562306a36Sopenharmony_ci ret = 0; /* Lines are not found */ 187662306a36Sopenharmony_ci else { 187762306a36Sopenharmony_ci zfree(&lf->lr->path); 187862306a36Sopenharmony_ci } 187962306a36Sopenharmony_ci return ret; 188062306a36Sopenharmony_ci} 188162306a36Sopenharmony_ci 188262306a36Sopenharmony_cistatic int line_range_inline_cb(Dwarf_Die *in_die, void *data) 188362306a36Sopenharmony_ci{ 188462306a36Sopenharmony_ci int ret = find_line_range_by_line(in_die, data); 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci /* 188762306a36Sopenharmony_ci * We have to check all instances of inlined function, because 188862306a36Sopenharmony_ci * some execution paths can be optimized out depends on the 188962306a36Sopenharmony_ci * function argument of instances. However, if an error occurs, 189062306a36Sopenharmony_ci * it should be handled by the caller. 189162306a36Sopenharmony_ci */ 189262306a36Sopenharmony_ci return ret < 0 ? ret : 0; 189362306a36Sopenharmony_ci} 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_ci/* Search function definition from function name */ 189662306a36Sopenharmony_cistatic int line_range_search_cb(Dwarf_Die *sp_die, void *data) 189762306a36Sopenharmony_ci{ 189862306a36Sopenharmony_ci struct dwarf_callback_param *param = data; 189962306a36Sopenharmony_ci struct line_finder *lf = param->data; 190062306a36Sopenharmony_ci struct line_range *lr = lf->lr; 190162306a36Sopenharmony_ci const char *fname; 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci /* Check declared file */ 190462306a36Sopenharmony_ci if (lr->file) { 190562306a36Sopenharmony_ci fname = die_get_decl_file(sp_die); 190662306a36Sopenharmony_ci if (!fname || strtailcmp(lr->file, fname)) 190762306a36Sopenharmony_ci return DWARF_CB_OK; 190862306a36Sopenharmony_ci } 190962306a36Sopenharmony_ci 191062306a36Sopenharmony_ci if (die_match_name(sp_die, lr->function) && die_is_func_def(sp_die)) { 191162306a36Sopenharmony_ci lf->fname = die_get_decl_file(sp_die); 191262306a36Sopenharmony_ci dwarf_decl_line(sp_die, &lr->offset); 191362306a36Sopenharmony_ci pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); 191462306a36Sopenharmony_ci lf->lno_s = lr->offset + lr->start; 191562306a36Sopenharmony_ci if (lf->lno_s < 0) /* Overflow */ 191662306a36Sopenharmony_ci lf->lno_s = INT_MAX; 191762306a36Sopenharmony_ci lf->lno_e = lr->offset + lr->end; 191862306a36Sopenharmony_ci if (lf->lno_e < 0) /* Overflow */ 191962306a36Sopenharmony_ci lf->lno_e = INT_MAX; 192062306a36Sopenharmony_ci pr_debug("New line range: %d to %d\n", lf->lno_s, lf->lno_e); 192162306a36Sopenharmony_ci lr->start = lf->lno_s; 192262306a36Sopenharmony_ci lr->end = lf->lno_e; 192362306a36Sopenharmony_ci if (!die_is_func_instance(sp_die)) 192462306a36Sopenharmony_ci param->retval = die_walk_instances(sp_die, 192562306a36Sopenharmony_ci line_range_inline_cb, lf); 192662306a36Sopenharmony_ci else 192762306a36Sopenharmony_ci param->retval = find_line_range_by_line(sp_die, lf); 192862306a36Sopenharmony_ci return DWARF_CB_ABORT; 192962306a36Sopenharmony_ci } 193062306a36Sopenharmony_ci return DWARF_CB_OK; 193162306a36Sopenharmony_ci} 193262306a36Sopenharmony_ci 193362306a36Sopenharmony_cistatic int find_line_range_by_func(struct line_finder *lf) 193462306a36Sopenharmony_ci{ 193562306a36Sopenharmony_ci struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0}; 193662306a36Sopenharmony_ci dwarf_getfuncs(&lf->cu_die, line_range_search_cb, ¶m, 0); 193762306a36Sopenharmony_ci return param.retval; 193862306a36Sopenharmony_ci} 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ciint debuginfo__find_line_range(struct debuginfo *dbg, struct line_range *lr) 194162306a36Sopenharmony_ci{ 194262306a36Sopenharmony_ci struct line_finder lf = {.lr = lr, .found = 0}; 194362306a36Sopenharmony_ci int ret = 0; 194462306a36Sopenharmony_ci Dwarf_Off off = 0, noff; 194562306a36Sopenharmony_ci size_t cuhl; 194662306a36Sopenharmony_ci Dwarf_Die *diep; 194762306a36Sopenharmony_ci const char *comp_dir; 194862306a36Sopenharmony_ci 194962306a36Sopenharmony_ci /* Fastpath: lookup by function name from .debug_pubnames section */ 195062306a36Sopenharmony_ci if (lr->function) { 195162306a36Sopenharmony_ci struct pubname_callback_param pubname_param = { 195262306a36Sopenharmony_ci .function = lr->function, .file = lr->file, 195362306a36Sopenharmony_ci .cu_die = &lf.cu_die, .sp_die = &lf.sp_die, .found = 0}; 195462306a36Sopenharmony_ci struct dwarf_callback_param line_range_param = { 195562306a36Sopenharmony_ci .data = (void *)&lf, .retval = 0}; 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci dwarf_getpubnames(dbg->dbg, pubname_search_cb, 195862306a36Sopenharmony_ci &pubname_param, 0); 195962306a36Sopenharmony_ci if (pubname_param.found) { 196062306a36Sopenharmony_ci line_range_search_cb(&lf.sp_die, &line_range_param); 196162306a36Sopenharmony_ci if (lf.found) 196262306a36Sopenharmony_ci goto found; 196362306a36Sopenharmony_ci } 196462306a36Sopenharmony_ci } 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_ci /* Loop on CUs (Compilation Unit) */ 196762306a36Sopenharmony_ci while (!lf.found && ret >= 0) { 196862306a36Sopenharmony_ci if (dwarf_nextcu(dbg->dbg, off, &noff, &cuhl, 196962306a36Sopenharmony_ci NULL, NULL, NULL) != 0) 197062306a36Sopenharmony_ci break; 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci /* Get the DIE(Debugging Information Entry) of this CU */ 197362306a36Sopenharmony_ci diep = dwarf_offdie(dbg->dbg, off + cuhl, &lf.cu_die); 197462306a36Sopenharmony_ci if (!diep) { 197562306a36Sopenharmony_ci off = noff; 197662306a36Sopenharmony_ci continue; 197762306a36Sopenharmony_ci } 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci /* Check if target file is included. */ 198062306a36Sopenharmony_ci if (lr->file) 198162306a36Sopenharmony_ci lf.fname = cu_find_realpath(&lf.cu_die, lr->file); 198262306a36Sopenharmony_ci else 198362306a36Sopenharmony_ci lf.fname = 0; 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_ci if (!lr->file || lf.fname) { 198662306a36Sopenharmony_ci if (lr->function) 198762306a36Sopenharmony_ci ret = find_line_range_by_func(&lf); 198862306a36Sopenharmony_ci else { 198962306a36Sopenharmony_ci lf.lno_s = lr->start; 199062306a36Sopenharmony_ci lf.lno_e = lr->end; 199162306a36Sopenharmony_ci ret = find_line_range_by_line(NULL, &lf); 199262306a36Sopenharmony_ci } 199362306a36Sopenharmony_ci } 199462306a36Sopenharmony_ci off = noff; 199562306a36Sopenharmony_ci } 199662306a36Sopenharmony_ci 199762306a36Sopenharmony_cifound: 199862306a36Sopenharmony_ci /* Store comp_dir */ 199962306a36Sopenharmony_ci if (lf.found) { 200062306a36Sopenharmony_ci comp_dir = cu_get_comp_dir(&lf.cu_die); 200162306a36Sopenharmony_ci if (comp_dir) { 200262306a36Sopenharmony_ci lr->comp_dir = strdup(comp_dir); 200362306a36Sopenharmony_ci if (!lr->comp_dir) 200462306a36Sopenharmony_ci ret = -ENOMEM; 200562306a36Sopenharmony_ci } 200662306a36Sopenharmony_ci } 200762306a36Sopenharmony_ci 200862306a36Sopenharmony_ci pr_debug("path: %s\n", lr->path); 200962306a36Sopenharmony_ci return (ret < 0) ? ret : lf.found; 201062306a36Sopenharmony_ci} 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci#ifdef HAVE_DEBUGINFOD_SUPPORT 201362306a36Sopenharmony_ci/* debuginfod doesn't require the comp_dir but buildid is required */ 201462306a36Sopenharmony_cistatic int get_source_from_debuginfod(const char *raw_path, 201562306a36Sopenharmony_ci const char *sbuild_id, char **new_path) 201662306a36Sopenharmony_ci{ 201762306a36Sopenharmony_ci debuginfod_client *c = debuginfod_begin(); 201862306a36Sopenharmony_ci const char *p = raw_path; 201962306a36Sopenharmony_ci int fd; 202062306a36Sopenharmony_ci 202162306a36Sopenharmony_ci if (!c) 202262306a36Sopenharmony_ci return -ENOMEM; 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_ci fd = debuginfod_find_source(c, (const unsigned char *)sbuild_id, 202562306a36Sopenharmony_ci 0, p, new_path); 202662306a36Sopenharmony_ci pr_debug("Search %s from debuginfod -> %d\n", p, fd); 202762306a36Sopenharmony_ci if (fd >= 0) 202862306a36Sopenharmony_ci close(fd); 202962306a36Sopenharmony_ci debuginfod_end(c); 203062306a36Sopenharmony_ci if (fd < 0) { 203162306a36Sopenharmony_ci pr_debug("Failed to find %s in debuginfod (%s)\n", 203262306a36Sopenharmony_ci raw_path, sbuild_id); 203362306a36Sopenharmony_ci return -ENOENT; 203462306a36Sopenharmony_ci } 203562306a36Sopenharmony_ci pr_debug("Got a source %s\n", *new_path); 203662306a36Sopenharmony_ci 203762306a36Sopenharmony_ci return 0; 203862306a36Sopenharmony_ci} 203962306a36Sopenharmony_ci#else 204062306a36Sopenharmony_cistatic inline int get_source_from_debuginfod(const char *raw_path __maybe_unused, 204162306a36Sopenharmony_ci const char *sbuild_id __maybe_unused, 204262306a36Sopenharmony_ci char **new_path __maybe_unused) 204362306a36Sopenharmony_ci{ 204462306a36Sopenharmony_ci return -ENOTSUP; 204562306a36Sopenharmony_ci} 204662306a36Sopenharmony_ci#endif 204762306a36Sopenharmony_ci/* 204862306a36Sopenharmony_ci * Find a src file from a DWARF tag path. Prepend optional source path prefix 204962306a36Sopenharmony_ci * and chop off leading directories that do not exist. Result is passed back as 205062306a36Sopenharmony_ci * a newly allocated path on success. 205162306a36Sopenharmony_ci * Return 0 if file was found and readable, -errno otherwise. 205262306a36Sopenharmony_ci */ 205362306a36Sopenharmony_ciint find_source_path(const char *raw_path, const char *sbuild_id, 205462306a36Sopenharmony_ci const char *comp_dir, char **new_path) 205562306a36Sopenharmony_ci{ 205662306a36Sopenharmony_ci const char *prefix = symbol_conf.source_prefix; 205762306a36Sopenharmony_ci 205862306a36Sopenharmony_ci if (sbuild_id && !prefix) { 205962306a36Sopenharmony_ci if (!get_source_from_debuginfod(raw_path, sbuild_id, new_path)) 206062306a36Sopenharmony_ci return 0; 206162306a36Sopenharmony_ci } 206262306a36Sopenharmony_ci 206362306a36Sopenharmony_ci if (!prefix) { 206462306a36Sopenharmony_ci if (raw_path[0] != '/' && comp_dir) 206562306a36Sopenharmony_ci /* If not an absolute path, try to use comp_dir */ 206662306a36Sopenharmony_ci prefix = comp_dir; 206762306a36Sopenharmony_ci else { 206862306a36Sopenharmony_ci if (access(raw_path, R_OK) == 0) { 206962306a36Sopenharmony_ci *new_path = strdup(raw_path); 207062306a36Sopenharmony_ci return *new_path ? 0 : -ENOMEM; 207162306a36Sopenharmony_ci } else 207262306a36Sopenharmony_ci return -errno; 207362306a36Sopenharmony_ci } 207462306a36Sopenharmony_ci } 207562306a36Sopenharmony_ci 207662306a36Sopenharmony_ci *new_path = malloc((strlen(prefix) + strlen(raw_path) + 2)); 207762306a36Sopenharmony_ci if (!*new_path) 207862306a36Sopenharmony_ci return -ENOMEM; 207962306a36Sopenharmony_ci 208062306a36Sopenharmony_ci for (;;) { 208162306a36Sopenharmony_ci sprintf(*new_path, "%s/%s", prefix, raw_path); 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_ci if (access(*new_path, R_OK) == 0) 208462306a36Sopenharmony_ci return 0; 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_ci if (!symbol_conf.source_prefix) { 208762306a36Sopenharmony_ci /* In case of searching comp_dir, don't retry */ 208862306a36Sopenharmony_ci zfree(new_path); 208962306a36Sopenharmony_ci return -errno; 209062306a36Sopenharmony_ci } 209162306a36Sopenharmony_ci 209262306a36Sopenharmony_ci switch (errno) { 209362306a36Sopenharmony_ci case ENAMETOOLONG: 209462306a36Sopenharmony_ci case ENOENT: 209562306a36Sopenharmony_ci case EROFS: 209662306a36Sopenharmony_ci case EFAULT: 209762306a36Sopenharmony_ci raw_path = strchr(++raw_path, '/'); 209862306a36Sopenharmony_ci if (!raw_path) { 209962306a36Sopenharmony_ci zfree(new_path); 210062306a36Sopenharmony_ci return -ENOENT; 210162306a36Sopenharmony_ci } 210262306a36Sopenharmony_ci continue; 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_ci default: 210562306a36Sopenharmony_ci zfree(new_path); 210662306a36Sopenharmony_ci return -errno; 210762306a36Sopenharmony_ci } 210862306a36Sopenharmony_ci } 210962306a36Sopenharmony_ci} 2110