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, &param, 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