xref: /linux/tools/perf/util/libdw.c (revision 67f8bc848ee31831336bd478e57d2f993551902e)
1 // SPDX-License-Identifier: GPL-2.0
2 #include "dso.h"
3 #include "libdw.h"
4 #include "srcline.h"
5 #include "symbol.h"
6 #include "dwarf-aux.h"
7 #include "callchain.h"
8 #include <fcntl.h>
9 #include <unistd.h>
10 #include <elfutils/libdwfl.h>
11 
12 static const Dwfl_Callbacks offline_callbacks = {
13 	.find_debuginfo = dwfl_standard_find_debuginfo,
14 	.section_address = dwfl_offline_section_address,
15 	.find_elf = dwfl_build_id_find_elf,
16 };
17 
18 void dso__free_libdw(struct dso *dso)
19 {
20 	Dwfl *dwfl = dso__libdw(dso);
21 
22 	if (dwfl) {
23 		dwfl_end(dwfl);
24 		dso__set_libdw(dso, NULL);
25 	}
26 }
27 
28 struct Dwfl *dso__libdw_dwfl(struct dso *dso)
29 {
30 	Dwfl *dwfl = dso__libdw(dso);
31 	const char *dso_name;
32 	Dwfl_Module *mod;
33 	int fd;
34 
35 	if (dwfl)
36 		return dwfl;
37 
38 	dso_name = dso__long_name(dso);
39 	/*
40 	 * Initialize Dwfl session.
41 	 * We need to open the DSO file to report it to libdw.
42 	 */
43 	fd = open(dso_name, O_RDONLY);
44 	if (fd < 0)
45 		return NULL;
46 
47 	dwfl = dwfl_begin(&offline_callbacks);
48 	if (!dwfl) {
49 		close(fd);
50 		return NULL;
51 	}
52 
53 	/*
54 	 * If the report is successful, the file descriptor fd is consumed
55 	 * and closed by the Dwfl. If not, it is not closed.
56 	 */
57 	mod = dwfl_report_offline(dwfl, dso_name, dso_name, fd);
58 	if (!mod) {
59 		dwfl_end(dwfl);
60 		close(fd);
61 		return NULL;
62 	}
63 
64 	if (dwfl_report_end(dwfl, /*removed=*/NULL, /*arg=*/NULL) != 0) {
65 		dwfl_end(dwfl);
66 		return NULL;
67 	}
68 	dso__set_libdw(dso, dwfl);
69 
70 	return dwfl;
71 }
72 
73 struct libdw_a2l_cb_args {
74 	struct dso *dso;
75 	struct symbol *sym;
76 	struct inline_node *node;
77 	char *leaf_srcline;
78 	bool leaf_srcline_used;
79 	int err;
80 };
81 
82 static int libdw_a2l_cb(Dwarf_Die *die, void *_args)
83 {
84 	struct libdw_a2l_cb_args *args  = _args;
85 	const char *call_fname = die_get_call_file(die);
86 	int call_lineno = die_get_call_lineno(die);
87 	char *call_srcline = srcline__unknown;
88 	struct symbol *inline_sym;
89 
90 	if (dwarf_tag(die) == DW_TAG_subprogram && args->sym) {
91 		/*
92 		 * cu_walk_functions_at() opens the walk with the
93 		 * containing DW_TAG_subprogram DIE (the non-inlined outer
94 		 * function). That's just the base symbol -- use it
95 		 * directly. Avoids a fragile name-vs-name compare in
96 		 * new_inline_sym() that misfires when GCC IPA passes
97 		 * (.isra/.constprop/.part/.cold) rename the ELF symbol
98 		 * while DWARF keeps the pre-clone linkage name, which
99 		 * left the outer frame spuriously tagged "(inlined)".
100 		 */
101 		inline_sym = args->sym;
102 	} else {
103 		/*
104 		 * Prefer DW_AT_linkage_name so C++ inline frames keep
105 		 * their namespace/class qualification. new_inline_sym()
106 		 * runs the name through dso__demangle_sym(), so the
107 		 * mangled linkage name is turned back into
108 		 * "Namespace::Class::method". Fall back to DW_AT_name
109 		 * (unqualified) when no linkage name is present, e.g.
110 		 * for C code or extern "C" functions.
111 		 */
112 		const char *funcname = die_get_linkage_name(die) ?: die_name(die);
113 
114 		inline_sym = new_inline_sym(args->dso, args->sym, funcname);
115 		if (!inline_sym)
116 			goto abort_enomem;
117 	}
118 
119 	/* Assign caller information to the parent. */
120 	if (call_fname)
121 		call_srcline = srcline_from_fileline(call_fname, call_lineno >= 0 ? call_lineno : 0);
122 
123 	if (!list_empty(&args->node->val)) {
124 		struct inline_list *parent;
125 
126 		if (callchain_param.order == ORDER_CALLEE)
127 			parent = list_first_entry(&args->node->val, struct inline_list, list);
128 		else
129 			parent = list_last_entry(&args->node->val, struct inline_list, list);
130 
131 		if (args->leaf_srcline == parent->srcline)
132 			args->leaf_srcline_used = false;
133 		else if (parent->srcline != srcline__unknown)
134 			free(parent->srcline);
135 		parent->srcline = call_srcline;
136 		call_srcline = NULL;
137 	}
138 	if (call_srcline && call_srcline != srcline__unknown)
139 		free(call_srcline);
140 
141 	/* Add this symbol to the chain as the leaf. */
142 	if (!args->leaf_srcline_used) {
143 		if (inline_list__append_tail(inline_sym, args->leaf_srcline, args->node) != 0)
144 			goto abort_delete_sym;
145 		args->leaf_srcline_used = true;
146 	} else {
147 		char *srcline = strdup(args->leaf_srcline);
148 
149 		if (!srcline)
150 			goto abort_delete_sym;
151 		if (inline_list__append_tail(inline_sym, srcline, args->node) != 0) {
152 			free(srcline);
153 			goto abort_delete_sym;
154 		}
155 	}
156 	return 0;
157 
158 abort_delete_sym:
159 	if (symbol__inlined(inline_sym))
160 		symbol__delete(inline_sym);
161 abort_enomem:
162 	args->err = -ENOMEM;
163 	return DWARF_CB_ABORT;
164 }
165 
166 int libdw__addr2line(u64 addr, char **file, unsigned int *line_nr,
167 		     struct dso *dso, bool unwind_inlines,
168 		     struct inline_node *node, struct symbol *sym)
169 {
170 	Dwfl *dwfl = dso__libdw_dwfl(dso);
171 	Dwfl_Module *mod;
172 	Dwfl_Line *dwline;
173 	Dwarf_Addr bias;
174 	const char *src;
175 	int lineno = 0;
176 
177 	if (!dwfl)
178 		return 0;
179 
180 	mod = dwfl_addrmodule(dwfl, addr);
181 	if (!mod)
182 		return 0;
183 
184 	/*
185 	 * Get/ignore the dwarf information. Determine the bias, difference
186 	 * between the regular ELF addr2line addresses and those to use with
187 	 * libdw.
188 	 */
189 	if (!dwfl_module_getdwarf(mod, &bias))
190 		return 0;
191 
192 	/* Find source line information for the address. */
193 	dwline = dwfl_module_getsrc(mod, addr + bias);
194 	if (!dwline)
195 		return 0;
196 
197 	/* Get line information. */
198 	src = dwfl_lineinfo(dwline, /*addr=*/NULL, &lineno, /*col=*/NULL, /*mtime=*/NULL,
199 			    /*length=*/NULL);
200 
201 	if (file)
202 		*file = src ? strdup(src) : NULL;
203 	if (line_nr)
204 		*line_nr = lineno;
205 
206 	/* Optionally unwind inline function call chain. */
207 	if (unwind_inlines && node) {
208 		Dwarf_Addr unused_bias;
209 		Dwarf_Die *cudie = dwfl_module_addrdie(mod, addr + bias, &unused_bias);
210 		struct libdw_a2l_cb_args args = {
211 			.dso = dso,
212 			.sym = sym,
213 			.node = node,
214 			.leaf_srcline = srcline_from_fileline(src ?: "<unknown>", lineno),
215 		};
216 
217 		if (!args.leaf_srcline) {
218 			if (file && *file) {
219 				free(*file);
220 				*file = NULL;
221 			}
222 			return 0;
223 		}
224 
225 		/* Walk from the parent down to the leaf. */
226 		if (cudie)
227 			cu_walk_functions_at(cudie, addr, libdw_a2l_cb, &args);
228 
229 		if (!args.leaf_srcline_used)
230 			free(args.leaf_srcline);
231 
232 		if (args.err) {
233 			if (file && *file) {
234 				free(*file);
235 				*file = NULL;
236 			}
237 			inline_node__clear_frames(node);
238 			return 0;
239 		}
240 	}
241 	return 1;
242 }
243