xref: /linux/tools/perf/util/probe-finder.c (revision 124bb83cd7de4d851af7595650233fb9e9279d5d)
14ea42b18SMasami Hiramatsu /*
24ea42b18SMasami Hiramatsu  * probe-finder.c : C expression to kprobe event converter
34ea42b18SMasami Hiramatsu  *
44ea42b18SMasami Hiramatsu  * Written by Masami Hiramatsu <mhiramat@redhat.com>
54ea42b18SMasami Hiramatsu  *
64ea42b18SMasami Hiramatsu  * This program is free software; you can redistribute it and/or modify
74ea42b18SMasami Hiramatsu  * it under the terms of the GNU General Public License as published by
84ea42b18SMasami Hiramatsu  * the Free Software Foundation; either version 2 of the License, or
94ea42b18SMasami Hiramatsu  * (at your option) any later version.
104ea42b18SMasami Hiramatsu  *
114ea42b18SMasami Hiramatsu  * This program is distributed in the hope that it will be useful,
124ea42b18SMasami Hiramatsu  * but WITHOUT ANY WARRANTY; without even the implied warranty of
134ea42b18SMasami Hiramatsu  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
144ea42b18SMasami Hiramatsu  * GNU General Public License for more details.
154ea42b18SMasami Hiramatsu  *
164ea42b18SMasami Hiramatsu  * You should have received a copy of the GNU General Public License
174ea42b18SMasami Hiramatsu  * along with this program; if not, write to the Free Software
184ea42b18SMasami Hiramatsu  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
194ea42b18SMasami Hiramatsu  *
204ea42b18SMasami Hiramatsu  */
214ea42b18SMasami Hiramatsu 
224ea42b18SMasami Hiramatsu #include <sys/utsname.h>
234ea42b18SMasami Hiramatsu #include <sys/types.h>
244ea42b18SMasami Hiramatsu #include <sys/stat.h>
254ea42b18SMasami Hiramatsu #include <fcntl.h>
264ea42b18SMasami Hiramatsu #include <errno.h>
274ea42b18SMasami Hiramatsu #include <stdio.h>
284ea42b18SMasami Hiramatsu #include <unistd.h>
294ea42b18SMasami Hiramatsu #include <getopt.h>
304ea42b18SMasami Hiramatsu #include <stdlib.h>
314ea42b18SMasami Hiramatsu #include <string.h>
324ea42b18SMasami Hiramatsu #include <stdarg.h>
334ea42b18SMasami Hiramatsu #include <ctype.h>
34cd932c59SIan Munsie #include <dwarf-regs.h>
35074fc0e4SMasami Hiramatsu 
36*124bb83cSMasami Hiramatsu #include <linux/bitops.h>
3789c69c0eSMasami Hiramatsu #include "event.h"
3889c69c0eSMasami Hiramatsu #include "debug.h"
39074fc0e4SMasami Hiramatsu #include "util.h"
409ed7e1b8SChase Douglas #include "symbol.h"
414ea42b18SMasami Hiramatsu #include "probe-finder.h"
424ea42b18SMasami Hiramatsu 
434984912eSMasami Hiramatsu /* Kprobe tracer basic type is up to u64 */
444984912eSMasami Hiramatsu #define MAX_BASIC_TYPE_BITS	64
454984912eSMasami Hiramatsu 
464ea42b18SMasami Hiramatsu /*
474ea42b18SMasami Hiramatsu  * Compare the tail of two strings.
484ea42b18SMasami Hiramatsu  * Return 0 if whole of either string is same as another's tail part.
494ea42b18SMasami Hiramatsu  */
504ea42b18SMasami Hiramatsu static int strtailcmp(const char *s1, const char *s2)
514ea42b18SMasami Hiramatsu {
524ea42b18SMasami Hiramatsu 	int i1 = strlen(s1);
534ea42b18SMasami Hiramatsu 	int i2 = strlen(s2);
54d56728b8SJuha Leppanen 	while (--i1 >= 0 && --i2 >= 0) {
554ea42b18SMasami Hiramatsu 		if (s1[i1] != s2[i2])
564ea42b18SMasami Hiramatsu 			return s1[i1] - s2[i2];
574ea42b18SMasami Hiramatsu 	}
584ea42b18SMasami Hiramatsu 	return 0;
594ea42b18SMasami Hiramatsu }
604ea42b18SMasami Hiramatsu 
612a9c8c36SMasami Hiramatsu /* Line number list operations */
622a9c8c36SMasami Hiramatsu 
632a9c8c36SMasami Hiramatsu /* Add a line to line number list */
64d3b63d7aSMasami Hiramatsu static int line_list__add_line(struct list_head *head, int line)
652a9c8c36SMasami Hiramatsu {
662a9c8c36SMasami Hiramatsu 	struct line_node *ln;
672a9c8c36SMasami Hiramatsu 	struct list_head *p;
682a9c8c36SMasami Hiramatsu 
692a9c8c36SMasami Hiramatsu 	/* Reverse search, because new line will be the last one */
702a9c8c36SMasami Hiramatsu 	list_for_each_entry_reverse(ln, head, list) {
712a9c8c36SMasami Hiramatsu 		if (ln->line < line) {
722a9c8c36SMasami Hiramatsu 			p = &ln->list;
732a9c8c36SMasami Hiramatsu 			goto found;
742a9c8c36SMasami Hiramatsu 		} else if (ln->line == line)	/* Already exist */
75e334016fSMasami Hiramatsu 			return 1;
762a9c8c36SMasami Hiramatsu 	}
772a9c8c36SMasami Hiramatsu 	/* List is empty, or the smallest entry */
782a9c8c36SMasami Hiramatsu 	p = head;
792a9c8c36SMasami Hiramatsu found:
802a9c8c36SMasami Hiramatsu 	pr_debug("line list: add a line %u\n", line);
81e334016fSMasami Hiramatsu 	ln = zalloc(sizeof(struct line_node));
82e334016fSMasami Hiramatsu 	if (ln == NULL)
83e334016fSMasami Hiramatsu 		return -ENOMEM;
842a9c8c36SMasami Hiramatsu 	ln->line = line;
852a9c8c36SMasami Hiramatsu 	INIT_LIST_HEAD(&ln->list);
862a9c8c36SMasami Hiramatsu 	list_add(&ln->list, p);
87e334016fSMasami Hiramatsu 	return 0;
882a9c8c36SMasami Hiramatsu }
892a9c8c36SMasami Hiramatsu 
902a9c8c36SMasami Hiramatsu /* Check if the line in line number list */
91d3b63d7aSMasami Hiramatsu static int line_list__has_line(struct list_head *head, int line)
922a9c8c36SMasami Hiramatsu {
932a9c8c36SMasami Hiramatsu 	struct line_node *ln;
942a9c8c36SMasami Hiramatsu 
952a9c8c36SMasami Hiramatsu 	/* Reverse search, because new line will be the last one */
962a9c8c36SMasami Hiramatsu 	list_for_each_entry(ln, head, list)
972a9c8c36SMasami Hiramatsu 		if (ln->line == line)
982a9c8c36SMasami Hiramatsu 			return 1;
992a9c8c36SMasami Hiramatsu 
1002a9c8c36SMasami Hiramatsu 	return 0;
1012a9c8c36SMasami Hiramatsu }
1022a9c8c36SMasami Hiramatsu 
1032a9c8c36SMasami Hiramatsu /* Init line number list */
1042a9c8c36SMasami Hiramatsu static void line_list__init(struct list_head *head)
1052a9c8c36SMasami Hiramatsu {
1062a9c8c36SMasami Hiramatsu 	INIT_LIST_HEAD(head);
1072a9c8c36SMasami Hiramatsu }
1082a9c8c36SMasami Hiramatsu 
1092a9c8c36SMasami Hiramatsu /* Free line number list */
1102a9c8c36SMasami Hiramatsu static void line_list__free(struct list_head *head)
1112a9c8c36SMasami Hiramatsu {
1122a9c8c36SMasami Hiramatsu 	struct line_node *ln;
1132a9c8c36SMasami Hiramatsu 	while (!list_empty(head)) {
1142a9c8c36SMasami Hiramatsu 		ln = list_first_entry(head, struct line_node, list);
1152a9c8c36SMasami Hiramatsu 		list_del(&ln->list);
1162a9c8c36SMasami Hiramatsu 		free(ln);
1172a9c8c36SMasami Hiramatsu 	}
1182a9c8c36SMasami Hiramatsu }
1192a9c8c36SMasami Hiramatsu 
120469b9b88SMasami Hiramatsu /* Dwarf FL wrappers */
121469b9b88SMasami Hiramatsu static char *debuginfo_path;	/* Currently dummy */
122469b9b88SMasami Hiramatsu 
123469b9b88SMasami Hiramatsu static const Dwfl_Callbacks offline_callbacks = {
124469b9b88SMasami Hiramatsu 	.find_debuginfo = dwfl_standard_find_debuginfo,
125469b9b88SMasami Hiramatsu 	.debuginfo_path = &debuginfo_path,
126469b9b88SMasami Hiramatsu 
127469b9b88SMasami Hiramatsu 	.section_address = dwfl_offline_section_address,
128469b9b88SMasami Hiramatsu 
129469b9b88SMasami Hiramatsu 	/* We use this table for core files too.  */
130469b9b88SMasami Hiramatsu 	.find_elf = dwfl_build_id_find_elf,
131469b9b88SMasami Hiramatsu };
132469b9b88SMasami Hiramatsu 
133469b9b88SMasami Hiramatsu /* Get a Dwarf from offline image */
134469b9b88SMasami Hiramatsu static Dwarf *dwfl_init_offline_dwarf(int fd, Dwfl **dwflp, Dwarf_Addr *bias)
135469b9b88SMasami Hiramatsu {
136469b9b88SMasami Hiramatsu 	Dwfl_Module *mod;
137469b9b88SMasami Hiramatsu 	Dwarf *dbg = NULL;
138469b9b88SMasami Hiramatsu 
139469b9b88SMasami Hiramatsu 	if (!dwflp)
140469b9b88SMasami Hiramatsu 		return NULL;
141469b9b88SMasami Hiramatsu 
142469b9b88SMasami Hiramatsu 	*dwflp = dwfl_begin(&offline_callbacks);
143469b9b88SMasami Hiramatsu 	if (!*dwflp)
144469b9b88SMasami Hiramatsu 		return NULL;
145469b9b88SMasami Hiramatsu 
146469b9b88SMasami Hiramatsu 	mod = dwfl_report_offline(*dwflp, "", "", fd);
147469b9b88SMasami Hiramatsu 	if (!mod)
148469b9b88SMasami Hiramatsu 		goto error;
149469b9b88SMasami Hiramatsu 
150469b9b88SMasami Hiramatsu 	dbg = dwfl_module_getdwarf(mod, bias);
151469b9b88SMasami Hiramatsu 	if (!dbg) {
152469b9b88SMasami Hiramatsu error:
153469b9b88SMasami Hiramatsu 		dwfl_end(*dwflp);
154469b9b88SMasami Hiramatsu 		*dwflp = NULL;
155469b9b88SMasami Hiramatsu 	}
156469b9b88SMasami Hiramatsu 	return dbg;
157469b9b88SMasami Hiramatsu }
158469b9b88SMasami Hiramatsu 
1593b4694deSMasami Hiramatsu #if _ELFUTILS_PREREQ(0, 148)
1603b4694deSMasami Hiramatsu /* This method is buggy if elfutils is older than 0.148 */
1613b4694deSMasami Hiramatsu static int __linux_kernel_find_elf(Dwfl_Module *mod,
1623b4694deSMasami Hiramatsu 				   void **userdata,
1633b4694deSMasami Hiramatsu 				   const char *module_name,
1643b4694deSMasami Hiramatsu 				   Dwarf_Addr base,
1653b4694deSMasami Hiramatsu 				   char **file_name, Elf **elfp)
1663b4694deSMasami Hiramatsu {
1673b4694deSMasami Hiramatsu 	int fd;
1683b4694deSMasami Hiramatsu 	const char *path = kernel_get_module_path(module_name);
1693b4694deSMasami Hiramatsu 
1703b4694deSMasami Hiramatsu 	pr_debug2("Use file %s for %s\n", path, module_name);
1713b4694deSMasami Hiramatsu 	if (path) {
1723b4694deSMasami Hiramatsu 		fd = open(path, O_RDONLY);
1733b4694deSMasami Hiramatsu 		if (fd >= 0) {
1743b4694deSMasami Hiramatsu 			*file_name = strdup(path);
1753b4694deSMasami Hiramatsu 			return fd;
1763b4694deSMasami Hiramatsu 		}
1773b4694deSMasami Hiramatsu 	}
1783b4694deSMasami Hiramatsu 	/* If failed, try to call standard method */
1793b4694deSMasami Hiramatsu 	return dwfl_linux_kernel_find_elf(mod, userdata, module_name, base,
1803b4694deSMasami Hiramatsu 					  file_name, elfp);
1813b4694deSMasami Hiramatsu }
1823b4694deSMasami Hiramatsu 
1833b4694deSMasami Hiramatsu static const Dwfl_Callbacks kernel_callbacks = {
1843b4694deSMasami Hiramatsu 	.find_debuginfo = dwfl_standard_find_debuginfo,
1853b4694deSMasami Hiramatsu 	.debuginfo_path = &debuginfo_path,
1863b4694deSMasami Hiramatsu 
1873b4694deSMasami Hiramatsu 	.find_elf = __linux_kernel_find_elf,
1883b4694deSMasami Hiramatsu 	.section_address = dwfl_linux_kernel_module_section_address,
1893b4694deSMasami Hiramatsu };
1903b4694deSMasami Hiramatsu 
191469b9b88SMasami Hiramatsu /* Get a Dwarf from live kernel image */
192469b9b88SMasami Hiramatsu static Dwarf *dwfl_init_live_kernel_dwarf(Dwarf_Addr addr, Dwfl **dwflp,
193469b9b88SMasami Hiramatsu 					  Dwarf_Addr *bias)
194469b9b88SMasami Hiramatsu {
195469b9b88SMasami Hiramatsu 	Dwarf *dbg;
196469b9b88SMasami Hiramatsu 
197469b9b88SMasami Hiramatsu 	if (!dwflp)
198469b9b88SMasami Hiramatsu 		return NULL;
199469b9b88SMasami Hiramatsu 
200469b9b88SMasami Hiramatsu 	*dwflp = dwfl_begin(&kernel_callbacks);
201469b9b88SMasami Hiramatsu 	if (!*dwflp)
202469b9b88SMasami Hiramatsu 		return NULL;
203469b9b88SMasami Hiramatsu 
204469b9b88SMasami Hiramatsu 	/* Load the kernel dwarves: Don't care the result here */
205469b9b88SMasami Hiramatsu 	dwfl_linux_kernel_report_kernel(*dwflp);
206469b9b88SMasami Hiramatsu 	dwfl_linux_kernel_report_modules(*dwflp);
207469b9b88SMasami Hiramatsu 
208469b9b88SMasami Hiramatsu 	dbg = dwfl_addrdwarf(*dwflp, addr, bias);
209469b9b88SMasami Hiramatsu 	/* Here, check whether we could get a real dwarf */
210469b9b88SMasami Hiramatsu 	if (!dbg) {
2113b4694deSMasami Hiramatsu 		pr_debug("Failed to find kernel dwarf at %lx\n",
2123b4694deSMasami Hiramatsu 			 (unsigned long)addr);
213469b9b88SMasami Hiramatsu 		dwfl_end(*dwflp);
214469b9b88SMasami Hiramatsu 		*dwflp = NULL;
215469b9b88SMasami Hiramatsu 	}
216469b9b88SMasami Hiramatsu 	return dbg;
217469b9b88SMasami Hiramatsu }
2183b4694deSMasami Hiramatsu #else
2193b4694deSMasami Hiramatsu /* With older elfutils, this just support kernel module... */
2203b4694deSMasami Hiramatsu static Dwarf *dwfl_init_live_kernel_dwarf(Dwarf_Addr addr __used, Dwfl **dwflp,
2213b4694deSMasami Hiramatsu 					  Dwarf_Addr *bias)
2223b4694deSMasami Hiramatsu {
2233b4694deSMasami Hiramatsu 	int fd;
2243b4694deSMasami Hiramatsu 	const char *path = kernel_get_module_path("kernel");
2253b4694deSMasami Hiramatsu 
2263b4694deSMasami Hiramatsu 	if (!path) {
2273b4694deSMasami Hiramatsu 		pr_err("Failed to find vmlinux path\n");
2283b4694deSMasami Hiramatsu 		return NULL;
2293b4694deSMasami Hiramatsu 	}
2303b4694deSMasami Hiramatsu 
2313b4694deSMasami Hiramatsu 	pr_debug2("Use file %s for debuginfo\n", path);
2323b4694deSMasami Hiramatsu 	fd = open(path, O_RDONLY);
2333b4694deSMasami Hiramatsu 	if (fd < 0)
2343b4694deSMasami Hiramatsu 		return NULL;
2353b4694deSMasami Hiramatsu 
2363b4694deSMasami Hiramatsu 	return dwfl_init_offline_dwarf(fd, dwflp, bias);
2373b4694deSMasami Hiramatsu }
2383b4694deSMasami Hiramatsu #endif
239469b9b88SMasami Hiramatsu 
2402a9c8c36SMasami Hiramatsu /* Dwarf wrappers */
2412a9c8c36SMasami Hiramatsu 
2422a9c8c36SMasami Hiramatsu /* Find the realpath of the target file. */
2432a9c8c36SMasami Hiramatsu static const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname)
2444ea42b18SMasami Hiramatsu {
245804b3606SMasami Hiramatsu 	Dwarf_Files *files;
246804b3606SMasami Hiramatsu 	size_t nfiles, i;
247accd3cc4SArnaldo Carvalho de Melo 	const char *src = NULL;
2484ea42b18SMasami Hiramatsu 	int ret;
2494ea42b18SMasami Hiramatsu 
2504ea42b18SMasami Hiramatsu 	if (!fname)
2512a9c8c36SMasami Hiramatsu 		return NULL;
252631c9defSMasami Hiramatsu 
253804b3606SMasami Hiramatsu 	ret = dwarf_getsrcfiles(cu_die, &files, &nfiles);
2542a9c8c36SMasami Hiramatsu 	if (ret != 0)
2552a9c8c36SMasami Hiramatsu 		return NULL;
2562a9c8c36SMasami Hiramatsu 
257804b3606SMasami Hiramatsu 	for (i = 0; i < nfiles; i++) {
258804b3606SMasami Hiramatsu 		src = dwarf_filesrc(files, i, NULL, NULL);
2592a9c8c36SMasami Hiramatsu 		if (strtailcmp(src, fname) == 0)
260804b3606SMasami Hiramatsu 			break;
261804b3606SMasami Hiramatsu 	}
262c9e38582SMasami Hiramatsu 	if (i == nfiles)
263c9e38582SMasami Hiramatsu 		return NULL;
2642a9c8c36SMasami Hiramatsu 	return src;
265631c9defSMasami Hiramatsu }
266631c9defSMasami Hiramatsu 
2676a330a3cSMasami Hiramatsu /* Get DW_AT_comp_dir (should be NULL with older gcc) */
2686a330a3cSMasami Hiramatsu static const char *cu_get_comp_dir(Dwarf_Die *cu_die)
2696a330a3cSMasami Hiramatsu {
2706a330a3cSMasami Hiramatsu 	Dwarf_Attribute attr;
2716a330a3cSMasami Hiramatsu 	if (dwarf_attr(cu_die, DW_AT_comp_dir, &attr) == NULL)
2726a330a3cSMasami Hiramatsu 		return NULL;
2736a330a3cSMasami Hiramatsu 	return dwarf_formstring(&attr);
2746a330a3cSMasami Hiramatsu }
2756a330a3cSMasami Hiramatsu 
276016f262eSMasami Hiramatsu /* Compare diename and tname */
277016f262eSMasami Hiramatsu static bool die_compare_name(Dwarf_Die *dw_die, const char *tname)
278016f262eSMasami Hiramatsu {
279016f262eSMasami Hiramatsu 	const char *name;
280016f262eSMasami Hiramatsu 	name = dwarf_diename(dw_die);
28182175633SMasami Hiramatsu 	return name ? (strcmp(tname, name) == 0) : false;
282016f262eSMasami Hiramatsu }
283016f262eSMasami Hiramatsu 
2845069ed86SMasami Hiramatsu /* Get callsite line number of inline-function instance */
2855069ed86SMasami Hiramatsu static int die_get_call_lineno(Dwarf_Die *in_die)
2865069ed86SMasami Hiramatsu {
2875069ed86SMasami Hiramatsu 	Dwarf_Attribute attr;
2885069ed86SMasami Hiramatsu 	Dwarf_Word ret;
2895069ed86SMasami Hiramatsu 
2905069ed86SMasami Hiramatsu 	if (!dwarf_attr(in_die, DW_AT_call_line, &attr))
2915069ed86SMasami Hiramatsu 		return -ENOENT;
2925069ed86SMasami Hiramatsu 
2935069ed86SMasami Hiramatsu 	dwarf_formudata(&attr, &ret);
2945069ed86SMasami Hiramatsu 	return (int)ret;
2955069ed86SMasami Hiramatsu }
2965069ed86SMasami Hiramatsu 
2974046b8bbSMasami Hiramatsu /* Get type die */
2984046b8bbSMasami Hiramatsu static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
2994046b8bbSMasami Hiramatsu {
3004046b8bbSMasami Hiramatsu 	Dwarf_Attribute attr;
3014046b8bbSMasami Hiramatsu 
3024046b8bbSMasami Hiramatsu 	if (dwarf_attr_integrate(vr_die, DW_AT_type, &attr) &&
3034046b8bbSMasami Hiramatsu 	    dwarf_formref_die(&attr, die_mem))
3044046b8bbSMasami Hiramatsu 		return die_mem;
3054046b8bbSMasami Hiramatsu 	else
3064046b8bbSMasami Hiramatsu 		return NULL;
3074046b8bbSMasami Hiramatsu }
3084046b8bbSMasami Hiramatsu 
309cf6eb489SMasami Hiramatsu /* Get a type die, but skip qualifiers */
310cf6eb489SMasami Hiramatsu static Dwarf_Die *__die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
3117df2f329SMasami Hiramatsu {
3127df2f329SMasami Hiramatsu 	int tag;
3137df2f329SMasami Hiramatsu 
3147df2f329SMasami Hiramatsu 	do {
3154046b8bbSMasami Hiramatsu 		vr_die = die_get_type(vr_die, die_mem);
3164046b8bbSMasami Hiramatsu 		if (!vr_die)
3174046b8bbSMasami Hiramatsu 			break;
3184046b8bbSMasami Hiramatsu 		tag = dwarf_tag(vr_die);
3197df2f329SMasami Hiramatsu 	} while (tag == DW_TAG_const_type ||
3207df2f329SMasami Hiramatsu 		 tag == DW_TAG_restrict_type ||
3217df2f329SMasami Hiramatsu 		 tag == DW_TAG_volatile_type ||
322cf6eb489SMasami Hiramatsu 		 tag == DW_TAG_shared_type);
323cf6eb489SMasami Hiramatsu 
324cf6eb489SMasami Hiramatsu 	return vr_die;
325cf6eb489SMasami Hiramatsu }
326cf6eb489SMasami Hiramatsu 
327cf6eb489SMasami Hiramatsu /* Get a type die, but skip qualifiers and typedef */
328cf6eb489SMasami Hiramatsu static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
329cf6eb489SMasami Hiramatsu {
330cf6eb489SMasami Hiramatsu 	do {
331cf6eb489SMasami Hiramatsu 		vr_die = __die_get_real_type(vr_die, die_mem);
332cf6eb489SMasami Hiramatsu 	} while (vr_die && dwarf_tag(vr_die) == DW_TAG_typedef);
3337df2f329SMasami Hiramatsu 
3344046b8bbSMasami Hiramatsu 	return vr_die;
3357df2f329SMasami Hiramatsu }
3367df2f329SMasami Hiramatsu 
337*124bb83cSMasami Hiramatsu static int die_get_attr_udata(Dwarf_Die *tp_die, unsigned int attr_name,
338*124bb83cSMasami Hiramatsu 			      Dwarf_Word *result)
3394984912eSMasami Hiramatsu {
3404984912eSMasami Hiramatsu 	Dwarf_Attribute attr;
341*124bb83cSMasami Hiramatsu 
342*124bb83cSMasami Hiramatsu 	if (dwarf_attr(tp_die, attr_name, &attr) == NULL ||
343*124bb83cSMasami Hiramatsu 	    dwarf_formudata(&attr, result) != 0)
344*124bb83cSMasami Hiramatsu 		return -ENOENT;
345*124bb83cSMasami Hiramatsu 
346*124bb83cSMasami Hiramatsu 	return 0;
347*124bb83cSMasami Hiramatsu }
348*124bb83cSMasami Hiramatsu 
349*124bb83cSMasami Hiramatsu static bool die_is_signed_type(Dwarf_Die *tp_die)
350*124bb83cSMasami Hiramatsu {
3514984912eSMasami Hiramatsu 	Dwarf_Word ret;
3524984912eSMasami Hiramatsu 
353*124bb83cSMasami Hiramatsu 	if (die_get_attr_udata(tp_die, DW_AT_encoding, &ret))
3544984912eSMasami Hiramatsu 		return false;
3554984912eSMasami Hiramatsu 
3564984912eSMasami Hiramatsu 	return (ret == DW_ATE_signed_char || ret == DW_ATE_signed ||
3574984912eSMasami Hiramatsu 		ret == DW_ATE_signed_fixed);
3584984912eSMasami Hiramatsu }
3594984912eSMasami Hiramatsu 
3604984912eSMasami Hiramatsu static int die_get_byte_size(Dwarf_Die *tp_die)
3614984912eSMasami Hiramatsu {
3624984912eSMasami Hiramatsu 	Dwarf_Word ret;
3634984912eSMasami Hiramatsu 
364*124bb83cSMasami Hiramatsu 	if (die_get_attr_udata(tp_die, DW_AT_byte_size, &ret))
365*124bb83cSMasami Hiramatsu 		return 0;
366*124bb83cSMasami Hiramatsu 
367*124bb83cSMasami Hiramatsu 	return (int)ret;
368*124bb83cSMasami Hiramatsu }
369*124bb83cSMasami Hiramatsu 
370*124bb83cSMasami Hiramatsu static int die_get_bit_size(Dwarf_Die *tp_die)
371*124bb83cSMasami Hiramatsu {
372*124bb83cSMasami Hiramatsu 	Dwarf_Word ret;
373*124bb83cSMasami Hiramatsu 
374*124bb83cSMasami Hiramatsu 	if (die_get_attr_udata(tp_die, DW_AT_bit_size, &ret))
375*124bb83cSMasami Hiramatsu 		return 0;
376*124bb83cSMasami Hiramatsu 
377*124bb83cSMasami Hiramatsu 	return (int)ret;
378*124bb83cSMasami Hiramatsu }
379*124bb83cSMasami Hiramatsu 
380*124bb83cSMasami Hiramatsu static int die_get_bit_offset(Dwarf_Die *tp_die)
381*124bb83cSMasami Hiramatsu {
382*124bb83cSMasami Hiramatsu 	Dwarf_Word ret;
383*124bb83cSMasami Hiramatsu 
384*124bb83cSMasami Hiramatsu 	if (die_get_attr_udata(tp_die, DW_AT_bit_offset, &ret))
3854984912eSMasami Hiramatsu 		return 0;
3864984912eSMasami Hiramatsu 
3874984912eSMasami Hiramatsu 	return (int)ret;
3884984912eSMasami Hiramatsu }
3894984912eSMasami Hiramatsu 
390de1439d8SMasami Hiramatsu /* Get data_member_location offset */
391de1439d8SMasami Hiramatsu static int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs)
392de1439d8SMasami Hiramatsu {
393de1439d8SMasami Hiramatsu 	Dwarf_Attribute attr;
394de1439d8SMasami Hiramatsu 	Dwarf_Op *expr;
395de1439d8SMasami Hiramatsu 	size_t nexpr;
396de1439d8SMasami Hiramatsu 	int ret;
397de1439d8SMasami Hiramatsu 
398de1439d8SMasami Hiramatsu 	if (dwarf_attr(mb_die, DW_AT_data_member_location, &attr) == NULL)
399de1439d8SMasami Hiramatsu 		return -ENOENT;
400de1439d8SMasami Hiramatsu 
401de1439d8SMasami Hiramatsu 	if (dwarf_formudata(&attr, offs) != 0) {
402de1439d8SMasami Hiramatsu 		/* DW_AT_data_member_location should be DW_OP_plus_uconst */
403de1439d8SMasami Hiramatsu 		ret = dwarf_getlocation(&attr, &expr, &nexpr);
404de1439d8SMasami Hiramatsu 		if (ret < 0 || nexpr == 0)
405de1439d8SMasami Hiramatsu 			return -ENOENT;
406de1439d8SMasami Hiramatsu 
407de1439d8SMasami Hiramatsu 		if (expr[0].atom != DW_OP_plus_uconst || nexpr != 1) {
408de1439d8SMasami Hiramatsu 			pr_debug("Unable to get offset:Unexpected OP %x (%zd)\n",
409de1439d8SMasami Hiramatsu 				 expr[0].atom, nexpr);
410de1439d8SMasami Hiramatsu 			return -ENOTSUP;
411de1439d8SMasami Hiramatsu 		}
412de1439d8SMasami Hiramatsu 		*offs = (Dwarf_Word)expr[0].number;
413de1439d8SMasami Hiramatsu 	}
414de1439d8SMasami Hiramatsu 	return 0;
415de1439d8SMasami Hiramatsu }
416de1439d8SMasami Hiramatsu 
417016f262eSMasami Hiramatsu /* Return values for die_find callbacks */
418016f262eSMasami Hiramatsu enum {
419016f262eSMasami Hiramatsu 	DIE_FIND_CB_FOUND = 0,		/* End of Search */
420016f262eSMasami Hiramatsu 	DIE_FIND_CB_CHILD = 1,		/* Search only children */
421016f262eSMasami Hiramatsu 	DIE_FIND_CB_SIBLING = 2,	/* Search only siblings */
422016f262eSMasami Hiramatsu 	DIE_FIND_CB_CONTINUE = 3,	/* Search children and siblings */
423016f262eSMasami Hiramatsu };
424016f262eSMasami Hiramatsu 
425016f262eSMasami Hiramatsu /* Search a child die */
426016f262eSMasami Hiramatsu static Dwarf_Die *die_find_child(Dwarf_Die *rt_die,
427016f262eSMasami Hiramatsu 				 int (*callback)(Dwarf_Die *, void *),
428016f262eSMasami Hiramatsu 				 void *data, Dwarf_Die *die_mem)
429016f262eSMasami Hiramatsu {
430016f262eSMasami Hiramatsu 	Dwarf_Die child_die;
431016f262eSMasami Hiramatsu 	int ret;
432016f262eSMasami Hiramatsu 
433016f262eSMasami Hiramatsu 	ret = dwarf_child(rt_die, die_mem);
434016f262eSMasami Hiramatsu 	if (ret != 0)
435016f262eSMasami Hiramatsu 		return NULL;
436016f262eSMasami Hiramatsu 
437016f262eSMasami Hiramatsu 	do {
438016f262eSMasami Hiramatsu 		ret = callback(die_mem, data);
439016f262eSMasami Hiramatsu 		if (ret == DIE_FIND_CB_FOUND)
440016f262eSMasami Hiramatsu 			return die_mem;
441016f262eSMasami Hiramatsu 
442016f262eSMasami Hiramatsu 		if ((ret & DIE_FIND_CB_CHILD) &&
443016f262eSMasami Hiramatsu 		    die_find_child(die_mem, callback, data, &child_die)) {
444016f262eSMasami Hiramatsu 			memcpy(die_mem, &child_die, sizeof(Dwarf_Die));
445016f262eSMasami Hiramatsu 			return die_mem;
446016f262eSMasami Hiramatsu 		}
447016f262eSMasami Hiramatsu 	} while ((ret & DIE_FIND_CB_SIBLING) &&
448016f262eSMasami Hiramatsu 		 dwarf_siblingof(die_mem, die_mem) == 0);
449016f262eSMasami Hiramatsu 
450016f262eSMasami Hiramatsu 	return NULL;
451016f262eSMasami Hiramatsu }
452016f262eSMasami Hiramatsu 
453804b3606SMasami Hiramatsu struct __addr_die_search_param {
454804b3606SMasami Hiramatsu 	Dwarf_Addr	addr;
455804b3606SMasami Hiramatsu 	Dwarf_Die	*die_mem;
456804b3606SMasami Hiramatsu };
457804b3606SMasami Hiramatsu 
458804b3606SMasami Hiramatsu static int __die_search_func_cb(Dwarf_Die *fn_die, void *data)
459804b3606SMasami Hiramatsu {
460804b3606SMasami Hiramatsu 	struct __addr_die_search_param *ad = data;
461804b3606SMasami Hiramatsu 
462804b3606SMasami Hiramatsu 	if (dwarf_tag(fn_die) == DW_TAG_subprogram &&
463804b3606SMasami Hiramatsu 	    dwarf_haspc(fn_die, ad->addr)) {
464804b3606SMasami Hiramatsu 		memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die));
465804b3606SMasami Hiramatsu 		return DWARF_CB_ABORT;
466804b3606SMasami Hiramatsu 	}
467804b3606SMasami Hiramatsu 	return DWARF_CB_OK;
468804b3606SMasami Hiramatsu }
469804b3606SMasami Hiramatsu 
470804b3606SMasami Hiramatsu /* Search a real subprogram including this line, */
47195a3e4c4SMasami Hiramatsu static Dwarf_Die *die_find_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr,
472804b3606SMasami Hiramatsu 					   Dwarf_Die *die_mem)
473804b3606SMasami Hiramatsu {
474804b3606SMasami Hiramatsu 	struct __addr_die_search_param ad;
475804b3606SMasami Hiramatsu 	ad.addr = addr;
476804b3606SMasami Hiramatsu 	ad.die_mem = die_mem;
477804b3606SMasami Hiramatsu 	/* dwarf_getscopes can't find subprogram. */
478804b3606SMasami Hiramatsu 	if (!dwarf_getfuncs(cu_die, __die_search_func_cb, &ad, 0))
479804b3606SMasami Hiramatsu 		return NULL;
480804b3606SMasami Hiramatsu 	else
481804b3606SMasami Hiramatsu 		return die_mem;
482804b3606SMasami Hiramatsu }
483804b3606SMasami Hiramatsu 
484016f262eSMasami Hiramatsu /* die_find callback for inline function search */
485016f262eSMasami Hiramatsu static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data)
486016f262eSMasami Hiramatsu {
487016f262eSMasami Hiramatsu 	Dwarf_Addr *addr = data;
488016f262eSMasami Hiramatsu 
489016f262eSMasami Hiramatsu 	if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine &&
490016f262eSMasami Hiramatsu 	    dwarf_haspc(die_mem, *addr))
491016f262eSMasami Hiramatsu 		return DIE_FIND_CB_FOUND;
492016f262eSMasami Hiramatsu 
493016f262eSMasami Hiramatsu 	return DIE_FIND_CB_CONTINUE;
494016f262eSMasami Hiramatsu }
495016f262eSMasami Hiramatsu 
496161a26b0SMasami Hiramatsu /* Similar to dwarf_getfuncs, but returns inlined_subroutine if exists. */
49795a3e4c4SMasami Hiramatsu static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr,
498161a26b0SMasami Hiramatsu 				      Dwarf_Die *die_mem)
499161a26b0SMasami Hiramatsu {
500016f262eSMasami Hiramatsu 	return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem);
501161a26b0SMasami Hiramatsu }
502161a26b0SMasami Hiramatsu 
5034cc9cec6SMasami Hiramatsu /* Walker on lines (Note: line number will not be sorted) */
5044cc9cec6SMasami Hiramatsu typedef int (* line_walk_handler_t) (const char *fname, int lineno,
5054cc9cec6SMasami Hiramatsu 				     Dwarf_Addr addr, void *data);
5064cc9cec6SMasami Hiramatsu 
5074cc9cec6SMasami Hiramatsu struct __line_walk_param {
5085069ed86SMasami Hiramatsu 	const char *fname;
5094cc9cec6SMasami Hiramatsu 	line_walk_handler_t handler;
5104cc9cec6SMasami Hiramatsu 	void *data;
5114cc9cec6SMasami Hiramatsu 	int retval;
5124cc9cec6SMasami Hiramatsu };
5134cc9cec6SMasami Hiramatsu 
5145069ed86SMasami Hiramatsu static int __die_walk_funclines_cb(Dwarf_Die *in_die, void *data)
5155069ed86SMasami Hiramatsu {
5165069ed86SMasami Hiramatsu 	struct __line_walk_param *lw = data;
5175069ed86SMasami Hiramatsu 	Dwarf_Addr addr;
5185069ed86SMasami Hiramatsu 	int lineno;
5195069ed86SMasami Hiramatsu 
5205069ed86SMasami Hiramatsu 	if (dwarf_tag(in_die) == DW_TAG_inlined_subroutine) {
5215069ed86SMasami Hiramatsu 		lineno = die_get_call_lineno(in_die);
5225069ed86SMasami Hiramatsu 		if (lineno > 0 && dwarf_entrypc(in_die, &addr) == 0) {
5235069ed86SMasami Hiramatsu 			lw->retval = lw->handler(lw->fname, lineno, addr,
5245069ed86SMasami Hiramatsu 						 lw->data);
5255069ed86SMasami Hiramatsu 			if (lw->retval != 0)
5265069ed86SMasami Hiramatsu 				return DIE_FIND_CB_FOUND;
5275069ed86SMasami Hiramatsu 		}
5285069ed86SMasami Hiramatsu 	}
5295069ed86SMasami Hiramatsu 	return DIE_FIND_CB_SIBLING;
5305069ed86SMasami Hiramatsu }
5315069ed86SMasami Hiramatsu 
5325069ed86SMasami Hiramatsu /* Walk on lines of blocks included in given DIE */
5334cc9cec6SMasami Hiramatsu static int __die_walk_funclines(Dwarf_Die *sp_die,
5344cc9cec6SMasami Hiramatsu 				line_walk_handler_t handler, void *data)
5354cc9cec6SMasami Hiramatsu {
5365069ed86SMasami Hiramatsu 	struct __line_walk_param lw = {
5375069ed86SMasami Hiramatsu 		.handler = handler,
5385069ed86SMasami Hiramatsu 		.data = data,
5395069ed86SMasami Hiramatsu 		.retval = 0,
5405069ed86SMasami Hiramatsu 	};
5415069ed86SMasami Hiramatsu 	Dwarf_Die die_mem;
5424cc9cec6SMasami Hiramatsu 	Dwarf_Addr addr;
5435069ed86SMasami Hiramatsu 	int lineno;
5444cc9cec6SMasami Hiramatsu 
5454cc9cec6SMasami Hiramatsu 	/* Handle function declaration line */
5465069ed86SMasami Hiramatsu 	lw.fname = dwarf_decl_file(sp_die);
5475069ed86SMasami Hiramatsu 	if (lw.fname && dwarf_decl_line(sp_die, &lineno) == 0 &&
5484cc9cec6SMasami Hiramatsu 	    dwarf_entrypc(sp_die, &addr) == 0) {
5495069ed86SMasami Hiramatsu 		lw.retval = handler(lw.fname, lineno, addr, data);
5505069ed86SMasami Hiramatsu 		if (lw.retval != 0)
5515069ed86SMasami Hiramatsu 			goto done;
5524cc9cec6SMasami Hiramatsu 	}
5535069ed86SMasami Hiramatsu 	die_find_child(sp_die, __die_walk_funclines_cb, &lw, &die_mem);
5545069ed86SMasami Hiramatsu done:
5555069ed86SMasami Hiramatsu 	return lw.retval;
5564cc9cec6SMasami Hiramatsu }
5574cc9cec6SMasami Hiramatsu 
5584cc9cec6SMasami Hiramatsu static int __die_walk_culines_cb(Dwarf_Die *sp_die, void *data)
5594cc9cec6SMasami Hiramatsu {
5604cc9cec6SMasami Hiramatsu 	struct __line_walk_param *lw = data;
5614cc9cec6SMasami Hiramatsu 
5624cc9cec6SMasami Hiramatsu 	lw->retval = __die_walk_funclines(sp_die, lw->handler, lw->data);
5634cc9cec6SMasami Hiramatsu 	if (lw->retval != 0)
5644cc9cec6SMasami Hiramatsu 		return DWARF_CB_ABORT;
5654cc9cec6SMasami Hiramatsu 
5664cc9cec6SMasami Hiramatsu 	return DWARF_CB_OK;
5674cc9cec6SMasami Hiramatsu }
5684cc9cec6SMasami Hiramatsu 
5694cc9cec6SMasami Hiramatsu /*
5704cc9cec6SMasami Hiramatsu  * Walk on lines inside given PDIE. If the PDIE is subprogram, walk only on
5714cc9cec6SMasami Hiramatsu  * the lines inside the subprogram, otherwise PDIE must be a CU DIE.
5724cc9cec6SMasami Hiramatsu  */
5734cc9cec6SMasami Hiramatsu static int die_walk_lines(Dwarf_Die *pdie, line_walk_handler_t handler,
5744cc9cec6SMasami Hiramatsu 			  void *data)
5754cc9cec6SMasami Hiramatsu {
5764cc9cec6SMasami Hiramatsu 	Dwarf_Lines *lines;
5774cc9cec6SMasami Hiramatsu 	Dwarf_Line *line;
5784cc9cec6SMasami Hiramatsu 	Dwarf_Addr addr;
5794cc9cec6SMasami Hiramatsu 	const char *fname;
5804cc9cec6SMasami Hiramatsu 	int lineno, ret = 0;
5814cc9cec6SMasami Hiramatsu 	Dwarf_Die die_mem, *cu_die;
5824cc9cec6SMasami Hiramatsu 	size_t nlines, i;
5834cc9cec6SMasami Hiramatsu 
5844cc9cec6SMasami Hiramatsu 	/* Get the CU die */
5854cc9cec6SMasami Hiramatsu 	if (dwarf_tag(pdie) == DW_TAG_subprogram)
5864cc9cec6SMasami Hiramatsu 		cu_die = dwarf_diecu(pdie, &die_mem, NULL, NULL);
5874cc9cec6SMasami Hiramatsu 	else
5884cc9cec6SMasami Hiramatsu 		cu_die = pdie;
5894cc9cec6SMasami Hiramatsu 	if (!cu_die) {
5904cc9cec6SMasami Hiramatsu 		pr_debug2("Failed to get CU from subprogram\n");
5914cc9cec6SMasami Hiramatsu 		return -EINVAL;
5924cc9cec6SMasami Hiramatsu 	}
5934cc9cec6SMasami Hiramatsu 
5944cc9cec6SMasami Hiramatsu 	/* Get lines list in the CU */
5954cc9cec6SMasami Hiramatsu 	if (dwarf_getsrclines(cu_die, &lines, &nlines) != 0) {
5964cc9cec6SMasami Hiramatsu 		pr_debug2("Failed to get source lines on this CU.\n");
5974cc9cec6SMasami Hiramatsu 		return -ENOENT;
5984cc9cec6SMasami Hiramatsu 	}
5994cc9cec6SMasami Hiramatsu 	pr_debug2("Get %zd lines from this CU\n", nlines);
6004cc9cec6SMasami Hiramatsu 
6014cc9cec6SMasami Hiramatsu 	/* Walk on the lines on lines list */
6024cc9cec6SMasami Hiramatsu 	for (i = 0; i < nlines; i++) {
6034cc9cec6SMasami Hiramatsu 		line = dwarf_onesrcline(lines, i);
6044cc9cec6SMasami Hiramatsu 		if (line == NULL ||
6054cc9cec6SMasami Hiramatsu 		    dwarf_lineno(line, &lineno) != 0 ||
6064cc9cec6SMasami Hiramatsu 		    dwarf_lineaddr(line, &addr) != 0) {
6074cc9cec6SMasami Hiramatsu 			pr_debug2("Failed to get line info. "
6084cc9cec6SMasami Hiramatsu 				  "Possible error in debuginfo.\n");
6094cc9cec6SMasami Hiramatsu 			continue;
6104cc9cec6SMasami Hiramatsu 		}
6114cc9cec6SMasami Hiramatsu 		/* Filter lines based on address */
6124cc9cec6SMasami Hiramatsu 		if (pdie != cu_die)
6134cc9cec6SMasami Hiramatsu 			/*
6144cc9cec6SMasami Hiramatsu 			 * Address filtering
6154cc9cec6SMasami Hiramatsu 			 * The line is included in given function, and
6164cc9cec6SMasami Hiramatsu 			 * no inline block includes it.
6174cc9cec6SMasami Hiramatsu 			 */
6184cc9cec6SMasami Hiramatsu 			if (!dwarf_haspc(pdie, addr) ||
6194cc9cec6SMasami Hiramatsu 			    die_find_inlinefunc(pdie, addr, &die_mem))
6204cc9cec6SMasami Hiramatsu 				continue;
6214cc9cec6SMasami Hiramatsu 		/* Get source line */
6224cc9cec6SMasami Hiramatsu 		fname = dwarf_linesrc(line, NULL, NULL);
6234cc9cec6SMasami Hiramatsu 
6244cc9cec6SMasami Hiramatsu 		ret = handler(fname, lineno, addr, data);
6254cc9cec6SMasami Hiramatsu 		if (ret != 0)
6264cc9cec6SMasami Hiramatsu 			return ret;
6274cc9cec6SMasami Hiramatsu 	}
6284cc9cec6SMasami Hiramatsu 
6294cc9cec6SMasami Hiramatsu 	/*
6304cc9cec6SMasami Hiramatsu 	 * Dwarf lines doesn't include function declarations and inlined
6314cc9cec6SMasami Hiramatsu 	 * subroutines. We have to check functions list or given function.
6324cc9cec6SMasami Hiramatsu 	 */
6334cc9cec6SMasami Hiramatsu 	if (pdie != cu_die)
6344cc9cec6SMasami Hiramatsu 		ret = __die_walk_funclines(pdie, handler, data);
6354cc9cec6SMasami Hiramatsu 	else {
6364cc9cec6SMasami Hiramatsu 		struct __line_walk_param param = {
6374cc9cec6SMasami Hiramatsu 			.handler = handler,
6384cc9cec6SMasami Hiramatsu 			.data = data,
6394cc9cec6SMasami Hiramatsu 			.retval = 0,
6404cc9cec6SMasami Hiramatsu 		};
6414cc9cec6SMasami Hiramatsu 		dwarf_getfuncs(cu_die, __die_walk_culines_cb, &param, 0);
6424cc9cec6SMasami Hiramatsu 		ret = param.retval;
6434cc9cec6SMasami Hiramatsu 	}
6444cc9cec6SMasami Hiramatsu 
6454cc9cec6SMasami Hiramatsu 	return ret;
6464cc9cec6SMasami Hiramatsu }
6474cc9cec6SMasami Hiramatsu 
648378eeaadSMasami Hiramatsu struct __find_variable_param {
649378eeaadSMasami Hiramatsu 	const char *name;
650378eeaadSMasami Hiramatsu 	Dwarf_Addr addr;
651378eeaadSMasami Hiramatsu };
652378eeaadSMasami Hiramatsu 
653016f262eSMasami Hiramatsu static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data)
6544ea42b18SMasami Hiramatsu {
655378eeaadSMasami Hiramatsu 	struct __find_variable_param *fvp = data;
656e92b85e1SMasami Hiramatsu 	int tag;
6574ea42b18SMasami Hiramatsu 
658e92b85e1SMasami Hiramatsu 	tag = dwarf_tag(die_mem);
659e92b85e1SMasami Hiramatsu 	if ((tag == DW_TAG_formal_parameter ||
660e92b85e1SMasami Hiramatsu 	     tag == DW_TAG_variable) &&
661378eeaadSMasami Hiramatsu 	    die_compare_name(die_mem, fvp->name))
662016f262eSMasami Hiramatsu 		return DIE_FIND_CB_FOUND;
6634ea42b18SMasami Hiramatsu 
664378eeaadSMasami Hiramatsu 	if (dwarf_haspc(die_mem, fvp->addr))
665016f262eSMasami Hiramatsu 		return DIE_FIND_CB_CONTINUE;
666378eeaadSMasami Hiramatsu 	else
667378eeaadSMasami Hiramatsu 		return DIE_FIND_CB_SIBLING;
6684ea42b18SMasami Hiramatsu }
6694ea42b18SMasami Hiramatsu 
670378eeaadSMasami Hiramatsu /* Find a variable called 'name' at given address */
671378eeaadSMasami Hiramatsu static Dwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name,
672378eeaadSMasami Hiramatsu 				       Dwarf_Addr addr, Dwarf_Die *die_mem)
673016f262eSMasami Hiramatsu {
674378eeaadSMasami Hiramatsu 	struct __find_variable_param fvp = { .name = name, .addr = addr};
675378eeaadSMasami Hiramatsu 
676378eeaadSMasami Hiramatsu 	return die_find_child(sp_die, __die_find_variable_cb, (void *)&fvp,
677016f262eSMasami Hiramatsu 			      die_mem);
678e92b85e1SMasami Hiramatsu }
679b0ef0732SMasami Hiramatsu 
6807df2f329SMasami Hiramatsu static int __die_find_member_cb(Dwarf_Die *die_mem, void *data)
6817df2f329SMasami Hiramatsu {
6827df2f329SMasami Hiramatsu 	const char *name = data;
6837df2f329SMasami Hiramatsu 
6847df2f329SMasami Hiramatsu 	if ((dwarf_tag(die_mem) == DW_TAG_member) &&
68582175633SMasami Hiramatsu 	    die_compare_name(die_mem, name))
6867df2f329SMasami Hiramatsu 		return DIE_FIND_CB_FOUND;
6877df2f329SMasami Hiramatsu 
6887df2f329SMasami Hiramatsu 	return DIE_FIND_CB_SIBLING;
6897df2f329SMasami Hiramatsu }
6907df2f329SMasami Hiramatsu 
6917df2f329SMasami Hiramatsu /* Find a member called 'name' */
6927df2f329SMasami Hiramatsu static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
6937df2f329SMasami Hiramatsu 				  Dwarf_Die *die_mem)
6947df2f329SMasami Hiramatsu {
6957df2f329SMasami Hiramatsu 	return die_find_child(st_die, __die_find_member_cb, (void *)name,
6967df2f329SMasami Hiramatsu 			      die_mem);
6977df2f329SMasami Hiramatsu }
6987df2f329SMasami Hiramatsu 
699cf6eb489SMasami Hiramatsu /* Get the name of given variable DIE */
700cf6eb489SMasami Hiramatsu static int die_get_typename(Dwarf_Die *vr_die, char *buf, int len)
701cf6eb489SMasami Hiramatsu {
702cf6eb489SMasami Hiramatsu 	Dwarf_Die type;
703cf6eb489SMasami Hiramatsu 	int tag, ret, ret2;
704cf6eb489SMasami Hiramatsu 	const char *tmp = "";
705cf6eb489SMasami Hiramatsu 
706cf6eb489SMasami Hiramatsu 	if (__die_get_real_type(vr_die, &type) == NULL)
707cf6eb489SMasami Hiramatsu 		return -ENOENT;
708cf6eb489SMasami Hiramatsu 
709cf6eb489SMasami Hiramatsu 	tag = dwarf_tag(&type);
710cf6eb489SMasami Hiramatsu 	if (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)
711cf6eb489SMasami Hiramatsu 		tmp = "*";
712cf6eb489SMasami Hiramatsu 	else if (tag == DW_TAG_subroutine_type) {
713cf6eb489SMasami Hiramatsu 		/* Function pointer */
714cf6eb489SMasami Hiramatsu 		ret = snprintf(buf, len, "(function_type)");
715cf6eb489SMasami Hiramatsu 		return (ret >= len) ? -E2BIG : ret;
716cf6eb489SMasami Hiramatsu 	} else {
717cf6eb489SMasami Hiramatsu 		if (!dwarf_diename(&type))
718cf6eb489SMasami Hiramatsu 			return -ENOENT;
719cf6eb489SMasami Hiramatsu 		if (tag == DW_TAG_union_type)
720cf6eb489SMasami Hiramatsu 			tmp = "union ";
721cf6eb489SMasami Hiramatsu 		else if (tag == DW_TAG_structure_type)
722cf6eb489SMasami Hiramatsu 			tmp = "struct ";
723cf6eb489SMasami Hiramatsu 		/* Write a base name */
724cf6eb489SMasami Hiramatsu 		ret = snprintf(buf, len, "%s%s", tmp, dwarf_diename(&type));
725cf6eb489SMasami Hiramatsu 		return (ret >= len) ? -E2BIG : ret;
726cf6eb489SMasami Hiramatsu 	}
727cf6eb489SMasami Hiramatsu 	ret = die_get_typename(&type, buf, len);
728cf6eb489SMasami Hiramatsu 	if (ret > 0) {
729cf6eb489SMasami Hiramatsu 		ret2 = snprintf(buf + ret, len - ret, "%s", tmp);
730cf6eb489SMasami Hiramatsu 		ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
731cf6eb489SMasami Hiramatsu 	}
732cf6eb489SMasami Hiramatsu 	return ret;
733cf6eb489SMasami Hiramatsu }
734cf6eb489SMasami Hiramatsu 
735cf6eb489SMasami Hiramatsu /* Get the name and type of given variable DIE, stored as "type\tname" */
736cf6eb489SMasami Hiramatsu static int die_get_varname(Dwarf_Die *vr_die, char *buf, int len)
737cf6eb489SMasami Hiramatsu {
738cf6eb489SMasami Hiramatsu 	int ret, ret2;
739cf6eb489SMasami Hiramatsu 
740cf6eb489SMasami Hiramatsu 	ret = die_get_typename(vr_die, buf, len);
741cf6eb489SMasami Hiramatsu 	if (ret < 0) {
742cf6eb489SMasami Hiramatsu 		pr_debug("Failed to get type, make it unknown.\n");
743cf6eb489SMasami Hiramatsu 		ret = snprintf(buf, len, "(unknown_type)");
744cf6eb489SMasami Hiramatsu 	}
745cf6eb489SMasami Hiramatsu 	if (ret > 0) {
746cf6eb489SMasami Hiramatsu 		ret2 = snprintf(buf + ret, len - ret, "\t%s",
747cf6eb489SMasami Hiramatsu 				dwarf_diename(vr_die));
748cf6eb489SMasami Hiramatsu 		ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
749cf6eb489SMasami Hiramatsu 	}
750cf6eb489SMasami Hiramatsu 	return ret;
751cf6eb489SMasami Hiramatsu }
752cf6eb489SMasami Hiramatsu 
7534ea42b18SMasami Hiramatsu /*
7544ea42b18SMasami Hiramatsu  * Probe finder related functions
7554ea42b18SMasami Hiramatsu  */
7564ea42b18SMasami Hiramatsu 
7570e60836bSSrikar Dronamraju static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs)
7584ea42b18SMasami Hiramatsu {
7590e60836bSSrikar Dronamraju 	struct probe_trace_arg_ref *ref;
7600e60836bSSrikar Dronamraju 	ref = zalloc(sizeof(struct probe_trace_arg_ref));
761b7dcb857SMasami Hiramatsu 	if (ref != NULL)
762b7dcb857SMasami Hiramatsu 		ref->offset = offs;
763b7dcb857SMasami Hiramatsu 	return ref;
764b7dcb857SMasami Hiramatsu }
765b7dcb857SMasami Hiramatsu 
766cf6eb489SMasami Hiramatsu /*
767cf6eb489SMasami Hiramatsu  * Convert a location into trace_arg.
768cf6eb489SMasami Hiramatsu  * If tvar == NULL, this just checks variable can be converted.
769cf6eb489SMasami Hiramatsu  */
770cf6eb489SMasami Hiramatsu static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr,
771cf6eb489SMasami Hiramatsu 				     Dwarf_Op *fb_ops,
772cf6eb489SMasami Hiramatsu 				     struct probe_trace_arg *tvar)
773b7dcb857SMasami Hiramatsu {
774b7dcb857SMasami Hiramatsu 	Dwarf_Attribute attr;
775b7dcb857SMasami Hiramatsu 	Dwarf_Op *op;
776b7dcb857SMasami Hiramatsu 	size_t nops;
777804b3606SMasami Hiramatsu 	unsigned int regn;
778804b3606SMasami Hiramatsu 	Dwarf_Word offs = 0;
7794235b045SMasami Hiramatsu 	bool ref = false;
7804ea42b18SMasami Hiramatsu 	const char *regs;
781b7dcb857SMasami Hiramatsu 	int ret;
782b7dcb857SMasami Hiramatsu 
783632941c4SMasami Hiramatsu 	if (dwarf_attr(vr_die, DW_AT_external, &attr) != NULL)
784632941c4SMasami Hiramatsu 		goto static_var;
785632941c4SMasami Hiramatsu 
786b7dcb857SMasami Hiramatsu 	/* TODO: handle more than 1 exprs */
787b7dcb857SMasami Hiramatsu 	if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL ||
788cf6eb489SMasami Hiramatsu 	    dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0 ||
789b7dcb857SMasami Hiramatsu 	    nops == 0) {
790b7dcb857SMasami Hiramatsu 		/* TODO: Support const_value */
791b7dcb857SMasami Hiramatsu 		return -ENOENT;
792b7dcb857SMasami Hiramatsu 	}
793b7dcb857SMasami Hiramatsu 
794b7dcb857SMasami Hiramatsu 	if (op->atom == DW_OP_addr) {
795632941c4SMasami Hiramatsu static_var:
796cf6eb489SMasami Hiramatsu 		if (!tvar)
797cf6eb489SMasami Hiramatsu 			return 0;
798b7dcb857SMasami Hiramatsu 		/* Static variables on memory (not stack), make @varname */
799b7dcb857SMasami Hiramatsu 		ret = strlen(dwarf_diename(vr_die));
800b7dcb857SMasami Hiramatsu 		tvar->value = zalloc(ret + 2);
801b7dcb857SMasami Hiramatsu 		if (tvar->value == NULL)
802b7dcb857SMasami Hiramatsu 			return -ENOMEM;
803b7dcb857SMasami Hiramatsu 		snprintf(tvar->value, ret + 2, "@%s", dwarf_diename(vr_die));
804b7dcb857SMasami Hiramatsu 		tvar->ref = alloc_trace_arg_ref((long)offs);
805b7dcb857SMasami Hiramatsu 		if (tvar->ref == NULL)
806b7dcb857SMasami Hiramatsu 			return -ENOMEM;
807b7dcb857SMasami Hiramatsu 		return 0;
808b7dcb857SMasami Hiramatsu 	}
8094ea42b18SMasami Hiramatsu 
8104ea42b18SMasami Hiramatsu 	/* If this is based on frame buffer, set the offset */
811804b3606SMasami Hiramatsu 	if (op->atom == DW_OP_fbreg) {
812cf6eb489SMasami Hiramatsu 		if (fb_ops == NULL)
813b55a87adSMasami Hiramatsu 			return -ENOTSUP;
8144235b045SMasami Hiramatsu 		ref = true;
815804b3606SMasami Hiramatsu 		offs = op->number;
816cf6eb489SMasami Hiramatsu 		op = &fb_ops[0];
817804b3606SMasami Hiramatsu 	}
8184ea42b18SMasami Hiramatsu 
819804b3606SMasami Hiramatsu 	if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) {
820804b3606SMasami Hiramatsu 		regn = op->atom - DW_OP_breg0;
821804b3606SMasami Hiramatsu 		offs += op->number;
8224235b045SMasami Hiramatsu 		ref = true;
823804b3606SMasami Hiramatsu 	} else if (op->atom >= DW_OP_reg0 && op->atom <= DW_OP_reg31) {
824804b3606SMasami Hiramatsu 		regn = op->atom - DW_OP_reg0;
825804b3606SMasami Hiramatsu 	} else if (op->atom == DW_OP_bregx) {
826804b3606SMasami Hiramatsu 		regn = op->number;
827804b3606SMasami Hiramatsu 		offs += op->number2;
8284235b045SMasami Hiramatsu 		ref = true;
829804b3606SMasami Hiramatsu 	} else if (op->atom == DW_OP_regx) {
830804b3606SMasami Hiramatsu 		regn = op->number;
831b55a87adSMasami Hiramatsu 	} else {
832cf6eb489SMasami Hiramatsu 		pr_debug("DW_OP %x is not supported.\n", op->atom);
833b55a87adSMasami Hiramatsu 		return -ENOTSUP;
834b55a87adSMasami Hiramatsu 	}
8354ea42b18SMasami Hiramatsu 
836cf6eb489SMasami Hiramatsu 	if (!tvar)
837cf6eb489SMasami Hiramatsu 		return 0;
838cf6eb489SMasami Hiramatsu 
8394ea42b18SMasami Hiramatsu 	regs = get_arch_regstr(regn);
840b55a87adSMasami Hiramatsu 	if (!regs) {
841cf6eb489SMasami Hiramatsu 		/* This should be a bug in DWARF or this tool */
8420e43e5d2SMasami Hiramatsu 		pr_warning("Mapping for the register number %u "
8430e43e5d2SMasami Hiramatsu 			   "missing on this architecture.\n", regn);
844b55a87adSMasami Hiramatsu 		return -ERANGE;
845b55a87adSMasami Hiramatsu 	}
8464ea42b18SMasami Hiramatsu 
84702b95dadSMasami Hiramatsu 	tvar->value = strdup(regs);
84802b95dadSMasami Hiramatsu 	if (tvar->value == NULL)
84902b95dadSMasami Hiramatsu 		return -ENOMEM;
85002b95dadSMasami Hiramatsu 
8514235b045SMasami Hiramatsu 	if (ref) {
852b7dcb857SMasami Hiramatsu 		tvar->ref = alloc_trace_arg_ref((long)offs);
853e334016fSMasami Hiramatsu 		if (tvar->ref == NULL)
854e334016fSMasami Hiramatsu 			return -ENOMEM;
8554235b045SMasami Hiramatsu 	}
856b55a87adSMasami Hiramatsu 	return 0;
8574ea42b18SMasami Hiramatsu }
8584ea42b18SMasami Hiramatsu 
859*124bb83cSMasami Hiramatsu #define BYTES_TO_BITS(nb)	((nb) * BITS_PER_LONG / sizeof(long))
860*124bb83cSMasami Hiramatsu 
861b55a87adSMasami Hiramatsu static int convert_variable_type(Dwarf_Die *vr_die,
8620e60836bSSrikar Dronamraju 				 struct probe_trace_arg *tvar,
86373317b95SMasami Hiramatsu 				 const char *cast)
8644984912eSMasami Hiramatsu {
8650e60836bSSrikar Dronamraju 	struct probe_trace_arg_ref **ref_ptr = &tvar->ref;
8664984912eSMasami Hiramatsu 	Dwarf_Die type;
8674984912eSMasami Hiramatsu 	char buf[16];
8684984912eSMasami Hiramatsu 	int ret;
8694984912eSMasami Hiramatsu 
87073317b95SMasami Hiramatsu 	/* TODO: check all types */
87173317b95SMasami Hiramatsu 	if (cast && strcmp(cast, "string") != 0) {
87273317b95SMasami Hiramatsu 		/* Non string type is OK */
87373317b95SMasami Hiramatsu 		tvar->type = strdup(cast);
87473317b95SMasami Hiramatsu 		return (tvar->type == NULL) ? -ENOMEM : 0;
87573317b95SMasami Hiramatsu 	}
87673317b95SMasami Hiramatsu 
877*124bb83cSMasami Hiramatsu 	if (die_get_bit_size(vr_die) != 0) {
878*124bb83cSMasami Hiramatsu 		/* This is a bitfield */
879*124bb83cSMasami Hiramatsu 		ret = snprintf(buf, 16, "b%d@%d/%zd", die_get_bit_size(vr_die),
880*124bb83cSMasami Hiramatsu 				die_get_bit_offset(vr_die),
881*124bb83cSMasami Hiramatsu 				BYTES_TO_BITS(die_get_byte_size(vr_die)));
882*124bb83cSMasami Hiramatsu 		goto formatted;
883*124bb83cSMasami Hiramatsu 	}
884*124bb83cSMasami Hiramatsu 
885b55a87adSMasami Hiramatsu 	if (die_get_real_type(vr_die, &type) == NULL) {
886b55a87adSMasami Hiramatsu 		pr_warning("Failed to get a type information of %s.\n",
8874984912eSMasami Hiramatsu 			   dwarf_diename(vr_die));
888b55a87adSMasami Hiramatsu 		return -ENOENT;
889b55a87adSMasami Hiramatsu 	}
8904984912eSMasami Hiramatsu 
891b2a3c12bSMasami Hiramatsu 	pr_debug("%s type is %s.\n",
892b2a3c12bSMasami Hiramatsu 		 dwarf_diename(vr_die), dwarf_diename(&type));
893b2a3c12bSMasami Hiramatsu 
89473317b95SMasami Hiramatsu 	if (cast && strcmp(cast, "string") == 0) {	/* String type */
89573317b95SMasami Hiramatsu 		ret = dwarf_tag(&type);
89673317b95SMasami Hiramatsu 		if (ret != DW_TAG_pointer_type &&
89773317b95SMasami Hiramatsu 		    ret != DW_TAG_array_type) {
89873317b95SMasami Hiramatsu 			pr_warning("Failed to cast into string: "
8990e43e5d2SMasami Hiramatsu 				   "%s(%s) is not a pointer nor array.\n",
90073317b95SMasami Hiramatsu 				   dwarf_diename(vr_die), dwarf_diename(&type));
90173317b95SMasami Hiramatsu 			return -EINVAL;
90273317b95SMasami Hiramatsu 		}
90373317b95SMasami Hiramatsu 		if (ret == DW_TAG_pointer_type) {
90473317b95SMasami Hiramatsu 			if (die_get_real_type(&type, &type) == NULL) {
9050e43e5d2SMasami Hiramatsu 				pr_warning("Failed to get a type"
9060e43e5d2SMasami Hiramatsu 					   " information.\n");
90773317b95SMasami Hiramatsu 				return -ENOENT;
90873317b95SMasami Hiramatsu 			}
90973317b95SMasami Hiramatsu 			while (*ref_ptr)
91073317b95SMasami Hiramatsu 				ref_ptr = &(*ref_ptr)->next;
91173317b95SMasami Hiramatsu 			/* Add new reference with offset +0 */
9120e60836bSSrikar Dronamraju 			*ref_ptr = zalloc(sizeof(struct probe_trace_arg_ref));
91373317b95SMasami Hiramatsu 			if (*ref_ptr == NULL) {
91473317b95SMasami Hiramatsu 				pr_warning("Out of memory error\n");
91573317b95SMasami Hiramatsu 				return -ENOMEM;
91673317b95SMasami Hiramatsu 			}
91773317b95SMasami Hiramatsu 		}
91882175633SMasami Hiramatsu 		if (!die_compare_name(&type, "char") &&
91982175633SMasami Hiramatsu 		    !die_compare_name(&type, "unsigned char")) {
92073317b95SMasami Hiramatsu 			pr_warning("Failed to cast into string: "
9210e43e5d2SMasami Hiramatsu 				   "%s is not (unsigned) char *.\n",
92273317b95SMasami Hiramatsu 				   dwarf_diename(vr_die));
92373317b95SMasami Hiramatsu 			return -EINVAL;
92473317b95SMasami Hiramatsu 		}
92573317b95SMasami Hiramatsu 		tvar->type = strdup(cast);
92673317b95SMasami Hiramatsu 		return (tvar->type == NULL) ? -ENOMEM : 0;
92773317b95SMasami Hiramatsu 	}
92873317b95SMasami Hiramatsu 
929*124bb83cSMasami Hiramatsu 	ret = BYTES_TO_BITS(die_get_byte_size(&type));
930*124bb83cSMasami Hiramatsu 	if (!ret)
931*124bb83cSMasami Hiramatsu 		/* No size ... try to use default type */
932*124bb83cSMasami Hiramatsu 		return 0;
933*124bb83cSMasami Hiramatsu 
9344984912eSMasami Hiramatsu 	/* Check the bitwidth */
9354984912eSMasami Hiramatsu 	if (ret > MAX_BASIC_TYPE_BITS) {
936*124bb83cSMasami Hiramatsu 		pr_info("%s exceeds max-bitwidth. Cut down to %d bits.\n",
9374984912eSMasami Hiramatsu 			dwarf_diename(&type), MAX_BASIC_TYPE_BITS);
9384984912eSMasami Hiramatsu 		ret = MAX_BASIC_TYPE_BITS;
9394984912eSMasami Hiramatsu 	}
9404984912eSMasami Hiramatsu 	ret = snprintf(buf, 16, "%c%d",
9414984912eSMasami Hiramatsu 		       die_is_signed_type(&type) ? 's' : 'u', ret);
942*124bb83cSMasami Hiramatsu 
943*124bb83cSMasami Hiramatsu formatted:
944b55a87adSMasami Hiramatsu 	if (ret < 0 || ret >= 16) {
945b55a87adSMasami Hiramatsu 		if (ret >= 16)
946b55a87adSMasami Hiramatsu 			ret = -E2BIG;
947b55a87adSMasami Hiramatsu 		pr_warning("Failed to convert variable type: %s\n",
948b55a87adSMasami Hiramatsu 			   strerror(-ret));
949b55a87adSMasami Hiramatsu 		return ret;
950b55a87adSMasami Hiramatsu 	}
95173317b95SMasami Hiramatsu 	tvar->type = strdup(buf);
95273317b95SMasami Hiramatsu 	if (tvar->type == NULL)
95302b95dadSMasami Hiramatsu 		return -ENOMEM;
954b55a87adSMasami Hiramatsu 	return 0;
9554984912eSMasami Hiramatsu }
9564984912eSMasami Hiramatsu 
957b55a87adSMasami Hiramatsu static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
9587df2f329SMasami Hiramatsu 				    struct perf_probe_arg_field *field,
9590e60836bSSrikar Dronamraju 				    struct probe_trace_arg_ref **ref_ptr,
9604984912eSMasami Hiramatsu 				    Dwarf_Die *die_mem)
9617df2f329SMasami Hiramatsu {
9620e60836bSSrikar Dronamraju 	struct probe_trace_arg_ref *ref = *ref_ptr;
9637df2f329SMasami Hiramatsu 	Dwarf_Die type;
9647df2f329SMasami Hiramatsu 	Dwarf_Word offs;
965b2a3c12bSMasami Hiramatsu 	int ret, tag;
9667df2f329SMasami Hiramatsu 
9677df2f329SMasami Hiramatsu 	pr_debug("converting %s in %s\n", field->name, varname);
968b55a87adSMasami Hiramatsu 	if (die_get_real_type(vr_die, &type) == NULL) {
969b55a87adSMasami Hiramatsu 		pr_warning("Failed to get the type of %s.\n", varname);
970b55a87adSMasami Hiramatsu 		return -ENOENT;
971b55a87adSMasami Hiramatsu 	}
972b2a3c12bSMasami Hiramatsu 	pr_debug2("Var real type: (%x)\n", (unsigned)dwarf_dieoffset(&type));
973b2a3c12bSMasami Hiramatsu 	tag = dwarf_tag(&type);
9747df2f329SMasami Hiramatsu 
975b2a3c12bSMasami Hiramatsu 	if (field->name[0] == '[' &&
976b2a3c12bSMasami Hiramatsu 	    (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)) {
977b2a3c12bSMasami Hiramatsu 		if (field->next)
978b2a3c12bSMasami Hiramatsu 			/* Save original type for next field */
979b2a3c12bSMasami Hiramatsu 			memcpy(die_mem, &type, sizeof(*die_mem));
980b2a3c12bSMasami Hiramatsu 		/* Get the type of this array */
981b2a3c12bSMasami Hiramatsu 		if (die_get_real_type(&type, &type) == NULL) {
982b2a3c12bSMasami Hiramatsu 			pr_warning("Failed to get the type of %s.\n", varname);
983b2a3c12bSMasami Hiramatsu 			return -ENOENT;
984b2a3c12bSMasami Hiramatsu 		}
985b2a3c12bSMasami Hiramatsu 		pr_debug2("Array real type: (%x)\n",
986b2a3c12bSMasami Hiramatsu 			 (unsigned)dwarf_dieoffset(&type));
987b2a3c12bSMasami Hiramatsu 		if (tag == DW_TAG_pointer_type) {
9880e60836bSSrikar Dronamraju 			ref = zalloc(sizeof(struct probe_trace_arg_ref));
989b2a3c12bSMasami Hiramatsu 			if (ref == NULL)
990b2a3c12bSMasami Hiramatsu 				return -ENOMEM;
991b2a3c12bSMasami Hiramatsu 			if (*ref_ptr)
992b2a3c12bSMasami Hiramatsu 				(*ref_ptr)->next = ref;
993b2a3c12bSMasami Hiramatsu 			else
994b2a3c12bSMasami Hiramatsu 				*ref_ptr = ref;
995b2a3c12bSMasami Hiramatsu 		}
996b2a3c12bSMasami Hiramatsu 		ref->offset += die_get_byte_size(&type) * field->index;
997b2a3c12bSMasami Hiramatsu 		if (!field->next)
998b2a3c12bSMasami Hiramatsu 			/* Save vr_die for converting types */
999b2a3c12bSMasami Hiramatsu 			memcpy(die_mem, vr_die, sizeof(*die_mem));
1000b2a3c12bSMasami Hiramatsu 		goto next;
1001b2a3c12bSMasami Hiramatsu 	} else if (tag == DW_TAG_pointer_type) {
10027df2f329SMasami Hiramatsu 		/* Check the pointer and dereference */
1003b55a87adSMasami Hiramatsu 		if (!field->ref) {
1004b55a87adSMasami Hiramatsu 			pr_err("Semantic error: %s must be referred by '->'\n",
10057df2f329SMasami Hiramatsu 			       field->name);
1006b55a87adSMasami Hiramatsu 			return -EINVAL;
1007b55a87adSMasami Hiramatsu 		}
10087df2f329SMasami Hiramatsu 		/* Get the type pointed by this pointer */
1009b55a87adSMasami Hiramatsu 		if (die_get_real_type(&type, &type) == NULL) {
1010b55a87adSMasami Hiramatsu 			pr_warning("Failed to get the type of %s.\n", varname);
1011b55a87adSMasami Hiramatsu 			return -ENOENT;
1012b55a87adSMasami Hiramatsu 		}
101312e5a7aeSMasami Hiramatsu 		/* Verify it is a data structure  */
1014b55a87adSMasami Hiramatsu 		if (dwarf_tag(&type) != DW_TAG_structure_type) {
1015b55a87adSMasami Hiramatsu 			pr_warning("%s is not a data structure.\n", varname);
1016b55a87adSMasami Hiramatsu 			return -EINVAL;
1017b55a87adSMasami Hiramatsu 		}
101812e5a7aeSMasami Hiramatsu 
10190e60836bSSrikar Dronamraju 		ref = zalloc(sizeof(struct probe_trace_arg_ref));
1020e334016fSMasami Hiramatsu 		if (ref == NULL)
1021e334016fSMasami Hiramatsu 			return -ENOMEM;
10227df2f329SMasami Hiramatsu 		if (*ref_ptr)
10237df2f329SMasami Hiramatsu 			(*ref_ptr)->next = ref;
10247df2f329SMasami Hiramatsu 		else
10257df2f329SMasami Hiramatsu 			*ref_ptr = ref;
10267df2f329SMasami Hiramatsu 	} else {
102712e5a7aeSMasami Hiramatsu 		/* Verify it is a data structure  */
1028b2a3c12bSMasami Hiramatsu 		if (tag != DW_TAG_structure_type) {
1029b55a87adSMasami Hiramatsu 			pr_warning("%s is not a data structure.\n", varname);
1030b55a87adSMasami Hiramatsu 			return -EINVAL;
1031b55a87adSMasami Hiramatsu 		}
1032b2a3c12bSMasami Hiramatsu 		if (field->name[0] == '[') {
10330e43e5d2SMasami Hiramatsu 			pr_err("Semantic error: %s is not a pointor"
10340e43e5d2SMasami Hiramatsu 			       " nor array.\n", varname);
1035b2a3c12bSMasami Hiramatsu 			return -EINVAL;
1036b2a3c12bSMasami Hiramatsu 		}
1037b55a87adSMasami Hiramatsu 		if (field->ref) {
1038b55a87adSMasami Hiramatsu 			pr_err("Semantic error: %s must be referred by '.'\n",
10397df2f329SMasami Hiramatsu 			       field->name);
1040b55a87adSMasami Hiramatsu 			return -EINVAL;
1041b55a87adSMasami Hiramatsu 		}
1042b55a87adSMasami Hiramatsu 		if (!ref) {
1043b55a87adSMasami Hiramatsu 			pr_warning("Structure on a register is not "
1044b55a87adSMasami Hiramatsu 				   "supported yet.\n");
1045b55a87adSMasami Hiramatsu 			return -ENOTSUP;
1046b55a87adSMasami Hiramatsu 		}
10477df2f329SMasami Hiramatsu 	}
10487df2f329SMasami Hiramatsu 
1049b55a87adSMasami Hiramatsu 	if (die_find_member(&type, field->name, die_mem) == NULL) {
1050b55a87adSMasami Hiramatsu 		pr_warning("%s(tyep:%s) has no member %s.\n", varname,
10517df2f329SMasami Hiramatsu 			   dwarf_diename(&type), field->name);
1052b55a87adSMasami Hiramatsu 		return -EINVAL;
1053b55a87adSMasami Hiramatsu 	}
10547df2f329SMasami Hiramatsu 
10557df2f329SMasami Hiramatsu 	/* Get the offset of the field */
1056de1439d8SMasami Hiramatsu 	ret = die_get_data_member_location(die_mem, &offs);
1057de1439d8SMasami Hiramatsu 	if (ret < 0) {
1058b55a87adSMasami Hiramatsu 		pr_warning("Failed to get the offset of %s.\n", field->name);
1059de1439d8SMasami Hiramatsu 		return ret;
1060b55a87adSMasami Hiramatsu 	}
10617df2f329SMasami Hiramatsu 	ref->offset += (long)offs;
10627df2f329SMasami Hiramatsu 
1063b2a3c12bSMasami Hiramatsu next:
10647df2f329SMasami Hiramatsu 	/* Converting next field */
10657df2f329SMasami Hiramatsu 	if (field->next)
1066b55a87adSMasami Hiramatsu 		return convert_variable_fields(die_mem, field->name,
1067b55a87adSMasami Hiramatsu 					field->next, &ref, die_mem);
1068b55a87adSMasami Hiramatsu 	else
1069b55a87adSMasami Hiramatsu 		return 0;
10707df2f329SMasami Hiramatsu }
10717df2f329SMasami Hiramatsu 
10724ea42b18SMasami Hiramatsu /* Show a variables in kprobe event format */
1073b55a87adSMasami Hiramatsu static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
10744ea42b18SMasami Hiramatsu {
10754984912eSMasami Hiramatsu 	Dwarf_Die die_mem;
10764ea42b18SMasami Hiramatsu 	int ret;
10774ea42b18SMasami Hiramatsu 
1078b7dcb857SMasami Hiramatsu 	pr_debug("Converting variable %s into trace event.\n",
1079b7dcb857SMasami Hiramatsu 		 dwarf_diename(vr_die));
1080804b3606SMasami Hiramatsu 
1081cf6eb489SMasami Hiramatsu 	ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops,
1082cf6eb489SMasami Hiramatsu 					pf->tvar);
1083cf6eb489SMasami Hiramatsu 	if (ret == -ENOENT)
1084cf6eb489SMasami Hiramatsu 		pr_err("Failed to find the location of %s at this address.\n"
1085cf6eb489SMasami Hiramatsu 		       " Perhaps, it has been optimized out.\n", pf->pvar->var);
1086cf6eb489SMasami Hiramatsu 	else if (ret == -ENOTSUP)
1087cf6eb489SMasami Hiramatsu 		pr_err("Sorry, we don't support this variable location yet.\n");
1088cf6eb489SMasami Hiramatsu 	else if (pf->pvar->field) {
1089b55a87adSMasami Hiramatsu 		ret = convert_variable_fields(vr_die, pf->pvar->var,
10904984912eSMasami Hiramatsu 					      pf->pvar->field, &pf->tvar->ref,
10914984912eSMasami Hiramatsu 					      &die_mem);
10924984912eSMasami Hiramatsu 		vr_die = &die_mem;
10934984912eSMasami Hiramatsu 	}
109473317b95SMasami Hiramatsu 	if (ret == 0)
109573317b95SMasami Hiramatsu 		ret = convert_variable_type(vr_die, pf->tvar, pf->pvar->type);
1096804b3606SMasami Hiramatsu 	/* *expr will be cached in libdw. Don't free it. */
1097b55a87adSMasami Hiramatsu 	return ret;
10984ea42b18SMasami Hiramatsu }
10994ea42b18SMasami Hiramatsu 
11004ea42b18SMasami Hiramatsu /* Find a variable in a subprogram die */
1101b55a87adSMasami Hiramatsu static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
11024ea42b18SMasami Hiramatsu {
1103b7dcb857SMasami Hiramatsu 	Dwarf_Die vr_die, *scopes;
110411a1ca35SMasami Hiramatsu 	char buf[32], *ptr;
1105b7dcb857SMasami Hiramatsu 	int ret, nscopes;
11064ea42b18SMasami Hiramatsu 
1107367e94c1SMasami Hiramatsu 	if (!is_c_varname(pf->pvar->var)) {
1108367e94c1SMasami Hiramatsu 		/* Copy raw parameters */
1109367e94c1SMasami Hiramatsu 		pf->tvar->value = strdup(pf->pvar->var);
1110367e94c1SMasami Hiramatsu 		if (pf->tvar->value == NULL)
1111367e94c1SMasami Hiramatsu 			return -ENOMEM;
1112367e94c1SMasami Hiramatsu 		if (pf->pvar->type) {
1113367e94c1SMasami Hiramatsu 			pf->tvar->type = strdup(pf->pvar->type);
1114367e94c1SMasami Hiramatsu 			if (pf->tvar->type == NULL)
1115367e94c1SMasami Hiramatsu 				return -ENOMEM;
1116367e94c1SMasami Hiramatsu 		}
1117367e94c1SMasami Hiramatsu 		if (pf->pvar->name) {
1118367e94c1SMasami Hiramatsu 			pf->tvar->name = strdup(pf->pvar->name);
1119367e94c1SMasami Hiramatsu 			if (pf->tvar->name == NULL)
1120367e94c1SMasami Hiramatsu 				return -ENOMEM;
1121367e94c1SMasami Hiramatsu 		} else
1122367e94c1SMasami Hiramatsu 			pf->tvar->name = NULL;
1123367e94c1SMasami Hiramatsu 		return 0;
1124367e94c1SMasami Hiramatsu 	}
1125367e94c1SMasami Hiramatsu 
112648481938SMasami Hiramatsu 	if (pf->pvar->name)
112702b95dadSMasami Hiramatsu 		pf->tvar->name = strdup(pf->pvar->name);
112848481938SMasami Hiramatsu 	else {
112902b95dadSMasami Hiramatsu 		ret = synthesize_perf_probe_arg(pf->pvar, buf, 32);
113002b95dadSMasami Hiramatsu 		if (ret < 0)
113102b95dadSMasami Hiramatsu 			return ret;
113211a1ca35SMasami Hiramatsu 		ptr = strchr(buf, ':');	/* Change type separator to _ */
113311a1ca35SMasami Hiramatsu 		if (ptr)
113411a1ca35SMasami Hiramatsu 			*ptr = '_';
113502b95dadSMasami Hiramatsu 		pf->tvar->name = strdup(buf);
113648481938SMasami Hiramatsu 	}
113702b95dadSMasami Hiramatsu 	if (pf->tvar->name == NULL)
113802b95dadSMasami Hiramatsu 		return -ENOMEM;
113948481938SMasami Hiramatsu 
11404235b045SMasami Hiramatsu 	pr_debug("Searching '%s' variable in context.\n",
114148481938SMasami Hiramatsu 		 pf->pvar->var);
11424ea42b18SMasami Hiramatsu 	/* Search child die for local variables and parameters. */
1143378eeaadSMasami Hiramatsu 	if (die_find_variable_at(sp_die, pf->pvar->var, pf->addr, &vr_die))
1144b7dcb857SMasami Hiramatsu 		ret = convert_variable(&vr_die, pf);
1145b7dcb857SMasami Hiramatsu 	else {
1146b7dcb857SMasami Hiramatsu 		/* Search upper class */
1147b7dcb857SMasami Hiramatsu 		nscopes = dwarf_getscopes_die(sp_die, &scopes);
1148632941c4SMasami Hiramatsu 		while (nscopes-- > 1) {
1149632941c4SMasami Hiramatsu 			pr_debug("Searching variables in %s\n",
1150632941c4SMasami Hiramatsu 				 dwarf_diename(&scopes[nscopes]));
1151632941c4SMasami Hiramatsu 			/* We should check this scope, so give dummy address */
1152632941c4SMasami Hiramatsu 			if (die_find_variable_at(&scopes[nscopes],
1153632941c4SMasami Hiramatsu 						 pf->pvar->var, 0,
1154632941c4SMasami Hiramatsu 						 &vr_die)) {
1155b7dcb857SMasami Hiramatsu 				ret = convert_variable(&vr_die, pf);
1156632941c4SMasami Hiramatsu 				goto found;
1157632941c4SMasami Hiramatsu 			}
1158632941c4SMasami Hiramatsu 		}
1159632941c4SMasami Hiramatsu 		if (scopes)
1160b7dcb857SMasami Hiramatsu 			free(scopes);
1161b7dcb857SMasami Hiramatsu 		ret = -ENOENT;
1162b7dcb857SMasami Hiramatsu 	}
1163632941c4SMasami Hiramatsu found:
1164b7dcb857SMasami Hiramatsu 	if (ret < 0)
1165b55a87adSMasami Hiramatsu 		pr_warning("Failed to find '%s' in this function.\n",
116648481938SMasami Hiramatsu 			   pf->pvar->var);
1167b7dcb857SMasami Hiramatsu 	return ret;
11684ea42b18SMasami Hiramatsu }
11694ea42b18SMasami Hiramatsu 
1170cf6eb489SMasami Hiramatsu /* Convert subprogram DIE to trace point */
1171cf6eb489SMasami Hiramatsu static int convert_to_trace_point(Dwarf_Die *sp_die, Dwarf_Addr paddr,
1172cf6eb489SMasami Hiramatsu 				  bool retprobe, struct probe_trace_point *tp)
11734ea42b18SMasami Hiramatsu {
1174e92b85e1SMasami Hiramatsu 	Dwarf_Addr eaddr;
1175804b3606SMasami Hiramatsu 	const char *name;
1176cf6eb489SMasami Hiramatsu 
1177cf6eb489SMasami Hiramatsu 	/* Copy the name of probe point */
1178cf6eb489SMasami Hiramatsu 	name = dwarf_diename(sp_die);
1179cf6eb489SMasami Hiramatsu 	if (name) {
1180cf6eb489SMasami Hiramatsu 		if (dwarf_entrypc(sp_die, &eaddr) != 0) {
11810e43e5d2SMasami Hiramatsu 			pr_warning("Failed to get entry address of %s\n",
1182cf6eb489SMasami Hiramatsu 				   dwarf_diename(sp_die));
1183cf6eb489SMasami Hiramatsu 			return -ENOENT;
1184cf6eb489SMasami Hiramatsu 		}
1185cf6eb489SMasami Hiramatsu 		tp->symbol = strdup(name);
1186cf6eb489SMasami Hiramatsu 		if (tp->symbol == NULL)
1187cf6eb489SMasami Hiramatsu 			return -ENOMEM;
1188cf6eb489SMasami Hiramatsu 		tp->offset = (unsigned long)(paddr - eaddr);
1189cf6eb489SMasami Hiramatsu 	} else
1190cf6eb489SMasami Hiramatsu 		/* This function has no name. */
1191cf6eb489SMasami Hiramatsu 		tp->offset = (unsigned long)paddr;
1192cf6eb489SMasami Hiramatsu 
1193cf6eb489SMasami Hiramatsu 	/* Return probe must be on the head of a subprogram */
1194cf6eb489SMasami Hiramatsu 	if (retprobe) {
1195cf6eb489SMasami Hiramatsu 		if (eaddr != paddr) {
1196cf6eb489SMasami Hiramatsu 			pr_warning("Return probe must be on the head of"
11970e43e5d2SMasami Hiramatsu 				   " a real function.\n");
1198cf6eb489SMasami Hiramatsu 			return -EINVAL;
1199cf6eb489SMasami Hiramatsu 		}
1200cf6eb489SMasami Hiramatsu 		tp->retprobe = true;
1201cf6eb489SMasami Hiramatsu 	}
1202cf6eb489SMasami Hiramatsu 
1203cf6eb489SMasami Hiramatsu 	return 0;
1204cf6eb489SMasami Hiramatsu }
1205cf6eb489SMasami Hiramatsu 
1206cf6eb489SMasami Hiramatsu /* Call probe_finder callback with real subprogram DIE */
1207cf6eb489SMasami Hiramatsu static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf)
1208cf6eb489SMasami Hiramatsu {
1209cf6eb489SMasami Hiramatsu 	Dwarf_Die die_mem;
1210804b3606SMasami Hiramatsu 	Dwarf_Attribute fb_attr;
1211804b3606SMasami Hiramatsu 	size_t nops;
1212cf6eb489SMasami Hiramatsu 	int ret;
12134235b045SMasami Hiramatsu 
1214e92b85e1SMasami Hiramatsu 	/* If no real subprogram, find a real one */
1215e92b85e1SMasami Hiramatsu 	if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) {
121695a3e4c4SMasami Hiramatsu 		sp_die = die_find_real_subprogram(&pf->cu_die,
1217e92b85e1SMasami Hiramatsu 						  pf->addr, &die_mem);
1218b55a87adSMasami Hiramatsu 		if (!sp_die) {
1219b55a87adSMasami Hiramatsu 			pr_warning("Failed to find probe point in any "
1220b55a87adSMasami Hiramatsu 				   "functions.\n");
1221b55a87adSMasami Hiramatsu 			return -ENOENT;
1222b55a87adSMasami Hiramatsu 		}
1223e92b85e1SMasami Hiramatsu 	}
1224e92b85e1SMasami Hiramatsu 
1225804b3606SMasami Hiramatsu 	/* Get the frame base attribute/ops */
1226804b3606SMasami Hiramatsu 	dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr);
1227d0cb4260SMasami Hiramatsu 	ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1);
1228a34a9854SMasami Hiramatsu 	if (ret <= 0 || nops == 0) {
1229804b3606SMasami Hiramatsu 		pf->fb_ops = NULL;
12307752f1b0SMasami Hiramatsu #if _ELFUTILS_PREREQ(0, 142)
1231a34a9854SMasami Hiramatsu 	} else if (nops == 1 && pf->fb_ops[0].atom == DW_OP_call_frame_cfa &&
1232a34a9854SMasami Hiramatsu 		   pf->cfi != NULL) {
1233a34a9854SMasami Hiramatsu 		Dwarf_Frame *frame;
1234b55a87adSMasami Hiramatsu 		if (dwarf_cfi_addrframe(pf->cfi, pf->addr, &frame) != 0 ||
1235b55a87adSMasami Hiramatsu 		    dwarf_frame_cfa(frame, &pf->fb_ops, &nops) != 0) {
12360e43e5d2SMasami Hiramatsu 			pr_warning("Failed to get call frame on 0x%jx\n",
1237b55a87adSMasami Hiramatsu 				   (uintmax_t)pf->addr);
1238b55a87adSMasami Hiramatsu 			return -ENOENT;
1239b55a87adSMasami Hiramatsu 		}
12407752f1b0SMasami Hiramatsu #endif
1241a34a9854SMasami Hiramatsu 	}
1242804b3606SMasami Hiramatsu 
1243cf6eb489SMasami Hiramatsu 	/* Call finder's callback handler */
1244cf6eb489SMasami Hiramatsu 	ret = pf->callback(sp_die, pf);
1245804b3606SMasami Hiramatsu 
1246804b3606SMasami Hiramatsu 	/* *pf->fb_ops will be cached in libdw. Don't free it. */
1247804b3606SMasami Hiramatsu 	pf->fb_ops = NULL;
1248cf6eb489SMasami Hiramatsu 
1249cf6eb489SMasami Hiramatsu 	return ret;
12504ea42b18SMasami Hiramatsu }
12514ea42b18SMasami Hiramatsu 
12524cc9cec6SMasami Hiramatsu static int probe_point_line_walker(const char *fname, int lineno,
12534cc9cec6SMasami Hiramatsu 				   Dwarf_Addr addr, void *data)
12544cc9cec6SMasami Hiramatsu {
12554cc9cec6SMasami Hiramatsu 	struct probe_finder *pf = data;
12564cc9cec6SMasami Hiramatsu 	int ret;
12574cc9cec6SMasami Hiramatsu 
12584cc9cec6SMasami Hiramatsu 	if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0)
12594cc9cec6SMasami Hiramatsu 		return 0;
12604cc9cec6SMasami Hiramatsu 
12614cc9cec6SMasami Hiramatsu 	pf->addr = addr;
12624cc9cec6SMasami Hiramatsu 	ret = call_probe_finder(NULL, pf);
12634cc9cec6SMasami Hiramatsu 
12644cc9cec6SMasami Hiramatsu 	/* Continue if no error, because the line will be in inline function */
12654cc9cec6SMasami Hiramatsu 	return ret < 0 ?: 0;
12664cc9cec6SMasami Hiramatsu }
12674cc9cec6SMasami Hiramatsu 
12684ea42b18SMasami Hiramatsu /* Find probe point from its line number */
1269b55a87adSMasami Hiramatsu static int find_probe_point_by_line(struct probe_finder *pf)
12704ea42b18SMasami Hiramatsu {
12714cc9cec6SMasami Hiramatsu 	return die_walk_lines(&pf->cu_die, probe_point_line_walker, pf);
12724ea42b18SMasami Hiramatsu }
12734ea42b18SMasami Hiramatsu 
12742a9c8c36SMasami Hiramatsu /* Find lines which match lazy pattern */
12752a9c8c36SMasami Hiramatsu static int find_lazy_match_lines(struct list_head *head,
12762a9c8c36SMasami Hiramatsu 				 const char *fname, const char *pat)
12772a9c8c36SMasami Hiramatsu {
1278f50c2169SFranck Bui-Huu 	FILE *fp;
1279f50c2169SFranck Bui-Huu 	char *line = NULL;
1280f50c2169SFranck Bui-Huu 	size_t line_len;
1281f50c2169SFranck Bui-Huu 	ssize_t len;
1282f50c2169SFranck Bui-Huu 	int count = 0, linenum = 1;
12832a9c8c36SMasami Hiramatsu 
1284f50c2169SFranck Bui-Huu 	fp = fopen(fname, "r");
1285f50c2169SFranck Bui-Huu 	if (!fp) {
1286f50c2169SFranck Bui-Huu 		pr_warning("Failed to open %s: %s\n", fname, strerror(errno));
1287b448c4b6SArnaldo Carvalho de Melo 		return -errno;
1288b55a87adSMasami Hiramatsu 	}
1289b55a87adSMasami Hiramatsu 
1290f50c2169SFranck Bui-Huu 	while ((len = getline(&line, &line_len, fp)) > 0) {
1291f50c2169SFranck Bui-Huu 
1292f50c2169SFranck Bui-Huu 		if (line[len - 1] == '\n')
1293f50c2169SFranck Bui-Huu 			line[len - 1] = '\0';
1294f50c2169SFranck Bui-Huu 
1295f50c2169SFranck Bui-Huu 		if (strlazymatch(line, pat)) {
1296f50c2169SFranck Bui-Huu 			line_list__add_line(head, linenum);
1297f50c2169SFranck Bui-Huu 			count++;
1298f50c2169SFranck Bui-Huu 		}
1299f50c2169SFranck Bui-Huu 		linenum++;
1300b55a87adSMasami Hiramatsu 	}
1301b448c4b6SArnaldo Carvalho de Melo 
1302f50c2169SFranck Bui-Huu 	if (ferror(fp))
1303f50c2169SFranck Bui-Huu 		count = -errno;
1304f50c2169SFranck Bui-Huu 	free(line);
1305f50c2169SFranck Bui-Huu 	fclose(fp);
1306f50c2169SFranck Bui-Huu 
1307f50c2169SFranck Bui-Huu 	if (count == 0)
1308f50c2169SFranck Bui-Huu 		pr_debug("No matched lines found in %s.\n", fname);
1309f50c2169SFranck Bui-Huu 	return count;
13102a9c8c36SMasami Hiramatsu }
13112a9c8c36SMasami Hiramatsu 
13124cc9cec6SMasami Hiramatsu static int probe_point_lazy_walker(const char *fname, int lineno,
13134cc9cec6SMasami Hiramatsu 				   Dwarf_Addr addr, void *data)
13144cc9cec6SMasami Hiramatsu {
13154cc9cec6SMasami Hiramatsu 	struct probe_finder *pf = data;
13164cc9cec6SMasami Hiramatsu 	int ret;
13174cc9cec6SMasami Hiramatsu 
13184cc9cec6SMasami Hiramatsu 	if (!line_list__has_line(&pf->lcache, lineno) ||
13194cc9cec6SMasami Hiramatsu 	    strtailcmp(fname, pf->fname) != 0)
13204cc9cec6SMasami Hiramatsu 		return 0;
13214cc9cec6SMasami Hiramatsu 
13224cc9cec6SMasami Hiramatsu 	pr_debug("Probe line found: line:%d addr:0x%llx\n",
13234cc9cec6SMasami Hiramatsu 		 lineno, (unsigned long long)addr);
13244cc9cec6SMasami Hiramatsu 	pf->addr = addr;
13254cc9cec6SMasami Hiramatsu 	ret = call_probe_finder(NULL, pf);
13264cc9cec6SMasami Hiramatsu 
13274cc9cec6SMasami Hiramatsu 	/*
13284cc9cec6SMasami Hiramatsu 	 * Continue if no error, because the lazy pattern will match
13294cc9cec6SMasami Hiramatsu 	 * to other lines
13304cc9cec6SMasami Hiramatsu 	 */
13314cc9cec6SMasami Hiramatsu 	return ret < 0 ?: 0;
13324cc9cec6SMasami Hiramatsu }
13334cc9cec6SMasami Hiramatsu 
13342a9c8c36SMasami Hiramatsu /* Find probe points from lazy pattern  */
1335b55a87adSMasami Hiramatsu static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
13362a9c8c36SMasami Hiramatsu {
1337b55a87adSMasami Hiramatsu 	int ret = 0;
13382a9c8c36SMasami Hiramatsu 
13392a9c8c36SMasami Hiramatsu 	if (list_empty(&pf->lcache)) {
13402a9c8c36SMasami Hiramatsu 		/* Matching lazy line pattern */
13412a9c8c36SMasami Hiramatsu 		ret = find_lazy_match_lines(&pf->lcache, pf->fname,
13424235b045SMasami Hiramatsu 					    pf->pev->point.lazy_line);
1343f50c2169SFranck Bui-Huu 		if (ret <= 0)
1344b55a87adSMasami Hiramatsu 			return ret;
13452a9c8c36SMasami Hiramatsu 	}
13462a9c8c36SMasami Hiramatsu 
13474cc9cec6SMasami Hiramatsu 	return die_walk_lines(sp_die, probe_point_lazy_walker, pf);
13482a9c8c36SMasami Hiramatsu }
13492a9c8c36SMasami Hiramatsu 
1350b55a87adSMasami Hiramatsu /* Callback parameter with return value */
1351b55a87adSMasami Hiramatsu struct dwarf_callback_param {
1352b55a87adSMasami Hiramatsu 	void *data;
1353b55a87adSMasami Hiramatsu 	int retval;
1354b55a87adSMasami Hiramatsu };
1355b55a87adSMasami Hiramatsu 
1356e92b85e1SMasami Hiramatsu static int probe_point_inline_cb(Dwarf_Die *in_die, void *data)
13574ea42b18SMasami Hiramatsu {
1358b55a87adSMasami Hiramatsu 	struct dwarf_callback_param *param = data;
1359b55a87adSMasami Hiramatsu 	struct probe_finder *pf = param->data;
13604235b045SMasami Hiramatsu 	struct perf_probe_point *pp = &pf->pev->point;
1361b55a87adSMasami Hiramatsu 	Dwarf_Addr addr;
13624ea42b18SMasami Hiramatsu 
13632a9c8c36SMasami Hiramatsu 	if (pp->lazy_line)
1364b55a87adSMasami Hiramatsu 		param->retval = find_probe_point_lazy(in_die, pf);
13652a9c8c36SMasami Hiramatsu 	else {
1366e92b85e1SMasami Hiramatsu 		/* Get probe address */
1367b55a87adSMasami Hiramatsu 		if (dwarf_entrypc(in_die, &addr) != 0) {
13680e43e5d2SMasami Hiramatsu 			pr_warning("Failed to get entry address of %s.\n",
1369b55a87adSMasami Hiramatsu 				   dwarf_diename(in_die));
1370b55a87adSMasami Hiramatsu 			param->retval = -ENOENT;
1371b55a87adSMasami Hiramatsu 			return DWARF_CB_ABORT;
1372b55a87adSMasami Hiramatsu 		}
1373b55a87adSMasami Hiramatsu 		pf->addr = addr;
1374e92b85e1SMasami Hiramatsu 		pf->addr += pp->offset;
13752a9c8c36SMasami Hiramatsu 		pr_debug("found inline addr: 0x%jx\n",
13762a9c8c36SMasami Hiramatsu 			 (uintmax_t)pf->addr);
1377e92b85e1SMasami Hiramatsu 
1378cf6eb489SMasami Hiramatsu 		param->retval = call_probe_finder(in_die, pf);
13795d1ee041SMasami Hiramatsu 		if (param->retval < 0)
13805d1ee041SMasami Hiramatsu 			return DWARF_CB_ABORT;
13812a9c8c36SMasami Hiramatsu 	}
13822a9c8c36SMasami Hiramatsu 
1383e92b85e1SMasami Hiramatsu 	return DWARF_CB_OK;
1384e92b85e1SMasami Hiramatsu }
1385e92b85e1SMasami Hiramatsu 
1386e92b85e1SMasami Hiramatsu /* Search function from function name */
1387e92b85e1SMasami Hiramatsu static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
1388e92b85e1SMasami Hiramatsu {
1389b55a87adSMasami Hiramatsu 	struct dwarf_callback_param *param = data;
1390b55a87adSMasami Hiramatsu 	struct probe_finder *pf = param->data;
13914235b045SMasami Hiramatsu 	struct perf_probe_point *pp = &pf->pev->point;
1392e92b85e1SMasami Hiramatsu 
1393e92b85e1SMasami Hiramatsu 	/* Check tag and diename */
1394e92b85e1SMasami Hiramatsu 	if (dwarf_tag(sp_die) != DW_TAG_subprogram ||
139582175633SMasami Hiramatsu 	    !die_compare_name(sp_die, pp->function))
1396b55a87adSMasami Hiramatsu 		return DWARF_CB_OK;
1397e92b85e1SMasami Hiramatsu 
1398e92b85e1SMasami Hiramatsu 	pf->fname = dwarf_decl_file(sp_die);
13992a9c8c36SMasami Hiramatsu 	if (pp->line) { /* Function relative line */
1400e92b85e1SMasami Hiramatsu 		dwarf_decl_line(sp_die, &pf->lno);
1401804b3606SMasami Hiramatsu 		pf->lno += pp->line;
1402b55a87adSMasami Hiramatsu 		param->retval = find_probe_point_by_line(pf);
1403e92b85e1SMasami Hiramatsu 	} else if (!dwarf_func_inline(sp_die)) {
1404e92b85e1SMasami Hiramatsu 		/* Real function */
14052a9c8c36SMasami Hiramatsu 		if (pp->lazy_line)
1406b55a87adSMasami Hiramatsu 			param->retval = find_probe_point_lazy(sp_die, pf);
14072a9c8c36SMasami Hiramatsu 		else {
1408b55a87adSMasami Hiramatsu 			if (dwarf_entrypc(sp_die, &pf->addr) != 0) {
14090e43e5d2SMasami Hiramatsu 				pr_warning("Failed to get entry address of "
14100e43e5d2SMasami Hiramatsu 					   "%s.\n", dwarf_diename(sp_die));
1411b55a87adSMasami Hiramatsu 				param->retval = -ENOENT;
1412b55a87adSMasami Hiramatsu 				return DWARF_CB_ABORT;
1413b55a87adSMasami Hiramatsu 			}
14144ea42b18SMasami Hiramatsu 			pf->addr += pp->offset;
14154ea42b18SMasami Hiramatsu 			/* TODO: Check the address in this function */
1416cf6eb489SMasami Hiramatsu 			param->retval = call_probe_finder(sp_die, pf);
14172a9c8c36SMasami Hiramatsu 		}
1418b55a87adSMasami Hiramatsu 	} else {
1419b55a87adSMasami Hiramatsu 		struct dwarf_callback_param _param = {.data = (void *)pf,
1420b55a87adSMasami Hiramatsu 						      .retval = 0};
1421e92b85e1SMasami Hiramatsu 		/* Inlined function: search instances */
1422b55a87adSMasami Hiramatsu 		dwarf_func_inline_instances(sp_die, probe_point_inline_cb,
1423b55a87adSMasami Hiramatsu 					    &_param);
1424b55a87adSMasami Hiramatsu 		param->retval = _param.retval;
14254ea42b18SMasami Hiramatsu 	}
14264ea42b18SMasami Hiramatsu 
1427b55a87adSMasami Hiramatsu 	return DWARF_CB_ABORT; /* Exit; no same symbol in this CU. */
1428b55a87adSMasami Hiramatsu }
1429b55a87adSMasami Hiramatsu 
1430b55a87adSMasami Hiramatsu static int find_probe_point_by_func(struct probe_finder *pf)
14314ea42b18SMasami Hiramatsu {
1432b55a87adSMasami Hiramatsu 	struct dwarf_callback_param _param = {.data = (void *)pf,
1433b55a87adSMasami Hiramatsu 					      .retval = 0};
1434b55a87adSMasami Hiramatsu 	dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, &_param, 0);
1435b55a87adSMasami Hiramatsu 	return _param.retval;
14364ea42b18SMasami Hiramatsu }
14374ea42b18SMasami Hiramatsu 
1438cf6eb489SMasami Hiramatsu /* Find probe points from debuginfo */
1439cf6eb489SMasami Hiramatsu static int find_probes(int fd, struct probe_finder *pf)
14404ea42b18SMasami Hiramatsu {
1441cf6eb489SMasami Hiramatsu 	struct perf_probe_point *pp = &pf->pev->point;
1442804b3606SMasami Hiramatsu 	Dwarf_Off off, noff;
1443804b3606SMasami Hiramatsu 	size_t cuhl;
1444804b3606SMasami Hiramatsu 	Dwarf_Die *diep;
1445469b9b88SMasami Hiramatsu 	Dwarf *dbg = NULL;
1446469b9b88SMasami Hiramatsu 	Dwfl *dwfl;
1447469b9b88SMasami Hiramatsu 	Dwarf_Addr bias;	/* Currently ignored */
1448b55a87adSMasami Hiramatsu 	int ret = 0;
14494ea42b18SMasami Hiramatsu 
1450469b9b88SMasami Hiramatsu 	dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias);
1451b55a87adSMasami Hiramatsu 	if (!dbg) {
14520e43e5d2SMasami Hiramatsu 		pr_warning("No debug information found in the vmlinux - "
1453b55a87adSMasami Hiramatsu 			"please rebuild with CONFIG_DEBUG_INFO=y.\n");
1454b55a87adSMasami Hiramatsu 		return -EBADF;
1455b55a87adSMasami Hiramatsu 	}
14564ea42b18SMasami Hiramatsu 
14577752f1b0SMasami Hiramatsu #if _ELFUTILS_PREREQ(0, 142)
1458a34a9854SMasami Hiramatsu 	/* Get the call frame information from this dwarf */
1459cf6eb489SMasami Hiramatsu 	pf->cfi = dwarf_getcfi(dbg);
14607752f1b0SMasami Hiramatsu #endif
1461a34a9854SMasami Hiramatsu 
1462804b3606SMasami Hiramatsu 	off = 0;
1463cf6eb489SMasami Hiramatsu 	line_list__init(&pf->lcache);
1464804b3606SMasami Hiramatsu 	/* Loop on CUs (Compilation Unit) */
1465b55a87adSMasami Hiramatsu 	while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) &&
1466b55a87adSMasami Hiramatsu 	       ret >= 0) {
14674ea42b18SMasami Hiramatsu 		/* Get the DIE(Debugging Information Entry) of this CU */
1468cf6eb489SMasami Hiramatsu 		diep = dwarf_offdie(dbg, off + cuhl, &pf->cu_die);
1469804b3606SMasami Hiramatsu 		if (!diep)
1470804b3606SMasami Hiramatsu 			continue;
14714ea42b18SMasami Hiramatsu 
14724ea42b18SMasami Hiramatsu 		/* Check if target file is included. */
14734ea42b18SMasami Hiramatsu 		if (pp->file)
1474cf6eb489SMasami Hiramatsu 			pf->fname = cu_find_realpath(&pf->cu_die, pp->file);
1475804b3606SMasami Hiramatsu 		else
1476cf6eb489SMasami Hiramatsu 			pf->fname = NULL;
14774ea42b18SMasami Hiramatsu 
1478cf6eb489SMasami Hiramatsu 		if (!pp->file || pf->fname) {
14794ea42b18SMasami Hiramatsu 			if (pp->function)
1480cf6eb489SMasami Hiramatsu 				ret = find_probe_point_by_func(pf);
14812a9c8c36SMasami Hiramatsu 			else if (pp->lazy_line)
1482cf6eb489SMasami Hiramatsu 				ret = find_probe_point_lazy(NULL, pf);
1483b0ef0732SMasami Hiramatsu 			else {
1484cf6eb489SMasami Hiramatsu 				pf->lno = pp->line;
1485cf6eb489SMasami Hiramatsu 				ret = find_probe_point_by_line(pf);
14864ea42b18SMasami Hiramatsu 			}
1487b0ef0732SMasami Hiramatsu 		}
1488804b3606SMasami Hiramatsu 		off = noff;
14894ea42b18SMasami Hiramatsu 	}
1490cf6eb489SMasami Hiramatsu 	line_list__free(&pf->lcache);
1491469b9b88SMasami Hiramatsu 	if (dwfl)
1492469b9b88SMasami Hiramatsu 		dwfl_end(dwfl);
14934ea42b18SMasami Hiramatsu 
1494cf6eb489SMasami Hiramatsu 	return ret;
1495cf6eb489SMasami Hiramatsu }
1496cf6eb489SMasami Hiramatsu 
1497cf6eb489SMasami Hiramatsu /* Add a found probe point into trace event list */
1498cf6eb489SMasami Hiramatsu static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf)
1499cf6eb489SMasami Hiramatsu {
1500cf6eb489SMasami Hiramatsu 	struct trace_event_finder *tf =
1501cf6eb489SMasami Hiramatsu 			container_of(pf, struct trace_event_finder, pf);
1502cf6eb489SMasami Hiramatsu 	struct probe_trace_event *tev;
1503cf6eb489SMasami Hiramatsu 	int ret, i;
1504cf6eb489SMasami Hiramatsu 
1505cf6eb489SMasami Hiramatsu 	/* Check number of tevs */
1506cf6eb489SMasami Hiramatsu 	if (tf->ntevs == tf->max_tevs) {
1507cf6eb489SMasami Hiramatsu 		pr_warning("Too many( > %d) probe point found.\n",
1508cf6eb489SMasami Hiramatsu 			   tf->max_tevs);
1509cf6eb489SMasami Hiramatsu 		return -ERANGE;
1510cf6eb489SMasami Hiramatsu 	}
1511cf6eb489SMasami Hiramatsu 	tev = &tf->tevs[tf->ntevs++];
1512cf6eb489SMasami Hiramatsu 
1513cf6eb489SMasami Hiramatsu 	ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe,
1514cf6eb489SMasami Hiramatsu 				     &tev->point);
1515cf6eb489SMasami Hiramatsu 	if (ret < 0)
1516cf6eb489SMasami Hiramatsu 		return ret;
1517cf6eb489SMasami Hiramatsu 
1518cf6eb489SMasami Hiramatsu 	pr_debug("Probe point found: %s+%lu\n", tev->point.symbol,
1519cf6eb489SMasami Hiramatsu 		 tev->point.offset);
1520cf6eb489SMasami Hiramatsu 
1521cf6eb489SMasami Hiramatsu 	/* Find each argument */
1522cf6eb489SMasami Hiramatsu 	tev->nargs = pf->pev->nargs;
1523cf6eb489SMasami Hiramatsu 	tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
1524cf6eb489SMasami Hiramatsu 	if (tev->args == NULL)
1525cf6eb489SMasami Hiramatsu 		return -ENOMEM;
1526cf6eb489SMasami Hiramatsu 	for (i = 0; i < pf->pev->nargs; i++) {
1527cf6eb489SMasami Hiramatsu 		pf->pvar = &pf->pev->args[i];
1528cf6eb489SMasami Hiramatsu 		pf->tvar = &tev->args[i];
1529cf6eb489SMasami Hiramatsu 		ret = find_variable(sp_die, pf);
1530cf6eb489SMasami Hiramatsu 		if (ret != 0)
1531cf6eb489SMasami Hiramatsu 			return ret;
1532cf6eb489SMasami Hiramatsu 	}
1533cf6eb489SMasami Hiramatsu 
1534cf6eb489SMasami Hiramatsu 	return 0;
1535cf6eb489SMasami Hiramatsu }
1536cf6eb489SMasami Hiramatsu 
1537cf6eb489SMasami Hiramatsu /* Find probe_trace_events specified by perf_probe_event from debuginfo */
1538cf6eb489SMasami Hiramatsu int find_probe_trace_events(int fd, struct perf_probe_event *pev,
1539cf6eb489SMasami Hiramatsu 			    struct probe_trace_event **tevs, int max_tevs)
1540cf6eb489SMasami Hiramatsu {
1541cf6eb489SMasami Hiramatsu 	struct trace_event_finder tf = {
1542cf6eb489SMasami Hiramatsu 			.pf = {.pev = pev, .callback = add_probe_trace_event},
1543cf6eb489SMasami Hiramatsu 			.max_tevs = max_tevs};
1544cf6eb489SMasami Hiramatsu 	int ret;
1545cf6eb489SMasami Hiramatsu 
1546cf6eb489SMasami Hiramatsu 	/* Allocate result tevs array */
1547cf6eb489SMasami Hiramatsu 	*tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs);
1548cf6eb489SMasami Hiramatsu 	if (*tevs == NULL)
1549cf6eb489SMasami Hiramatsu 		return -ENOMEM;
1550cf6eb489SMasami Hiramatsu 
1551cf6eb489SMasami Hiramatsu 	tf.tevs = *tevs;
1552cf6eb489SMasami Hiramatsu 	tf.ntevs = 0;
1553cf6eb489SMasami Hiramatsu 
1554cf6eb489SMasami Hiramatsu 	ret = find_probes(fd, &tf.pf);
1555cf6eb489SMasami Hiramatsu 	if (ret < 0) {
1556cf6eb489SMasami Hiramatsu 		free(*tevs);
1557cf6eb489SMasami Hiramatsu 		*tevs = NULL;
1558cf6eb489SMasami Hiramatsu 		return ret;
1559cf6eb489SMasami Hiramatsu 	}
1560cf6eb489SMasami Hiramatsu 
1561cf6eb489SMasami Hiramatsu 	return (ret < 0) ? ret : tf.ntevs;
1562cf6eb489SMasami Hiramatsu }
1563cf6eb489SMasami Hiramatsu 
1564cf6eb489SMasami Hiramatsu #define MAX_VAR_LEN 64
1565cf6eb489SMasami Hiramatsu 
1566cf6eb489SMasami Hiramatsu /* Collect available variables in this scope */
1567cf6eb489SMasami Hiramatsu static int collect_variables_cb(Dwarf_Die *die_mem, void *data)
1568cf6eb489SMasami Hiramatsu {
1569cf6eb489SMasami Hiramatsu 	struct available_var_finder *af = data;
1570cf6eb489SMasami Hiramatsu 	struct variable_list *vl;
1571cf6eb489SMasami Hiramatsu 	char buf[MAX_VAR_LEN];
1572cf6eb489SMasami Hiramatsu 	int tag, ret;
1573cf6eb489SMasami Hiramatsu 
1574cf6eb489SMasami Hiramatsu 	vl = &af->vls[af->nvls - 1];
1575cf6eb489SMasami Hiramatsu 
1576cf6eb489SMasami Hiramatsu 	tag = dwarf_tag(die_mem);
1577cf6eb489SMasami Hiramatsu 	if (tag == DW_TAG_formal_parameter ||
1578cf6eb489SMasami Hiramatsu 	    tag == DW_TAG_variable) {
1579cf6eb489SMasami Hiramatsu 		ret = convert_variable_location(die_mem, af->pf.addr,
1580cf6eb489SMasami Hiramatsu 						af->pf.fb_ops, NULL);
1581cf6eb489SMasami Hiramatsu 		if (ret == 0) {
1582cf6eb489SMasami Hiramatsu 			ret = die_get_varname(die_mem, buf, MAX_VAR_LEN);
1583fb8c5a56SMasami Hiramatsu 			pr_debug2("Add new var: %s\n", buf);
1584cf6eb489SMasami Hiramatsu 			if (ret > 0)
1585cf6eb489SMasami Hiramatsu 				strlist__add(vl->vars, buf);
1586cf6eb489SMasami Hiramatsu 		}
1587cf6eb489SMasami Hiramatsu 	}
1588cf6eb489SMasami Hiramatsu 
1589fb8c5a56SMasami Hiramatsu 	if (af->child && dwarf_haspc(die_mem, af->pf.addr))
1590cf6eb489SMasami Hiramatsu 		return DIE_FIND_CB_CONTINUE;
1591cf6eb489SMasami Hiramatsu 	else
1592cf6eb489SMasami Hiramatsu 		return DIE_FIND_CB_SIBLING;
1593cf6eb489SMasami Hiramatsu }
1594cf6eb489SMasami Hiramatsu 
1595cf6eb489SMasami Hiramatsu /* Add a found vars into available variables list */
1596cf6eb489SMasami Hiramatsu static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf)
1597cf6eb489SMasami Hiramatsu {
1598cf6eb489SMasami Hiramatsu 	struct available_var_finder *af =
1599cf6eb489SMasami Hiramatsu 			container_of(pf, struct available_var_finder, pf);
1600cf6eb489SMasami Hiramatsu 	struct variable_list *vl;
1601fb8c5a56SMasami Hiramatsu 	Dwarf_Die die_mem, *scopes = NULL;
1602fb8c5a56SMasami Hiramatsu 	int ret, nscopes;
1603cf6eb489SMasami Hiramatsu 
1604cf6eb489SMasami Hiramatsu 	/* Check number of tevs */
1605cf6eb489SMasami Hiramatsu 	if (af->nvls == af->max_vls) {
1606cf6eb489SMasami Hiramatsu 		pr_warning("Too many( > %d) probe point found.\n", af->max_vls);
1607cf6eb489SMasami Hiramatsu 		return -ERANGE;
1608cf6eb489SMasami Hiramatsu 	}
1609cf6eb489SMasami Hiramatsu 	vl = &af->vls[af->nvls++];
1610cf6eb489SMasami Hiramatsu 
1611cf6eb489SMasami Hiramatsu 	ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe,
1612cf6eb489SMasami Hiramatsu 				     &vl->point);
1613cf6eb489SMasami Hiramatsu 	if (ret < 0)
1614cf6eb489SMasami Hiramatsu 		return ret;
1615cf6eb489SMasami Hiramatsu 
1616cf6eb489SMasami Hiramatsu 	pr_debug("Probe point found: %s+%lu\n", vl->point.symbol,
1617cf6eb489SMasami Hiramatsu 		 vl->point.offset);
1618cf6eb489SMasami Hiramatsu 
1619cf6eb489SMasami Hiramatsu 	/* Find local variables */
1620cf6eb489SMasami Hiramatsu 	vl->vars = strlist__new(true, NULL);
1621cf6eb489SMasami Hiramatsu 	if (vl->vars == NULL)
1622cf6eb489SMasami Hiramatsu 		return -ENOMEM;
1623fb8c5a56SMasami Hiramatsu 	af->child = true;
1624cf6eb489SMasami Hiramatsu 	die_find_child(sp_die, collect_variables_cb, (void *)af, &die_mem);
1625cf6eb489SMasami Hiramatsu 
1626fb8c5a56SMasami Hiramatsu 	/* Find external variables */
1627fb8c5a56SMasami Hiramatsu 	if (!af->externs)
1628fb8c5a56SMasami Hiramatsu 		goto out;
1629fb8c5a56SMasami Hiramatsu 	/* Don't need to search child DIE for externs. */
1630fb8c5a56SMasami Hiramatsu 	af->child = false;
1631fb8c5a56SMasami Hiramatsu 	nscopes = dwarf_getscopes_die(sp_die, &scopes);
1632fb8c5a56SMasami Hiramatsu 	while (nscopes-- > 1)
1633fb8c5a56SMasami Hiramatsu 		die_find_child(&scopes[nscopes], collect_variables_cb,
1634fb8c5a56SMasami Hiramatsu 			       (void *)af, &die_mem);
1635fb8c5a56SMasami Hiramatsu 	if (scopes)
1636fb8c5a56SMasami Hiramatsu 		free(scopes);
1637fb8c5a56SMasami Hiramatsu 
1638fb8c5a56SMasami Hiramatsu out:
1639cf6eb489SMasami Hiramatsu 	if (strlist__empty(vl->vars)) {
1640cf6eb489SMasami Hiramatsu 		strlist__delete(vl->vars);
1641cf6eb489SMasami Hiramatsu 		vl->vars = NULL;
1642cf6eb489SMasami Hiramatsu 	}
1643cf6eb489SMasami Hiramatsu 
1644cf6eb489SMasami Hiramatsu 	return ret;
1645cf6eb489SMasami Hiramatsu }
1646cf6eb489SMasami Hiramatsu 
1647cf6eb489SMasami Hiramatsu /* Find available variables at given probe point */
1648cf6eb489SMasami Hiramatsu int find_available_vars_at(int fd, struct perf_probe_event *pev,
1649fb8c5a56SMasami Hiramatsu 			   struct variable_list **vls, int max_vls,
1650fb8c5a56SMasami Hiramatsu 			   bool externs)
1651cf6eb489SMasami Hiramatsu {
1652cf6eb489SMasami Hiramatsu 	struct available_var_finder af = {
1653cf6eb489SMasami Hiramatsu 			.pf = {.pev = pev, .callback = add_available_vars},
1654fb8c5a56SMasami Hiramatsu 			.max_vls = max_vls, .externs = externs};
1655cf6eb489SMasami Hiramatsu 	int ret;
1656cf6eb489SMasami Hiramatsu 
1657cf6eb489SMasami Hiramatsu 	/* Allocate result vls array */
1658cf6eb489SMasami Hiramatsu 	*vls = zalloc(sizeof(struct variable_list) * max_vls);
1659cf6eb489SMasami Hiramatsu 	if (*vls == NULL)
1660cf6eb489SMasami Hiramatsu 		return -ENOMEM;
1661cf6eb489SMasami Hiramatsu 
1662cf6eb489SMasami Hiramatsu 	af.vls = *vls;
1663cf6eb489SMasami Hiramatsu 	af.nvls = 0;
1664cf6eb489SMasami Hiramatsu 
1665cf6eb489SMasami Hiramatsu 	ret = find_probes(fd, &af.pf);
1666cf6eb489SMasami Hiramatsu 	if (ret < 0) {
1667cf6eb489SMasami Hiramatsu 		/* Free vlist for error */
1668cf6eb489SMasami Hiramatsu 		while (af.nvls--) {
1669cf6eb489SMasami Hiramatsu 			if (af.vls[af.nvls].point.symbol)
1670cf6eb489SMasami Hiramatsu 				free(af.vls[af.nvls].point.symbol);
1671cf6eb489SMasami Hiramatsu 			if (af.vls[af.nvls].vars)
1672cf6eb489SMasami Hiramatsu 				strlist__delete(af.vls[af.nvls].vars);
1673cf6eb489SMasami Hiramatsu 		}
1674cf6eb489SMasami Hiramatsu 		free(af.vls);
1675cf6eb489SMasami Hiramatsu 		*vls = NULL;
1676cf6eb489SMasami Hiramatsu 		return ret;
1677cf6eb489SMasami Hiramatsu 	}
1678cf6eb489SMasami Hiramatsu 
1679cf6eb489SMasami Hiramatsu 	return (ret < 0) ? ret : af.nvls;
16804ea42b18SMasami Hiramatsu }
16814ea42b18SMasami Hiramatsu 
1682fb1587d8SMasami Hiramatsu /* Reverse search */
1683469b9b88SMasami Hiramatsu int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt)
1684fb1587d8SMasami Hiramatsu {
1685fb1587d8SMasami Hiramatsu 	Dwarf_Die cudie, spdie, indie;
1686469b9b88SMasami Hiramatsu 	Dwarf *dbg = NULL;
1687469b9b88SMasami Hiramatsu 	Dwfl *dwfl = NULL;
1688fb1587d8SMasami Hiramatsu 	Dwarf_Line *line;
1689469b9b88SMasami Hiramatsu 	Dwarf_Addr laddr, eaddr, bias = 0;
1690fb1587d8SMasami Hiramatsu 	const char *tmp;
1691fb1587d8SMasami Hiramatsu 	int lineno, ret = 0;
1692b55a87adSMasami Hiramatsu 	bool found = false;
1693fb1587d8SMasami Hiramatsu 
1694469b9b88SMasami Hiramatsu 	/* Open the live linux kernel */
1695469b9b88SMasami Hiramatsu 	dbg = dwfl_init_live_kernel_dwarf(addr, &dwfl, &bias);
1696469b9b88SMasami Hiramatsu 	if (!dbg) {
16970e43e5d2SMasami Hiramatsu 		pr_warning("No debug information found in the vmlinux - "
1698469b9b88SMasami Hiramatsu 			"please rebuild with CONFIG_DEBUG_INFO=y.\n");
1699469b9b88SMasami Hiramatsu 		ret = -EINVAL;
1700469b9b88SMasami Hiramatsu 		goto end;
1701469b9b88SMasami Hiramatsu 	}
1702fb1587d8SMasami Hiramatsu 
1703469b9b88SMasami Hiramatsu 	/* Adjust address with bias */
1704469b9b88SMasami Hiramatsu 	addr += bias;
1705fb1587d8SMasami Hiramatsu 	/* Find cu die */
1706469b9b88SMasami Hiramatsu 	if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr - bias, &cudie)) {
17070e43e5d2SMasami Hiramatsu 		pr_warning("Failed to find debug information for address %lx\n",
17080e43e5d2SMasami Hiramatsu 			   addr);
170975ec5a24SMasami Hiramatsu 		ret = -EINVAL;
171075ec5a24SMasami Hiramatsu 		goto end;
171175ec5a24SMasami Hiramatsu 	}
1712fb1587d8SMasami Hiramatsu 
1713fb1587d8SMasami Hiramatsu 	/* Find a corresponding line */
1714fb1587d8SMasami Hiramatsu 	line = dwarf_getsrc_die(&cudie, (Dwarf_Addr)addr);
1715fb1587d8SMasami Hiramatsu 	if (line) {
1716b55a87adSMasami Hiramatsu 		if (dwarf_lineaddr(line, &laddr) == 0 &&
1717b55a87adSMasami Hiramatsu 		    (Dwarf_Addr)addr == laddr &&
1718b55a87adSMasami Hiramatsu 		    dwarf_lineno(line, &lineno) == 0) {
1719fb1587d8SMasami Hiramatsu 			tmp = dwarf_linesrc(line, NULL, NULL);
1720b55a87adSMasami Hiramatsu 			if (tmp) {
1721b55a87adSMasami Hiramatsu 				ppt->line = lineno;
172202b95dadSMasami Hiramatsu 				ppt->file = strdup(tmp);
172302b95dadSMasami Hiramatsu 				if (ppt->file == NULL) {
172402b95dadSMasami Hiramatsu 					ret = -ENOMEM;
172502b95dadSMasami Hiramatsu 					goto end;
172602b95dadSMasami Hiramatsu 				}
1727b55a87adSMasami Hiramatsu 				found = true;
1728b55a87adSMasami Hiramatsu 			}
1729fb1587d8SMasami Hiramatsu 		}
1730fb1587d8SMasami Hiramatsu 	}
1731fb1587d8SMasami Hiramatsu 
1732fb1587d8SMasami Hiramatsu 	/* Find a corresponding function */
1733fb1587d8SMasami Hiramatsu 	if (die_find_real_subprogram(&cudie, (Dwarf_Addr)addr, &spdie)) {
1734fb1587d8SMasami Hiramatsu 		tmp = dwarf_diename(&spdie);
1735b55a87adSMasami Hiramatsu 		if (!tmp || dwarf_entrypc(&spdie, &eaddr) != 0)
1736fb1587d8SMasami Hiramatsu 			goto end;
1737fb1587d8SMasami Hiramatsu 
1738b55a87adSMasami Hiramatsu 		if (ppt->line) {
1739b55a87adSMasami Hiramatsu 			if (die_find_inlinefunc(&spdie, (Dwarf_Addr)addr,
1740b55a87adSMasami Hiramatsu 						&indie)) {
1741fb1587d8SMasami Hiramatsu 				/* addr in an inline function */
1742fb1587d8SMasami Hiramatsu 				tmp = dwarf_diename(&indie);
1743fb1587d8SMasami Hiramatsu 				if (!tmp)
1744fb1587d8SMasami Hiramatsu 					goto end;
1745b55a87adSMasami Hiramatsu 				ret = dwarf_decl_line(&indie, &lineno);
1746fb1587d8SMasami Hiramatsu 			} else {
1747b55a87adSMasami Hiramatsu 				if (eaddr == addr) {	/* Function entry */
1748fb1587d8SMasami Hiramatsu 					lineno = ppt->line;
1749b55a87adSMasami Hiramatsu 					ret = 0;
1750b55a87adSMasami Hiramatsu 				} else
1751b55a87adSMasami Hiramatsu 					ret = dwarf_decl_line(&spdie, &lineno);
1752fb1587d8SMasami Hiramatsu 			}
1753b55a87adSMasami Hiramatsu 			if (ret == 0) {
1754b55a87adSMasami Hiramatsu 				/* Make a relative line number */
1755b55a87adSMasami Hiramatsu 				ppt->line -= lineno;
1756b55a87adSMasami Hiramatsu 				goto found;
1757b55a87adSMasami Hiramatsu 			}
1758b55a87adSMasami Hiramatsu 		}
1759b55a87adSMasami Hiramatsu 		/* We don't have a line number, let's use offset */
1760b55a87adSMasami Hiramatsu 		ppt->offset = addr - (unsigned long)eaddr;
1761b55a87adSMasami Hiramatsu found:
176202b95dadSMasami Hiramatsu 		ppt->function = strdup(tmp);
176302b95dadSMasami Hiramatsu 		if (ppt->function == NULL) {
176402b95dadSMasami Hiramatsu 			ret = -ENOMEM;
176502b95dadSMasami Hiramatsu 			goto end;
176602b95dadSMasami Hiramatsu 		}
1767b55a87adSMasami Hiramatsu 		found = true;
1768fb1587d8SMasami Hiramatsu 	}
1769fb1587d8SMasami Hiramatsu 
1770fb1587d8SMasami Hiramatsu end:
1771469b9b88SMasami Hiramatsu 	if (dwfl)
1772469b9b88SMasami Hiramatsu 		dwfl_end(dwfl);
1773b55a87adSMasami Hiramatsu 	if (ret >= 0)
1774b55a87adSMasami Hiramatsu 		ret = found ? 1 : 0;
1775fb1587d8SMasami Hiramatsu 	return ret;
1776fb1587d8SMasami Hiramatsu }
1777fb1587d8SMasami Hiramatsu 
1778f6c903f5SMasami Hiramatsu /* Add a line and store the src path */
1779f6c903f5SMasami Hiramatsu static int line_range_add_line(const char *src, unsigned int lineno,
1780f6c903f5SMasami Hiramatsu 			       struct line_range *lr)
1781f6c903f5SMasami Hiramatsu {
17827cf0b79eSMasami Hiramatsu 	/* Copy source path */
1783f6c903f5SMasami Hiramatsu 	if (!lr->path) {
17847cf0b79eSMasami Hiramatsu 		lr->path = strdup(src);
17857cf0b79eSMasami Hiramatsu 		if (lr->path == NULL)
17867cf0b79eSMasami Hiramatsu 			return -ENOMEM;
1787f6c903f5SMasami Hiramatsu 	}
1788f6c903f5SMasami Hiramatsu 	return line_list__add_line(&lr->line_list, lineno);
1789f6c903f5SMasami Hiramatsu }
1790f6c903f5SMasami Hiramatsu 
17914cc9cec6SMasami Hiramatsu static int line_range_walk_cb(const char *fname, int lineno,
17924cc9cec6SMasami Hiramatsu 			      Dwarf_Addr addr __used,
17934cc9cec6SMasami Hiramatsu 			      void *data)
1794f6c903f5SMasami Hiramatsu {
17954cc9cec6SMasami Hiramatsu 	struct line_finder *lf = data;
1796f6c903f5SMasami Hiramatsu 
17974cc9cec6SMasami Hiramatsu 	if ((strtailcmp(fname, lf->fname) != 0) ||
1798f6c903f5SMasami Hiramatsu 	    (lf->lno_s > lineno || lf->lno_e < lineno))
17994cc9cec6SMasami Hiramatsu 		return 0;
1800f6c903f5SMasami Hiramatsu 
18014cc9cec6SMasami Hiramatsu 	if (line_range_add_line(fname, lineno, lf->lr) < 0)
18024cc9cec6SMasami Hiramatsu 		return -EINVAL;
1803f6c903f5SMasami Hiramatsu 
18044cc9cec6SMasami Hiramatsu 	return 0;
1805f6c903f5SMasami Hiramatsu }
1806fb1587d8SMasami Hiramatsu 
1807631c9defSMasami Hiramatsu /* Find line range from its line number */
1808b55a87adSMasami Hiramatsu static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf)
1809631c9defSMasami Hiramatsu {
18104cc9cec6SMasami Hiramatsu 	int ret;
1811631c9defSMasami Hiramatsu 
18124cc9cec6SMasami Hiramatsu 	ret = die_walk_lines(sp_die ?: &lf->cu_die, line_range_walk_cb, lf);
1813f6c903f5SMasami Hiramatsu 
1814804b3606SMasami Hiramatsu 	/* Update status */
1815f6c903f5SMasami Hiramatsu 	if (ret >= 0)
1816631c9defSMasami Hiramatsu 		if (!list_empty(&lf->lr->line_list))
1817f6c903f5SMasami Hiramatsu 			ret = lf->found = 1;
1818f6c903f5SMasami Hiramatsu 		else
1819f6c903f5SMasami Hiramatsu 			ret = 0;	/* Lines are not found */
1820804b3606SMasami Hiramatsu 	else {
1821804b3606SMasami Hiramatsu 		free(lf->lr->path);
1822804b3606SMasami Hiramatsu 		lf->lr->path = NULL;
1823804b3606SMasami Hiramatsu 	}
1824f6c903f5SMasami Hiramatsu 	return ret;
1825631c9defSMasami Hiramatsu }
1826631c9defSMasami Hiramatsu 
1827161a26b0SMasami Hiramatsu static int line_range_inline_cb(Dwarf_Die *in_die, void *data)
1828161a26b0SMasami Hiramatsu {
1829b55a87adSMasami Hiramatsu 	struct dwarf_callback_param *param = data;
1830b55a87adSMasami Hiramatsu 
1831b55a87adSMasami Hiramatsu 	param->retval = find_line_range_by_line(in_die, param->data);
1832161a26b0SMasami Hiramatsu 	return DWARF_CB_ABORT;	/* No need to find other instances */
1833161a26b0SMasami Hiramatsu }
1834161a26b0SMasami Hiramatsu 
1835631c9defSMasami Hiramatsu /* Search function from function name */
1836e92b85e1SMasami Hiramatsu static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
1837631c9defSMasami Hiramatsu {
1838b55a87adSMasami Hiramatsu 	struct dwarf_callback_param *param = data;
1839b55a87adSMasami Hiramatsu 	struct line_finder *lf = param->data;
1840631c9defSMasami Hiramatsu 	struct line_range *lr = lf->lr;
1841631c9defSMasami Hiramatsu 
1842e92b85e1SMasami Hiramatsu 	if (dwarf_tag(sp_die) == DW_TAG_subprogram &&
184382175633SMasami Hiramatsu 	    die_compare_name(sp_die, lr->function)) {
1844e92b85e1SMasami Hiramatsu 		lf->fname = dwarf_decl_file(sp_die);
1845e92b85e1SMasami Hiramatsu 		dwarf_decl_line(sp_die, &lr->offset);
1846804b3606SMasami Hiramatsu 		pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset);
1847631c9defSMasami Hiramatsu 		lf->lno_s = lr->offset + lr->start;
1848d3b63d7aSMasami Hiramatsu 		if (lf->lno_s < 0)	/* Overflow */
1849d3b63d7aSMasami Hiramatsu 			lf->lno_s = INT_MAX;
1850631c9defSMasami Hiramatsu 		lf->lno_e = lr->offset + lr->end;
1851d3b63d7aSMasami Hiramatsu 		if (lf->lno_e < 0)	/* Overflow */
1852d3b63d7aSMasami Hiramatsu 			lf->lno_e = INT_MAX;
1853d3b63d7aSMasami Hiramatsu 		pr_debug("New line range: %d to %d\n", lf->lno_s, lf->lno_e);
1854631c9defSMasami Hiramatsu 		lr->start = lf->lno_s;
1855631c9defSMasami Hiramatsu 		lr->end = lf->lno_e;
1856b55a87adSMasami Hiramatsu 		if (dwarf_func_inline(sp_die)) {
1857b55a87adSMasami Hiramatsu 			struct dwarf_callback_param _param;
1858b55a87adSMasami Hiramatsu 			_param.data = (void *)lf;
1859b55a87adSMasami Hiramatsu 			_param.retval = 0;
1860161a26b0SMasami Hiramatsu 			dwarf_func_inline_instances(sp_die,
1861b55a87adSMasami Hiramatsu 						    line_range_inline_cb,
1862b55a87adSMasami Hiramatsu 						    &_param);
1863b55a87adSMasami Hiramatsu 			param->retval = _param.retval;
1864b55a87adSMasami Hiramatsu 		} else
1865b55a87adSMasami Hiramatsu 			param->retval = find_line_range_by_line(sp_die, lf);
1866b55a87adSMasami Hiramatsu 		return DWARF_CB_ABORT;
1867631c9defSMasami Hiramatsu 	}
1868b55a87adSMasami Hiramatsu 	return DWARF_CB_OK;
1869631c9defSMasami Hiramatsu }
1870631c9defSMasami Hiramatsu 
1871b55a87adSMasami Hiramatsu static int find_line_range_by_func(struct line_finder *lf)
1872631c9defSMasami Hiramatsu {
1873b55a87adSMasami Hiramatsu 	struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0};
1874b55a87adSMasami Hiramatsu 	dwarf_getfuncs(&lf->cu_die, line_range_search_cb, &param, 0);
1875b55a87adSMasami Hiramatsu 	return param.retval;
1876631c9defSMasami Hiramatsu }
1877631c9defSMasami Hiramatsu 
1878631c9defSMasami Hiramatsu int find_line_range(int fd, struct line_range *lr)
1879631c9defSMasami Hiramatsu {
1880804b3606SMasami Hiramatsu 	struct line_finder lf = {.lr = lr, .found = 0};
1881b55a87adSMasami Hiramatsu 	int ret = 0;
1882804b3606SMasami Hiramatsu 	Dwarf_Off off = 0, noff;
1883804b3606SMasami Hiramatsu 	size_t cuhl;
1884804b3606SMasami Hiramatsu 	Dwarf_Die *diep;
1885469b9b88SMasami Hiramatsu 	Dwarf *dbg = NULL;
1886469b9b88SMasami Hiramatsu 	Dwfl *dwfl;
1887469b9b88SMasami Hiramatsu 	Dwarf_Addr bias;	/* Currently ignored */
18886a330a3cSMasami Hiramatsu 	const char *comp_dir;
1889631c9defSMasami Hiramatsu 
1890469b9b88SMasami Hiramatsu 	dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias);
1891b55a87adSMasami Hiramatsu 	if (!dbg) {
18920e43e5d2SMasami Hiramatsu 		pr_warning("No debug information found in the vmlinux - "
1893b55a87adSMasami Hiramatsu 			"please rebuild with CONFIG_DEBUG_INFO=y.\n");
1894b55a87adSMasami Hiramatsu 		return -EBADF;
1895b55a87adSMasami Hiramatsu 	}
1896631c9defSMasami Hiramatsu 
1897804b3606SMasami Hiramatsu 	/* Loop on CUs (Compilation Unit) */
1898b55a87adSMasami Hiramatsu 	while (!lf.found && ret >= 0) {
1899b55a87adSMasami Hiramatsu 		if (dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) != 0)
1900631c9defSMasami Hiramatsu 			break;
1901631c9defSMasami Hiramatsu 
1902631c9defSMasami Hiramatsu 		/* Get the DIE(Debugging Information Entry) of this CU */
1903804b3606SMasami Hiramatsu 		diep = dwarf_offdie(dbg, off + cuhl, &lf.cu_die);
1904804b3606SMasami Hiramatsu 		if (!diep)
1905804b3606SMasami Hiramatsu 			continue;
1906631c9defSMasami Hiramatsu 
1907631c9defSMasami Hiramatsu 		/* Check if target file is included. */
1908631c9defSMasami Hiramatsu 		if (lr->file)
19092a9c8c36SMasami Hiramatsu 			lf.fname = cu_find_realpath(&lf.cu_die, lr->file);
1910804b3606SMasami Hiramatsu 		else
19112a9c8c36SMasami Hiramatsu 			lf.fname = 0;
1912631c9defSMasami Hiramatsu 
19132a9c8c36SMasami Hiramatsu 		if (!lr->file || lf.fname) {
1914631c9defSMasami Hiramatsu 			if (lr->function)
1915b55a87adSMasami Hiramatsu 				ret = find_line_range_by_func(&lf);
1916631c9defSMasami Hiramatsu 			else {
1917631c9defSMasami Hiramatsu 				lf.lno_s = lr->start;
1918631c9defSMasami Hiramatsu 				lf.lno_e = lr->end;
1919b55a87adSMasami Hiramatsu 				ret = find_line_range_by_line(NULL, &lf);
1920631c9defSMasami Hiramatsu 			}
1921631c9defSMasami Hiramatsu 		}
1922804b3606SMasami Hiramatsu 		off = noff;
1923631c9defSMasami Hiramatsu 	}
19246a330a3cSMasami Hiramatsu 
19256a330a3cSMasami Hiramatsu 	/* Store comp_dir */
19266a330a3cSMasami Hiramatsu 	if (lf.found) {
19276a330a3cSMasami Hiramatsu 		comp_dir = cu_get_comp_dir(&lf.cu_die);
19286a330a3cSMasami Hiramatsu 		if (comp_dir) {
19296a330a3cSMasami Hiramatsu 			lr->comp_dir = strdup(comp_dir);
19306a330a3cSMasami Hiramatsu 			if (!lr->comp_dir)
19316a330a3cSMasami Hiramatsu 				ret = -ENOMEM;
19326a330a3cSMasami Hiramatsu 		}
19336a330a3cSMasami Hiramatsu 	}
19346a330a3cSMasami Hiramatsu 
19357cf0b79eSMasami Hiramatsu 	pr_debug("path: %s\n", lr->path);
1936469b9b88SMasami Hiramatsu 	dwfl_end(dwfl);
1937b55a87adSMasami Hiramatsu 	return (ret < 0) ? ret : lf.found;
1938631c9defSMasami Hiramatsu }
1939631c9defSMasami Hiramatsu 
1940