Commit 951e273060d15b233a7f7ccaf76ba682b5b05a03
Exists in
ti-lsk-linux-4.1.y
and in
12 other branches
Merge branch 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull perf fixes from Ingo Molnar: "Two last minute tooling fixes" * 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: perf probe: Fix perf probe to find correct variable DIE perf probe: Fix a segfault if asked for variable it doesn't find
Showing 2 changed files Inline Diff
tools/perf/util/dwarf-aux.c
1 | /* | 1 | /* |
2 | * dwarf-aux.c : libdw auxiliary interfaces | 2 | * dwarf-aux.c : libdw auxiliary interfaces |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License as published by | 5 | * it under the terms of the GNU General Public License as published by |
6 | * the Free Software Foundation; either version 2 of the License, or | 6 | * the Free Software Foundation; either version 2 of the License, or |
7 | * (at your option) any later version. | 7 | * (at your option) any later version. |
8 | * | 8 | * |
9 | * This program is distributed in the hope that it will be useful, | 9 | * This program is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | * GNU General Public License for more details. | 12 | * GNU General Public License for more details. |
13 | * | 13 | * |
14 | * You should have received a copy of the GNU General Public License | 14 | * You should have received a copy of the GNU General Public License |
15 | * along with this program; if not, write to the Free Software | 15 | * along with this program; if not, write to the Free Software |
16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | 16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
17 | * | 17 | * |
18 | */ | 18 | */ |
19 | 19 | ||
20 | #include <stdbool.h> | 20 | #include <stdbool.h> |
21 | #include "util.h" | 21 | #include "util.h" |
22 | #include "debug.h" | 22 | #include "debug.h" |
23 | #include "dwarf-aux.h" | 23 | #include "dwarf-aux.h" |
24 | 24 | ||
25 | /** | 25 | /** |
26 | * cu_find_realpath - Find the realpath of the target file | 26 | * cu_find_realpath - Find the realpath of the target file |
27 | * @cu_die: A DIE(dwarf information entry) of CU(compilation Unit) | 27 | * @cu_die: A DIE(dwarf information entry) of CU(compilation Unit) |
28 | * @fname: The tail filename of the target file | 28 | * @fname: The tail filename of the target file |
29 | * | 29 | * |
30 | * Find the real(long) path of @fname in @cu_die. | 30 | * Find the real(long) path of @fname in @cu_die. |
31 | */ | 31 | */ |
32 | const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname) | 32 | const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname) |
33 | { | 33 | { |
34 | Dwarf_Files *files; | 34 | Dwarf_Files *files; |
35 | size_t nfiles, i; | 35 | size_t nfiles, i; |
36 | const char *src = NULL; | 36 | const char *src = NULL; |
37 | int ret; | 37 | int ret; |
38 | 38 | ||
39 | if (!fname) | 39 | if (!fname) |
40 | return NULL; | 40 | return NULL; |
41 | 41 | ||
42 | ret = dwarf_getsrcfiles(cu_die, &files, &nfiles); | 42 | ret = dwarf_getsrcfiles(cu_die, &files, &nfiles); |
43 | if (ret != 0) | 43 | if (ret != 0) |
44 | return NULL; | 44 | return NULL; |
45 | 45 | ||
46 | for (i = 0; i < nfiles; i++) { | 46 | for (i = 0; i < nfiles; i++) { |
47 | src = dwarf_filesrc(files, i, NULL, NULL); | 47 | src = dwarf_filesrc(files, i, NULL, NULL); |
48 | if (strtailcmp(src, fname) == 0) | 48 | if (strtailcmp(src, fname) == 0) |
49 | break; | 49 | break; |
50 | } | 50 | } |
51 | if (i == nfiles) | 51 | if (i == nfiles) |
52 | return NULL; | 52 | return NULL; |
53 | return src; | 53 | return src; |
54 | } | 54 | } |
55 | 55 | ||
56 | /** | 56 | /** |
57 | * cu_get_comp_dir - Get the path of compilation directory | 57 | * cu_get_comp_dir - Get the path of compilation directory |
58 | * @cu_die: a CU DIE | 58 | * @cu_die: a CU DIE |
59 | * | 59 | * |
60 | * Get the path of compilation directory of given @cu_die. | 60 | * Get the path of compilation directory of given @cu_die. |
61 | * Since this depends on DW_AT_comp_dir, older gcc will not | 61 | * Since this depends on DW_AT_comp_dir, older gcc will not |
62 | * embedded it. In that case, this returns NULL. | 62 | * embedded it. In that case, this returns NULL. |
63 | */ | 63 | */ |
64 | const char *cu_get_comp_dir(Dwarf_Die *cu_die) | 64 | const char *cu_get_comp_dir(Dwarf_Die *cu_die) |
65 | { | 65 | { |
66 | Dwarf_Attribute attr; | 66 | Dwarf_Attribute attr; |
67 | if (dwarf_attr(cu_die, DW_AT_comp_dir, &attr) == NULL) | 67 | if (dwarf_attr(cu_die, DW_AT_comp_dir, &attr) == NULL) |
68 | return NULL; | 68 | return NULL; |
69 | return dwarf_formstring(&attr); | 69 | return dwarf_formstring(&attr); |
70 | } | 70 | } |
71 | 71 | ||
72 | /** | 72 | /** |
73 | * cu_find_lineinfo - Get a line number and file name for given address | 73 | * cu_find_lineinfo - Get a line number and file name for given address |
74 | * @cu_die: a CU DIE | 74 | * @cu_die: a CU DIE |
75 | * @addr: An address | 75 | * @addr: An address |
76 | * @fname: a pointer which returns the file name string | 76 | * @fname: a pointer which returns the file name string |
77 | * @lineno: a pointer which returns the line number | 77 | * @lineno: a pointer which returns the line number |
78 | * | 78 | * |
79 | * Find a line number and file name for @addr in @cu_die. | 79 | * Find a line number and file name for @addr in @cu_die. |
80 | */ | 80 | */ |
81 | int cu_find_lineinfo(Dwarf_Die *cu_die, unsigned long addr, | 81 | int cu_find_lineinfo(Dwarf_Die *cu_die, unsigned long addr, |
82 | const char **fname, int *lineno) | 82 | const char **fname, int *lineno) |
83 | { | 83 | { |
84 | Dwarf_Line *line; | 84 | Dwarf_Line *line; |
85 | Dwarf_Addr laddr; | 85 | Dwarf_Addr laddr; |
86 | 86 | ||
87 | line = dwarf_getsrc_die(cu_die, (Dwarf_Addr)addr); | 87 | line = dwarf_getsrc_die(cu_die, (Dwarf_Addr)addr); |
88 | if (line && dwarf_lineaddr(line, &laddr) == 0 && | 88 | if (line && dwarf_lineaddr(line, &laddr) == 0 && |
89 | addr == (unsigned long)laddr && dwarf_lineno(line, lineno) == 0) { | 89 | addr == (unsigned long)laddr && dwarf_lineno(line, lineno) == 0) { |
90 | *fname = dwarf_linesrc(line, NULL, NULL); | 90 | *fname = dwarf_linesrc(line, NULL, NULL); |
91 | if (!*fname) | 91 | if (!*fname) |
92 | /* line number is useless without filename */ | 92 | /* line number is useless without filename */ |
93 | *lineno = 0; | 93 | *lineno = 0; |
94 | } | 94 | } |
95 | 95 | ||
96 | return *lineno ?: -ENOENT; | 96 | return *lineno ?: -ENOENT; |
97 | } | 97 | } |
98 | 98 | ||
99 | static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data); | 99 | static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data); |
100 | 100 | ||
101 | /** | 101 | /** |
102 | * cu_walk_functions_at - Walk on function DIEs at given address | 102 | * cu_walk_functions_at - Walk on function DIEs at given address |
103 | * @cu_die: A CU DIE | 103 | * @cu_die: A CU DIE |
104 | * @addr: An address | 104 | * @addr: An address |
105 | * @callback: A callback which called with found DIEs | 105 | * @callback: A callback which called with found DIEs |
106 | * @data: A user data | 106 | * @data: A user data |
107 | * | 107 | * |
108 | * Walk on function DIEs at given @addr in @cu_die. Passed DIEs | 108 | * Walk on function DIEs at given @addr in @cu_die. Passed DIEs |
109 | * should be subprogram or inlined-subroutines. | 109 | * should be subprogram or inlined-subroutines. |
110 | */ | 110 | */ |
111 | int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr, | 111 | int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr, |
112 | int (*callback)(Dwarf_Die *, void *), void *data) | 112 | int (*callback)(Dwarf_Die *, void *), void *data) |
113 | { | 113 | { |
114 | Dwarf_Die die_mem; | 114 | Dwarf_Die die_mem; |
115 | Dwarf_Die *sc_die; | 115 | Dwarf_Die *sc_die; |
116 | int ret = -ENOENT; | 116 | int ret = -ENOENT; |
117 | 117 | ||
118 | /* Inlined function could be recursive. Trace it until fail */ | 118 | /* Inlined function could be recursive. Trace it until fail */ |
119 | for (sc_die = die_find_realfunc(cu_die, addr, &die_mem); | 119 | for (sc_die = die_find_realfunc(cu_die, addr, &die_mem); |
120 | sc_die != NULL; | 120 | sc_die != NULL; |
121 | sc_die = die_find_child(sc_die, __die_find_inline_cb, &addr, | 121 | sc_die = die_find_child(sc_die, __die_find_inline_cb, &addr, |
122 | &die_mem)) { | 122 | &die_mem)) { |
123 | ret = callback(sc_die, data); | 123 | ret = callback(sc_die, data); |
124 | if (ret) | 124 | if (ret) |
125 | break; | 125 | break; |
126 | } | 126 | } |
127 | 127 | ||
128 | return ret; | 128 | return ret; |
129 | 129 | ||
130 | } | 130 | } |
131 | 131 | ||
132 | /** | 132 | /** |
133 | * die_compare_name - Compare diename and tname | 133 | * die_compare_name - Compare diename and tname |
134 | * @dw_die: a DIE | 134 | * @dw_die: a DIE |
135 | * @tname: a string of target name | 135 | * @tname: a string of target name |
136 | * | 136 | * |
137 | * Compare the name of @dw_die and @tname. Return false if @dw_die has no name. | 137 | * Compare the name of @dw_die and @tname. Return false if @dw_die has no name. |
138 | */ | 138 | */ |
139 | bool die_compare_name(Dwarf_Die *dw_die, const char *tname) | 139 | bool die_compare_name(Dwarf_Die *dw_die, const char *tname) |
140 | { | 140 | { |
141 | const char *name; | 141 | const char *name; |
142 | name = dwarf_diename(dw_die); | 142 | name = dwarf_diename(dw_die); |
143 | return name ? (strcmp(tname, name) == 0) : false; | 143 | return name ? (strcmp(tname, name) == 0) : false; |
144 | } | 144 | } |
145 | 145 | ||
146 | /** | 146 | /** |
147 | * die_get_call_lineno - Get callsite line number of inline-function instance | 147 | * die_get_call_lineno - Get callsite line number of inline-function instance |
148 | * @in_die: a DIE of an inlined function instance | 148 | * @in_die: a DIE of an inlined function instance |
149 | * | 149 | * |
150 | * Get call-site line number of @in_die. This means from where the inline | 150 | * Get call-site line number of @in_die. This means from where the inline |
151 | * function is called. | 151 | * function is called. |
152 | */ | 152 | */ |
153 | int die_get_call_lineno(Dwarf_Die *in_die) | 153 | int die_get_call_lineno(Dwarf_Die *in_die) |
154 | { | 154 | { |
155 | Dwarf_Attribute attr; | 155 | Dwarf_Attribute attr; |
156 | Dwarf_Word ret; | 156 | Dwarf_Word ret; |
157 | 157 | ||
158 | if (!dwarf_attr(in_die, DW_AT_call_line, &attr)) | 158 | if (!dwarf_attr(in_die, DW_AT_call_line, &attr)) |
159 | return -ENOENT; | 159 | return -ENOENT; |
160 | 160 | ||
161 | dwarf_formudata(&attr, &ret); | 161 | dwarf_formudata(&attr, &ret); |
162 | return (int)ret; | 162 | return (int)ret; |
163 | } | 163 | } |
164 | 164 | ||
165 | /** | 165 | /** |
166 | * die_get_type - Get type DIE | 166 | * die_get_type - Get type DIE |
167 | * @vr_die: a DIE of a variable | 167 | * @vr_die: a DIE of a variable |
168 | * @die_mem: where to store a type DIE | 168 | * @die_mem: where to store a type DIE |
169 | * | 169 | * |
170 | * Get a DIE of the type of given variable (@vr_die), and store | 170 | * Get a DIE of the type of given variable (@vr_die), and store |
171 | * it to die_mem. Return NULL if fails to get a type DIE. | 171 | * it to die_mem. Return NULL if fails to get a type DIE. |
172 | */ | 172 | */ |
173 | Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) | 173 | Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) |
174 | { | 174 | { |
175 | Dwarf_Attribute attr; | 175 | Dwarf_Attribute attr; |
176 | 176 | ||
177 | if (dwarf_attr_integrate(vr_die, DW_AT_type, &attr) && | 177 | if (dwarf_attr_integrate(vr_die, DW_AT_type, &attr) && |
178 | dwarf_formref_die(&attr, die_mem)) | 178 | dwarf_formref_die(&attr, die_mem)) |
179 | return die_mem; | 179 | return die_mem; |
180 | else | 180 | else |
181 | return NULL; | 181 | return NULL; |
182 | } | 182 | } |
183 | 183 | ||
184 | /* Get a type die, but skip qualifiers */ | 184 | /* Get a type die, but skip qualifiers */ |
185 | static Dwarf_Die *__die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) | 185 | static Dwarf_Die *__die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) |
186 | { | 186 | { |
187 | int tag; | 187 | int tag; |
188 | 188 | ||
189 | do { | 189 | do { |
190 | vr_die = die_get_type(vr_die, die_mem); | 190 | vr_die = die_get_type(vr_die, die_mem); |
191 | if (!vr_die) | 191 | if (!vr_die) |
192 | break; | 192 | break; |
193 | tag = dwarf_tag(vr_die); | 193 | tag = dwarf_tag(vr_die); |
194 | } while (tag == DW_TAG_const_type || | 194 | } while (tag == DW_TAG_const_type || |
195 | tag == DW_TAG_restrict_type || | 195 | tag == DW_TAG_restrict_type || |
196 | tag == DW_TAG_volatile_type || | 196 | tag == DW_TAG_volatile_type || |
197 | tag == DW_TAG_shared_type); | 197 | tag == DW_TAG_shared_type); |
198 | 198 | ||
199 | return vr_die; | 199 | return vr_die; |
200 | } | 200 | } |
201 | 201 | ||
202 | /** | 202 | /** |
203 | * die_get_real_type - Get a type die, but skip qualifiers and typedef | 203 | * die_get_real_type - Get a type die, but skip qualifiers and typedef |
204 | * @vr_die: a DIE of a variable | 204 | * @vr_die: a DIE of a variable |
205 | * @die_mem: where to store a type DIE | 205 | * @die_mem: where to store a type DIE |
206 | * | 206 | * |
207 | * Get a DIE of the type of given variable (@vr_die), and store | 207 | * Get a DIE of the type of given variable (@vr_die), and store |
208 | * it to die_mem. Return NULL if fails to get a type DIE. | 208 | * it to die_mem. Return NULL if fails to get a type DIE. |
209 | * If the type is qualifiers (e.g. const) or typedef, this skips it | 209 | * If the type is qualifiers (e.g. const) or typedef, this skips it |
210 | * and tries to find real type (structure or basic types, e.g. int). | 210 | * and tries to find real type (structure or basic types, e.g. int). |
211 | */ | 211 | */ |
212 | Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) | 212 | Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) |
213 | { | 213 | { |
214 | do { | 214 | do { |
215 | vr_die = __die_get_real_type(vr_die, die_mem); | 215 | vr_die = __die_get_real_type(vr_die, die_mem); |
216 | } while (vr_die && dwarf_tag(vr_die) == DW_TAG_typedef); | 216 | } while (vr_die && dwarf_tag(vr_die) == DW_TAG_typedef); |
217 | 217 | ||
218 | return vr_die; | 218 | return vr_die; |
219 | } | 219 | } |
220 | 220 | ||
221 | /* Get attribute and translate it as a udata */ | 221 | /* Get attribute and translate it as a udata */ |
222 | static int die_get_attr_udata(Dwarf_Die *tp_die, unsigned int attr_name, | 222 | static int die_get_attr_udata(Dwarf_Die *tp_die, unsigned int attr_name, |
223 | Dwarf_Word *result) | 223 | Dwarf_Word *result) |
224 | { | 224 | { |
225 | Dwarf_Attribute attr; | 225 | Dwarf_Attribute attr; |
226 | 226 | ||
227 | if (dwarf_attr(tp_die, attr_name, &attr) == NULL || | 227 | if (dwarf_attr(tp_die, attr_name, &attr) == NULL || |
228 | dwarf_formudata(&attr, result) != 0) | 228 | dwarf_formudata(&attr, result) != 0) |
229 | return -ENOENT; | 229 | return -ENOENT; |
230 | 230 | ||
231 | return 0; | 231 | return 0; |
232 | } | 232 | } |
233 | 233 | ||
234 | /* Get attribute and translate it as a sdata */ | 234 | /* Get attribute and translate it as a sdata */ |
235 | static int die_get_attr_sdata(Dwarf_Die *tp_die, unsigned int attr_name, | 235 | static int die_get_attr_sdata(Dwarf_Die *tp_die, unsigned int attr_name, |
236 | Dwarf_Sword *result) | 236 | Dwarf_Sword *result) |
237 | { | 237 | { |
238 | Dwarf_Attribute attr; | 238 | Dwarf_Attribute attr; |
239 | 239 | ||
240 | if (dwarf_attr(tp_die, attr_name, &attr) == NULL || | 240 | if (dwarf_attr(tp_die, attr_name, &attr) == NULL || |
241 | dwarf_formsdata(&attr, result) != 0) | 241 | dwarf_formsdata(&attr, result) != 0) |
242 | return -ENOENT; | 242 | return -ENOENT; |
243 | 243 | ||
244 | return 0; | 244 | return 0; |
245 | } | 245 | } |
246 | 246 | ||
247 | /** | 247 | /** |
248 | * die_is_signed_type - Check whether a type DIE is signed or not | 248 | * die_is_signed_type - Check whether a type DIE is signed or not |
249 | * @tp_die: a DIE of a type | 249 | * @tp_die: a DIE of a type |
250 | * | 250 | * |
251 | * Get the encoding of @tp_die and return true if the encoding | 251 | * Get the encoding of @tp_die and return true if the encoding |
252 | * is signed. | 252 | * is signed. |
253 | */ | 253 | */ |
254 | bool die_is_signed_type(Dwarf_Die *tp_die) | 254 | bool die_is_signed_type(Dwarf_Die *tp_die) |
255 | { | 255 | { |
256 | Dwarf_Word ret; | 256 | Dwarf_Word ret; |
257 | 257 | ||
258 | if (die_get_attr_udata(tp_die, DW_AT_encoding, &ret)) | 258 | if (die_get_attr_udata(tp_die, DW_AT_encoding, &ret)) |
259 | return false; | 259 | return false; |
260 | 260 | ||
261 | return (ret == DW_ATE_signed_char || ret == DW_ATE_signed || | 261 | return (ret == DW_ATE_signed_char || ret == DW_ATE_signed || |
262 | ret == DW_ATE_signed_fixed); | 262 | ret == DW_ATE_signed_fixed); |
263 | } | 263 | } |
264 | 264 | ||
265 | /** | 265 | /** |
266 | * die_is_func_def - Ensure that this DIE is a subprogram and definition | 266 | * die_is_func_def - Ensure that this DIE is a subprogram and definition |
267 | * @dw_die: a DIE | 267 | * @dw_die: a DIE |
268 | * | 268 | * |
269 | * Ensure that this DIE is a subprogram and NOT a declaration. This | 269 | * Ensure that this DIE is a subprogram and NOT a declaration. This |
270 | * returns true if @dw_die is a function definition. | 270 | * returns true if @dw_die is a function definition. |
271 | **/ | 271 | **/ |
272 | bool die_is_func_def(Dwarf_Die *dw_die) | 272 | bool die_is_func_def(Dwarf_Die *dw_die) |
273 | { | 273 | { |
274 | Dwarf_Attribute attr; | 274 | Dwarf_Attribute attr; |
275 | 275 | ||
276 | return (dwarf_tag(dw_die) == DW_TAG_subprogram && | 276 | return (dwarf_tag(dw_die) == DW_TAG_subprogram && |
277 | dwarf_attr(dw_die, DW_AT_declaration, &attr) == NULL); | 277 | dwarf_attr(dw_die, DW_AT_declaration, &attr) == NULL); |
278 | } | 278 | } |
279 | 279 | ||
280 | /** | 280 | /** |
281 | * die_get_data_member_location - Get the data-member offset | 281 | * die_get_data_member_location - Get the data-member offset |
282 | * @mb_die: a DIE of a member of a data structure | 282 | * @mb_die: a DIE of a member of a data structure |
283 | * @offs: The offset of the member in the data structure | 283 | * @offs: The offset of the member in the data structure |
284 | * | 284 | * |
285 | * Get the offset of @mb_die in the data structure including @mb_die, and | 285 | * Get the offset of @mb_die in the data structure including @mb_die, and |
286 | * stores result offset to @offs. If any error occurs this returns errno. | 286 | * stores result offset to @offs. If any error occurs this returns errno. |
287 | */ | 287 | */ |
288 | int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs) | 288 | int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs) |
289 | { | 289 | { |
290 | Dwarf_Attribute attr; | 290 | Dwarf_Attribute attr; |
291 | Dwarf_Op *expr; | 291 | Dwarf_Op *expr; |
292 | size_t nexpr; | 292 | size_t nexpr; |
293 | int ret; | 293 | int ret; |
294 | 294 | ||
295 | if (dwarf_attr(mb_die, DW_AT_data_member_location, &attr) == NULL) | 295 | if (dwarf_attr(mb_die, DW_AT_data_member_location, &attr) == NULL) |
296 | return -ENOENT; | 296 | return -ENOENT; |
297 | 297 | ||
298 | if (dwarf_formudata(&attr, offs) != 0) { | 298 | if (dwarf_formudata(&attr, offs) != 0) { |
299 | /* DW_AT_data_member_location should be DW_OP_plus_uconst */ | 299 | /* DW_AT_data_member_location should be DW_OP_plus_uconst */ |
300 | ret = dwarf_getlocation(&attr, &expr, &nexpr); | 300 | ret = dwarf_getlocation(&attr, &expr, &nexpr); |
301 | if (ret < 0 || nexpr == 0) | 301 | if (ret < 0 || nexpr == 0) |
302 | return -ENOENT; | 302 | return -ENOENT; |
303 | 303 | ||
304 | if (expr[0].atom != DW_OP_plus_uconst || nexpr != 1) { | 304 | if (expr[0].atom != DW_OP_plus_uconst || nexpr != 1) { |
305 | pr_debug("Unable to get offset:Unexpected OP %x (%zd)\n", | 305 | pr_debug("Unable to get offset:Unexpected OP %x (%zd)\n", |
306 | expr[0].atom, nexpr); | 306 | expr[0].atom, nexpr); |
307 | return -ENOTSUP; | 307 | return -ENOTSUP; |
308 | } | 308 | } |
309 | *offs = (Dwarf_Word)expr[0].number; | 309 | *offs = (Dwarf_Word)expr[0].number; |
310 | } | 310 | } |
311 | return 0; | 311 | return 0; |
312 | } | 312 | } |
313 | 313 | ||
314 | /* Get the call file index number in CU DIE */ | 314 | /* Get the call file index number in CU DIE */ |
315 | static int die_get_call_fileno(Dwarf_Die *in_die) | 315 | static int die_get_call_fileno(Dwarf_Die *in_die) |
316 | { | 316 | { |
317 | Dwarf_Sword idx; | 317 | Dwarf_Sword idx; |
318 | 318 | ||
319 | if (die_get_attr_sdata(in_die, DW_AT_call_file, &idx) == 0) | 319 | if (die_get_attr_sdata(in_die, DW_AT_call_file, &idx) == 0) |
320 | return (int)idx; | 320 | return (int)idx; |
321 | else | 321 | else |
322 | return -ENOENT; | 322 | return -ENOENT; |
323 | } | 323 | } |
324 | 324 | ||
325 | /* Get the declared file index number in CU DIE */ | 325 | /* Get the declared file index number in CU DIE */ |
326 | static int die_get_decl_fileno(Dwarf_Die *pdie) | 326 | static int die_get_decl_fileno(Dwarf_Die *pdie) |
327 | { | 327 | { |
328 | Dwarf_Sword idx; | 328 | Dwarf_Sword idx; |
329 | 329 | ||
330 | if (die_get_attr_sdata(pdie, DW_AT_decl_file, &idx) == 0) | 330 | if (die_get_attr_sdata(pdie, DW_AT_decl_file, &idx) == 0) |
331 | return (int)idx; | 331 | return (int)idx; |
332 | else | 332 | else |
333 | return -ENOENT; | 333 | return -ENOENT; |
334 | } | 334 | } |
335 | 335 | ||
336 | /** | 336 | /** |
337 | * die_get_call_file - Get callsite file name of inlined function instance | 337 | * die_get_call_file - Get callsite file name of inlined function instance |
338 | * @in_die: a DIE of an inlined function instance | 338 | * @in_die: a DIE of an inlined function instance |
339 | * | 339 | * |
340 | * Get call-site file name of @in_die. This means from which file the inline | 340 | * Get call-site file name of @in_die. This means from which file the inline |
341 | * function is called. | 341 | * function is called. |
342 | */ | 342 | */ |
343 | const char *die_get_call_file(Dwarf_Die *in_die) | 343 | const char *die_get_call_file(Dwarf_Die *in_die) |
344 | { | 344 | { |
345 | Dwarf_Die cu_die; | 345 | Dwarf_Die cu_die; |
346 | Dwarf_Files *files; | 346 | Dwarf_Files *files; |
347 | int idx; | 347 | int idx; |
348 | 348 | ||
349 | idx = die_get_call_fileno(in_die); | 349 | idx = die_get_call_fileno(in_die); |
350 | if (idx < 0 || !dwarf_diecu(in_die, &cu_die, NULL, NULL) || | 350 | if (idx < 0 || !dwarf_diecu(in_die, &cu_die, NULL, NULL) || |
351 | dwarf_getsrcfiles(&cu_die, &files, NULL) != 0) | 351 | dwarf_getsrcfiles(&cu_die, &files, NULL) != 0) |
352 | return NULL; | 352 | return NULL; |
353 | 353 | ||
354 | return dwarf_filesrc(files, idx, NULL, NULL); | 354 | return dwarf_filesrc(files, idx, NULL, NULL); |
355 | } | 355 | } |
356 | 356 | ||
357 | 357 | ||
358 | /** | 358 | /** |
359 | * die_find_child - Generic DIE search function in DIE tree | 359 | * die_find_child - Generic DIE search function in DIE tree |
360 | * @rt_die: a root DIE | 360 | * @rt_die: a root DIE |
361 | * @callback: a callback function | 361 | * @callback: a callback function |
362 | * @data: a user data passed to the callback function | 362 | * @data: a user data passed to the callback function |
363 | * @die_mem: a buffer for result DIE | 363 | * @die_mem: a buffer for result DIE |
364 | * | 364 | * |
365 | * Trace DIE tree from @rt_die and call @callback for each child DIE. | 365 | * Trace DIE tree from @rt_die and call @callback for each child DIE. |
366 | * If @callback returns DIE_FIND_CB_END, this stores the DIE into | 366 | * If @callback returns DIE_FIND_CB_END, this stores the DIE into |
367 | * @die_mem and returns it. If @callback returns DIE_FIND_CB_CONTINUE, | 367 | * @die_mem and returns it. If @callback returns DIE_FIND_CB_CONTINUE, |
368 | * this continues to trace the tree. Optionally, @callback can return | 368 | * this continues to trace the tree. Optionally, @callback can return |
369 | * DIE_FIND_CB_CHILD and DIE_FIND_CB_SIBLING, those means trace only | 369 | * DIE_FIND_CB_CHILD and DIE_FIND_CB_SIBLING, those means trace only |
370 | * the children and trace only the siblings respectively. | 370 | * the children and trace only the siblings respectively. |
371 | * Returns NULL if @callback can't find any appropriate DIE. | 371 | * Returns NULL if @callback can't find any appropriate DIE. |
372 | */ | 372 | */ |
373 | Dwarf_Die *die_find_child(Dwarf_Die *rt_die, | 373 | Dwarf_Die *die_find_child(Dwarf_Die *rt_die, |
374 | int (*callback)(Dwarf_Die *, void *), | 374 | int (*callback)(Dwarf_Die *, void *), |
375 | void *data, Dwarf_Die *die_mem) | 375 | void *data, Dwarf_Die *die_mem) |
376 | { | 376 | { |
377 | Dwarf_Die child_die; | 377 | Dwarf_Die child_die; |
378 | int ret; | 378 | int ret; |
379 | 379 | ||
380 | ret = dwarf_child(rt_die, die_mem); | 380 | ret = dwarf_child(rt_die, die_mem); |
381 | if (ret != 0) | 381 | if (ret != 0) |
382 | return NULL; | 382 | return NULL; |
383 | 383 | ||
384 | do { | 384 | do { |
385 | ret = callback(die_mem, data); | 385 | ret = callback(die_mem, data); |
386 | if (ret == DIE_FIND_CB_END) | 386 | if (ret == DIE_FIND_CB_END) |
387 | return die_mem; | 387 | return die_mem; |
388 | 388 | ||
389 | if ((ret & DIE_FIND_CB_CHILD) && | 389 | if ((ret & DIE_FIND_CB_CHILD) && |
390 | die_find_child(die_mem, callback, data, &child_die)) { | 390 | die_find_child(die_mem, callback, data, &child_die)) { |
391 | memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); | 391 | memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); |
392 | return die_mem; | 392 | return die_mem; |
393 | } | 393 | } |
394 | } while ((ret & DIE_FIND_CB_SIBLING) && | 394 | } while ((ret & DIE_FIND_CB_SIBLING) && |
395 | dwarf_siblingof(die_mem, die_mem) == 0); | 395 | dwarf_siblingof(die_mem, die_mem) == 0); |
396 | 396 | ||
397 | return NULL; | 397 | return NULL; |
398 | } | 398 | } |
399 | 399 | ||
400 | struct __addr_die_search_param { | 400 | struct __addr_die_search_param { |
401 | Dwarf_Addr addr; | 401 | Dwarf_Addr addr; |
402 | Dwarf_Die *die_mem; | 402 | Dwarf_Die *die_mem; |
403 | }; | 403 | }; |
404 | 404 | ||
405 | /* die_find callback for non-inlined function search */ | 405 | /* die_find callback for non-inlined function search */ |
406 | static int __die_search_func_cb(Dwarf_Die *fn_die, void *data) | 406 | static int __die_search_func_cb(Dwarf_Die *fn_die, void *data) |
407 | { | 407 | { |
408 | struct __addr_die_search_param *ad = data; | 408 | struct __addr_die_search_param *ad = data; |
409 | 409 | ||
410 | /* | 410 | /* |
411 | * Since a declaration entry doesn't has given pc, this always returns | 411 | * Since a declaration entry doesn't has given pc, this always returns |
412 | * function definition entry. | 412 | * function definition entry. |
413 | */ | 413 | */ |
414 | if (dwarf_tag(fn_die) == DW_TAG_subprogram && | 414 | if (dwarf_tag(fn_die) == DW_TAG_subprogram && |
415 | dwarf_haspc(fn_die, ad->addr)) { | 415 | dwarf_haspc(fn_die, ad->addr)) { |
416 | memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die)); | 416 | memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die)); |
417 | return DWARF_CB_ABORT; | 417 | return DWARF_CB_ABORT; |
418 | } | 418 | } |
419 | return DWARF_CB_OK; | 419 | return DWARF_CB_OK; |
420 | } | 420 | } |
421 | 421 | ||
422 | /** | 422 | /** |
423 | * die_find_realfunc - Search a non-inlined function at given address | 423 | * die_find_realfunc - Search a non-inlined function at given address |
424 | * @cu_die: a CU DIE which including @addr | 424 | * @cu_die: a CU DIE which including @addr |
425 | * @addr: target address | 425 | * @addr: target address |
426 | * @die_mem: a buffer for result DIE | 426 | * @die_mem: a buffer for result DIE |
427 | * | 427 | * |
428 | * Search a non-inlined function DIE which includes @addr. Stores the | 428 | * Search a non-inlined function DIE which includes @addr. Stores the |
429 | * DIE to @die_mem and returns it if found. Returns NULL if failed. | 429 | * DIE to @die_mem and returns it if found. Returns NULL if failed. |
430 | */ | 430 | */ |
431 | Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, | 431 | Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, |
432 | Dwarf_Die *die_mem) | 432 | Dwarf_Die *die_mem) |
433 | { | 433 | { |
434 | struct __addr_die_search_param ad; | 434 | struct __addr_die_search_param ad; |
435 | ad.addr = addr; | 435 | ad.addr = addr; |
436 | ad.die_mem = die_mem; | 436 | ad.die_mem = die_mem; |
437 | /* dwarf_getscopes can't find subprogram. */ | 437 | /* dwarf_getscopes can't find subprogram. */ |
438 | if (!dwarf_getfuncs(cu_die, __die_search_func_cb, &ad, 0)) | 438 | if (!dwarf_getfuncs(cu_die, __die_search_func_cb, &ad, 0)) |
439 | return NULL; | 439 | return NULL; |
440 | else | 440 | else |
441 | return die_mem; | 441 | return die_mem; |
442 | } | 442 | } |
443 | 443 | ||
444 | /* die_find callback for inline function search */ | 444 | /* die_find callback for inline function search */ |
445 | static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data) | 445 | static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data) |
446 | { | 446 | { |
447 | Dwarf_Addr *addr = data; | 447 | Dwarf_Addr *addr = data; |
448 | 448 | ||
449 | if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine && | 449 | if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine && |
450 | dwarf_haspc(die_mem, *addr)) | 450 | dwarf_haspc(die_mem, *addr)) |
451 | return DIE_FIND_CB_END; | 451 | return DIE_FIND_CB_END; |
452 | 452 | ||
453 | return DIE_FIND_CB_CONTINUE; | 453 | return DIE_FIND_CB_CONTINUE; |
454 | } | 454 | } |
455 | 455 | ||
456 | /** | 456 | /** |
457 | * die_find_top_inlinefunc - Search the top inlined function at given address | 457 | * die_find_top_inlinefunc - Search the top inlined function at given address |
458 | * @sp_die: a subprogram DIE which including @addr | 458 | * @sp_die: a subprogram DIE which including @addr |
459 | * @addr: target address | 459 | * @addr: target address |
460 | * @die_mem: a buffer for result DIE | 460 | * @die_mem: a buffer for result DIE |
461 | * | 461 | * |
462 | * Search an inlined function DIE which includes @addr. Stores the | 462 | * Search an inlined function DIE which includes @addr. Stores the |
463 | * DIE to @die_mem and returns it if found. Returns NULL if failed. | 463 | * DIE to @die_mem and returns it if found. Returns NULL if failed. |
464 | * Even if several inlined functions are expanded recursively, this | 464 | * Even if several inlined functions are expanded recursively, this |
465 | * doesn't trace it down, and returns the topmost one. | 465 | * doesn't trace it down, and returns the topmost one. |
466 | */ | 466 | */ |
467 | Dwarf_Die *die_find_top_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, | 467 | Dwarf_Die *die_find_top_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, |
468 | Dwarf_Die *die_mem) | 468 | Dwarf_Die *die_mem) |
469 | { | 469 | { |
470 | return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem); | 470 | return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem); |
471 | } | 471 | } |
472 | 472 | ||
473 | /** | 473 | /** |
474 | * die_find_inlinefunc - Search an inlined function at given address | 474 | * die_find_inlinefunc - Search an inlined function at given address |
475 | * @sp_die: a subprogram DIE which including @addr | 475 | * @sp_die: a subprogram DIE which including @addr |
476 | * @addr: target address | 476 | * @addr: target address |
477 | * @die_mem: a buffer for result DIE | 477 | * @die_mem: a buffer for result DIE |
478 | * | 478 | * |
479 | * Search an inlined function DIE which includes @addr. Stores the | 479 | * Search an inlined function DIE which includes @addr. Stores the |
480 | * DIE to @die_mem and returns it if found. Returns NULL if failed. | 480 | * DIE to @die_mem and returns it if found. Returns NULL if failed. |
481 | * If several inlined functions are expanded recursively, this trace | 481 | * If several inlined functions are expanded recursively, this trace |
482 | * it down and returns deepest one. | 482 | * it down and returns deepest one. |
483 | */ | 483 | */ |
484 | Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, | 484 | Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, |
485 | Dwarf_Die *die_mem) | 485 | Dwarf_Die *die_mem) |
486 | { | 486 | { |
487 | Dwarf_Die tmp_die; | 487 | Dwarf_Die tmp_die; |
488 | 488 | ||
489 | sp_die = die_find_child(sp_die, __die_find_inline_cb, &addr, &tmp_die); | 489 | sp_die = die_find_child(sp_die, __die_find_inline_cb, &addr, &tmp_die); |
490 | if (!sp_die) | 490 | if (!sp_die) |
491 | return NULL; | 491 | return NULL; |
492 | 492 | ||
493 | /* Inlined function could be recursive. Trace it until fail */ | 493 | /* Inlined function could be recursive. Trace it until fail */ |
494 | while (sp_die) { | 494 | while (sp_die) { |
495 | memcpy(die_mem, sp_die, sizeof(Dwarf_Die)); | 495 | memcpy(die_mem, sp_die, sizeof(Dwarf_Die)); |
496 | sp_die = die_find_child(sp_die, __die_find_inline_cb, &addr, | 496 | sp_die = die_find_child(sp_die, __die_find_inline_cb, &addr, |
497 | &tmp_die); | 497 | &tmp_die); |
498 | } | 498 | } |
499 | 499 | ||
500 | return die_mem; | 500 | return die_mem; |
501 | } | 501 | } |
502 | 502 | ||
503 | struct __instance_walk_param { | 503 | struct __instance_walk_param { |
504 | void *addr; | 504 | void *addr; |
505 | int (*callback)(Dwarf_Die *, void *); | 505 | int (*callback)(Dwarf_Die *, void *); |
506 | void *data; | 506 | void *data; |
507 | int retval; | 507 | int retval; |
508 | }; | 508 | }; |
509 | 509 | ||
510 | static int __die_walk_instances_cb(Dwarf_Die *inst, void *data) | 510 | static int __die_walk_instances_cb(Dwarf_Die *inst, void *data) |
511 | { | 511 | { |
512 | struct __instance_walk_param *iwp = data; | 512 | struct __instance_walk_param *iwp = data; |
513 | Dwarf_Attribute attr_mem; | 513 | Dwarf_Attribute attr_mem; |
514 | Dwarf_Die origin_mem; | 514 | Dwarf_Die origin_mem; |
515 | Dwarf_Attribute *attr; | 515 | Dwarf_Attribute *attr; |
516 | Dwarf_Die *origin; | 516 | Dwarf_Die *origin; |
517 | int tmp; | 517 | int tmp; |
518 | 518 | ||
519 | attr = dwarf_attr(inst, DW_AT_abstract_origin, &attr_mem); | 519 | attr = dwarf_attr(inst, DW_AT_abstract_origin, &attr_mem); |
520 | if (attr == NULL) | 520 | if (attr == NULL) |
521 | return DIE_FIND_CB_CONTINUE; | 521 | return DIE_FIND_CB_CONTINUE; |
522 | 522 | ||
523 | origin = dwarf_formref_die(attr, &origin_mem); | 523 | origin = dwarf_formref_die(attr, &origin_mem); |
524 | if (origin == NULL || origin->addr != iwp->addr) | 524 | if (origin == NULL || origin->addr != iwp->addr) |
525 | return DIE_FIND_CB_CONTINUE; | 525 | return DIE_FIND_CB_CONTINUE; |
526 | 526 | ||
527 | /* Ignore redundant instances */ | 527 | /* Ignore redundant instances */ |
528 | if (dwarf_tag(inst) == DW_TAG_inlined_subroutine) { | 528 | if (dwarf_tag(inst) == DW_TAG_inlined_subroutine) { |
529 | dwarf_decl_line(origin, &tmp); | 529 | dwarf_decl_line(origin, &tmp); |
530 | if (die_get_call_lineno(inst) == tmp) { | 530 | if (die_get_call_lineno(inst) == tmp) { |
531 | tmp = die_get_decl_fileno(origin); | 531 | tmp = die_get_decl_fileno(origin); |
532 | if (die_get_call_fileno(inst) == tmp) | 532 | if (die_get_call_fileno(inst) == tmp) |
533 | return DIE_FIND_CB_CONTINUE; | 533 | return DIE_FIND_CB_CONTINUE; |
534 | } | 534 | } |
535 | } | 535 | } |
536 | 536 | ||
537 | iwp->retval = iwp->callback(inst, iwp->data); | 537 | iwp->retval = iwp->callback(inst, iwp->data); |
538 | 538 | ||
539 | return (iwp->retval) ? DIE_FIND_CB_END : DIE_FIND_CB_CONTINUE; | 539 | return (iwp->retval) ? DIE_FIND_CB_END : DIE_FIND_CB_CONTINUE; |
540 | } | 540 | } |
541 | 541 | ||
542 | /** | 542 | /** |
543 | * die_walk_instances - Walk on instances of given DIE | 543 | * die_walk_instances - Walk on instances of given DIE |
544 | * @or_die: an abstract original DIE | 544 | * @or_die: an abstract original DIE |
545 | * @callback: a callback function which is called with instance DIE | 545 | * @callback: a callback function which is called with instance DIE |
546 | * @data: user data | 546 | * @data: user data |
547 | * | 547 | * |
548 | * Walk on the instances of give @in_die. @in_die must be an inlined function | 548 | * Walk on the instances of give @in_die. @in_die must be an inlined function |
549 | * declartion. This returns the return value of @callback if it returns | 549 | * declartion. This returns the return value of @callback if it returns |
550 | * non-zero value, or -ENOENT if there is no instance. | 550 | * non-zero value, or -ENOENT if there is no instance. |
551 | */ | 551 | */ |
552 | int die_walk_instances(Dwarf_Die *or_die, int (*callback)(Dwarf_Die *, void *), | 552 | int die_walk_instances(Dwarf_Die *or_die, int (*callback)(Dwarf_Die *, void *), |
553 | void *data) | 553 | void *data) |
554 | { | 554 | { |
555 | Dwarf_Die cu_die; | 555 | Dwarf_Die cu_die; |
556 | Dwarf_Die die_mem; | 556 | Dwarf_Die die_mem; |
557 | struct __instance_walk_param iwp = { | 557 | struct __instance_walk_param iwp = { |
558 | .addr = or_die->addr, | 558 | .addr = or_die->addr, |
559 | .callback = callback, | 559 | .callback = callback, |
560 | .data = data, | 560 | .data = data, |
561 | .retval = -ENOENT, | 561 | .retval = -ENOENT, |
562 | }; | 562 | }; |
563 | 563 | ||
564 | if (dwarf_diecu(or_die, &cu_die, NULL, NULL) == NULL) | 564 | if (dwarf_diecu(or_die, &cu_die, NULL, NULL) == NULL) |
565 | return -ENOENT; | 565 | return -ENOENT; |
566 | 566 | ||
567 | die_find_child(&cu_die, __die_walk_instances_cb, &iwp, &die_mem); | 567 | die_find_child(&cu_die, __die_walk_instances_cb, &iwp, &die_mem); |
568 | 568 | ||
569 | return iwp.retval; | 569 | return iwp.retval; |
570 | } | 570 | } |
571 | 571 | ||
572 | /* Line walker internal parameters */ | 572 | /* Line walker internal parameters */ |
573 | struct __line_walk_param { | 573 | struct __line_walk_param { |
574 | bool recursive; | 574 | bool recursive; |
575 | line_walk_callback_t callback; | 575 | line_walk_callback_t callback; |
576 | void *data; | 576 | void *data; |
577 | int retval; | 577 | int retval; |
578 | }; | 578 | }; |
579 | 579 | ||
580 | static int __die_walk_funclines_cb(Dwarf_Die *in_die, void *data) | 580 | static int __die_walk_funclines_cb(Dwarf_Die *in_die, void *data) |
581 | { | 581 | { |
582 | struct __line_walk_param *lw = data; | 582 | struct __line_walk_param *lw = data; |
583 | Dwarf_Addr addr = 0; | 583 | Dwarf_Addr addr = 0; |
584 | const char *fname; | 584 | const char *fname; |
585 | int lineno; | 585 | int lineno; |
586 | 586 | ||
587 | if (dwarf_tag(in_die) == DW_TAG_inlined_subroutine) { | 587 | if (dwarf_tag(in_die) == DW_TAG_inlined_subroutine) { |
588 | fname = die_get_call_file(in_die); | 588 | fname = die_get_call_file(in_die); |
589 | lineno = die_get_call_lineno(in_die); | 589 | lineno = die_get_call_lineno(in_die); |
590 | if (fname && lineno > 0 && dwarf_entrypc(in_die, &addr) == 0) { | 590 | if (fname && lineno > 0 && dwarf_entrypc(in_die, &addr) == 0) { |
591 | lw->retval = lw->callback(fname, lineno, addr, lw->data); | 591 | lw->retval = lw->callback(fname, lineno, addr, lw->data); |
592 | if (lw->retval != 0) | 592 | if (lw->retval != 0) |
593 | return DIE_FIND_CB_END; | 593 | return DIE_FIND_CB_END; |
594 | } | 594 | } |
595 | } | 595 | } |
596 | if (!lw->recursive) | 596 | if (!lw->recursive) |
597 | /* Don't need to search recursively */ | 597 | /* Don't need to search recursively */ |
598 | return DIE_FIND_CB_SIBLING; | 598 | return DIE_FIND_CB_SIBLING; |
599 | 599 | ||
600 | if (addr) { | 600 | if (addr) { |
601 | fname = dwarf_decl_file(in_die); | 601 | fname = dwarf_decl_file(in_die); |
602 | if (fname && dwarf_decl_line(in_die, &lineno) == 0) { | 602 | if (fname && dwarf_decl_line(in_die, &lineno) == 0) { |
603 | lw->retval = lw->callback(fname, lineno, addr, lw->data); | 603 | lw->retval = lw->callback(fname, lineno, addr, lw->data); |
604 | if (lw->retval != 0) | 604 | if (lw->retval != 0) |
605 | return DIE_FIND_CB_END; | 605 | return DIE_FIND_CB_END; |
606 | } | 606 | } |
607 | } | 607 | } |
608 | 608 | ||
609 | /* Continue to search nested inlined function call-sites */ | 609 | /* Continue to search nested inlined function call-sites */ |
610 | return DIE_FIND_CB_CONTINUE; | 610 | return DIE_FIND_CB_CONTINUE; |
611 | } | 611 | } |
612 | 612 | ||
613 | /* Walk on lines of blocks included in given DIE */ | 613 | /* Walk on lines of blocks included in given DIE */ |
614 | static int __die_walk_funclines(Dwarf_Die *sp_die, bool recursive, | 614 | static int __die_walk_funclines(Dwarf_Die *sp_die, bool recursive, |
615 | line_walk_callback_t callback, void *data) | 615 | line_walk_callback_t callback, void *data) |
616 | { | 616 | { |
617 | struct __line_walk_param lw = { | 617 | struct __line_walk_param lw = { |
618 | .recursive = recursive, | 618 | .recursive = recursive, |
619 | .callback = callback, | 619 | .callback = callback, |
620 | .data = data, | 620 | .data = data, |
621 | .retval = 0, | 621 | .retval = 0, |
622 | }; | 622 | }; |
623 | Dwarf_Die die_mem; | 623 | Dwarf_Die die_mem; |
624 | Dwarf_Addr addr; | 624 | Dwarf_Addr addr; |
625 | const char *fname; | 625 | const char *fname; |
626 | int lineno; | 626 | int lineno; |
627 | 627 | ||
628 | /* Handle function declaration line */ | 628 | /* Handle function declaration line */ |
629 | fname = dwarf_decl_file(sp_die); | 629 | fname = dwarf_decl_file(sp_die); |
630 | if (fname && dwarf_decl_line(sp_die, &lineno) == 0 && | 630 | if (fname && dwarf_decl_line(sp_die, &lineno) == 0 && |
631 | dwarf_entrypc(sp_die, &addr) == 0) { | 631 | dwarf_entrypc(sp_die, &addr) == 0) { |
632 | lw.retval = callback(fname, lineno, addr, data); | 632 | lw.retval = callback(fname, lineno, addr, data); |
633 | if (lw.retval != 0) | 633 | if (lw.retval != 0) |
634 | goto done; | 634 | goto done; |
635 | } | 635 | } |
636 | die_find_child(sp_die, __die_walk_funclines_cb, &lw, &die_mem); | 636 | die_find_child(sp_die, __die_walk_funclines_cb, &lw, &die_mem); |
637 | done: | 637 | done: |
638 | return lw.retval; | 638 | return lw.retval; |
639 | } | 639 | } |
640 | 640 | ||
641 | static int __die_walk_culines_cb(Dwarf_Die *sp_die, void *data) | 641 | static int __die_walk_culines_cb(Dwarf_Die *sp_die, void *data) |
642 | { | 642 | { |
643 | struct __line_walk_param *lw = data; | 643 | struct __line_walk_param *lw = data; |
644 | 644 | ||
645 | lw->retval = __die_walk_funclines(sp_die, true, lw->callback, lw->data); | 645 | lw->retval = __die_walk_funclines(sp_die, true, lw->callback, lw->data); |
646 | if (lw->retval != 0) | 646 | if (lw->retval != 0) |
647 | return DWARF_CB_ABORT; | 647 | return DWARF_CB_ABORT; |
648 | 648 | ||
649 | return DWARF_CB_OK; | 649 | return DWARF_CB_OK; |
650 | } | 650 | } |
651 | 651 | ||
652 | /** | 652 | /** |
653 | * die_walk_lines - Walk on lines inside given DIE | 653 | * die_walk_lines - Walk on lines inside given DIE |
654 | * @rt_die: a root DIE (CU, subprogram or inlined_subroutine) | 654 | * @rt_die: a root DIE (CU, subprogram or inlined_subroutine) |
655 | * @callback: callback routine | 655 | * @callback: callback routine |
656 | * @data: user data | 656 | * @data: user data |
657 | * | 657 | * |
658 | * Walk on all lines inside given @rt_die and call @callback on each line. | 658 | * Walk on all lines inside given @rt_die and call @callback on each line. |
659 | * If the @rt_die is a function, walk only on the lines inside the function, | 659 | * If the @rt_die is a function, walk only on the lines inside the function, |
660 | * otherwise @rt_die must be a CU DIE. | 660 | * otherwise @rt_die must be a CU DIE. |
661 | * Note that this walks not only dwarf line list, but also function entries | 661 | * Note that this walks not only dwarf line list, but also function entries |
662 | * and inline call-site. | 662 | * and inline call-site. |
663 | */ | 663 | */ |
664 | int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data) | 664 | int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data) |
665 | { | 665 | { |
666 | Dwarf_Lines *lines; | 666 | Dwarf_Lines *lines; |
667 | Dwarf_Line *line; | 667 | Dwarf_Line *line; |
668 | Dwarf_Addr addr; | 668 | Dwarf_Addr addr; |
669 | const char *fname; | 669 | const char *fname; |
670 | int lineno, ret = 0; | 670 | int lineno, ret = 0; |
671 | Dwarf_Die die_mem, *cu_die; | 671 | Dwarf_Die die_mem, *cu_die; |
672 | size_t nlines, i; | 672 | size_t nlines, i; |
673 | 673 | ||
674 | /* Get the CU die */ | 674 | /* Get the CU die */ |
675 | if (dwarf_tag(rt_die) != DW_TAG_compile_unit) | 675 | if (dwarf_tag(rt_die) != DW_TAG_compile_unit) |
676 | cu_die = dwarf_diecu(rt_die, &die_mem, NULL, NULL); | 676 | cu_die = dwarf_diecu(rt_die, &die_mem, NULL, NULL); |
677 | else | 677 | else |
678 | cu_die = rt_die; | 678 | cu_die = rt_die; |
679 | if (!cu_die) { | 679 | if (!cu_die) { |
680 | pr_debug2("Failed to get CU from given DIE.\n"); | 680 | pr_debug2("Failed to get CU from given DIE.\n"); |
681 | return -EINVAL; | 681 | return -EINVAL; |
682 | } | 682 | } |
683 | 683 | ||
684 | /* Get lines list in the CU */ | 684 | /* Get lines list in the CU */ |
685 | if (dwarf_getsrclines(cu_die, &lines, &nlines) != 0) { | 685 | if (dwarf_getsrclines(cu_die, &lines, &nlines) != 0) { |
686 | pr_debug2("Failed to get source lines on this CU.\n"); | 686 | pr_debug2("Failed to get source lines on this CU.\n"); |
687 | return -ENOENT; | 687 | return -ENOENT; |
688 | } | 688 | } |
689 | pr_debug2("Get %zd lines from this CU\n", nlines); | 689 | pr_debug2("Get %zd lines from this CU\n", nlines); |
690 | 690 | ||
691 | /* Walk on the lines on lines list */ | 691 | /* Walk on the lines on lines list */ |
692 | for (i = 0; i < nlines; i++) { | 692 | for (i = 0; i < nlines; i++) { |
693 | line = dwarf_onesrcline(lines, i); | 693 | line = dwarf_onesrcline(lines, i); |
694 | if (line == NULL || | 694 | if (line == NULL || |
695 | dwarf_lineno(line, &lineno) != 0 || | 695 | dwarf_lineno(line, &lineno) != 0 || |
696 | dwarf_lineaddr(line, &addr) != 0) { | 696 | dwarf_lineaddr(line, &addr) != 0) { |
697 | pr_debug2("Failed to get line info. " | 697 | pr_debug2("Failed to get line info. " |
698 | "Possible error in debuginfo.\n"); | 698 | "Possible error in debuginfo.\n"); |
699 | continue; | 699 | continue; |
700 | } | 700 | } |
701 | /* Filter lines based on address */ | 701 | /* Filter lines based on address */ |
702 | if (rt_die != cu_die) | 702 | if (rt_die != cu_die) |
703 | /* | 703 | /* |
704 | * Address filtering | 704 | * Address filtering |
705 | * The line is included in given function, and | 705 | * The line is included in given function, and |
706 | * no inline block includes it. | 706 | * no inline block includes it. |
707 | */ | 707 | */ |
708 | if (!dwarf_haspc(rt_die, addr) || | 708 | if (!dwarf_haspc(rt_die, addr) || |
709 | die_find_inlinefunc(rt_die, addr, &die_mem)) | 709 | die_find_inlinefunc(rt_die, addr, &die_mem)) |
710 | continue; | 710 | continue; |
711 | /* Get source line */ | 711 | /* Get source line */ |
712 | fname = dwarf_linesrc(line, NULL, NULL); | 712 | fname = dwarf_linesrc(line, NULL, NULL); |
713 | 713 | ||
714 | ret = callback(fname, lineno, addr, data); | 714 | ret = callback(fname, lineno, addr, data); |
715 | if (ret != 0) | 715 | if (ret != 0) |
716 | return ret; | 716 | return ret; |
717 | } | 717 | } |
718 | 718 | ||
719 | /* | 719 | /* |
720 | * Dwarf lines doesn't include function declarations and inlined | 720 | * Dwarf lines doesn't include function declarations and inlined |
721 | * subroutines. We have to check functions list or given function. | 721 | * subroutines. We have to check functions list or given function. |
722 | */ | 722 | */ |
723 | if (rt_die != cu_die) | 723 | if (rt_die != cu_die) |
724 | /* | 724 | /* |
725 | * Don't need walk functions recursively, because nested | 725 | * Don't need walk functions recursively, because nested |
726 | * inlined functions don't have lines of the specified DIE. | 726 | * inlined functions don't have lines of the specified DIE. |
727 | */ | 727 | */ |
728 | ret = __die_walk_funclines(rt_die, false, callback, data); | 728 | ret = __die_walk_funclines(rt_die, false, callback, data); |
729 | else { | 729 | else { |
730 | struct __line_walk_param param = { | 730 | struct __line_walk_param param = { |
731 | .callback = callback, | 731 | .callback = callback, |
732 | .data = data, | 732 | .data = data, |
733 | .retval = 0, | 733 | .retval = 0, |
734 | }; | 734 | }; |
735 | dwarf_getfuncs(cu_die, __die_walk_culines_cb, ¶m, 0); | 735 | dwarf_getfuncs(cu_die, __die_walk_culines_cb, ¶m, 0); |
736 | ret = param.retval; | 736 | ret = param.retval; |
737 | } | 737 | } |
738 | 738 | ||
739 | return ret; | 739 | return ret; |
740 | } | 740 | } |
741 | 741 | ||
742 | struct __find_variable_param { | 742 | struct __find_variable_param { |
743 | const char *name; | 743 | const char *name; |
744 | Dwarf_Addr addr; | 744 | Dwarf_Addr addr; |
745 | }; | 745 | }; |
746 | 746 | ||
747 | static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data) | 747 | static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data) |
748 | { | 748 | { |
749 | struct __find_variable_param *fvp = data; | 749 | struct __find_variable_param *fvp = data; |
750 | Dwarf_Attribute attr; | ||
750 | int tag; | 751 | int tag; |
751 | 752 | ||
752 | tag = dwarf_tag(die_mem); | 753 | tag = dwarf_tag(die_mem); |
753 | if ((tag == DW_TAG_formal_parameter || | 754 | if ((tag == DW_TAG_formal_parameter || |
754 | tag == DW_TAG_variable) && | 755 | tag == DW_TAG_variable) && |
755 | die_compare_name(die_mem, fvp->name)) | 756 | die_compare_name(die_mem, fvp->name) && |
757 | /* Does the DIE have location information or external instance? */ | ||
758 | (dwarf_attr(die_mem, DW_AT_external, &attr) || | ||
759 | dwarf_attr(die_mem, DW_AT_location, &attr))) | ||
756 | return DIE_FIND_CB_END; | 760 | return DIE_FIND_CB_END; |
757 | |||
758 | if (dwarf_haspc(die_mem, fvp->addr)) | 761 | if (dwarf_haspc(die_mem, fvp->addr)) |
759 | return DIE_FIND_CB_CONTINUE; | 762 | return DIE_FIND_CB_CONTINUE; |
760 | else | 763 | else |
761 | return DIE_FIND_CB_SIBLING; | 764 | return DIE_FIND_CB_SIBLING; |
762 | } | 765 | } |
763 | 766 | ||
764 | /** | 767 | /** |
765 | * die_find_variable_at - Find a given name variable at given address | 768 | * die_find_variable_at - Find a given name variable at given address |
766 | * @sp_die: a function DIE | 769 | * @sp_die: a function DIE |
767 | * @name: variable name | 770 | * @name: variable name |
768 | * @addr: address | 771 | * @addr: address |
769 | * @die_mem: a buffer for result DIE | 772 | * @die_mem: a buffer for result DIE |
770 | * | 773 | * |
771 | * Find a variable DIE called @name at @addr in @sp_die. | 774 | * Find a variable DIE called @name at @addr in @sp_die. |
772 | */ | 775 | */ |
773 | Dwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name, | 776 | Dwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name, |
774 | Dwarf_Addr addr, Dwarf_Die *die_mem) | 777 | Dwarf_Addr addr, Dwarf_Die *die_mem) |
775 | { | 778 | { |
776 | struct __find_variable_param fvp = { .name = name, .addr = addr}; | 779 | struct __find_variable_param fvp = { .name = name, .addr = addr}; |
777 | 780 | ||
778 | return die_find_child(sp_die, __die_find_variable_cb, (void *)&fvp, | 781 | return die_find_child(sp_die, __die_find_variable_cb, (void *)&fvp, |
779 | die_mem); | 782 | die_mem); |
780 | } | 783 | } |
781 | 784 | ||
782 | static int __die_find_member_cb(Dwarf_Die *die_mem, void *data) | 785 | static int __die_find_member_cb(Dwarf_Die *die_mem, void *data) |
783 | { | 786 | { |
784 | const char *name = data; | 787 | const char *name = data; |
785 | 788 | ||
786 | if ((dwarf_tag(die_mem) == DW_TAG_member) && | 789 | if ((dwarf_tag(die_mem) == DW_TAG_member) && |
787 | die_compare_name(die_mem, name)) | 790 | die_compare_name(die_mem, name)) |
788 | return DIE_FIND_CB_END; | 791 | return DIE_FIND_CB_END; |
789 | 792 | ||
790 | return DIE_FIND_CB_SIBLING; | 793 | return DIE_FIND_CB_SIBLING; |
791 | } | 794 | } |
792 | 795 | ||
793 | /** | 796 | /** |
794 | * die_find_member - Find a given name member in a data structure | 797 | * die_find_member - Find a given name member in a data structure |
795 | * @st_die: a data structure type DIE | 798 | * @st_die: a data structure type DIE |
796 | * @name: member name | 799 | * @name: member name |
797 | * @die_mem: a buffer for result DIE | 800 | * @die_mem: a buffer for result DIE |
798 | * | 801 | * |
799 | * Find a member DIE called @name in @st_die. | 802 | * Find a member DIE called @name in @st_die. |
800 | */ | 803 | */ |
801 | Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name, | 804 | Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name, |
802 | Dwarf_Die *die_mem) | 805 | Dwarf_Die *die_mem) |
803 | { | 806 | { |
804 | return die_find_child(st_die, __die_find_member_cb, (void *)name, | 807 | return die_find_child(st_die, __die_find_member_cb, (void *)name, |
805 | die_mem); | 808 | die_mem); |
806 | } | 809 | } |
807 | 810 | ||
808 | /** | 811 | /** |
809 | * die_get_typename - Get the name of given variable DIE | 812 | * die_get_typename - Get the name of given variable DIE |
810 | * @vr_die: a variable DIE | 813 | * @vr_die: a variable DIE |
811 | * @buf: a buffer for result type name | 814 | * @buf: a buffer for result type name |
812 | * @len: a max-length of @buf | 815 | * @len: a max-length of @buf |
813 | * | 816 | * |
814 | * Get the name of @vr_die and stores it to @buf. Return the actual length | 817 | * Get the name of @vr_die and stores it to @buf. Return the actual length |
815 | * of type name if succeeded. Return -E2BIG if @len is not enough long, and | 818 | * of type name if succeeded. Return -E2BIG if @len is not enough long, and |
816 | * Return -ENOENT if failed to find type name. | 819 | * Return -ENOENT if failed to find type name. |
817 | * Note that the result will stores typedef name if possible, and stores | 820 | * Note that the result will stores typedef name if possible, and stores |
818 | * "*(function_type)" if the type is a function pointer. | 821 | * "*(function_type)" if the type is a function pointer. |
819 | */ | 822 | */ |
820 | int die_get_typename(Dwarf_Die *vr_die, char *buf, int len) | 823 | int die_get_typename(Dwarf_Die *vr_die, char *buf, int len) |
821 | { | 824 | { |
822 | Dwarf_Die type; | 825 | Dwarf_Die type; |
823 | int tag, ret, ret2; | 826 | int tag, ret, ret2; |
824 | const char *tmp = ""; | 827 | const char *tmp = ""; |
825 | 828 | ||
826 | if (__die_get_real_type(vr_die, &type) == NULL) | 829 | if (__die_get_real_type(vr_die, &type) == NULL) |
827 | return -ENOENT; | 830 | return -ENOENT; |
828 | 831 | ||
829 | tag = dwarf_tag(&type); | 832 | tag = dwarf_tag(&type); |
830 | if (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type) | 833 | if (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type) |
831 | tmp = "*"; | 834 | tmp = "*"; |
832 | else if (tag == DW_TAG_subroutine_type) { | 835 | else if (tag == DW_TAG_subroutine_type) { |
833 | /* Function pointer */ | 836 | /* Function pointer */ |
834 | ret = snprintf(buf, len, "(function_type)"); | 837 | ret = snprintf(buf, len, "(function_type)"); |
835 | return (ret >= len) ? -E2BIG : ret; | 838 | return (ret >= len) ? -E2BIG : ret; |
836 | } else { | 839 | } else { |
837 | if (!dwarf_diename(&type)) | 840 | if (!dwarf_diename(&type)) |
838 | return -ENOENT; | 841 | return -ENOENT; |
839 | if (tag == DW_TAG_union_type) | 842 | if (tag == DW_TAG_union_type) |
840 | tmp = "union "; | 843 | tmp = "union "; |
841 | else if (tag == DW_TAG_structure_type) | 844 | else if (tag == DW_TAG_structure_type) |
842 | tmp = "struct "; | 845 | tmp = "struct "; |
843 | else if (tag == DW_TAG_enumeration_type) | 846 | else if (tag == DW_TAG_enumeration_type) |
844 | tmp = "enum "; | 847 | tmp = "enum "; |
845 | /* Write a base name */ | 848 | /* Write a base name */ |
846 | ret = snprintf(buf, len, "%s%s", tmp, dwarf_diename(&type)); | 849 | ret = snprintf(buf, len, "%s%s", tmp, dwarf_diename(&type)); |
847 | return (ret >= len) ? -E2BIG : ret; | 850 | return (ret >= len) ? -E2BIG : ret; |
848 | } | 851 | } |
849 | ret = die_get_typename(&type, buf, len); | 852 | ret = die_get_typename(&type, buf, len); |
850 | if (ret > 0) { | 853 | if (ret > 0) { |
851 | ret2 = snprintf(buf + ret, len - ret, "%s", tmp); | 854 | ret2 = snprintf(buf + ret, len - ret, "%s", tmp); |
852 | ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret; | 855 | ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret; |
853 | } | 856 | } |
854 | return ret; | 857 | return ret; |
855 | } | 858 | } |
856 | 859 | ||
857 | /** | 860 | /** |
858 | * die_get_varname - Get the name and type of given variable DIE | 861 | * die_get_varname - Get the name and type of given variable DIE |
859 | * @vr_die: a variable DIE | 862 | * @vr_die: a variable DIE |
860 | * @buf: a buffer for type and variable name | 863 | * @buf: a buffer for type and variable name |
861 | * @len: the max-length of @buf | 864 | * @len: the max-length of @buf |
862 | * | 865 | * |
863 | * Get the name and type of @vr_die and stores it in @buf as "type\tname". | 866 | * Get the name and type of @vr_die and stores it in @buf as "type\tname". |
864 | */ | 867 | */ |
865 | int die_get_varname(Dwarf_Die *vr_die, char *buf, int len) | 868 | int die_get_varname(Dwarf_Die *vr_die, char *buf, int len) |
866 | { | 869 | { |
867 | int ret, ret2; | 870 | int ret, ret2; |
868 | 871 | ||
869 | ret = die_get_typename(vr_die, buf, len); | 872 | ret = die_get_typename(vr_die, buf, len); |
870 | if (ret < 0) { | 873 | if (ret < 0) { |
871 | pr_debug("Failed to get type, make it unknown.\n"); | 874 | pr_debug("Failed to get type, make it unknown.\n"); |
872 | ret = snprintf(buf, len, "(unknown_type)"); | 875 | ret = snprintf(buf, len, "(unknown_type)"); |
873 | } | 876 | } |
874 | if (ret > 0) { | 877 | if (ret > 0) { |
875 | ret2 = snprintf(buf + ret, len - ret, "\t%s", | 878 | ret2 = snprintf(buf + ret, len - ret, "\t%s", |
876 | dwarf_diename(vr_die)); | 879 | dwarf_diename(vr_die)); |
877 | ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret; | 880 | ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret; |
878 | } | 881 | } |
879 | return ret; | 882 | return ret; |
880 | } | 883 | } |
881 | 884 |
tools/perf/util/probe-finder.c
1 | /* | 1 | /* |
2 | * probe-finder.c : C expression to kprobe event converter | 2 | * probe-finder.c : C expression to kprobe event converter |
3 | * | 3 | * |
4 | * Written by Masami Hiramatsu <mhiramat@redhat.com> | 4 | * Written by Masami Hiramatsu <mhiramat@redhat.com> |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or modify | 6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by | 7 | * it under the terms of the GNU General Public License as published by |
8 | * the Free Software Foundation; either version 2 of the License, or | 8 | * the Free Software Foundation; either version 2 of the License, or |
9 | * (at your option) any later version. | 9 | * (at your option) any later version. |
10 | * | 10 | * |
11 | * This program is distributed in the hope that it will be useful, | 11 | * This program is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. | 14 | * GNU General Public License for more details. |
15 | * | 15 | * |
16 | * You should have received a copy of the GNU General Public License | 16 | * You should have received a copy of the GNU General Public License |
17 | * along with this program; if not, write to the Free Software | 17 | * along with this program; if not, write to the Free Software |
18 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | 18 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
19 | * | 19 | * |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #include <sys/utsname.h> | 22 | #include <sys/utsname.h> |
23 | #include <sys/types.h> | 23 | #include <sys/types.h> |
24 | #include <sys/stat.h> | 24 | #include <sys/stat.h> |
25 | #include <fcntl.h> | 25 | #include <fcntl.h> |
26 | #include <errno.h> | 26 | #include <errno.h> |
27 | #include <stdio.h> | 27 | #include <stdio.h> |
28 | #include <unistd.h> | 28 | #include <unistd.h> |
29 | #include <getopt.h> | 29 | #include <getopt.h> |
30 | #include <stdlib.h> | 30 | #include <stdlib.h> |
31 | #include <string.h> | 31 | #include <string.h> |
32 | #include <stdarg.h> | 32 | #include <stdarg.h> |
33 | #include <dwarf-regs.h> | 33 | #include <dwarf-regs.h> |
34 | 34 | ||
35 | #include <linux/bitops.h> | 35 | #include <linux/bitops.h> |
36 | #include "event.h" | 36 | #include "event.h" |
37 | #include "dso.h" | 37 | #include "dso.h" |
38 | #include "debug.h" | 38 | #include "debug.h" |
39 | #include "intlist.h" | 39 | #include "intlist.h" |
40 | #include "util.h" | 40 | #include "util.h" |
41 | #include "symbol.h" | 41 | #include "symbol.h" |
42 | #include "probe-finder.h" | 42 | #include "probe-finder.h" |
43 | 43 | ||
44 | /* Kprobe tracer basic type is up to u64 */ | 44 | /* Kprobe tracer basic type is up to u64 */ |
45 | #define MAX_BASIC_TYPE_BITS 64 | 45 | #define MAX_BASIC_TYPE_BITS 64 |
46 | 46 | ||
47 | /* Dwarf FL wrappers */ | 47 | /* Dwarf FL wrappers */ |
48 | static char *debuginfo_path; /* Currently dummy */ | 48 | static char *debuginfo_path; /* Currently dummy */ |
49 | 49 | ||
50 | static const Dwfl_Callbacks offline_callbacks = { | 50 | static const Dwfl_Callbacks offline_callbacks = { |
51 | .find_debuginfo = dwfl_standard_find_debuginfo, | 51 | .find_debuginfo = dwfl_standard_find_debuginfo, |
52 | .debuginfo_path = &debuginfo_path, | 52 | .debuginfo_path = &debuginfo_path, |
53 | 53 | ||
54 | .section_address = dwfl_offline_section_address, | 54 | .section_address = dwfl_offline_section_address, |
55 | 55 | ||
56 | /* We use this table for core files too. */ | 56 | /* We use this table for core files too. */ |
57 | .find_elf = dwfl_build_id_find_elf, | 57 | .find_elf = dwfl_build_id_find_elf, |
58 | }; | 58 | }; |
59 | 59 | ||
60 | /* Get a Dwarf from offline image */ | 60 | /* Get a Dwarf from offline image */ |
61 | static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, | 61 | static int debuginfo__init_offline_dwarf(struct debuginfo *dbg, |
62 | const char *path) | 62 | const char *path) |
63 | { | 63 | { |
64 | int fd; | 64 | int fd; |
65 | 65 | ||
66 | fd = open(path, O_RDONLY); | 66 | fd = open(path, O_RDONLY); |
67 | if (fd < 0) | 67 | if (fd < 0) |
68 | return fd; | 68 | return fd; |
69 | 69 | ||
70 | dbg->dwfl = dwfl_begin(&offline_callbacks); | 70 | dbg->dwfl = dwfl_begin(&offline_callbacks); |
71 | if (!dbg->dwfl) | 71 | if (!dbg->dwfl) |
72 | goto error; | 72 | goto error; |
73 | 73 | ||
74 | dbg->mod = dwfl_report_offline(dbg->dwfl, "", "", fd); | 74 | dbg->mod = dwfl_report_offline(dbg->dwfl, "", "", fd); |
75 | if (!dbg->mod) | 75 | if (!dbg->mod) |
76 | goto error; | 76 | goto error; |
77 | 77 | ||
78 | dbg->dbg = dwfl_module_getdwarf(dbg->mod, &dbg->bias); | 78 | dbg->dbg = dwfl_module_getdwarf(dbg->mod, &dbg->bias); |
79 | if (!dbg->dbg) | 79 | if (!dbg->dbg) |
80 | goto error; | 80 | goto error; |
81 | 81 | ||
82 | return 0; | 82 | return 0; |
83 | error: | 83 | error: |
84 | if (dbg->dwfl) | 84 | if (dbg->dwfl) |
85 | dwfl_end(dbg->dwfl); | 85 | dwfl_end(dbg->dwfl); |
86 | else | 86 | else |
87 | close(fd); | 87 | close(fd); |
88 | memset(dbg, 0, sizeof(*dbg)); | 88 | memset(dbg, 0, sizeof(*dbg)); |
89 | 89 | ||
90 | return -ENOENT; | 90 | return -ENOENT; |
91 | } | 91 | } |
92 | 92 | ||
93 | static struct debuginfo *__debuginfo__new(const char *path) | 93 | static struct debuginfo *__debuginfo__new(const char *path) |
94 | { | 94 | { |
95 | struct debuginfo *dbg = zalloc(sizeof(*dbg)); | 95 | struct debuginfo *dbg = zalloc(sizeof(*dbg)); |
96 | if (!dbg) | 96 | if (!dbg) |
97 | return NULL; | 97 | return NULL; |
98 | 98 | ||
99 | if (debuginfo__init_offline_dwarf(dbg, path) < 0) | 99 | if (debuginfo__init_offline_dwarf(dbg, path) < 0) |
100 | zfree(&dbg); | 100 | zfree(&dbg); |
101 | if (dbg) | 101 | if (dbg) |
102 | pr_debug("Open Debuginfo file: %s\n", path); | 102 | pr_debug("Open Debuginfo file: %s\n", path); |
103 | return dbg; | 103 | return dbg; |
104 | } | 104 | } |
105 | 105 | ||
106 | enum dso_binary_type distro_dwarf_types[] = { | 106 | enum dso_binary_type distro_dwarf_types[] = { |
107 | DSO_BINARY_TYPE__FEDORA_DEBUGINFO, | 107 | DSO_BINARY_TYPE__FEDORA_DEBUGINFO, |
108 | DSO_BINARY_TYPE__UBUNTU_DEBUGINFO, | 108 | DSO_BINARY_TYPE__UBUNTU_DEBUGINFO, |
109 | DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, | 109 | DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, |
110 | DSO_BINARY_TYPE__BUILDID_DEBUGINFO, | 110 | DSO_BINARY_TYPE__BUILDID_DEBUGINFO, |
111 | DSO_BINARY_TYPE__NOT_FOUND, | 111 | DSO_BINARY_TYPE__NOT_FOUND, |
112 | }; | 112 | }; |
113 | 113 | ||
114 | struct debuginfo *debuginfo__new(const char *path) | 114 | struct debuginfo *debuginfo__new(const char *path) |
115 | { | 115 | { |
116 | enum dso_binary_type *type; | 116 | enum dso_binary_type *type; |
117 | char buf[PATH_MAX], nil = '\0'; | 117 | char buf[PATH_MAX], nil = '\0'; |
118 | struct dso *dso; | 118 | struct dso *dso; |
119 | struct debuginfo *dinfo = NULL; | 119 | struct debuginfo *dinfo = NULL; |
120 | 120 | ||
121 | /* Try to open distro debuginfo files */ | 121 | /* Try to open distro debuginfo files */ |
122 | dso = dso__new(path); | 122 | dso = dso__new(path); |
123 | if (!dso) | 123 | if (!dso) |
124 | goto out; | 124 | goto out; |
125 | 125 | ||
126 | for (type = distro_dwarf_types; | 126 | for (type = distro_dwarf_types; |
127 | !dinfo && *type != DSO_BINARY_TYPE__NOT_FOUND; | 127 | !dinfo && *type != DSO_BINARY_TYPE__NOT_FOUND; |
128 | type++) { | 128 | type++) { |
129 | if (dso__read_binary_type_filename(dso, *type, &nil, | 129 | if (dso__read_binary_type_filename(dso, *type, &nil, |
130 | buf, PATH_MAX) < 0) | 130 | buf, PATH_MAX) < 0) |
131 | continue; | 131 | continue; |
132 | dinfo = __debuginfo__new(buf); | 132 | dinfo = __debuginfo__new(buf); |
133 | } | 133 | } |
134 | dso__delete(dso); | 134 | dso__delete(dso); |
135 | 135 | ||
136 | out: | 136 | out: |
137 | /* if failed to open all distro debuginfo, open given binary */ | 137 | /* if failed to open all distro debuginfo, open given binary */ |
138 | return dinfo ? : __debuginfo__new(path); | 138 | return dinfo ? : __debuginfo__new(path); |
139 | } | 139 | } |
140 | 140 | ||
141 | void debuginfo__delete(struct debuginfo *dbg) | 141 | void debuginfo__delete(struct debuginfo *dbg) |
142 | { | 142 | { |
143 | if (dbg) { | 143 | if (dbg) { |
144 | if (dbg->dwfl) | 144 | if (dbg->dwfl) |
145 | dwfl_end(dbg->dwfl); | 145 | dwfl_end(dbg->dwfl); |
146 | free(dbg); | 146 | free(dbg); |
147 | } | 147 | } |
148 | } | 148 | } |
149 | 149 | ||
150 | /* | 150 | /* |
151 | * Probe finder related functions | 151 | * Probe finder related functions |
152 | */ | 152 | */ |
153 | 153 | ||
154 | static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs) | 154 | static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs) |
155 | { | 155 | { |
156 | struct probe_trace_arg_ref *ref; | 156 | struct probe_trace_arg_ref *ref; |
157 | ref = zalloc(sizeof(struct probe_trace_arg_ref)); | 157 | ref = zalloc(sizeof(struct probe_trace_arg_ref)); |
158 | if (ref != NULL) | 158 | if (ref != NULL) |
159 | ref->offset = offs; | 159 | ref->offset = offs; |
160 | return ref; | 160 | return ref; |
161 | } | 161 | } |
162 | 162 | ||
163 | /* | 163 | /* |
164 | * Convert a location into trace_arg. | 164 | * Convert a location into trace_arg. |
165 | * If tvar == NULL, this just checks variable can be converted. | 165 | * If tvar == NULL, this just checks variable can be converted. |
166 | * If fentry == true and vr_die is a parameter, do huristic search | 166 | * If fentry == true and vr_die is a parameter, do huristic search |
167 | * for the location fuzzed by function entry mcount. | 167 | * for the location fuzzed by function entry mcount. |
168 | */ | 168 | */ |
169 | static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, | 169 | static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, |
170 | Dwarf_Op *fb_ops, Dwarf_Die *sp_die, | 170 | Dwarf_Op *fb_ops, Dwarf_Die *sp_die, |
171 | struct probe_trace_arg *tvar) | 171 | struct probe_trace_arg *tvar) |
172 | { | 172 | { |
173 | Dwarf_Attribute attr; | 173 | Dwarf_Attribute attr; |
174 | Dwarf_Addr tmp = 0; | 174 | Dwarf_Addr tmp = 0; |
175 | Dwarf_Op *op; | 175 | Dwarf_Op *op; |
176 | size_t nops; | 176 | size_t nops; |
177 | unsigned int regn; | 177 | unsigned int regn; |
178 | Dwarf_Word offs = 0; | 178 | Dwarf_Word offs = 0; |
179 | bool ref = false; | 179 | bool ref = false; |
180 | const char *regs; | 180 | const char *regs; |
181 | int ret; | 181 | int ret; |
182 | 182 | ||
183 | if (dwarf_attr(vr_die, DW_AT_external, &attr) != NULL) | 183 | if (dwarf_attr(vr_die, DW_AT_external, &attr) != NULL) |
184 | goto static_var; | 184 | goto static_var; |
185 | 185 | ||
186 | /* TODO: handle more than 1 exprs */ | 186 | /* TODO: handle more than 1 exprs */ |
187 | if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL) | 187 | if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL) |
188 | return -EINVAL; /* Broken DIE ? */ | 188 | return -EINVAL; /* Broken DIE ? */ |
189 | if (dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0) { | 189 | if (dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0) { |
190 | ret = dwarf_entrypc(sp_die, &tmp); | 190 | ret = dwarf_entrypc(sp_die, &tmp); |
191 | if (ret || addr != tmp || | 191 | if (ret || addr != tmp || |
192 | dwarf_tag(vr_die) != DW_TAG_formal_parameter || | 192 | dwarf_tag(vr_die) != DW_TAG_formal_parameter || |
193 | dwarf_highpc(sp_die, &tmp)) | 193 | dwarf_highpc(sp_die, &tmp)) |
194 | return -ENOENT; | 194 | return -ENOENT; |
195 | /* | 195 | /* |
196 | * This is fuzzed by fentry mcount. We try to find the | 196 | * This is fuzzed by fentry mcount. We try to find the |
197 | * parameter location at the earliest address. | 197 | * parameter location at the earliest address. |
198 | */ | 198 | */ |
199 | for (addr += 1; addr <= tmp; addr++) { | 199 | for (addr += 1; addr <= tmp; addr++) { |
200 | if (dwarf_getlocation_addr(&attr, addr, &op, | 200 | if (dwarf_getlocation_addr(&attr, addr, &op, |
201 | &nops, 1) > 0) | 201 | &nops, 1) > 0) |
202 | goto found; | 202 | goto found; |
203 | } | 203 | } |
204 | return -ENOENT; | 204 | return -ENOENT; |
205 | } | 205 | } |
206 | found: | 206 | found: |
207 | if (nops == 0) | 207 | if (nops == 0) |
208 | /* TODO: Support const_value */ | 208 | /* TODO: Support const_value */ |
209 | return -ENOENT; | 209 | return -ENOENT; |
210 | 210 | ||
211 | if (op->atom == DW_OP_addr) { | 211 | if (op->atom == DW_OP_addr) { |
212 | static_var: | 212 | static_var: |
213 | if (!tvar) | 213 | if (!tvar) |
214 | return 0; | 214 | return 0; |
215 | /* Static variables on memory (not stack), make @varname */ | 215 | /* Static variables on memory (not stack), make @varname */ |
216 | ret = strlen(dwarf_diename(vr_die)); | 216 | ret = strlen(dwarf_diename(vr_die)); |
217 | tvar->value = zalloc(ret + 2); | 217 | tvar->value = zalloc(ret + 2); |
218 | if (tvar->value == NULL) | 218 | if (tvar->value == NULL) |
219 | return -ENOMEM; | 219 | return -ENOMEM; |
220 | snprintf(tvar->value, ret + 2, "@%s", dwarf_diename(vr_die)); | 220 | snprintf(tvar->value, ret + 2, "@%s", dwarf_diename(vr_die)); |
221 | tvar->ref = alloc_trace_arg_ref((long)offs); | 221 | tvar->ref = alloc_trace_arg_ref((long)offs); |
222 | if (tvar->ref == NULL) | 222 | if (tvar->ref == NULL) |
223 | return -ENOMEM; | 223 | return -ENOMEM; |
224 | return 0; | 224 | return 0; |
225 | } | 225 | } |
226 | 226 | ||
227 | /* If this is based on frame buffer, set the offset */ | 227 | /* If this is based on frame buffer, set the offset */ |
228 | if (op->atom == DW_OP_fbreg) { | 228 | if (op->atom == DW_OP_fbreg) { |
229 | if (fb_ops == NULL) | 229 | if (fb_ops == NULL) |
230 | return -ENOTSUP; | 230 | return -ENOTSUP; |
231 | ref = true; | 231 | ref = true; |
232 | offs = op->number; | 232 | offs = op->number; |
233 | op = &fb_ops[0]; | 233 | op = &fb_ops[0]; |
234 | } | 234 | } |
235 | 235 | ||
236 | if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) { | 236 | if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) { |
237 | regn = op->atom - DW_OP_breg0; | 237 | regn = op->atom - DW_OP_breg0; |
238 | offs += op->number; | 238 | offs += op->number; |
239 | ref = true; | 239 | ref = true; |
240 | } else if (op->atom >= DW_OP_reg0 && op->atom <= DW_OP_reg31) { | 240 | } else if (op->atom >= DW_OP_reg0 && op->atom <= DW_OP_reg31) { |
241 | regn = op->atom - DW_OP_reg0; | 241 | regn = op->atom - DW_OP_reg0; |
242 | } else if (op->atom == DW_OP_bregx) { | 242 | } else if (op->atom == DW_OP_bregx) { |
243 | regn = op->number; | 243 | regn = op->number; |
244 | offs += op->number2; | 244 | offs += op->number2; |
245 | ref = true; | 245 | ref = true; |
246 | } else if (op->atom == DW_OP_regx) { | 246 | } else if (op->atom == DW_OP_regx) { |
247 | regn = op->number; | 247 | regn = op->number; |
248 | } else { | 248 | } else { |
249 | pr_debug("DW_OP %x is not supported.\n", op->atom); | 249 | pr_debug("DW_OP %x is not supported.\n", op->atom); |
250 | return -ENOTSUP; | 250 | return -ENOTSUP; |
251 | } | 251 | } |
252 | 252 | ||
253 | if (!tvar) | 253 | if (!tvar) |
254 | return 0; | 254 | return 0; |
255 | 255 | ||
256 | regs = get_arch_regstr(regn); | 256 | regs = get_arch_regstr(regn); |
257 | if (!regs) { | 257 | if (!regs) { |
258 | /* This should be a bug in DWARF or this tool */ | 258 | /* This should be a bug in DWARF or this tool */ |
259 | pr_warning("Mapping for the register number %u " | 259 | pr_warning("Mapping for the register number %u " |
260 | "missing on this architecture.\n", regn); | 260 | "missing on this architecture.\n", regn); |
261 | return -ERANGE; | 261 | return -ERANGE; |
262 | } | 262 | } |
263 | 263 | ||
264 | tvar->value = strdup(regs); | 264 | tvar->value = strdup(regs); |
265 | if (tvar->value == NULL) | 265 | if (tvar->value == NULL) |
266 | return -ENOMEM; | 266 | return -ENOMEM; |
267 | 267 | ||
268 | if (ref) { | 268 | if (ref) { |
269 | tvar->ref = alloc_trace_arg_ref((long)offs); | 269 | tvar->ref = alloc_trace_arg_ref((long)offs); |
270 | if (tvar->ref == NULL) | 270 | if (tvar->ref == NULL) |
271 | return -ENOMEM; | 271 | return -ENOMEM; |
272 | } | 272 | } |
273 | return 0; | 273 | return 0; |
274 | } | 274 | } |
275 | 275 | ||
276 | #define BYTES_TO_BITS(nb) ((nb) * BITS_PER_LONG / sizeof(long)) | 276 | #define BYTES_TO_BITS(nb) ((nb) * BITS_PER_LONG / sizeof(long)) |
277 | 277 | ||
278 | static int convert_variable_type(Dwarf_Die *vr_die, | 278 | static int convert_variable_type(Dwarf_Die *vr_die, |
279 | struct probe_trace_arg *tvar, | 279 | struct probe_trace_arg *tvar, |
280 | const char *cast) | 280 | const char *cast) |
281 | { | 281 | { |
282 | struct probe_trace_arg_ref **ref_ptr = &tvar->ref; | 282 | struct probe_trace_arg_ref **ref_ptr = &tvar->ref; |
283 | Dwarf_Die type; | 283 | Dwarf_Die type; |
284 | char buf[16]; | 284 | char buf[16]; |
285 | int bsize, boffs, total; | 285 | int bsize, boffs, total; |
286 | int ret; | 286 | int ret; |
287 | 287 | ||
288 | /* TODO: check all types */ | 288 | /* TODO: check all types */ |
289 | if (cast && strcmp(cast, "string") != 0) { | 289 | if (cast && strcmp(cast, "string") != 0) { |
290 | /* Non string type is OK */ | 290 | /* Non string type is OK */ |
291 | tvar->type = strdup(cast); | 291 | tvar->type = strdup(cast); |
292 | return (tvar->type == NULL) ? -ENOMEM : 0; | 292 | return (tvar->type == NULL) ? -ENOMEM : 0; |
293 | } | 293 | } |
294 | 294 | ||
295 | bsize = dwarf_bitsize(vr_die); | 295 | bsize = dwarf_bitsize(vr_die); |
296 | if (bsize > 0) { | 296 | if (bsize > 0) { |
297 | /* This is a bitfield */ | 297 | /* This is a bitfield */ |
298 | boffs = dwarf_bitoffset(vr_die); | 298 | boffs = dwarf_bitoffset(vr_die); |
299 | total = dwarf_bytesize(vr_die); | 299 | total = dwarf_bytesize(vr_die); |
300 | if (boffs < 0 || total < 0) | 300 | if (boffs < 0 || total < 0) |
301 | return -ENOENT; | 301 | return -ENOENT; |
302 | ret = snprintf(buf, 16, "b%d@%d/%zd", bsize, boffs, | 302 | ret = snprintf(buf, 16, "b%d@%d/%zd", bsize, boffs, |
303 | BYTES_TO_BITS(total)); | 303 | BYTES_TO_BITS(total)); |
304 | goto formatted; | 304 | goto formatted; |
305 | } | 305 | } |
306 | 306 | ||
307 | if (die_get_real_type(vr_die, &type) == NULL) { | 307 | if (die_get_real_type(vr_die, &type) == NULL) { |
308 | pr_warning("Failed to get a type information of %s.\n", | 308 | pr_warning("Failed to get a type information of %s.\n", |
309 | dwarf_diename(vr_die)); | 309 | dwarf_diename(vr_die)); |
310 | return -ENOENT; | 310 | return -ENOENT; |
311 | } | 311 | } |
312 | 312 | ||
313 | pr_debug("%s type is %s.\n", | 313 | pr_debug("%s type is %s.\n", |
314 | dwarf_diename(vr_die), dwarf_diename(&type)); | 314 | dwarf_diename(vr_die), dwarf_diename(&type)); |
315 | 315 | ||
316 | if (cast && strcmp(cast, "string") == 0) { /* String type */ | 316 | if (cast && strcmp(cast, "string") == 0) { /* String type */ |
317 | ret = dwarf_tag(&type); | 317 | ret = dwarf_tag(&type); |
318 | if (ret != DW_TAG_pointer_type && | 318 | if (ret != DW_TAG_pointer_type && |
319 | ret != DW_TAG_array_type) { | 319 | ret != DW_TAG_array_type) { |
320 | pr_warning("Failed to cast into string: " | 320 | pr_warning("Failed to cast into string: " |
321 | "%s(%s) is not a pointer nor array.\n", | 321 | "%s(%s) is not a pointer nor array.\n", |
322 | dwarf_diename(vr_die), dwarf_diename(&type)); | 322 | dwarf_diename(vr_die), dwarf_diename(&type)); |
323 | return -EINVAL; | 323 | return -EINVAL; |
324 | } | 324 | } |
325 | if (die_get_real_type(&type, &type) == NULL) { | 325 | if (die_get_real_type(&type, &type) == NULL) { |
326 | pr_warning("Failed to get a type" | 326 | pr_warning("Failed to get a type" |
327 | " information.\n"); | 327 | " information.\n"); |
328 | return -ENOENT; | 328 | return -ENOENT; |
329 | } | 329 | } |
330 | if (ret == DW_TAG_pointer_type) { | 330 | if (ret == DW_TAG_pointer_type) { |
331 | while (*ref_ptr) | 331 | while (*ref_ptr) |
332 | ref_ptr = &(*ref_ptr)->next; | 332 | ref_ptr = &(*ref_ptr)->next; |
333 | /* Add new reference with offset +0 */ | 333 | /* Add new reference with offset +0 */ |
334 | *ref_ptr = zalloc(sizeof(struct probe_trace_arg_ref)); | 334 | *ref_ptr = zalloc(sizeof(struct probe_trace_arg_ref)); |
335 | if (*ref_ptr == NULL) { | 335 | if (*ref_ptr == NULL) { |
336 | pr_warning("Out of memory error\n"); | 336 | pr_warning("Out of memory error\n"); |
337 | return -ENOMEM; | 337 | return -ENOMEM; |
338 | } | 338 | } |
339 | } | 339 | } |
340 | if (!die_compare_name(&type, "char") && | 340 | if (!die_compare_name(&type, "char") && |
341 | !die_compare_name(&type, "unsigned char")) { | 341 | !die_compare_name(&type, "unsigned char")) { |
342 | pr_warning("Failed to cast into string: " | 342 | pr_warning("Failed to cast into string: " |
343 | "%s is not (unsigned) char *.\n", | 343 | "%s is not (unsigned) char *.\n", |
344 | dwarf_diename(vr_die)); | 344 | dwarf_diename(vr_die)); |
345 | return -EINVAL; | 345 | return -EINVAL; |
346 | } | 346 | } |
347 | tvar->type = strdup(cast); | 347 | tvar->type = strdup(cast); |
348 | return (tvar->type == NULL) ? -ENOMEM : 0; | 348 | return (tvar->type == NULL) ? -ENOMEM : 0; |
349 | } | 349 | } |
350 | 350 | ||
351 | ret = dwarf_bytesize(&type); | 351 | ret = dwarf_bytesize(&type); |
352 | if (ret <= 0) | 352 | if (ret <= 0) |
353 | /* No size ... try to use default type */ | 353 | /* No size ... try to use default type */ |
354 | return 0; | 354 | return 0; |
355 | ret = BYTES_TO_BITS(ret); | 355 | ret = BYTES_TO_BITS(ret); |
356 | 356 | ||
357 | /* Check the bitwidth */ | 357 | /* Check the bitwidth */ |
358 | if (ret > MAX_BASIC_TYPE_BITS) { | 358 | if (ret > MAX_BASIC_TYPE_BITS) { |
359 | pr_info("%s exceeds max-bitwidth. Cut down to %d bits.\n", | 359 | pr_info("%s exceeds max-bitwidth. Cut down to %d bits.\n", |
360 | dwarf_diename(&type), MAX_BASIC_TYPE_BITS); | 360 | dwarf_diename(&type), MAX_BASIC_TYPE_BITS); |
361 | ret = MAX_BASIC_TYPE_BITS; | 361 | ret = MAX_BASIC_TYPE_BITS; |
362 | } | 362 | } |
363 | ret = snprintf(buf, 16, "%c%d", | 363 | ret = snprintf(buf, 16, "%c%d", |
364 | die_is_signed_type(&type) ? 's' : 'u', ret); | 364 | die_is_signed_type(&type) ? 's' : 'u', ret); |
365 | 365 | ||
366 | formatted: | 366 | formatted: |
367 | if (ret < 0 || ret >= 16) { | 367 | if (ret < 0 || ret >= 16) { |
368 | if (ret >= 16) | 368 | if (ret >= 16) |
369 | ret = -E2BIG; | 369 | ret = -E2BIG; |
370 | pr_warning("Failed to convert variable type: %s\n", | 370 | pr_warning("Failed to convert variable type: %s\n", |
371 | strerror(-ret)); | 371 | strerror(-ret)); |
372 | return ret; | 372 | return ret; |
373 | } | 373 | } |
374 | tvar->type = strdup(buf); | 374 | tvar->type = strdup(buf); |
375 | if (tvar->type == NULL) | 375 | if (tvar->type == NULL) |
376 | return -ENOMEM; | 376 | return -ENOMEM; |
377 | return 0; | 377 | return 0; |
378 | } | 378 | } |
379 | 379 | ||
380 | static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | 380 | static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, |
381 | struct perf_probe_arg_field *field, | 381 | struct perf_probe_arg_field *field, |
382 | struct probe_trace_arg_ref **ref_ptr, | 382 | struct probe_trace_arg_ref **ref_ptr, |
383 | Dwarf_Die *die_mem) | 383 | Dwarf_Die *die_mem) |
384 | { | 384 | { |
385 | struct probe_trace_arg_ref *ref = *ref_ptr; | 385 | struct probe_trace_arg_ref *ref = *ref_ptr; |
386 | Dwarf_Die type; | 386 | Dwarf_Die type; |
387 | Dwarf_Word offs; | 387 | Dwarf_Word offs; |
388 | int ret, tag; | 388 | int ret, tag; |
389 | 389 | ||
390 | pr_debug("converting %s in %s\n", field->name, varname); | 390 | pr_debug("converting %s in %s\n", field->name, varname); |
391 | if (die_get_real_type(vr_die, &type) == NULL) { | 391 | if (die_get_real_type(vr_die, &type) == NULL) { |
392 | pr_warning("Failed to get the type of %s.\n", varname); | 392 | pr_warning("Failed to get the type of %s.\n", varname); |
393 | return -ENOENT; | 393 | return -ENOENT; |
394 | } | 394 | } |
395 | pr_debug2("Var real type: (%x)\n", (unsigned)dwarf_dieoffset(&type)); | 395 | pr_debug2("Var real type: (%x)\n", (unsigned)dwarf_dieoffset(&type)); |
396 | tag = dwarf_tag(&type); | 396 | tag = dwarf_tag(&type); |
397 | 397 | ||
398 | if (field->name[0] == '[' && | 398 | if (field->name[0] == '[' && |
399 | (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)) { | 399 | (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)) { |
400 | if (field->next) | 400 | if (field->next) |
401 | /* Save original type for next field */ | 401 | /* Save original type for next field */ |
402 | memcpy(die_mem, &type, sizeof(*die_mem)); | 402 | memcpy(die_mem, &type, sizeof(*die_mem)); |
403 | /* Get the type of this array */ | 403 | /* Get the type of this array */ |
404 | if (die_get_real_type(&type, &type) == NULL) { | 404 | if (die_get_real_type(&type, &type) == NULL) { |
405 | pr_warning("Failed to get the type of %s.\n", varname); | 405 | pr_warning("Failed to get the type of %s.\n", varname); |
406 | return -ENOENT; | 406 | return -ENOENT; |
407 | } | 407 | } |
408 | pr_debug2("Array real type: (%x)\n", | 408 | pr_debug2("Array real type: (%x)\n", |
409 | (unsigned)dwarf_dieoffset(&type)); | 409 | (unsigned)dwarf_dieoffset(&type)); |
410 | if (tag == DW_TAG_pointer_type) { | 410 | if (tag == DW_TAG_pointer_type) { |
411 | ref = zalloc(sizeof(struct probe_trace_arg_ref)); | 411 | ref = zalloc(sizeof(struct probe_trace_arg_ref)); |
412 | if (ref == NULL) | 412 | if (ref == NULL) |
413 | return -ENOMEM; | 413 | return -ENOMEM; |
414 | if (*ref_ptr) | 414 | if (*ref_ptr) |
415 | (*ref_ptr)->next = ref; | 415 | (*ref_ptr)->next = ref; |
416 | else | 416 | else |
417 | *ref_ptr = ref; | 417 | *ref_ptr = ref; |
418 | } | 418 | } |
419 | ref->offset += dwarf_bytesize(&type) * field->index; | 419 | ref->offset += dwarf_bytesize(&type) * field->index; |
420 | if (!field->next) | 420 | if (!field->next) |
421 | /* Save vr_die for converting types */ | 421 | /* Save vr_die for converting types */ |
422 | memcpy(die_mem, vr_die, sizeof(*die_mem)); | 422 | memcpy(die_mem, vr_die, sizeof(*die_mem)); |
423 | goto next; | 423 | goto next; |
424 | } else if (tag == DW_TAG_pointer_type) { | 424 | } else if (tag == DW_TAG_pointer_type) { |
425 | /* Check the pointer and dereference */ | 425 | /* Check the pointer and dereference */ |
426 | if (!field->ref) { | 426 | if (!field->ref) { |
427 | pr_err("Semantic error: %s must be referred by '->'\n", | 427 | pr_err("Semantic error: %s must be referred by '->'\n", |
428 | field->name); | 428 | field->name); |
429 | return -EINVAL; | 429 | return -EINVAL; |
430 | } | 430 | } |
431 | /* Get the type pointed by this pointer */ | 431 | /* Get the type pointed by this pointer */ |
432 | if (die_get_real_type(&type, &type) == NULL) { | 432 | if (die_get_real_type(&type, &type) == NULL) { |
433 | pr_warning("Failed to get the type of %s.\n", varname); | 433 | pr_warning("Failed to get the type of %s.\n", varname); |
434 | return -ENOENT; | 434 | return -ENOENT; |
435 | } | 435 | } |
436 | /* Verify it is a data structure */ | 436 | /* Verify it is a data structure */ |
437 | tag = dwarf_tag(&type); | 437 | tag = dwarf_tag(&type); |
438 | if (tag != DW_TAG_structure_type && tag != DW_TAG_union_type) { | 438 | if (tag != DW_TAG_structure_type && tag != DW_TAG_union_type) { |
439 | pr_warning("%s is not a data structure nor an union.\n", | 439 | pr_warning("%s is not a data structure nor an union.\n", |
440 | varname); | 440 | varname); |
441 | return -EINVAL; | 441 | return -EINVAL; |
442 | } | 442 | } |
443 | 443 | ||
444 | ref = zalloc(sizeof(struct probe_trace_arg_ref)); | 444 | ref = zalloc(sizeof(struct probe_trace_arg_ref)); |
445 | if (ref == NULL) | 445 | if (ref == NULL) |
446 | return -ENOMEM; | 446 | return -ENOMEM; |
447 | if (*ref_ptr) | 447 | if (*ref_ptr) |
448 | (*ref_ptr)->next = ref; | 448 | (*ref_ptr)->next = ref; |
449 | else | 449 | else |
450 | *ref_ptr = ref; | 450 | *ref_ptr = ref; |
451 | } else { | 451 | } else { |
452 | /* Verify it is a data structure */ | 452 | /* Verify it is a data structure */ |
453 | if (tag != DW_TAG_structure_type && tag != DW_TAG_union_type) { | 453 | if (tag != DW_TAG_structure_type && tag != DW_TAG_union_type) { |
454 | pr_warning("%s is not a data structure nor an union.\n", | 454 | pr_warning("%s is not a data structure nor an union.\n", |
455 | varname); | 455 | varname); |
456 | return -EINVAL; | 456 | return -EINVAL; |
457 | } | 457 | } |
458 | if (field->name[0] == '[') { | 458 | if (field->name[0] == '[') { |
459 | pr_err("Semantic error: %s is not a pointor" | 459 | pr_err("Semantic error: %s is not a pointor" |
460 | " nor array.\n", varname); | 460 | " nor array.\n", varname); |
461 | return -EINVAL; | 461 | return -EINVAL; |
462 | } | 462 | } |
463 | if (field->ref) { | 463 | if (field->ref) { |
464 | pr_err("Semantic error: %s must be referred by '.'\n", | 464 | pr_err("Semantic error: %s must be referred by '.'\n", |
465 | field->name); | 465 | field->name); |
466 | return -EINVAL; | 466 | return -EINVAL; |
467 | } | 467 | } |
468 | if (!ref) { | 468 | if (!ref) { |
469 | pr_warning("Structure on a register is not " | 469 | pr_warning("Structure on a register is not " |
470 | "supported yet.\n"); | 470 | "supported yet.\n"); |
471 | return -ENOTSUP; | 471 | return -ENOTSUP; |
472 | } | 472 | } |
473 | } | 473 | } |
474 | 474 | ||
475 | if (die_find_member(&type, field->name, die_mem) == NULL) { | 475 | if (die_find_member(&type, field->name, die_mem) == NULL) { |
476 | pr_warning("%s(type:%s) has no member %s.\n", varname, | 476 | pr_warning("%s(type:%s) has no member %s.\n", varname, |
477 | dwarf_diename(&type), field->name); | 477 | dwarf_diename(&type), field->name); |
478 | return -EINVAL; | 478 | return -EINVAL; |
479 | } | 479 | } |
480 | 480 | ||
481 | /* Get the offset of the field */ | 481 | /* Get the offset of the field */ |
482 | if (tag == DW_TAG_union_type) { | 482 | if (tag == DW_TAG_union_type) { |
483 | offs = 0; | 483 | offs = 0; |
484 | } else { | 484 | } else { |
485 | ret = die_get_data_member_location(die_mem, &offs); | 485 | ret = die_get_data_member_location(die_mem, &offs); |
486 | if (ret < 0) { | 486 | if (ret < 0) { |
487 | pr_warning("Failed to get the offset of %s.\n", | 487 | pr_warning("Failed to get the offset of %s.\n", |
488 | field->name); | 488 | field->name); |
489 | return ret; | 489 | return ret; |
490 | } | 490 | } |
491 | } | 491 | } |
492 | ref->offset += (long)offs; | 492 | ref->offset += (long)offs; |
493 | 493 | ||
494 | next: | 494 | next: |
495 | /* Converting next field */ | 495 | /* Converting next field */ |
496 | if (field->next) | 496 | if (field->next) |
497 | return convert_variable_fields(die_mem, field->name, | 497 | return convert_variable_fields(die_mem, field->name, |
498 | field->next, &ref, die_mem); | 498 | field->next, &ref, die_mem); |
499 | else | 499 | else |
500 | return 0; | 500 | return 0; |
501 | } | 501 | } |
502 | 502 | ||
503 | /* Show a variables in kprobe event format */ | 503 | /* Show a variables in kprobe event format */ |
504 | static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) | 504 | static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) |
505 | { | 505 | { |
506 | Dwarf_Die die_mem; | 506 | Dwarf_Die die_mem; |
507 | int ret; | 507 | int ret; |
508 | 508 | ||
509 | pr_debug("Converting variable %s into trace event.\n", | 509 | pr_debug("Converting variable %s into trace event.\n", |
510 | dwarf_diename(vr_die)); | 510 | dwarf_diename(vr_die)); |
511 | 511 | ||
512 | ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops, | 512 | ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops, |
513 | &pf->sp_die, pf->tvar); | 513 | &pf->sp_die, pf->tvar); |
514 | if (ret == -ENOENT) | 514 | if (ret == -ENOENT || ret == -EINVAL) |
515 | pr_err("Failed to find the location of %s at this address.\n" | 515 | pr_err("Failed to find the location of %s at this address.\n" |
516 | " Perhaps, it has been optimized out.\n", pf->pvar->var); | 516 | " Perhaps, it has been optimized out.\n", pf->pvar->var); |
517 | else if (ret == -ENOTSUP) | 517 | else if (ret == -ENOTSUP) |
518 | pr_err("Sorry, we don't support this variable location yet.\n"); | 518 | pr_err("Sorry, we don't support this variable location yet.\n"); |
519 | else if (pf->pvar->field) { | 519 | else if (ret == 0 && pf->pvar->field) { |
520 | ret = convert_variable_fields(vr_die, pf->pvar->var, | 520 | ret = convert_variable_fields(vr_die, pf->pvar->var, |
521 | pf->pvar->field, &pf->tvar->ref, | 521 | pf->pvar->field, &pf->tvar->ref, |
522 | &die_mem); | 522 | &die_mem); |
523 | vr_die = &die_mem; | 523 | vr_die = &die_mem; |
524 | } | 524 | } |
525 | if (ret == 0) | 525 | if (ret == 0) |
526 | ret = convert_variable_type(vr_die, pf->tvar, pf->pvar->type); | 526 | ret = convert_variable_type(vr_die, pf->tvar, pf->pvar->type); |
527 | /* *expr will be cached in libdw. Don't free it. */ | 527 | /* *expr will be cached in libdw. Don't free it. */ |
528 | return ret; | 528 | return ret; |
529 | } | 529 | } |
530 | 530 | ||
531 | /* Find a variable in a scope DIE */ | 531 | /* Find a variable in a scope DIE */ |
532 | static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf) | 532 | static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf) |
533 | { | 533 | { |
534 | Dwarf_Die vr_die; | 534 | Dwarf_Die vr_die; |
535 | char buf[32], *ptr; | 535 | char buf[32], *ptr; |
536 | int ret = 0; | 536 | int ret = 0; |
537 | 537 | ||
538 | if (!is_c_varname(pf->pvar->var)) { | 538 | if (!is_c_varname(pf->pvar->var)) { |
539 | /* Copy raw parameters */ | 539 | /* Copy raw parameters */ |
540 | pf->tvar->value = strdup(pf->pvar->var); | 540 | pf->tvar->value = strdup(pf->pvar->var); |
541 | if (pf->tvar->value == NULL) | 541 | if (pf->tvar->value == NULL) |
542 | return -ENOMEM; | 542 | return -ENOMEM; |
543 | if (pf->pvar->type) { | 543 | if (pf->pvar->type) { |
544 | pf->tvar->type = strdup(pf->pvar->type); | 544 | pf->tvar->type = strdup(pf->pvar->type); |
545 | if (pf->tvar->type == NULL) | 545 | if (pf->tvar->type == NULL) |
546 | return -ENOMEM; | 546 | return -ENOMEM; |
547 | } | 547 | } |
548 | if (pf->pvar->name) { | 548 | if (pf->pvar->name) { |
549 | pf->tvar->name = strdup(pf->pvar->name); | 549 | pf->tvar->name = strdup(pf->pvar->name); |
550 | if (pf->tvar->name == NULL) | 550 | if (pf->tvar->name == NULL) |
551 | return -ENOMEM; | 551 | return -ENOMEM; |
552 | } else | 552 | } else |
553 | pf->tvar->name = NULL; | 553 | pf->tvar->name = NULL; |
554 | return 0; | 554 | return 0; |
555 | } | 555 | } |
556 | 556 | ||
557 | if (pf->pvar->name) | 557 | if (pf->pvar->name) |
558 | pf->tvar->name = strdup(pf->pvar->name); | 558 | pf->tvar->name = strdup(pf->pvar->name); |
559 | else { | 559 | else { |
560 | ret = synthesize_perf_probe_arg(pf->pvar, buf, 32); | 560 | ret = synthesize_perf_probe_arg(pf->pvar, buf, 32); |
561 | if (ret < 0) | 561 | if (ret < 0) |
562 | return ret; | 562 | return ret; |
563 | ptr = strchr(buf, ':'); /* Change type separator to _ */ | 563 | ptr = strchr(buf, ':'); /* Change type separator to _ */ |
564 | if (ptr) | 564 | if (ptr) |
565 | *ptr = '_'; | 565 | *ptr = '_'; |
566 | pf->tvar->name = strdup(buf); | 566 | pf->tvar->name = strdup(buf); |
567 | } | 567 | } |
568 | if (pf->tvar->name == NULL) | 568 | if (pf->tvar->name == NULL) |
569 | return -ENOMEM; | 569 | return -ENOMEM; |
570 | 570 | ||
571 | pr_debug("Searching '%s' variable in context.\n", pf->pvar->var); | 571 | pr_debug("Searching '%s' variable in context.\n", pf->pvar->var); |
572 | /* Search child die for local variables and parameters. */ | 572 | /* Search child die for local variables and parameters. */ |
573 | if (!die_find_variable_at(sc_die, pf->pvar->var, pf->addr, &vr_die)) { | 573 | if (!die_find_variable_at(sc_die, pf->pvar->var, pf->addr, &vr_die)) { |
574 | /* Search again in global variables */ | 574 | /* Search again in global variables */ |
575 | if (!die_find_variable_at(&pf->cu_die, pf->pvar->var, 0, &vr_die)) | 575 | if (!die_find_variable_at(&pf->cu_die, pf->pvar->var, 0, &vr_die)) |
576 | ret = -ENOENT; | 576 | ret = -ENOENT; |
577 | } | 577 | } |
578 | if (ret >= 0) | 578 | if (ret >= 0) |
579 | ret = convert_variable(&vr_die, pf); | 579 | ret = convert_variable(&vr_die, pf); |
580 | 580 | ||
581 | if (ret < 0) | 581 | if (ret < 0) |
582 | pr_warning("Failed to find '%s' in this function.\n", | 582 | pr_warning("Failed to find '%s' in this function.\n", |
583 | pf->pvar->var); | 583 | pf->pvar->var); |
584 | return ret; | 584 | return ret; |
585 | } | 585 | } |
586 | 586 | ||
587 | /* Convert subprogram DIE to trace point */ | 587 | /* Convert subprogram DIE to trace point */ |
588 | static int convert_to_trace_point(Dwarf_Die *sp_die, Dwfl_Module *mod, | 588 | static int convert_to_trace_point(Dwarf_Die *sp_die, Dwfl_Module *mod, |
589 | Dwarf_Addr paddr, bool retprobe, | 589 | Dwarf_Addr paddr, bool retprobe, |
590 | struct probe_trace_point *tp) | 590 | struct probe_trace_point *tp) |
591 | { | 591 | { |
592 | Dwarf_Addr eaddr, highaddr; | 592 | Dwarf_Addr eaddr, highaddr; |
593 | GElf_Sym sym; | 593 | GElf_Sym sym; |
594 | const char *symbol; | 594 | const char *symbol; |
595 | 595 | ||
596 | /* Verify the address is correct */ | 596 | /* Verify the address is correct */ |
597 | if (dwarf_entrypc(sp_die, &eaddr) != 0) { | 597 | if (dwarf_entrypc(sp_die, &eaddr) != 0) { |
598 | pr_warning("Failed to get entry address of %s\n", | 598 | pr_warning("Failed to get entry address of %s\n", |
599 | dwarf_diename(sp_die)); | 599 | dwarf_diename(sp_die)); |
600 | return -ENOENT; | 600 | return -ENOENT; |
601 | } | 601 | } |
602 | if (dwarf_highpc(sp_die, &highaddr) != 0) { | 602 | if (dwarf_highpc(sp_die, &highaddr) != 0) { |
603 | pr_warning("Failed to get end address of %s\n", | 603 | pr_warning("Failed to get end address of %s\n", |
604 | dwarf_diename(sp_die)); | 604 | dwarf_diename(sp_die)); |
605 | return -ENOENT; | 605 | return -ENOENT; |
606 | } | 606 | } |
607 | if (paddr > highaddr) { | 607 | if (paddr > highaddr) { |
608 | pr_warning("Offset specified is greater than size of %s\n", | 608 | pr_warning("Offset specified is greater than size of %s\n", |
609 | dwarf_diename(sp_die)); | 609 | dwarf_diename(sp_die)); |
610 | return -EINVAL; | 610 | return -EINVAL; |
611 | } | 611 | } |
612 | 612 | ||
613 | /* Get an appropriate symbol from symtab */ | 613 | /* Get an appropriate symbol from symtab */ |
614 | symbol = dwfl_module_addrsym(mod, paddr, &sym, NULL); | 614 | symbol = dwfl_module_addrsym(mod, paddr, &sym, NULL); |
615 | if (!symbol) { | 615 | if (!symbol) { |
616 | pr_warning("Failed to find symbol at 0x%lx\n", | 616 | pr_warning("Failed to find symbol at 0x%lx\n", |
617 | (unsigned long)paddr); | 617 | (unsigned long)paddr); |
618 | return -ENOENT; | 618 | return -ENOENT; |
619 | } | 619 | } |
620 | tp->offset = (unsigned long)(paddr - sym.st_value); | 620 | tp->offset = (unsigned long)(paddr - sym.st_value); |
621 | tp->address = (unsigned long)paddr; | 621 | tp->address = (unsigned long)paddr; |
622 | tp->symbol = strdup(symbol); | 622 | tp->symbol = strdup(symbol); |
623 | if (!tp->symbol) | 623 | if (!tp->symbol) |
624 | return -ENOMEM; | 624 | return -ENOMEM; |
625 | 625 | ||
626 | /* Return probe must be on the head of a subprogram */ | 626 | /* Return probe must be on the head of a subprogram */ |
627 | if (retprobe) { | 627 | if (retprobe) { |
628 | if (eaddr != paddr) { | 628 | if (eaddr != paddr) { |
629 | pr_warning("Return probe must be on the head of" | 629 | pr_warning("Return probe must be on the head of" |
630 | " a real function.\n"); | 630 | " a real function.\n"); |
631 | return -EINVAL; | 631 | return -EINVAL; |
632 | } | 632 | } |
633 | tp->retprobe = true; | 633 | tp->retprobe = true; |
634 | } | 634 | } |
635 | 635 | ||
636 | return 0; | 636 | return 0; |
637 | } | 637 | } |
638 | 638 | ||
639 | /* Call probe_finder callback with scope DIE */ | 639 | /* Call probe_finder callback with scope DIE */ |
640 | static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf) | 640 | static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf) |
641 | { | 641 | { |
642 | Dwarf_Attribute fb_attr; | 642 | Dwarf_Attribute fb_attr; |
643 | size_t nops; | 643 | size_t nops; |
644 | int ret; | 644 | int ret; |
645 | 645 | ||
646 | if (!sc_die) { | 646 | if (!sc_die) { |
647 | pr_err("Caller must pass a scope DIE. Program error.\n"); | 647 | pr_err("Caller must pass a scope DIE. Program error.\n"); |
648 | return -EINVAL; | 648 | return -EINVAL; |
649 | } | 649 | } |
650 | 650 | ||
651 | /* If not a real subprogram, find a real one */ | 651 | /* If not a real subprogram, find a real one */ |
652 | if (!die_is_func_def(sc_die)) { | 652 | if (!die_is_func_def(sc_die)) { |
653 | if (!die_find_realfunc(&pf->cu_die, pf->addr, &pf->sp_die)) { | 653 | if (!die_find_realfunc(&pf->cu_die, pf->addr, &pf->sp_die)) { |
654 | pr_warning("Failed to find probe point in any " | 654 | pr_warning("Failed to find probe point in any " |
655 | "functions.\n"); | 655 | "functions.\n"); |
656 | return -ENOENT; | 656 | return -ENOENT; |
657 | } | 657 | } |
658 | } else | 658 | } else |
659 | memcpy(&pf->sp_die, sc_die, sizeof(Dwarf_Die)); | 659 | memcpy(&pf->sp_die, sc_die, sizeof(Dwarf_Die)); |
660 | 660 | ||
661 | /* Get the frame base attribute/ops from subprogram */ | 661 | /* Get the frame base attribute/ops from subprogram */ |
662 | dwarf_attr(&pf->sp_die, DW_AT_frame_base, &fb_attr); | 662 | dwarf_attr(&pf->sp_die, DW_AT_frame_base, &fb_attr); |
663 | ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); | 663 | ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); |
664 | if (ret <= 0 || nops == 0) { | 664 | if (ret <= 0 || nops == 0) { |
665 | pf->fb_ops = NULL; | 665 | pf->fb_ops = NULL; |
666 | #if _ELFUTILS_PREREQ(0, 142) | 666 | #if _ELFUTILS_PREREQ(0, 142) |
667 | } else if (nops == 1 && pf->fb_ops[0].atom == DW_OP_call_frame_cfa && | 667 | } else if (nops == 1 && pf->fb_ops[0].atom == DW_OP_call_frame_cfa && |
668 | pf->cfi != NULL) { | 668 | pf->cfi != NULL) { |
669 | Dwarf_Frame *frame; | 669 | Dwarf_Frame *frame; |
670 | if (dwarf_cfi_addrframe(pf->cfi, pf->addr, &frame) != 0 || | 670 | if (dwarf_cfi_addrframe(pf->cfi, pf->addr, &frame) != 0 || |
671 | dwarf_frame_cfa(frame, &pf->fb_ops, &nops) != 0) { | 671 | dwarf_frame_cfa(frame, &pf->fb_ops, &nops) != 0) { |
672 | pr_warning("Failed to get call frame on 0x%jx\n", | 672 | pr_warning("Failed to get call frame on 0x%jx\n", |
673 | (uintmax_t)pf->addr); | 673 | (uintmax_t)pf->addr); |
674 | return -ENOENT; | 674 | return -ENOENT; |
675 | } | 675 | } |
676 | #endif | 676 | #endif |
677 | } | 677 | } |
678 | 678 | ||
679 | /* Call finder's callback handler */ | 679 | /* Call finder's callback handler */ |
680 | ret = pf->callback(sc_die, pf); | 680 | ret = pf->callback(sc_die, pf); |
681 | 681 | ||
682 | /* *pf->fb_ops will be cached in libdw. Don't free it. */ | 682 | /* *pf->fb_ops will be cached in libdw. Don't free it. */ |
683 | pf->fb_ops = NULL; | 683 | pf->fb_ops = NULL; |
684 | 684 | ||
685 | return ret; | 685 | return ret; |
686 | } | 686 | } |
687 | 687 | ||
688 | struct find_scope_param { | 688 | struct find_scope_param { |
689 | const char *function; | 689 | const char *function; |
690 | const char *file; | 690 | const char *file; |
691 | int line; | 691 | int line; |
692 | int diff; | 692 | int diff; |
693 | Dwarf_Die *die_mem; | 693 | Dwarf_Die *die_mem; |
694 | bool found; | 694 | bool found; |
695 | }; | 695 | }; |
696 | 696 | ||
697 | static int find_best_scope_cb(Dwarf_Die *fn_die, void *data) | 697 | static int find_best_scope_cb(Dwarf_Die *fn_die, void *data) |
698 | { | 698 | { |
699 | struct find_scope_param *fsp = data; | 699 | struct find_scope_param *fsp = data; |
700 | const char *file; | 700 | const char *file; |
701 | int lno; | 701 | int lno; |
702 | 702 | ||
703 | /* Skip if declared file name does not match */ | 703 | /* Skip if declared file name does not match */ |
704 | if (fsp->file) { | 704 | if (fsp->file) { |
705 | file = dwarf_decl_file(fn_die); | 705 | file = dwarf_decl_file(fn_die); |
706 | if (!file || strcmp(fsp->file, file) != 0) | 706 | if (!file || strcmp(fsp->file, file) != 0) |
707 | return 0; | 707 | return 0; |
708 | } | 708 | } |
709 | /* If the function name is given, that's what user expects */ | 709 | /* If the function name is given, that's what user expects */ |
710 | if (fsp->function) { | 710 | if (fsp->function) { |
711 | if (die_compare_name(fn_die, fsp->function)) { | 711 | if (die_compare_name(fn_die, fsp->function)) { |
712 | memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die)); | 712 | memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die)); |
713 | fsp->found = true; | 713 | fsp->found = true; |
714 | return 1; | 714 | return 1; |
715 | } | 715 | } |
716 | } else { | 716 | } else { |
717 | /* With the line number, find the nearest declared DIE */ | 717 | /* With the line number, find the nearest declared DIE */ |
718 | dwarf_decl_line(fn_die, &lno); | 718 | dwarf_decl_line(fn_die, &lno); |
719 | if (lno < fsp->line && fsp->diff > fsp->line - lno) { | 719 | if (lno < fsp->line && fsp->diff > fsp->line - lno) { |
720 | /* Keep a candidate and continue */ | 720 | /* Keep a candidate and continue */ |
721 | fsp->diff = fsp->line - lno; | 721 | fsp->diff = fsp->line - lno; |
722 | memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die)); | 722 | memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die)); |
723 | fsp->found = true; | 723 | fsp->found = true; |
724 | } | 724 | } |
725 | } | 725 | } |
726 | return 0; | 726 | return 0; |
727 | } | 727 | } |
728 | 728 | ||
729 | /* Find an appropriate scope fits to given conditions */ | 729 | /* Find an appropriate scope fits to given conditions */ |
730 | static Dwarf_Die *find_best_scope(struct probe_finder *pf, Dwarf_Die *die_mem) | 730 | static Dwarf_Die *find_best_scope(struct probe_finder *pf, Dwarf_Die *die_mem) |
731 | { | 731 | { |
732 | struct find_scope_param fsp = { | 732 | struct find_scope_param fsp = { |
733 | .function = pf->pev->point.function, | 733 | .function = pf->pev->point.function, |
734 | .file = pf->fname, | 734 | .file = pf->fname, |
735 | .line = pf->lno, | 735 | .line = pf->lno, |
736 | .diff = INT_MAX, | 736 | .diff = INT_MAX, |
737 | .die_mem = die_mem, | 737 | .die_mem = die_mem, |
738 | .found = false, | 738 | .found = false, |
739 | }; | 739 | }; |
740 | 740 | ||
741 | cu_walk_functions_at(&pf->cu_die, pf->addr, find_best_scope_cb, &fsp); | 741 | cu_walk_functions_at(&pf->cu_die, pf->addr, find_best_scope_cb, &fsp); |
742 | 742 | ||
743 | return fsp.found ? die_mem : NULL; | 743 | return fsp.found ? die_mem : NULL; |
744 | } | 744 | } |
745 | 745 | ||
746 | static int probe_point_line_walker(const char *fname, int lineno, | 746 | static int probe_point_line_walker(const char *fname, int lineno, |
747 | Dwarf_Addr addr, void *data) | 747 | Dwarf_Addr addr, void *data) |
748 | { | 748 | { |
749 | struct probe_finder *pf = data; | 749 | struct probe_finder *pf = data; |
750 | Dwarf_Die *sc_die, die_mem; | 750 | Dwarf_Die *sc_die, die_mem; |
751 | int ret; | 751 | int ret; |
752 | 752 | ||
753 | if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0) | 753 | if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0) |
754 | return 0; | 754 | return 0; |
755 | 755 | ||
756 | pf->addr = addr; | 756 | pf->addr = addr; |
757 | sc_die = find_best_scope(pf, &die_mem); | 757 | sc_die = find_best_scope(pf, &die_mem); |
758 | if (!sc_die) { | 758 | if (!sc_die) { |
759 | pr_warning("Failed to find scope of probe point.\n"); | 759 | pr_warning("Failed to find scope of probe point.\n"); |
760 | return -ENOENT; | 760 | return -ENOENT; |
761 | } | 761 | } |
762 | 762 | ||
763 | ret = call_probe_finder(sc_die, pf); | 763 | ret = call_probe_finder(sc_die, pf); |
764 | 764 | ||
765 | /* Continue if no error, because the line will be in inline function */ | 765 | /* Continue if no error, because the line will be in inline function */ |
766 | return ret < 0 ? ret : 0; | 766 | return ret < 0 ? ret : 0; |
767 | } | 767 | } |
768 | 768 | ||
769 | /* Find probe point from its line number */ | 769 | /* Find probe point from its line number */ |
770 | static int find_probe_point_by_line(struct probe_finder *pf) | 770 | static int find_probe_point_by_line(struct probe_finder *pf) |
771 | { | 771 | { |
772 | return die_walk_lines(&pf->cu_die, probe_point_line_walker, pf); | 772 | return die_walk_lines(&pf->cu_die, probe_point_line_walker, pf); |
773 | } | 773 | } |
774 | 774 | ||
775 | /* Find lines which match lazy pattern */ | 775 | /* Find lines which match lazy pattern */ |
776 | static int find_lazy_match_lines(struct intlist *list, | 776 | static int find_lazy_match_lines(struct intlist *list, |
777 | const char *fname, const char *pat) | 777 | const char *fname, const char *pat) |
778 | { | 778 | { |
779 | FILE *fp; | 779 | FILE *fp; |
780 | char *line = NULL; | 780 | char *line = NULL; |
781 | size_t line_len; | 781 | size_t line_len; |
782 | ssize_t len; | 782 | ssize_t len; |
783 | int count = 0, linenum = 1; | 783 | int count = 0, linenum = 1; |
784 | 784 | ||
785 | fp = fopen(fname, "r"); | 785 | fp = fopen(fname, "r"); |
786 | if (!fp) { | 786 | if (!fp) { |
787 | pr_warning("Failed to open %s: %s\n", fname, strerror(errno)); | 787 | pr_warning("Failed to open %s: %s\n", fname, strerror(errno)); |
788 | return -errno; | 788 | return -errno; |
789 | } | 789 | } |
790 | 790 | ||
791 | while ((len = getline(&line, &line_len, fp)) > 0) { | 791 | while ((len = getline(&line, &line_len, fp)) > 0) { |
792 | 792 | ||
793 | if (line[len - 1] == '\n') | 793 | if (line[len - 1] == '\n') |
794 | line[len - 1] = '\0'; | 794 | line[len - 1] = '\0'; |
795 | 795 | ||
796 | if (strlazymatch(line, pat)) { | 796 | if (strlazymatch(line, pat)) { |
797 | intlist__add(list, linenum); | 797 | intlist__add(list, linenum); |
798 | count++; | 798 | count++; |
799 | } | 799 | } |
800 | linenum++; | 800 | linenum++; |
801 | } | 801 | } |
802 | 802 | ||
803 | if (ferror(fp)) | 803 | if (ferror(fp)) |
804 | count = -errno; | 804 | count = -errno; |
805 | free(line); | 805 | free(line); |
806 | fclose(fp); | 806 | fclose(fp); |
807 | 807 | ||
808 | if (count == 0) | 808 | if (count == 0) |
809 | pr_debug("No matched lines found in %s.\n", fname); | 809 | pr_debug("No matched lines found in %s.\n", fname); |
810 | return count; | 810 | return count; |
811 | } | 811 | } |
812 | 812 | ||
813 | static int probe_point_lazy_walker(const char *fname, int lineno, | 813 | static int probe_point_lazy_walker(const char *fname, int lineno, |
814 | Dwarf_Addr addr, void *data) | 814 | Dwarf_Addr addr, void *data) |
815 | { | 815 | { |
816 | struct probe_finder *pf = data; | 816 | struct probe_finder *pf = data; |
817 | Dwarf_Die *sc_die, die_mem; | 817 | Dwarf_Die *sc_die, die_mem; |
818 | int ret; | 818 | int ret; |
819 | 819 | ||
820 | if (!intlist__has_entry(pf->lcache, lineno) || | 820 | if (!intlist__has_entry(pf->lcache, lineno) || |
821 | strtailcmp(fname, pf->fname) != 0) | 821 | strtailcmp(fname, pf->fname) != 0) |
822 | return 0; | 822 | return 0; |
823 | 823 | ||
824 | pr_debug("Probe line found: line:%d addr:0x%llx\n", | 824 | pr_debug("Probe line found: line:%d addr:0x%llx\n", |
825 | lineno, (unsigned long long)addr); | 825 | lineno, (unsigned long long)addr); |
826 | pf->addr = addr; | 826 | pf->addr = addr; |
827 | pf->lno = lineno; | 827 | pf->lno = lineno; |
828 | sc_die = find_best_scope(pf, &die_mem); | 828 | sc_die = find_best_scope(pf, &die_mem); |
829 | if (!sc_die) { | 829 | if (!sc_die) { |
830 | pr_warning("Failed to find scope of probe point.\n"); | 830 | pr_warning("Failed to find scope of probe point.\n"); |
831 | return -ENOENT; | 831 | return -ENOENT; |
832 | } | 832 | } |
833 | 833 | ||
834 | ret = call_probe_finder(sc_die, pf); | 834 | ret = call_probe_finder(sc_die, pf); |
835 | 835 | ||
836 | /* | 836 | /* |
837 | * Continue if no error, because the lazy pattern will match | 837 | * Continue if no error, because the lazy pattern will match |
838 | * to other lines | 838 | * to other lines |
839 | */ | 839 | */ |
840 | return ret < 0 ? ret : 0; | 840 | return ret < 0 ? ret : 0; |
841 | } | 841 | } |
842 | 842 | ||
843 | /* Find probe points from lazy pattern */ | 843 | /* Find probe points from lazy pattern */ |
844 | static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) | 844 | static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) |
845 | { | 845 | { |
846 | int ret = 0; | 846 | int ret = 0; |
847 | 847 | ||
848 | if (intlist__empty(pf->lcache)) { | 848 | if (intlist__empty(pf->lcache)) { |
849 | /* Matching lazy line pattern */ | 849 | /* Matching lazy line pattern */ |
850 | ret = find_lazy_match_lines(pf->lcache, pf->fname, | 850 | ret = find_lazy_match_lines(pf->lcache, pf->fname, |
851 | pf->pev->point.lazy_line); | 851 | pf->pev->point.lazy_line); |
852 | if (ret <= 0) | 852 | if (ret <= 0) |
853 | return ret; | 853 | return ret; |
854 | } | 854 | } |
855 | 855 | ||
856 | return die_walk_lines(sp_die, probe_point_lazy_walker, pf); | 856 | return die_walk_lines(sp_die, probe_point_lazy_walker, pf); |
857 | } | 857 | } |
858 | 858 | ||
859 | static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) | 859 | static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) |
860 | { | 860 | { |
861 | struct probe_finder *pf = data; | 861 | struct probe_finder *pf = data; |
862 | struct perf_probe_point *pp = &pf->pev->point; | 862 | struct perf_probe_point *pp = &pf->pev->point; |
863 | Dwarf_Addr addr; | 863 | Dwarf_Addr addr; |
864 | int ret; | 864 | int ret; |
865 | 865 | ||
866 | if (pp->lazy_line) | 866 | if (pp->lazy_line) |
867 | ret = find_probe_point_lazy(in_die, pf); | 867 | ret = find_probe_point_lazy(in_die, pf); |
868 | else { | 868 | else { |
869 | /* Get probe address */ | 869 | /* Get probe address */ |
870 | if (dwarf_entrypc(in_die, &addr) != 0) { | 870 | if (dwarf_entrypc(in_die, &addr) != 0) { |
871 | pr_warning("Failed to get entry address of %s.\n", | 871 | pr_warning("Failed to get entry address of %s.\n", |
872 | dwarf_diename(in_die)); | 872 | dwarf_diename(in_die)); |
873 | return -ENOENT; | 873 | return -ENOENT; |
874 | } | 874 | } |
875 | pf->addr = addr; | 875 | pf->addr = addr; |
876 | pf->addr += pp->offset; | 876 | pf->addr += pp->offset; |
877 | pr_debug("found inline addr: 0x%jx\n", | 877 | pr_debug("found inline addr: 0x%jx\n", |
878 | (uintmax_t)pf->addr); | 878 | (uintmax_t)pf->addr); |
879 | 879 | ||
880 | ret = call_probe_finder(in_die, pf); | 880 | ret = call_probe_finder(in_die, pf); |
881 | } | 881 | } |
882 | 882 | ||
883 | return ret; | 883 | return ret; |
884 | } | 884 | } |
885 | 885 | ||
886 | /* Callback parameter with return value for libdw */ | 886 | /* Callback parameter with return value for libdw */ |
887 | struct dwarf_callback_param { | 887 | struct dwarf_callback_param { |
888 | void *data; | 888 | void *data; |
889 | int retval; | 889 | int retval; |
890 | }; | 890 | }; |
891 | 891 | ||
892 | /* Search function from function name */ | 892 | /* Search function from function name */ |
893 | static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) | 893 | static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) |
894 | { | 894 | { |
895 | struct dwarf_callback_param *param = data; | 895 | struct dwarf_callback_param *param = data; |
896 | struct probe_finder *pf = param->data; | 896 | struct probe_finder *pf = param->data; |
897 | struct perf_probe_point *pp = &pf->pev->point; | 897 | struct perf_probe_point *pp = &pf->pev->point; |
898 | 898 | ||
899 | /* Check tag and diename */ | 899 | /* Check tag and diename */ |
900 | if (!die_is_func_def(sp_die) || | 900 | if (!die_is_func_def(sp_die) || |
901 | !die_compare_name(sp_die, pp->function)) | 901 | !die_compare_name(sp_die, pp->function)) |
902 | return DWARF_CB_OK; | 902 | return DWARF_CB_OK; |
903 | 903 | ||
904 | /* Check declared file */ | 904 | /* Check declared file */ |
905 | if (pp->file && strtailcmp(pp->file, dwarf_decl_file(sp_die))) | 905 | if (pp->file && strtailcmp(pp->file, dwarf_decl_file(sp_die))) |
906 | return DWARF_CB_OK; | 906 | return DWARF_CB_OK; |
907 | 907 | ||
908 | pf->fname = dwarf_decl_file(sp_die); | 908 | pf->fname = dwarf_decl_file(sp_die); |
909 | if (pp->line) { /* Function relative line */ | 909 | if (pp->line) { /* Function relative line */ |
910 | dwarf_decl_line(sp_die, &pf->lno); | 910 | dwarf_decl_line(sp_die, &pf->lno); |
911 | pf->lno += pp->line; | 911 | pf->lno += pp->line; |
912 | param->retval = find_probe_point_by_line(pf); | 912 | param->retval = find_probe_point_by_line(pf); |
913 | } else if (!dwarf_func_inline(sp_die)) { | 913 | } else if (!dwarf_func_inline(sp_die)) { |
914 | /* Real function */ | 914 | /* Real function */ |
915 | if (pp->lazy_line) | 915 | if (pp->lazy_line) |
916 | param->retval = find_probe_point_lazy(sp_die, pf); | 916 | param->retval = find_probe_point_lazy(sp_die, pf); |
917 | else { | 917 | else { |
918 | if (dwarf_entrypc(sp_die, &pf->addr) != 0) { | 918 | if (dwarf_entrypc(sp_die, &pf->addr) != 0) { |
919 | pr_warning("Failed to get entry address of " | 919 | pr_warning("Failed to get entry address of " |
920 | "%s.\n", dwarf_diename(sp_die)); | 920 | "%s.\n", dwarf_diename(sp_die)); |
921 | param->retval = -ENOENT; | 921 | param->retval = -ENOENT; |
922 | return DWARF_CB_ABORT; | 922 | return DWARF_CB_ABORT; |
923 | } | 923 | } |
924 | pf->addr += pp->offset; | 924 | pf->addr += pp->offset; |
925 | /* TODO: Check the address in this function */ | 925 | /* TODO: Check the address in this function */ |
926 | param->retval = call_probe_finder(sp_die, pf); | 926 | param->retval = call_probe_finder(sp_die, pf); |
927 | } | 927 | } |
928 | } else | 928 | } else |
929 | /* Inlined function: search instances */ | 929 | /* Inlined function: search instances */ |
930 | param->retval = die_walk_instances(sp_die, | 930 | param->retval = die_walk_instances(sp_die, |
931 | probe_point_inline_cb, (void *)pf); | 931 | probe_point_inline_cb, (void *)pf); |
932 | 932 | ||
933 | return DWARF_CB_ABORT; /* Exit; no same symbol in this CU. */ | 933 | return DWARF_CB_ABORT; /* Exit; no same symbol in this CU. */ |
934 | } | 934 | } |
935 | 935 | ||
936 | static int find_probe_point_by_func(struct probe_finder *pf) | 936 | static int find_probe_point_by_func(struct probe_finder *pf) |
937 | { | 937 | { |
938 | struct dwarf_callback_param _param = {.data = (void *)pf, | 938 | struct dwarf_callback_param _param = {.data = (void *)pf, |
939 | .retval = 0}; | 939 | .retval = 0}; |
940 | dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, &_param, 0); | 940 | dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, &_param, 0); |
941 | return _param.retval; | 941 | return _param.retval; |
942 | } | 942 | } |
943 | 943 | ||
944 | struct pubname_callback_param { | 944 | struct pubname_callback_param { |
945 | char *function; | 945 | char *function; |
946 | char *file; | 946 | char *file; |
947 | Dwarf_Die *cu_die; | 947 | Dwarf_Die *cu_die; |
948 | Dwarf_Die *sp_die; | 948 | Dwarf_Die *sp_die; |
949 | int found; | 949 | int found; |
950 | }; | 950 | }; |
951 | 951 | ||
952 | static int pubname_search_cb(Dwarf *dbg, Dwarf_Global *gl, void *data) | 952 | static int pubname_search_cb(Dwarf *dbg, Dwarf_Global *gl, void *data) |
953 | { | 953 | { |
954 | struct pubname_callback_param *param = data; | 954 | struct pubname_callback_param *param = data; |
955 | 955 | ||
956 | if (dwarf_offdie(dbg, gl->die_offset, param->sp_die)) { | 956 | if (dwarf_offdie(dbg, gl->die_offset, param->sp_die)) { |
957 | if (dwarf_tag(param->sp_die) != DW_TAG_subprogram) | 957 | if (dwarf_tag(param->sp_die) != DW_TAG_subprogram) |
958 | return DWARF_CB_OK; | 958 | return DWARF_CB_OK; |
959 | 959 | ||
960 | if (die_compare_name(param->sp_die, param->function)) { | 960 | if (die_compare_name(param->sp_die, param->function)) { |
961 | if (!dwarf_offdie(dbg, gl->cu_offset, param->cu_die)) | 961 | if (!dwarf_offdie(dbg, gl->cu_offset, param->cu_die)) |
962 | return DWARF_CB_OK; | 962 | return DWARF_CB_OK; |
963 | 963 | ||
964 | if (param->file && | 964 | if (param->file && |
965 | strtailcmp(param->file, dwarf_decl_file(param->sp_die))) | 965 | strtailcmp(param->file, dwarf_decl_file(param->sp_die))) |
966 | return DWARF_CB_OK; | 966 | return DWARF_CB_OK; |
967 | 967 | ||
968 | param->found = 1; | 968 | param->found = 1; |
969 | return DWARF_CB_ABORT; | 969 | return DWARF_CB_ABORT; |
970 | } | 970 | } |
971 | } | 971 | } |
972 | 972 | ||
973 | return DWARF_CB_OK; | 973 | return DWARF_CB_OK; |
974 | } | 974 | } |
975 | 975 | ||
976 | /* Find probe points from debuginfo */ | 976 | /* Find probe points from debuginfo */ |
977 | static int debuginfo__find_probes(struct debuginfo *dbg, | 977 | static int debuginfo__find_probes(struct debuginfo *dbg, |
978 | struct probe_finder *pf) | 978 | struct probe_finder *pf) |
979 | { | 979 | { |
980 | struct perf_probe_point *pp = &pf->pev->point; | 980 | struct perf_probe_point *pp = &pf->pev->point; |
981 | Dwarf_Off off, noff; | 981 | Dwarf_Off off, noff; |
982 | size_t cuhl; | 982 | size_t cuhl; |
983 | Dwarf_Die *diep; | 983 | Dwarf_Die *diep; |
984 | int ret = 0; | 984 | int ret = 0; |
985 | 985 | ||
986 | #if _ELFUTILS_PREREQ(0, 142) | 986 | #if _ELFUTILS_PREREQ(0, 142) |
987 | /* Get the call frame information from this dwarf */ | 987 | /* Get the call frame information from this dwarf */ |
988 | pf->cfi = dwarf_getcfi_elf(dwarf_getelf(dbg->dbg)); | 988 | pf->cfi = dwarf_getcfi_elf(dwarf_getelf(dbg->dbg)); |
989 | #endif | 989 | #endif |
990 | 990 | ||
991 | off = 0; | 991 | off = 0; |
992 | pf->lcache = intlist__new(NULL); | 992 | pf->lcache = intlist__new(NULL); |
993 | if (!pf->lcache) | 993 | if (!pf->lcache) |
994 | return -ENOMEM; | 994 | return -ENOMEM; |
995 | 995 | ||
996 | /* Fastpath: lookup by function name from .debug_pubnames section */ | 996 | /* Fastpath: lookup by function name from .debug_pubnames section */ |
997 | if (pp->function) { | 997 | if (pp->function) { |
998 | struct pubname_callback_param pubname_param = { | 998 | struct pubname_callback_param pubname_param = { |
999 | .function = pp->function, | 999 | .function = pp->function, |
1000 | .file = pp->file, | 1000 | .file = pp->file, |
1001 | .cu_die = &pf->cu_die, | 1001 | .cu_die = &pf->cu_die, |
1002 | .sp_die = &pf->sp_die, | 1002 | .sp_die = &pf->sp_die, |
1003 | .found = 0, | 1003 | .found = 0, |
1004 | }; | 1004 | }; |
1005 | struct dwarf_callback_param probe_param = { | 1005 | struct dwarf_callback_param probe_param = { |
1006 | .data = pf, | 1006 | .data = pf, |
1007 | }; | 1007 | }; |
1008 | 1008 | ||
1009 | dwarf_getpubnames(dbg->dbg, pubname_search_cb, | 1009 | dwarf_getpubnames(dbg->dbg, pubname_search_cb, |
1010 | &pubname_param, 0); | 1010 | &pubname_param, 0); |
1011 | if (pubname_param.found) { | 1011 | if (pubname_param.found) { |
1012 | ret = probe_point_search_cb(&pf->sp_die, &probe_param); | 1012 | ret = probe_point_search_cb(&pf->sp_die, &probe_param); |
1013 | if (ret) | 1013 | if (ret) |
1014 | goto found; | 1014 | goto found; |
1015 | } | 1015 | } |
1016 | } | 1016 | } |
1017 | 1017 | ||
1018 | /* Loop on CUs (Compilation Unit) */ | 1018 | /* Loop on CUs (Compilation Unit) */ |
1019 | while (!dwarf_nextcu(dbg->dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { | 1019 | while (!dwarf_nextcu(dbg->dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { |
1020 | /* Get the DIE(Debugging Information Entry) of this CU */ | 1020 | /* Get the DIE(Debugging Information Entry) of this CU */ |
1021 | diep = dwarf_offdie(dbg->dbg, off + cuhl, &pf->cu_die); | 1021 | diep = dwarf_offdie(dbg->dbg, off + cuhl, &pf->cu_die); |
1022 | if (!diep) | 1022 | if (!diep) |
1023 | continue; | 1023 | continue; |
1024 | 1024 | ||
1025 | /* Check if target file is included. */ | 1025 | /* Check if target file is included. */ |
1026 | if (pp->file) | 1026 | if (pp->file) |
1027 | pf->fname = cu_find_realpath(&pf->cu_die, pp->file); | 1027 | pf->fname = cu_find_realpath(&pf->cu_die, pp->file); |
1028 | else | 1028 | else |
1029 | pf->fname = NULL; | 1029 | pf->fname = NULL; |
1030 | 1030 | ||
1031 | if (!pp->file || pf->fname) { | 1031 | if (!pp->file || pf->fname) { |
1032 | if (pp->function) | 1032 | if (pp->function) |
1033 | ret = find_probe_point_by_func(pf); | 1033 | ret = find_probe_point_by_func(pf); |
1034 | else if (pp->lazy_line) | 1034 | else if (pp->lazy_line) |
1035 | ret = find_probe_point_lazy(NULL, pf); | 1035 | ret = find_probe_point_lazy(NULL, pf); |
1036 | else { | 1036 | else { |
1037 | pf->lno = pp->line; | 1037 | pf->lno = pp->line; |
1038 | ret = find_probe_point_by_line(pf); | 1038 | ret = find_probe_point_by_line(pf); |
1039 | } | 1039 | } |
1040 | if (ret < 0) | 1040 | if (ret < 0) |
1041 | break; | 1041 | break; |
1042 | } | 1042 | } |
1043 | off = noff; | 1043 | off = noff; |
1044 | } | 1044 | } |
1045 | 1045 | ||
1046 | found: | 1046 | found: |
1047 | intlist__delete(pf->lcache); | 1047 | intlist__delete(pf->lcache); |
1048 | pf->lcache = NULL; | 1048 | pf->lcache = NULL; |
1049 | 1049 | ||
1050 | return ret; | 1050 | return ret; |
1051 | } | 1051 | } |
1052 | 1052 | ||
1053 | struct local_vars_finder { | 1053 | struct local_vars_finder { |
1054 | struct probe_finder *pf; | 1054 | struct probe_finder *pf; |
1055 | struct perf_probe_arg *args; | 1055 | struct perf_probe_arg *args; |
1056 | int max_args; | 1056 | int max_args; |
1057 | int nargs; | 1057 | int nargs; |
1058 | int ret; | 1058 | int ret; |
1059 | }; | 1059 | }; |
1060 | 1060 | ||
1061 | /* Collect available variables in this scope */ | 1061 | /* Collect available variables in this scope */ |
1062 | static int copy_variables_cb(Dwarf_Die *die_mem, void *data) | 1062 | static int copy_variables_cb(Dwarf_Die *die_mem, void *data) |
1063 | { | 1063 | { |
1064 | struct local_vars_finder *vf = data; | 1064 | struct local_vars_finder *vf = data; |
1065 | struct probe_finder *pf = vf->pf; | 1065 | struct probe_finder *pf = vf->pf; |
1066 | int tag; | 1066 | int tag; |
1067 | 1067 | ||
1068 | tag = dwarf_tag(die_mem); | 1068 | tag = dwarf_tag(die_mem); |
1069 | if (tag == DW_TAG_formal_parameter || | 1069 | if (tag == DW_TAG_formal_parameter || |
1070 | tag == DW_TAG_variable) { | 1070 | tag == DW_TAG_variable) { |
1071 | if (convert_variable_location(die_mem, vf->pf->addr, | 1071 | if (convert_variable_location(die_mem, vf->pf->addr, |
1072 | vf->pf->fb_ops, &pf->sp_die, | 1072 | vf->pf->fb_ops, &pf->sp_die, |
1073 | NULL) == 0) { | 1073 | NULL) == 0) { |
1074 | vf->args[vf->nargs].var = (char *)dwarf_diename(die_mem); | 1074 | vf->args[vf->nargs].var = (char *)dwarf_diename(die_mem); |
1075 | if (vf->args[vf->nargs].var == NULL) { | 1075 | if (vf->args[vf->nargs].var == NULL) { |
1076 | vf->ret = -ENOMEM; | 1076 | vf->ret = -ENOMEM; |
1077 | return DIE_FIND_CB_END; | 1077 | return DIE_FIND_CB_END; |
1078 | } | 1078 | } |
1079 | pr_debug(" %s", vf->args[vf->nargs].var); | 1079 | pr_debug(" %s", vf->args[vf->nargs].var); |
1080 | vf->nargs++; | 1080 | vf->nargs++; |
1081 | } | 1081 | } |
1082 | } | 1082 | } |
1083 | 1083 | ||
1084 | if (dwarf_haspc(die_mem, vf->pf->addr)) | 1084 | if (dwarf_haspc(die_mem, vf->pf->addr)) |
1085 | return DIE_FIND_CB_CONTINUE; | 1085 | return DIE_FIND_CB_CONTINUE; |
1086 | else | 1086 | else |
1087 | return DIE_FIND_CB_SIBLING; | 1087 | return DIE_FIND_CB_SIBLING; |
1088 | } | 1088 | } |
1089 | 1089 | ||
1090 | static int expand_probe_args(Dwarf_Die *sc_die, struct probe_finder *pf, | 1090 | static int expand_probe_args(Dwarf_Die *sc_die, struct probe_finder *pf, |
1091 | struct perf_probe_arg *args) | 1091 | struct perf_probe_arg *args) |
1092 | { | 1092 | { |
1093 | Dwarf_Die die_mem; | 1093 | Dwarf_Die die_mem; |
1094 | int i; | 1094 | int i; |
1095 | int n = 0; | 1095 | int n = 0; |
1096 | struct local_vars_finder vf = {.pf = pf, .args = args, | 1096 | struct local_vars_finder vf = {.pf = pf, .args = args, |
1097 | .max_args = MAX_PROBE_ARGS, .ret = 0}; | 1097 | .max_args = MAX_PROBE_ARGS, .ret = 0}; |
1098 | 1098 | ||
1099 | for (i = 0; i < pf->pev->nargs; i++) { | 1099 | for (i = 0; i < pf->pev->nargs; i++) { |
1100 | /* var never be NULL */ | 1100 | /* var never be NULL */ |
1101 | if (strcmp(pf->pev->args[i].var, "$vars") == 0) { | 1101 | if (strcmp(pf->pev->args[i].var, "$vars") == 0) { |
1102 | pr_debug("Expanding $vars into:"); | 1102 | pr_debug("Expanding $vars into:"); |
1103 | vf.nargs = n; | 1103 | vf.nargs = n; |
1104 | /* Special local variables */ | 1104 | /* Special local variables */ |
1105 | die_find_child(sc_die, copy_variables_cb, (void *)&vf, | 1105 | die_find_child(sc_die, copy_variables_cb, (void *)&vf, |
1106 | &die_mem); | 1106 | &die_mem); |
1107 | pr_debug(" (%d)\n", vf.nargs - n); | 1107 | pr_debug(" (%d)\n", vf.nargs - n); |
1108 | if (vf.ret < 0) | 1108 | if (vf.ret < 0) |
1109 | return vf.ret; | 1109 | return vf.ret; |
1110 | n = vf.nargs; | 1110 | n = vf.nargs; |
1111 | } else { | 1111 | } else { |
1112 | /* Copy normal argument */ | 1112 | /* Copy normal argument */ |
1113 | args[n] = pf->pev->args[i]; | 1113 | args[n] = pf->pev->args[i]; |
1114 | n++; | 1114 | n++; |
1115 | } | 1115 | } |
1116 | } | 1116 | } |
1117 | return n; | 1117 | return n; |
1118 | } | 1118 | } |
1119 | 1119 | ||
1120 | /* Add a found probe point into trace event list */ | 1120 | /* Add a found probe point into trace event list */ |
1121 | static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) | 1121 | static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) |
1122 | { | 1122 | { |
1123 | struct trace_event_finder *tf = | 1123 | struct trace_event_finder *tf = |
1124 | container_of(pf, struct trace_event_finder, pf); | 1124 | container_of(pf, struct trace_event_finder, pf); |
1125 | struct probe_trace_event *tev; | 1125 | struct probe_trace_event *tev; |
1126 | struct perf_probe_arg *args; | 1126 | struct perf_probe_arg *args; |
1127 | int ret, i; | 1127 | int ret, i; |
1128 | 1128 | ||
1129 | /* Check number of tevs */ | 1129 | /* Check number of tevs */ |
1130 | if (tf->ntevs == tf->max_tevs) { | 1130 | if (tf->ntevs == tf->max_tevs) { |
1131 | pr_warning("Too many( > %d) probe point found.\n", | 1131 | pr_warning("Too many( > %d) probe point found.\n", |
1132 | tf->max_tevs); | 1132 | tf->max_tevs); |
1133 | return -ERANGE; | 1133 | return -ERANGE; |
1134 | } | 1134 | } |
1135 | tev = &tf->tevs[tf->ntevs++]; | 1135 | tev = &tf->tevs[tf->ntevs++]; |
1136 | 1136 | ||
1137 | /* Trace point should be converted from subprogram DIE */ | 1137 | /* Trace point should be converted from subprogram DIE */ |
1138 | ret = convert_to_trace_point(&pf->sp_die, tf->mod, pf->addr, | 1138 | ret = convert_to_trace_point(&pf->sp_die, tf->mod, pf->addr, |
1139 | pf->pev->point.retprobe, &tev->point); | 1139 | pf->pev->point.retprobe, &tev->point); |
1140 | if (ret < 0) | 1140 | if (ret < 0) |
1141 | return ret; | 1141 | return ret; |
1142 | 1142 | ||
1143 | pr_debug("Probe point found: %s+%lu\n", tev->point.symbol, | 1143 | pr_debug("Probe point found: %s+%lu\n", tev->point.symbol, |
1144 | tev->point.offset); | 1144 | tev->point.offset); |
1145 | 1145 | ||
1146 | /* Expand special probe argument if exist */ | 1146 | /* Expand special probe argument if exist */ |
1147 | args = zalloc(sizeof(struct perf_probe_arg) * MAX_PROBE_ARGS); | 1147 | args = zalloc(sizeof(struct perf_probe_arg) * MAX_PROBE_ARGS); |
1148 | if (args == NULL) | 1148 | if (args == NULL) |
1149 | return -ENOMEM; | 1149 | return -ENOMEM; |
1150 | 1150 | ||
1151 | ret = expand_probe_args(sc_die, pf, args); | 1151 | ret = expand_probe_args(sc_die, pf, args); |
1152 | if (ret < 0) | 1152 | if (ret < 0) |
1153 | goto end; | 1153 | goto end; |
1154 | 1154 | ||
1155 | tev->nargs = ret; | 1155 | tev->nargs = ret; |
1156 | tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); | 1156 | tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); |
1157 | if (tev->args == NULL) { | 1157 | if (tev->args == NULL) { |
1158 | ret = -ENOMEM; | 1158 | ret = -ENOMEM; |
1159 | goto end; | 1159 | goto end; |
1160 | } | 1160 | } |
1161 | 1161 | ||
1162 | /* Find each argument */ | 1162 | /* Find each argument */ |
1163 | for (i = 0; i < tev->nargs; i++) { | 1163 | for (i = 0; i < tev->nargs; i++) { |
1164 | pf->pvar = &args[i]; | 1164 | pf->pvar = &args[i]; |
1165 | pf->tvar = &tev->args[i]; | 1165 | pf->tvar = &tev->args[i]; |
1166 | /* Variable should be found from scope DIE */ | 1166 | /* Variable should be found from scope DIE */ |
1167 | ret = find_variable(sc_die, pf); | 1167 | ret = find_variable(sc_die, pf); |
1168 | if (ret != 0) | 1168 | if (ret != 0) |
1169 | break; | 1169 | break; |
1170 | } | 1170 | } |
1171 | 1171 | ||
1172 | end: | 1172 | end: |
1173 | free(args); | 1173 | free(args); |
1174 | return ret; | 1174 | return ret; |
1175 | } | 1175 | } |
1176 | 1176 | ||
1177 | /* Find probe_trace_events specified by perf_probe_event from debuginfo */ | 1177 | /* Find probe_trace_events specified by perf_probe_event from debuginfo */ |
1178 | int debuginfo__find_trace_events(struct debuginfo *dbg, | 1178 | int debuginfo__find_trace_events(struct debuginfo *dbg, |
1179 | struct perf_probe_event *pev, | 1179 | struct perf_probe_event *pev, |
1180 | struct probe_trace_event **tevs, int max_tevs) | 1180 | struct probe_trace_event **tevs, int max_tevs) |
1181 | { | 1181 | { |
1182 | struct trace_event_finder tf = { | 1182 | struct trace_event_finder tf = { |
1183 | .pf = {.pev = pev, .callback = add_probe_trace_event}, | 1183 | .pf = {.pev = pev, .callback = add_probe_trace_event}, |
1184 | .mod = dbg->mod, .max_tevs = max_tevs}; | 1184 | .mod = dbg->mod, .max_tevs = max_tevs}; |
1185 | int ret; | 1185 | int ret; |
1186 | 1186 | ||
1187 | /* Allocate result tevs array */ | 1187 | /* Allocate result tevs array */ |
1188 | *tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs); | 1188 | *tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs); |
1189 | if (*tevs == NULL) | 1189 | if (*tevs == NULL) |
1190 | return -ENOMEM; | 1190 | return -ENOMEM; |
1191 | 1191 | ||
1192 | tf.tevs = *tevs; | 1192 | tf.tevs = *tevs; |
1193 | tf.ntevs = 0; | 1193 | tf.ntevs = 0; |
1194 | 1194 | ||
1195 | ret = debuginfo__find_probes(dbg, &tf.pf); | 1195 | ret = debuginfo__find_probes(dbg, &tf.pf); |
1196 | if (ret < 0) { | 1196 | if (ret < 0) { |
1197 | zfree(tevs); | 1197 | zfree(tevs); |
1198 | return ret; | 1198 | return ret; |
1199 | } | 1199 | } |
1200 | 1200 | ||
1201 | return (ret < 0) ? ret : tf.ntevs; | 1201 | return (ret < 0) ? ret : tf.ntevs; |
1202 | } | 1202 | } |
1203 | 1203 | ||
1204 | #define MAX_VAR_LEN 64 | 1204 | #define MAX_VAR_LEN 64 |
1205 | 1205 | ||
1206 | /* Collect available variables in this scope */ | 1206 | /* Collect available variables in this scope */ |
1207 | static int collect_variables_cb(Dwarf_Die *die_mem, void *data) | 1207 | static int collect_variables_cb(Dwarf_Die *die_mem, void *data) |
1208 | { | 1208 | { |
1209 | struct available_var_finder *af = data; | 1209 | struct available_var_finder *af = data; |
1210 | struct variable_list *vl; | 1210 | struct variable_list *vl; |
1211 | char buf[MAX_VAR_LEN]; | 1211 | char buf[MAX_VAR_LEN]; |
1212 | int tag, ret; | 1212 | int tag, ret; |
1213 | 1213 | ||
1214 | vl = &af->vls[af->nvls - 1]; | 1214 | vl = &af->vls[af->nvls - 1]; |
1215 | 1215 | ||
1216 | tag = dwarf_tag(die_mem); | 1216 | tag = dwarf_tag(die_mem); |
1217 | if (tag == DW_TAG_formal_parameter || | 1217 | if (tag == DW_TAG_formal_parameter || |
1218 | tag == DW_TAG_variable) { | 1218 | tag == DW_TAG_variable) { |
1219 | ret = convert_variable_location(die_mem, af->pf.addr, | 1219 | ret = convert_variable_location(die_mem, af->pf.addr, |
1220 | af->pf.fb_ops, &af->pf.sp_die, | 1220 | af->pf.fb_ops, &af->pf.sp_die, |
1221 | NULL); | 1221 | NULL); |
1222 | if (ret == 0) { | 1222 | if (ret == 0) { |
1223 | ret = die_get_varname(die_mem, buf, MAX_VAR_LEN); | 1223 | ret = die_get_varname(die_mem, buf, MAX_VAR_LEN); |
1224 | pr_debug2("Add new var: %s\n", buf); | 1224 | pr_debug2("Add new var: %s\n", buf); |
1225 | if (ret > 0) | 1225 | if (ret > 0) |
1226 | strlist__add(vl->vars, buf); | 1226 | strlist__add(vl->vars, buf); |
1227 | } | 1227 | } |
1228 | } | 1228 | } |
1229 | 1229 | ||
1230 | if (af->child && dwarf_haspc(die_mem, af->pf.addr)) | 1230 | if (af->child && dwarf_haspc(die_mem, af->pf.addr)) |
1231 | return DIE_FIND_CB_CONTINUE; | 1231 | return DIE_FIND_CB_CONTINUE; |
1232 | else | 1232 | else |
1233 | return DIE_FIND_CB_SIBLING; | 1233 | return DIE_FIND_CB_SIBLING; |
1234 | } | 1234 | } |
1235 | 1235 | ||
1236 | /* Add a found vars into available variables list */ | 1236 | /* Add a found vars into available variables list */ |
1237 | static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf) | 1237 | static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf) |
1238 | { | 1238 | { |
1239 | struct available_var_finder *af = | 1239 | struct available_var_finder *af = |
1240 | container_of(pf, struct available_var_finder, pf); | 1240 | container_of(pf, struct available_var_finder, pf); |
1241 | struct variable_list *vl; | 1241 | struct variable_list *vl; |
1242 | Dwarf_Die die_mem; | 1242 | Dwarf_Die die_mem; |
1243 | int ret; | 1243 | int ret; |
1244 | 1244 | ||
1245 | /* Check number of tevs */ | 1245 | /* Check number of tevs */ |
1246 | if (af->nvls == af->max_vls) { | 1246 | if (af->nvls == af->max_vls) { |
1247 | pr_warning("Too many( > %d) probe point found.\n", af->max_vls); | 1247 | pr_warning("Too many( > %d) probe point found.\n", af->max_vls); |
1248 | return -ERANGE; | 1248 | return -ERANGE; |
1249 | } | 1249 | } |
1250 | vl = &af->vls[af->nvls++]; | 1250 | vl = &af->vls[af->nvls++]; |
1251 | 1251 | ||
1252 | /* Trace point should be converted from subprogram DIE */ | 1252 | /* Trace point should be converted from subprogram DIE */ |
1253 | ret = convert_to_trace_point(&pf->sp_die, af->mod, pf->addr, | 1253 | ret = convert_to_trace_point(&pf->sp_die, af->mod, pf->addr, |
1254 | pf->pev->point.retprobe, &vl->point); | 1254 | pf->pev->point.retprobe, &vl->point); |
1255 | if (ret < 0) | 1255 | if (ret < 0) |
1256 | return ret; | 1256 | return ret; |
1257 | 1257 | ||
1258 | pr_debug("Probe point found: %s+%lu\n", vl->point.symbol, | 1258 | pr_debug("Probe point found: %s+%lu\n", vl->point.symbol, |
1259 | vl->point.offset); | 1259 | vl->point.offset); |
1260 | 1260 | ||
1261 | /* Find local variables */ | 1261 | /* Find local variables */ |
1262 | vl->vars = strlist__new(true, NULL); | 1262 | vl->vars = strlist__new(true, NULL); |
1263 | if (vl->vars == NULL) | 1263 | if (vl->vars == NULL) |
1264 | return -ENOMEM; | 1264 | return -ENOMEM; |
1265 | af->child = true; | 1265 | af->child = true; |
1266 | die_find_child(sc_die, collect_variables_cb, (void *)af, &die_mem); | 1266 | die_find_child(sc_die, collect_variables_cb, (void *)af, &die_mem); |
1267 | 1267 | ||
1268 | /* Find external variables */ | 1268 | /* Find external variables */ |
1269 | if (!af->externs) | 1269 | if (!af->externs) |
1270 | goto out; | 1270 | goto out; |
1271 | /* Don't need to search child DIE for externs. */ | 1271 | /* Don't need to search child DIE for externs. */ |
1272 | af->child = false; | 1272 | af->child = false; |
1273 | die_find_child(&pf->cu_die, collect_variables_cb, (void *)af, &die_mem); | 1273 | die_find_child(&pf->cu_die, collect_variables_cb, (void *)af, &die_mem); |
1274 | 1274 | ||
1275 | out: | 1275 | out: |
1276 | if (strlist__empty(vl->vars)) { | 1276 | if (strlist__empty(vl->vars)) { |
1277 | strlist__delete(vl->vars); | 1277 | strlist__delete(vl->vars); |
1278 | vl->vars = NULL; | 1278 | vl->vars = NULL; |
1279 | } | 1279 | } |
1280 | 1280 | ||
1281 | return ret; | 1281 | return ret; |
1282 | } | 1282 | } |
1283 | 1283 | ||
1284 | /* Find available variables at given probe point */ | 1284 | /* Find available variables at given probe point */ |
1285 | int debuginfo__find_available_vars_at(struct debuginfo *dbg, | 1285 | int debuginfo__find_available_vars_at(struct debuginfo *dbg, |
1286 | struct perf_probe_event *pev, | 1286 | struct perf_probe_event *pev, |
1287 | struct variable_list **vls, | 1287 | struct variable_list **vls, |
1288 | int max_vls, bool externs) | 1288 | int max_vls, bool externs) |
1289 | { | 1289 | { |
1290 | struct available_var_finder af = { | 1290 | struct available_var_finder af = { |
1291 | .pf = {.pev = pev, .callback = add_available_vars}, | 1291 | .pf = {.pev = pev, .callback = add_available_vars}, |
1292 | .mod = dbg->mod, | 1292 | .mod = dbg->mod, |
1293 | .max_vls = max_vls, .externs = externs}; | 1293 | .max_vls = max_vls, .externs = externs}; |
1294 | int ret; | 1294 | int ret; |
1295 | 1295 | ||
1296 | /* Allocate result vls array */ | 1296 | /* Allocate result vls array */ |
1297 | *vls = zalloc(sizeof(struct variable_list) * max_vls); | 1297 | *vls = zalloc(sizeof(struct variable_list) * max_vls); |
1298 | if (*vls == NULL) | 1298 | if (*vls == NULL) |
1299 | return -ENOMEM; | 1299 | return -ENOMEM; |
1300 | 1300 | ||
1301 | af.vls = *vls; | 1301 | af.vls = *vls; |
1302 | af.nvls = 0; | 1302 | af.nvls = 0; |
1303 | 1303 | ||
1304 | ret = debuginfo__find_probes(dbg, &af.pf); | 1304 | ret = debuginfo__find_probes(dbg, &af.pf); |
1305 | if (ret < 0) { | 1305 | if (ret < 0) { |
1306 | /* Free vlist for error */ | 1306 | /* Free vlist for error */ |
1307 | while (af.nvls--) { | 1307 | while (af.nvls--) { |
1308 | zfree(&af.vls[af.nvls].point.symbol); | 1308 | zfree(&af.vls[af.nvls].point.symbol); |
1309 | strlist__delete(af.vls[af.nvls].vars); | 1309 | strlist__delete(af.vls[af.nvls].vars); |
1310 | } | 1310 | } |
1311 | zfree(vls); | 1311 | zfree(vls); |
1312 | return ret; | 1312 | return ret; |
1313 | } | 1313 | } |
1314 | 1314 | ||
1315 | return (ret < 0) ? ret : af.nvls; | 1315 | return (ret < 0) ? ret : af.nvls; |
1316 | } | 1316 | } |
1317 | 1317 | ||
1318 | /* Reverse search */ | 1318 | /* Reverse search */ |
1319 | int debuginfo__find_probe_point(struct debuginfo *dbg, unsigned long addr, | 1319 | int debuginfo__find_probe_point(struct debuginfo *dbg, unsigned long addr, |
1320 | struct perf_probe_point *ppt) | 1320 | struct perf_probe_point *ppt) |
1321 | { | 1321 | { |
1322 | Dwarf_Die cudie, spdie, indie; | 1322 | Dwarf_Die cudie, spdie, indie; |
1323 | Dwarf_Addr _addr = 0, baseaddr = 0; | 1323 | Dwarf_Addr _addr = 0, baseaddr = 0; |
1324 | const char *fname = NULL, *func = NULL, *basefunc = NULL, *tmp; | 1324 | const char *fname = NULL, *func = NULL, *basefunc = NULL, *tmp; |
1325 | int baseline = 0, lineno = 0, ret = 0; | 1325 | int baseline = 0, lineno = 0, ret = 0; |
1326 | 1326 | ||
1327 | /* Adjust address with bias */ | 1327 | /* Adjust address with bias */ |
1328 | addr += dbg->bias; | 1328 | addr += dbg->bias; |
1329 | 1329 | ||
1330 | /* Find cu die */ | 1330 | /* Find cu die */ |
1331 | if (!dwarf_addrdie(dbg->dbg, (Dwarf_Addr)addr - dbg->bias, &cudie)) { | 1331 | if (!dwarf_addrdie(dbg->dbg, (Dwarf_Addr)addr - dbg->bias, &cudie)) { |
1332 | pr_warning("Failed to find debug information for address %lx\n", | 1332 | pr_warning("Failed to find debug information for address %lx\n", |
1333 | addr); | 1333 | addr); |
1334 | ret = -EINVAL; | 1334 | ret = -EINVAL; |
1335 | goto end; | 1335 | goto end; |
1336 | } | 1336 | } |
1337 | 1337 | ||
1338 | /* Find a corresponding line (filename and lineno) */ | 1338 | /* Find a corresponding line (filename and lineno) */ |
1339 | cu_find_lineinfo(&cudie, addr, &fname, &lineno); | 1339 | cu_find_lineinfo(&cudie, addr, &fname, &lineno); |
1340 | /* Don't care whether it failed or not */ | 1340 | /* Don't care whether it failed or not */ |
1341 | 1341 | ||
1342 | /* Find a corresponding function (name, baseline and baseaddr) */ | 1342 | /* Find a corresponding function (name, baseline and baseaddr) */ |
1343 | if (die_find_realfunc(&cudie, (Dwarf_Addr)addr, &spdie)) { | 1343 | if (die_find_realfunc(&cudie, (Dwarf_Addr)addr, &spdie)) { |
1344 | /* Get function entry information */ | 1344 | /* Get function entry information */ |
1345 | func = basefunc = dwarf_diename(&spdie); | 1345 | func = basefunc = dwarf_diename(&spdie); |
1346 | if (!func || | 1346 | if (!func || |
1347 | dwarf_entrypc(&spdie, &baseaddr) != 0 || | 1347 | dwarf_entrypc(&spdie, &baseaddr) != 0 || |
1348 | dwarf_decl_line(&spdie, &baseline) != 0) { | 1348 | dwarf_decl_line(&spdie, &baseline) != 0) { |
1349 | lineno = 0; | 1349 | lineno = 0; |
1350 | goto post; | 1350 | goto post; |
1351 | } | 1351 | } |
1352 | 1352 | ||
1353 | fname = dwarf_decl_file(&spdie); | 1353 | fname = dwarf_decl_file(&spdie); |
1354 | if (addr == (unsigned long)baseaddr) { | 1354 | if (addr == (unsigned long)baseaddr) { |
1355 | /* Function entry - Relative line number is 0 */ | 1355 | /* Function entry - Relative line number is 0 */ |
1356 | lineno = baseline; | 1356 | lineno = baseline; |
1357 | goto post; | 1357 | goto post; |
1358 | } | 1358 | } |
1359 | 1359 | ||
1360 | /* Track down the inline functions step by step */ | 1360 | /* Track down the inline functions step by step */ |
1361 | while (die_find_top_inlinefunc(&spdie, (Dwarf_Addr)addr, | 1361 | while (die_find_top_inlinefunc(&spdie, (Dwarf_Addr)addr, |
1362 | &indie)) { | 1362 | &indie)) { |
1363 | /* There is an inline function */ | 1363 | /* There is an inline function */ |
1364 | if (dwarf_entrypc(&indie, &_addr) == 0 && | 1364 | if (dwarf_entrypc(&indie, &_addr) == 0 && |
1365 | _addr == addr) { | 1365 | _addr == addr) { |
1366 | /* | 1366 | /* |
1367 | * addr is at an inline function entry. | 1367 | * addr is at an inline function entry. |
1368 | * In this case, lineno should be the call-site | 1368 | * In this case, lineno should be the call-site |
1369 | * line number. (overwrite lineinfo) | 1369 | * line number. (overwrite lineinfo) |
1370 | */ | 1370 | */ |
1371 | lineno = die_get_call_lineno(&indie); | 1371 | lineno = die_get_call_lineno(&indie); |
1372 | fname = die_get_call_file(&indie); | 1372 | fname = die_get_call_file(&indie); |
1373 | break; | 1373 | break; |
1374 | } else { | 1374 | } else { |
1375 | /* | 1375 | /* |
1376 | * addr is in an inline function body. | 1376 | * addr is in an inline function body. |
1377 | * Since lineno points one of the lines | 1377 | * Since lineno points one of the lines |
1378 | * of the inline function, baseline should | 1378 | * of the inline function, baseline should |
1379 | * be the entry line of the inline function. | 1379 | * be the entry line of the inline function. |
1380 | */ | 1380 | */ |
1381 | tmp = dwarf_diename(&indie); | 1381 | tmp = dwarf_diename(&indie); |
1382 | if (!tmp || | 1382 | if (!tmp || |
1383 | dwarf_decl_line(&indie, &baseline) != 0) | 1383 | dwarf_decl_line(&indie, &baseline) != 0) |
1384 | break; | 1384 | break; |
1385 | func = tmp; | 1385 | func = tmp; |
1386 | spdie = indie; | 1386 | spdie = indie; |
1387 | } | 1387 | } |
1388 | } | 1388 | } |
1389 | /* Verify the lineno and baseline are in a same file */ | 1389 | /* Verify the lineno and baseline are in a same file */ |
1390 | tmp = dwarf_decl_file(&spdie); | 1390 | tmp = dwarf_decl_file(&spdie); |
1391 | if (!tmp || strcmp(tmp, fname) != 0) | 1391 | if (!tmp || strcmp(tmp, fname) != 0) |
1392 | lineno = 0; | 1392 | lineno = 0; |
1393 | } | 1393 | } |
1394 | 1394 | ||
1395 | post: | 1395 | post: |
1396 | /* Make a relative line number or an offset */ | 1396 | /* Make a relative line number or an offset */ |
1397 | if (lineno) | 1397 | if (lineno) |
1398 | ppt->line = lineno - baseline; | 1398 | ppt->line = lineno - baseline; |
1399 | else if (basefunc) { | 1399 | else if (basefunc) { |
1400 | ppt->offset = addr - (unsigned long)baseaddr; | 1400 | ppt->offset = addr - (unsigned long)baseaddr; |
1401 | func = basefunc; | 1401 | func = basefunc; |
1402 | } | 1402 | } |
1403 | 1403 | ||
1404 | /* Duplicate strings */ | 1404 | /* Duplicate strings */ |
1405 | if (func) { | 1405 | if (func) { |
1406 | ppt->function = strdup(func); | 1406 | ppt->function = strdup(func); |
1407 | if (ppt->function == NULL) { | 1407 | if (ppt->function == NULL) { |
1408 | ret = -ENOMEM; | 1408 | ret = -ENOMEM; |
1409 | goto end; | 1409 | goto end; |
1410 | } | 1410 | } |
1411 | } | 1411 | } |
1412 | if (fname) { | 1412 | if (fname) { |
1413 | ppt->file = strdup(fname); | 1413 | ppt->file = strdup(fname); |
1414 | if (ppt->file == NULL) { | 1414 | if (ppt->file == NULL) { |
1415 | zfree(&ppt->function); | 1415 | zfree(&ppt->function); |
1416 | ret = -ENOMEM; | 1416 | ret = -ENOMEM; |
1417 | goto end; | 1417 | goto end; |
1418 | } | 1418 | } |
1419 | } | 1419 | } |
1420 | end: | 1420 | end: |
1421 | if (ret == 0 && (fname || func)) | 1421 | if (ret == 0 && (fname || func)) |
1422 | ret = 1; /* Found a point */ | 1422 | ret = 1; /* Found a point */ |
1423 | return ret; | 1423 | return ret; |
1424 | } | 1424 | } |
1425 | 1425 | ||
1426 | /* Add a line and store the src path */ | 1426 | /* Add a line and store the src path */ |
1427 | static int line_range_add_line(const char *src, unsigned int lineno, | 1427 | static int line_range_add_line(const char *src, unsigned int lineno, |
1428 | struct line_range *lr) | 1428 | struct line_range *lr) |
1429 | { | 1429 | { |
1430 | /* Copy source path */ | 1430 | /* Copy source path */ |
1431 | if (!lr->path) { | 1431 | if (!lr->path) { |
1432 | lr->path = strdup(src); | 1432 | lr->path = strdup(src); |
1433 | if (lr->path == NULL) | 1433 | if (lr->path == NULL) |
1434 | return -ENOMEM; | 1434 | return -ENOMEM; |
1435 | } | 1435 | } |
1436 | return intlist__add(lr->line_list, lineno); | 1436 | return intlist__add(lr->line_list, lineno); |
1437 | } | 1437 | } |
1438 | 1438 | ||
1439 | static int line_range_walk_cb(const char *fname, int lineno, | 1439 | static int line_range_walk_cb(const char *fname, int lineno, |
1440 | Dwarf_Addr addr __maybe_unused, | 1440 | Dwarf_Addr addr __maybe_unused, |
1441 | void *data) | 1441 | void *data) |
1442 | { | 1442 | { |
1443 | struct line_finder *lf = data; | 1443 | struct line_finder *lf = data; |
1444 | int err; | 1444 | int err; |
1445 | 1445 | ||
1446 | if ((strtailcmp(fname, lf->fname) != 0) || | 1446 | if ((strtailcmp(fname, lf->fname) != 0) || |
1447 | (lf->lno_s > lineno || lf->lno_e < lineno)) | 1447 | (lf->lno_s > lineno || lf->lno_e < lineno)) |
1448 | return 0; | 1448 | return 0; |
1449 | 1449 | ||
1450 | err = line_range_add_line(fname, lineno, lf->lr); | 1450 | err = line_range_add_line(fname, lineno, lf->lr); |
1451 | if (err < 0 && err != -EEXIST) | 1451 | if (err < 0 && err != -EEXIST) |
1452 | return err; | 1452 | return err; |
1453 | 1453 | ||
1454 | return 0; | 1454 | return 0; |
1455 | } | 1455 | } |
1456 | 1456 | ||
1457 | /* Find line range from its line number */ | 1457 | /* Find line range from its line number */ |
1458 | static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) | 1458 | static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) |
1459 | { | 1459 | { |
1460 | int ret; | 1460 | int ret; |
1461 | 1461 | ||
1462 | ret = die_walk_lines(sp_die ?: &lf->cu_die, line_range_walk_cb, lf); | 1462 | ret = die_walk_lines(sp_die ?: &lf->cu_die, line_range_walk_cb, lf); |
1463 | 1463 | ||
1464 | /* Update status */ | 1464 | /* Update status */ |
1465 | if (ret >= 0) | 1465 | if (ret >= 0) |
1466 | if (!intlist__empty(lf->lr->line_list)) | 1466 | if (!intlist__empty(lf->lr->line_list)) |
1467 | ret = lf->found = 1; | 1467 | ret = lf->found = 1; |
1468 | else | 1468 | else |
1469 | ret = 0; /* Lines are not found */ | 1469 | ret = 0; /* Lines are not found */ |
1470 | else { | 1470 | else { |
1471 | zfree(&lf->lr->path); | 1471 | zfree(&lf->lr->path); |
1472 | } | 1472 | } |
1473 | return ret; | 1473 | return ret; |
1474 | } | 1474 | } |
1475 | 1475 | ||
1476 | static int line_range_inline_cb(Dwarf_Die *in_die, void *data) | 1476 | static int line_range_inline_cb(Dwarf_Die *in_die, void *data) |
1477 | { | 1477 | { |
1478 | int ret = find_line_range_by_line(in_die, data); | 1478 | int ret = find_line_range_by_line(in_die, data); |
1479 | 1479 | ||
1480 | /* | 1480 | /* |
1481 | * We have to check all instances of inlined function, because | 1481 | * We have to check all instances of inlined function, because |
1482 | * some execution paths can be optimized out depends on the | 1482 | * some execution paths can be optimized out depends on the |
1483 | * function argument of instances. However, if an error occurs, | 1483 | * function argument of instances. However, if an error occurs, |
1484 | * it should be handled by the caller. | 1484 | * it should be handled by the caller. |
1485 | */ | 1485 | */ |
1486 | return ret < 0 ? ret : 0; | 1486 | return ret < 0 ? ret : 0; |
1487 | } | 1487 | } |
1488 | 1488 | ||
1489 | /* Search function definition from function name */ | 1489 | /* Search function definition from function name */ |
1490 | static int line_range_search_cb(Dwarf_Die *sp_die, void *data) | 1490 | static int line_range_search_cb(Dwarf_Die *sp_die, void *data) |
1491 | { | 1491 | { |
1492 | struct dwarf_callback_param *param = data; | 1492 | struct dwarf_callback_param *param = data; |
1493 | struct line_finder *lf = param->data; | 1493 | struct line_finder *lf = param->data; |
1494 | struct line_range *lr = lf->lr; | 1494 | struct line_range *lr = lf->lr; |
1495 | 1495 | ||
1496 | /* Check declared file */ | 1496 | /* Check declared file */ |
1497 | if (lr->file && strtailcmp(lr->file, dwarf_decl_file(sp_die))) | 1497 | if (lr->file && strtailcmp(lr->file, dwarf_decl_file(sp_die))) |
1498 | return DWARF_CB_OK; | 1498 | return DWARF_CB_OK; |
1499 | 1499 | ||
1500 | if (die_is_func_def(sp_die) && | 1500 | if (die_is_func_def(sp_die) && |
1501 | die_compare_name(sp_die, lr->function)) { | 1501 | die_compare_name(sp_die, lr->function)) { |
1502 | lf->fname = dwarf_decl_file(sp_die); | 1502 | lf->fname = dwarf_decl_file(sp_die); |
1503 | dwarf_decl_line(sp_die, &lr->offset); | 1503 | dwarf_decl_line(sp_die, &lr->offset); |
1504 | pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); | 1504 | pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); |
1505 | lf->lno_s = lr->offset + lr->start; | 1505 | lf->lno_s = lr->offset + lr->start; |
1506 | if (lf->lno_s < 0) /* Overflow */ | 1506 | if (lf->lno_s < 0) /* Overflow */ |
1507 | lf->lno_s = INT_MAX; | 1507 | lf->lno_s = INT_MAX; |
1508 | lf->lno_e = lr->offset + lr->end; | 1508 | lf->lno_e = lr->offset + lr->end; |
1509 | if (lf->lno_e < 0) /* Overflow */ | 1509 | if (lf->lno_e < 0) /* Overflow */ |
1510 | lf->lno_e = INT_MAX; | 1510 | lf->lno_e = INT_MAX; |
1511 | pr_debug("New line range: %d to %d\n", lf->lno_s, lf->lno_e); | 1511 | pr_debug("New line range: %d to %d\n", lf->lno_s, lf->lno_e); |
1512 | lr->start = lf->lno_s; | 1512 | lr->start = lf->lno_s; |
1513 | lr->end = lf->lno_e; | 1513 | lr->end = lf->lno_e; |
1514 | if (dwarf_func_inline(sp_die)) | 1514 | if (dwarf_func_inline(sp_die)) |
1515 | param->retval = die_walk_instances(sp_die, | 1515 | param->retval = die_walk_instances(sp_die, |
1516 | line_range_inline_cb, lf); | 1516 | line_range_inline_cb, lf); |
1517 | else | 1517 | else |
1518 | param->retval = find_line_range_by_line(sp_die, lf); | 1518 | param->retval = find_line_range_by_line(sp_die, lf); |
1519 | return DWARF_CB_ABORT; | 1519 | return DWARF_CB_ABORT; |
1520 | } | 1520 | } |
1521 | return DWARF_CB_OK; | 1521 | return DWARF_CB_OK; |
1522 | } | 1522 | } |
1523 | 1523 | ||
1524 | static int find_line_range_by_func(struct line_finder *lf) | 1524 | static int find_line_range_by_func(struct line_finder *lf) |
1525 | { | 1525 | { |
1526 | struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0}; | 1526 | struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0}; |
1527 | dwarf_getfuncs(&lf->cu_die, line_range_search_cb, ¶m, 0); | 1527 | dwarf_getfuncs(&lf->cu_die, line_range_search_cb, ¶m, 0); |
1528 | return param.retval; | 1528 | return param.retval; |
1529 | } | 1529 | } |
1530 | 1530 | ||
1531 | int debuginfo__find_line_range(struct debuginfo *dbg, struct line_range *lr) | 1531 | int debuginfo__find_line_range(struct debuginfo *dbg, struct line_range *lr) |
1532 | { | 1532 | { |
1533 | struct line_finder lf = {.lr = lr, .found = 0}; | 1533 | struct line_finder lf = {.lr = lr, .found = 0}; |
1534 | int ret = 0; | 1534 | int ret = 0; |
1535 | Dwarf_Off off = 0, noff; | 1535 | Dwarf_Off off = 0, noff; |
1536 | size_t cuhl; | 1536 | size_t cuhl; |
1537 | Dwarf_Die *diep; | 1537 | Dwarf_Die *diep; |
1538 | const char *comp_dir; | 1538 | const char *comp_dir; |
1539 | 1539 | ||
1540 | /* Fastpath: lookup by function name from .debug_pubnames section */ | 1540 | /* Fastpath: lookup by function name from .debug_pubnames section */ |
1541 | if (lr->function) { | 1541 | if (lr->function) { |
1542 | struct pubname_callback_param pubname_param = { | 1542 | struct pubname_callback_param pubname_param = { |
1543 | .function = lr->function, .file = lr->file, | 1543 | .function = lr->function, .file = lr->file, |
1544 | .cu_die = &lf.cu_die, .sp_die = &lf.sp_die, .found = 0}; | 1544 | .cu_die = &lf.cu_die, .sp_die = &lf.sp_die, .found = 0}; |
1545 | struct dwarf_callback_param line_range_param = { | 1545 | struct dwarf_callback_param line_range_param = { |
1546 | .data = (void *)&lf, .retval = 0}; | 1546 | .data = (void *)&lf, .retval = 0}; |
1547 | 1547 | ||
1548 | dwarf_getpubnames(dbg->dbg, pubname_search_cb, | 1548 | dwarf_getpubnames(dbg->dbg, pubname_search_cb, |
1549 | &pubname_param, 0); | 1549 | &pubname_param, 0); |
1550 | if (pubname_param.found) { | 1550 | if (pubname_param.found) { |
1551 | line_range_search_cb(&lf.sp_die, &line_range_param); | 1551 | line_range_search_cb(&lf.sp_die, &line_range_param); |
1552 | if (lf.found) | 1552 | if (lf.found) |
1553 | goto found; | 1553 | goto found; |
1554 | } | 1554 | } |
1555 | } | 1555 | } |
1556 | 1556 | ||
1557 | /* Loop on CUs (Compilation Unit) */ | 1557 | /* Loop on CUs (Compilation Unit) */ |
1558 | while (!lf.found && ret >= 0) { | 1558 | while (!lf.found && ret >= 0) { |
1559 | if (dwarf_nextcu(dbg->dbg, off, &noff, &cuhl, | 1559 | if (dwarf_nextcu(dbg->dbg, off, &noff, &cuhl, |
1560 | NULL, NULL, NULL) != 0) | 1560 | NULL, NULL, NULL) != 0) |
1561 | break; | 1561 | break; |
1562 | 1562 | ||
1563 | /* Get the DIE(Debugging Information Entry) of this CU */ | 1563 | /* Get the DIE(Debugging Information Entry) of this CU */ |
1564 | diep = dwarf_offdie(dbg->dbg, off + cuhl, &lf.cu_die); | 1564 | diep = dwarf_offdie(dbg->dbg, off + cuhl, &lf.cu_die); |
1565 | if (!diep) | 1565 | if (!diep) |
1566 | continue; | 1566 | continue; |
1567 | 1567 | ||
1568 | /* Check if target file is included. */ | 1568 | /* Check if target file is included. */ |
1569 | if (lr->file) | 1569 | if (lr->file) |
1570 | lf.fname = cu_find_realpath(&lf.cu_die, lr->file); | 1570 | lf.fname = cu_find_realpath(&lf.cu_die, lr->file); |
1571 | else | 1571 | else |
1572 | lf.fname = 0; | 1572 | lf.fname = 0; |
1573 | 1573 | ||
1574 | if (!lr->file || lf.fname) { | 1574 | if (!lr->file || lf.fname) { |
1575 | if (lr->function) | 1575 | if (lr->function) |
1576 | ret = find_line_range_by_func(&lf); | 1576 | ret = find_line_range_by_func(&lf); |
1577 | else { | 1577 | else { |
1578 | lf.lno_s = lr->start; | 1578 | lf.lno_s = lr->start; |
1579 | lf.lno_e = lr->end; | 1579 | lf.lno_e = lr->end; |
1580 | ret = find_line_range_by_line(NULL, &lf); | 1580 | ret = find_line_range_by_line(NULL, &lf); |
1581 | } | 1581 | } |
1582 | } | 1582 | } |
1583 | off = noff; | 1583 | off = noff; |
1584 | } | 1584 | } |
1585 | 1585 | ||
1586 | found: | 1586 | found: |
1587 | /* Store comp_dir */ | 1587 | /* Store comp_dir */ |
1588 | if (lf.found) { | 1588 | if (lf.found) { |
1589 | comp_dir = cu_get_comp_dir(&lf.cu_die); | 1589 | comp_dir = cu_get_comp_dir(&lf.cu_die); |
1590 | if (comp_dir) { | 1590 | if (comp_dir) { |
1591 | lr->comp_dir = strdup(comp_dir); | 1591 | lr->comp_dir = strdup(comp_dir); |
1592 | if (!lr->comp_dir) | 1592 | if (!lr->comp_dir) |
1593 | ret = -ENOMEM; | 1593 | ret = -ENOMEM; |
1594 | } | 1594 | } |
1595 | } | 1595 | } |
1596 | 1596 | ||
1597 | pr_debug("path: %s\n", lr->path); | 1597 | pr_debug("path: %s\n", lr->path); |
1598 | return (ret < 0) ? ret : lf.found; | 1598 | return (ret < 0) ? ret : lf.found; |
1599 | } | 1599 | } |
1600 | 1600 | ||
1601 | 1601 |