]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
fd20e811 | 2 | #include <inttypes.h> |
be8ecc57 | 3 | #include <signal.h> |
f048d548 NK |
4 | #include <stdio.h> |
5 | #include <stdlib.h> | |
6 | #include <string.h> | |
be8ecc57 | 7 | #include <sys/types.h> |
f048d548 | 8 | |
c3f8644c | 9 | #include <linux/compiler.h> |
f048d548 | 10 | #include <linux/kernel.h> |
13c230ab | 11 | #include <linux/string.h> |
7f7c536f | 12 | #include <linux/zalloc.h> |
f048d548 | 13 | |
b3801e79 IR |
14 | #include <api/io.h> |
15 | ||
86c98cab | 16 | #include "util/dso.h" |
f048d548 | 17 | #include "util/debug.h" |
a64489c5 | 18 | #include "util/callchain.h" |
b10c78c5 | 19 | #include "util/symbol_conf.h" |
c3f8644c SG |
20 | #ifdef HAVE_LIBLLVM_SUPPORT |
21 | #include "util/llvm-c-helpers.h" | |
22 | #endif | |
632a5cab | 23 | #include "srcline.h" |
7285cf33 | 24 | #include "string2.h" |
85c116a6 | 25 | #include "symbol.h" |
be8ecc57 | 26 | #include "subcmd/run-command.h" |
85c116a6 | 27 | |
701677b9 IR |
28 | /* If addr2line doesn't return data for 1 second then timeout. */ |
29 | int addr2line_timeout_ms = 1 * 1000; | |
a9710ba0 AK |
30 | bool srcline_full_filename; |
31 | ||
922db21d ACM |
32 | char *srcline__unknown = (char *)"??:0"; |
33 | ||
ee756ef7 | 34 | static const char *srcline_dso_name(struct dso *dso) |
5580338d JY |
35 | { |
36 | const char *dso_name; | |
37 | ||
ee756ef7 IR |
38 | if (dso__symsrc_filename(dso)) |
39 | dso_name = dso__symsrc_filename(dso); | |
5580338d | 40 | else |
ee756ef7 | 41 | dso_name = dso__long_name(dso); |
5580338d JY |
42 | |
43 | if (dso_name[0] == '[') | |
44 | return NULL; | |
45 | ||
b9241f15 | 46 | if (is_perf_pid_map_name(dso_name)) |
5580338d JY |
47 | return NULL; |
48 | ||
49 | return dso_name; | |
50 | } | |
51 | ||
2be8832f MW |
52 | static int inline_list__append(struct symbol *symbol, char *srcline, |
53 | struct inline_node *node) | |
a64489c5 JY |
54 | { |
55 | struct inline_list *ilist; | |
a64489c5 JY |
56 | |
57 | ilist = zalloc(sizeof(*ilist)); | |
58 | if (ilist == NULL) | |
59 | return -1; | |
60 | ||
fea0cf84 | 61 | ilist->symbol = symbol; |
2be8832f | 62 | ilist->srcline = srcline; |
a64489c5 | 63 | |
28071f51 MW |
64 | if (callchain_param.order == ORDER_CALLEE) |
65 | list_add_tail(&ilist->list, &node->val); | |
66 | else | |
67 | list_add(&ilist->list, &node->val); | |
a64489c5 JY |
68 | |
69 | return 0; | |
70 | } | |
71 | ||
2be8832f MW |
72 | /* basename version that takes a const input string */ |
73 | static const char *gnu_basename(const char *path) | |
74 | { | |
75 | const char *base = strrchr(path, '/'); | |
76 | ||
77 | return base ? base + 1 : path; | |
78 | } | |
79 | ||
80 | static char *srcline_from_fileline(const char *file, unsigned int line) | |
81 | { | |
82 | char *srcline; | |
83 | ||
84 | if (!file) | |
85 | return NULL; | |
86 | ||
87 | if (!srcline_full_filename) | |
88 | file = gnu_basename(file); | |
89 | ||
90 | if (asprintf(&srcline, "%s:%u", file, line) < 0) | |
91 | return NULL; | |
92 | ||
93 | return srcline; | |
94 | } | |
95 | ||
7285cf33 NK |
96 | static struct symbol *new_inline_sym(struct dso *dso, |
97 | struct symbol *base_sym, | |
98 | const char *funcname) | |
99 | { | |
100 | struct symbol *inline_sym; | |
101 | char *demangled = NULL; | |
102 | ||
d4046e8e MW |
103 | if (!funcname) |
104 | funcname = "??"; | |
105 | ||
7285cf33 NK |
106 | if (dso) { |
107 | demangled = dso__demangle_sym(dso, 0, funcname); | |
108 | if (demangled) | |
109 | funcname = demangled; | |
110 | } | |
111 | ||
112 | if (base_sym && strcmp(funcname, base_sym->name) == 0) { | |
113 | /* reuse the real, existing symbol */ | |
114 | inline_sym = base_sym; | |
115 | /* ensure that we don't alias an inlined symbol, which could | |
116 | * lead to double frees in inline_node__delete | |
117 | */ | |
118 | assert(!base_sym->inlined); | |
119 | } else { | |
120 | /* create a fake symbol for the inline frame */ | |
121 | inline_sym = symbol__new(base_sym ? base_sym->start : 0, | |
7346195e | 122 | base_sym ? (base_sym->end - base_sym->start) : 0, |
7285cf33 | 123 | base_sym ? base_sym->binding : 0, |
af30bffa | 124 | base_sym ? base_sym->type : 0, |
7285cf33 NK |
125 | funcname); |
126 | if (inline_sym) | |
127 | inline_sym->inlined = 1; | |
128 | } | |
129 | ||
130 | free(demangled); | |
131 | ||
132 | return inline_sym; | |
133 | } | |
134 | ||
be8ecc57 TGJ |
135 | #define MAX_INLINE_NEST 1024 |
136 | ||
c3f8644c SG |
137 | #ifdef HAVE_LIBLLVM_SUPPORT |
138 | ||
139 | static void free_llvm_inline_frames(struct llvm_a2l_frame *inline_frames, | |
140 | int num_frames) | |
141 | { | |
142 | if (inline_frames != NULL) { | |
143 | for (int i = 0; i < num_frames; ++i) { | |
144 | zfree(&inline_frames[i].filename); | |
145 | zfree(&inline_frames[i].funcname); | |
146 | } | |
147 | zfree(&inline_frames); | |
148 | } | |
149 | } | |
150 | ||
151 | static int addr2line(const char *dso_name, u64 addr, | |
152 | char **file, unsigned int *line, struct dso *dso, | |
153 | bool unwind_inlines, struct inline_node *node, | |
154 | struct symbol *sym) | |
155 | { | |
156 | struct llvm_a2l_frame *inline_frames = NULL; | |
157 | int num_frames = llvm_addr2line(dso_name, addr, file, line, | |
158 | node && unwind_inlines, &inline_frames); | |
159 | ||
160 | if (num_frames == 0 || !inline_frames) { | |
161 | /* Error, or we didn't want inlines. */ | |
162 | return num_frames; | |
163 | } | |
164 | ||
165 | for (int i = 0; i < num_frames; ++i) { | |
166 | struct symbol *inline_sym = | |
167 | new_inline_sym(dso, sym, inline_frames[i].funcname); | |
168 | char *srcline = NULL; | |
169 | ||
170 | if (inline_frames[i].filename) { | |
171 | srcline = | |
172 | srcline_from_fileline(inline_frames[i].filename, | |
173 | inline_frames[i].line); | |
174 | } | |
175 | if (inline_list__append(inline_sym, srcline, node) != 0) { | |
176 | free_llvm_inline_frames(inline_frames, num_frames); | |
177 | return 0; | |
178 | } | |
179 | } | |
180 | free_llvm_inline_frames(inline_frames, num_frames); | |
181 | ||
182 | return num_frames; | |
183 | } | |
184 | ||
185 | void dso__free_a2l(struct dso *dso __maybe_unused) | |
186 | { | |
187 | /* Nothing to free. */ | |
188 | } | |
189 | ||
190 | #elif defined(HAVE_LIBBFD_SUPPORT) | |
2f48fcd8 RV |
191 | |
192 | /* | |
193 | * Implement addr2line using libbfd. | |
194 | */ | |
195 | #define PACKAGE "perf" | |
196 | #include <bfd.h> | |
197 | ||
198 | struct a2l_data { | |
199 | const char *input; | |
ac931f87 | 200 | u64 addr; |
2f48fcd8 RV |
201 | |
202 | bool found; | |
203 | const char *filename; | |
204 | const char *funcname; | |
205 | unsigned line; | |
206 | ||
207 | bfd *abfd; | |
208 | asymbol **syms; | |
209 | }; | |
210 | ||
211 | static int bfd_error(const char *string) | |
212 | { | |
213 | const char *errmsg; | |
214 | ||
215 | errmsg = bfd_errmsg(bfd_get_error()); | |
216 | fflush(stdout); | |
217 | ||
218 | if (string) | |
219 | pr_debug("%s: %s\n", string, errmsg); | |
220 | else | |
221 | pr_debug("%s\n", errmsg); | |
222 | ||
223 | return -1; | |
224 | } | |
225 | ||
226 | static int slurp_symtab(bfd *abfd, struct a2l_data *a2l) | |
227 | { | |
228 | long storage; | |
229 | long symcount; | |
230 | asymbol **syms; | |
231 | bfd_boolean dynamic = FALSE; | |
232 | ||
233 | if ((bfd_get_file_flags(abfd) & HAS_SYMS) == 0) | |
234 | return bfd_error(bfd_get_filename(abfd)); | |
235 | ||
236 | storage = bfd_get_symtab_upper_bound(abfd); | |
237 | if (storage == 0L) { | |
238 | storage = bfd_get_dynamic_symtab_upper_bound(abfd); | |
239 | dynamic = TRUE; | |
240 | } | |
241 | if (storage < 0L) | |
242 | return bfd_error(bfd_get_filename(abfd)); | |
243 | ||
244 | syms = malloc(storage); | |
245 | if (dynamic) | |
246 | symcount = bfd_canonicalize_dynamic_symtab(abfd, syms); | |
247 | else | |
248 | symcount = bfd_canonicalize_symtab(abfd, syms); | |
249 | ||
250 | if (symcount < 0) { | |
251 | free(syms); | |
252 | return bfd_error(bfd_get_filename(abfd)); | |
253 | } | |
254 | ||
255 | a2l->syms = syms; | |
256 | return 0; | |
257 | } | |
258 | ||
259 | static void find_address_in_section(bfd *abfd, asection *section, void *data) | |
260 | { | |
261 | bfd_vma pc, vma; | |
262 | bfd_size_type size; | |
263 | struct a2l_data *a2l = data; | |
0ada120c | 264 | flagword flags; |
2f48fcd8 RV |
265 | |
266 | if (a2l->found) | |
267 | return; | |
268 | ||
0ada120c CD |
269 | #ifdef bfd_get_section_flags |
270 | flags = bfd_get_section_flags(abfd, section); | |
271 | #else | |
272 | flags = bfd_section_flags(section); | |
273 | #endif | |
274 | if ((flags & SEC_ALLOC) == 0) | |
2f48fcd8 RV |
275 | return; |
276 | ||
277 | pc = a2l->addr; | |
0ada120c | 278 | #ifdef bfd_get_section_vma |
2f48fcd8 | 279 | vma = bfd_get_section_vma(abfd, section); |
0ada120c CD |
280 | #else |
281 | vma = bfd_section_vma(section); | |
282 | #endif | |
283 | #ifdef bfd_get_section_size | |
2f48fcd8 | 284 | size = bfd_get_section_size(section); |
0ada120c CD |
285 | #else |
286 | size = bfd_section_size(section); | |
287 | #endif | |
2f48fcd8 RV |
288 | |
289 | if (pc < vma || pc >= vma + size) | |
290 | return; | |
291 | ||
292 | a2l->found = bfd_find_nearest_line(abfd, section, a2l->syms, pc - vma, | |
293 | &a2l->filename, &a2l->funcname, | |
294 | &a2l->line); | |
d964b1cd MW |
295 | |
296 | if (a2l->filename && !strlen(a2l->filename)) | |
297 | a2l->filename = NULL; | |
2f48fcd8 RV |
298 | } |
299 | ||
300 | static struct a2l_data *addr2line_init(const char *path) | |
301 | { | |
302 | bfd *abfd; | |
303 | struct a2l_data *a2l = NULL; | |
304 | ||
305 | abfd = bfd_openr(path, NULL); | |
306 | if (abfd == NULL) | |
307 | return NULL; | |
308 | ||
309 | if (!bfd_check_format(abfd, bfd_object)) | |
310 | goto out; | |
311 | ||
312 | a2l = zalloc(sizeof(*a2l)); | |
313 | if (a2l == NULL) | |
314 | goto out; | |
315 | ||
316 | a2l->abfd = abfd; | |
317 | a2l->input = strdup(path); | |
318 | if (a2l->input == NULL) | |
319 | goto out; | |
320 | ||
321 | if (slurp_symtab(abfd, a2l)) | |
322 | goto out; | |
323 | ||
324 | return a2l; | |
325 | ||
326 | out: | |
327 | if (a2l) { | |
7d16c634 | 328 | zfree((char **)&a2l->input); |
2f48fcd8 RV |
329 | free(a2l); |
330 | } | |
331 | bfd_close(abfd); | |
332 | return NULL; | |
333 | } | |
334 | ||
335 | static void addr2line_cleanup(struct a2l_data *a2l) | |
336 | { | |
337 | if (a2l->abfd) | |
338 | bfd_close(a2l->abfd); | |
7d16c634 | 339 | zfree((char **)&a2l->input); |
74cf249d | 340 | zfree(&a2l->syms); |
2f48fcd8 RV |
341 | free(a2l); |
342 | } | |
343 | ||
4d53b9d5 | 344 | static int inline_list__append_dso_a2l(struct dso *dso, |
fea0cf84 MW |
345 | struct inline_node *node, |
346 | struct symbol *sym) | |
4d53b9d5 | 347 | { |
1553419c | 348 | struct a2l_data *a2l = dso__a2l(dso); |
fea0cf84 | 349 | struct symbol *inline_sym = new_inline_sym(dso, sym, a2l->funcname); |
2be8832f | 350 | char *srcline = NULL; |
4d53b9d5 | 351 | |
2be8832f MW |
352 | if (a2l->filename) |
353 | srcline = srcline_from_fileline(a2l->filename, a2l->line); | |
354 | ||
355 | return inline_list__append(inline_sym, srcline, node); | |
4d53b9d5 MW |
356 | } |
357 | ||
ac931f87 | 358 | static int addr2line(const char *dso_name, u64 addr, |
2f84b42b | 359 | char **file, unsigned int *line, struct dso *dso, |
fea0cf84 MW |
360 | bool unwind_inlines, struct inline_node *node, |
361 | struct symbol *sym) | |
2f48fcd8 RV |
362 | { |
363 | int ret = 0; | |
1553419c | 364 | struct a2l_data *a2l = dso__a2l(dso); |
454ff00f AH |
365 | |
366 | if (!a2l) { | |
1553419c IR |
367 | a2l = addr2line_init(dso_name); |
368 | dso__set_a2l(dso, a2l); | |
454ff00f | 369 | } |
2f48fcd8 | 370 | |
2f48fcd8 | 371 | if (a2l == NULL) { |
b10c78c5 JY |
372 | if (!symbol_conf.disable_add2line_warn) |
373 | pr_warning("addr2line_init failed for %s\n", dso_name); | |
2f48fcd8 RV |
374 | return 0; |
375 | } | |
376 | ||
377 | a2l->addr = addr; | |
454ff00f AH |
378 | a2l->found = false; |
379 | ||
2f48fcd8 RV |
380 | bfd_map_over_sections(a2l->abfd, find_address_in_section, a2l); |
381 | ||
b21cc978 MW |
382 | if (!a2l->found) |
383 | return 0; | |
384 | ||
385 | if (unwind_inlines) { | |
2f84b42b AK |
386 | int cnt = 0; |
387 | ||
fea0cf84 | 388 | if (node && inline_list__append_dso_a2l(dso, node, sym)) |
4d53b9d5 MW |
389 | return 0; |
390 | ||
2f84b42b AK |
391 | while (bfd_find_inliner_info(a2l->abfd, &a2l->filename, |
392 | &a2l->funcname, &a2l->line) && | |
a64489c5 JY |
393 | cnt++ < MAX_INLINE_NEST) { |
394 | ||
d964b1cd MW |
395 | if (a2l->filename && !strlen(a2l->filename)) |
396 | a2l->filename = NULL; | |
397 | ||
a64489c5 | 398 | if (node != NULL) { |
fea0cf84 | 399 | if (inline_list__append_dso_a2l(dso, node, sym)) |
a64489c5 | 400 | return 0; |
b21cc978 MW |
401 | // found at least one inline frame |
402 | ret = 1; | |
a64489c5 JY |
403 | } |
404 | } | |
2f84b42b AK |
405 | } |
406 | ||
b21cc978 MW |
407 | if (file) { |
408 | *file = a2l->filename ? strdup(a2l->filename) : NULL; | |
409 | ret = *file ? 1 : 0; | |
2f48fcd8 RV |
410 | } |
411 | ||
b21cc978 MW |
412 | if (line) |
413 | *line = a2l->line; | |
414 | ||
2f48fcd8 RV |
415 | return ret; |
416 | } | |
417 | ||
454ff00f AH |
418 | void dso__free_a2l(struct dso *dso) |
419 | { | |
1553419c | 420 | struct a2l_data *a2l = dso__a2l(dso); |
454ff00f AH |
421 | |
422 | if (!a2l) | |
423 | return; | |
424 | ||
425 | addr2line_cleanup(a2l); | |
426 | ||
1553419c | 427 | dso__set_a2l(dso, NULL); |
454ff00f AH |
428 | } |
429 | ||
2f48fcd8 RV |
430 | #else /* HAVE_LIBBFD_SUPPORT */ |
431 | ||
5580338d JY |
432 | static int filename_split(char *filename, unsigned int *line_nr) |
433 | { | |
434 | char *sep; | |
435 | ||
436 | sep = strchr(filename, '\n'); | |
437 | if (sep) | |
438 | *sep = '\0'; | |
439 | ||
440 | if (!strcmp(filename, "??:0")) | |
441 | return 0; | |
442 | ||
443 | sep = strchr(filename, ':'); | |
444 | if (sep) { | |
445 | *sep++ = '\0'; | |
446 | *line_nr = strtoul(sep, NULL, 0); | |
447 | return 1; | |
448 | } | |
e90208e9 | 449 | pr_debug("addr2line missing ':' in filename split\n"); |
5580338d JY |
450 | return 0; |
451 | } | |
452 | ||
b3801e79 | 453 | static void addr2line_subprocess_cleanup(struct child_process *a2l) |
f048d548 | 454 | { |
b3801e79 IR |
455 | if (a2l->pid != -1) { |
456 | kill(a2l->pid, SIGKILL); | |
457 | finish_command(a2l); /* ignore result, we don't care */ | |
458 | a2l->pid = -1; | |
c7ba9d18 IR |
459 | close(a2l->in); |
460 | close(a2l->out); | |
be8ecc57 TGJ |
461 | } |
462 | ||
463 | free(a2l); | |
464 | } | |
465 | ||
b3801e79 | 466 | static struct child_process *addr2line_subprocess_init(const char *addr2line_path, |
57594454 | 467 | const char *binary_path) |
be8ecc57 | 468 | { |
57594454 IR |
469 | const char *argv[] = { |
470 | addr2line_path ?: "addr2line", | |
471 | "-e", binary_path, | |
8dc26b6f | 472 | "-a", "-i", "-f", NULL |
57594454 | 473 | }; |
b3801e79 | 474 | struct child_process *a2l = zalloc(sizeof(*a2l)); |
be8ecc57 TGJ |
475 | int start_command_status = 0; |
476 | ||
b3801e79 IR |
477 | if (a2l == NULL) { |
478 | pr_err("Failed to allocate memory for addr2line"); | |
479 | return NULL; | |
480 | } | |
be8ecc57 | 481 | |
b3801e79 IR |
482 | a2l->pid = -1; |
483 | a2l->in = -1; | |
484 | a2l->out = -1; | |
485 | a2l->no_stderr = 1; | |
be8ecc57 | 486 | |
b3801e79 IR |
487 | a2l->argv = argv; |
488 | start_command_status = start_command(a2l); | |
489 | a2l->argv = NULL; /* it's not used after start_command; avoid dangling pointers */ | |
be8ecc57 TGJ |
490 | |
491 | if (start_command_status != 0) { | |
57594454 IR |
492 | pr_warning("could not start addr2line (%s) for %s: start_command return code %d\n", |
493 | addr2line_path, binary_path, start_command_status); | |
b3801e79 IR |
494 | addr2line_subprocess_cleanup(a2l); |
495 | return NULL; | |
f048d548 NK |
496 | } |
497 | ||
be8ecc57 | 498 | return a2l; |
f048d548 | 499 | } |
454ff00f | 500 | |
2c4b9280 IR |
501 | enum a2l_style { |
502 | BROKEN, | |
503 | GNU_BINUTILS, | |
504 | LLVM, | |
505 | }; | |
506 | ||
c7a0023a | 507 | static enum a2l_style addr2line_configure(struct child_process *a2l, const char *dso_name) |
2c4b9280 IR |
508 | { |
509 | static bool cached; | |
510 | static enum a2l_style style; | |
511 | ||
512 | if (!cached) { | |
513 | char buf[128]; | |
514 | struct io io; | |
515 | int ch; | |
c7a0023a | 516 | int lines; |
2c4b9280 IR |
517 | |
518 | if (write(a2l->in, ",\n", 2) != 2) | |
519 | return BROKEN; | |
520 | ||
521 | io__init(&io, a2l->out, buf, sizeof(buf)); | |
522 | ch = io__get_char(&io); | |
523 | if (ch == ',') { | |
524 | style = LLVM; | |
525 | cached = true; | |
c7a0023a | 526 | lines = 1; |
e90208e9 | 527 | pr_debug("Detected LLVM addr2line style\n"); |
8dc26b6f | 528 | } else if (ch == '0') { |
2c4b9280 IR |
529 | style = GNU_BINUTILS; |
530 | cached = true; | |
8dc26b6f | 531 | lines = 3; |
e90208e9 | 532 | pr_debug("Detected binutils addr2line style\n"); |
2c4b9280 | 533 | } else { |
c7a0023a IR |
534 | if (!symbol_conf.disable_add2line_warn) { |
535 | char *output = NULL; | |
536 | size_t output_len; | |
537 | ||
538 | io__getline(&io, &output, &output_len); | |
539 | pr_warning("%s %s: addr2line configuration failed\n", | |
540 | __func__, dso_name); | |
541 | pr_warning("\t%c%s", ch, output); | |
542 | } | |
e90208e9 | 543 | pr_debug("Unknown/broken addr2line style\n"); |
c7a0023a | 544 | return BROKEN; |
2c4b9280 | 545 | } |
c7a0023a | 546 | while (lines) { |
2c4b9280 | 547 | ch = io__get_char(&io); |
c7a0023a IR |
548 | if (ch <= 0) |
549 | break; | |
550 | if (ch == '\n') | |
551 | lines--; | |
2c4b9280 | 552 | } |
75a616c6 IR |
553 | /* Ignore SIGPIPE in the event addr2line exits. */ |
554 | signal(SIGPIPE, SIG_IGN); | |
2c4b9280 IR |
555 | } |
556 | return style; | |
557 | } | |
558 | ||
b3801e79 | 559 | static int read_addr2line_record(struct io *io, |
2c4b9280 | 560 | enum a2l_style style, |
e90208e9 IR |
561 | const char *dso_name, |
562 | u64 addr, | |
563 | bool first, | |
be8ecc57 TGJ |
564 | char **function, |
565 | char **filename, | |
566 | unsigned int *line_nr) | |
454ff00f | 567 | { |
be8ecc57 TGJ |
568 | /* |
569 | * Returns: | |
570 | * -1 ==> error | |
571 | * 0 ==> sentinel (or other ill-formed) record read | |
572 | * 1 ==> a genuine record read | |
573 | */ | |
574 | char *line = NULL; | |
575 | size_t line_len = 0; | |
576 | unsigned int dummy_line_nr = 0; | |
577 | int ret = -1; | |
578 | ||
579 | if (function != NULL) | |
580 | zfree(function); | |
581 | ||
582 | if (filename != NULL) | |
583 | zfree(filename); | |
584 | ||
585 | if (line_nr != NULL) | |
586 | *line_nr = 0; | |
587 | ||
8dc26b6f | 588 | /* |
e90208e9 IR |
589 | * Read the first line. Without an error this will be: |
590 | * - for the first line an address like 0x1234, | |
591 | * - the binutils sentinel 0x0000000000000000, | |
592 | * - the llvm-addr2line the sentinel ',' character, | |
593 | * - the function name line for an inlined function. | |
8dc26b6f | 594 | */ |
b3801e79 | 595 | if (io__getline(io, &line, &line_len) < 0 || !line_len) |
be8ecc57 | 596 | goto error; |
2c4b9280 | 597 | |
e90208e9 IR |
598 | pr_debug("%s %s: addr2line read address for sentinel: %s", __func__, dso_name, line); |
599 | if (style == LLVM && line_len == 2 && line[0] == ',') { | |
600 | /* Found the llvm-addr2line sentinel character. */ | |
601 | zfree(&line); | |
602 | return 0; | |
603 | } else if (style == GNU_BINUTILS && (!first || addr != 0)) { | |
8dc26b6f | 604 | int zero_count = 0, non_zero_count = 0; |
e90208e9 IR |
605 | /* |
606 | * Check for binutils sentinel ignoring it for the case the | |
607 | * requested address is 0. | |
608 | */ | |
8dc26b6f | 609 | |
e90208e9 IR |
610 | /* A given address should always start 0x. */ |
611 | if (line_len >= 2 || line[0] != '0' || line[1] != 'x') { | |
612 | for (size_t i = 2; i < line_len; i++) { | |
613 | if (line[i] == '0') | |
614 | zero_count++; | |
615 | else if (line[i] != '\n') | |
616 | non_zero_count++; | |
617 | } | |
618 | if (!non_zero_count) { | |
619 | int ch; | |
620 | ||
621 | if (first && !zero_count) { | |
622 | /* Line was erroneous just '0x'. */ | |
623 | goto error; | |
624 | } | |
625 | /* | |
626 | * Line was 0x0..0, the sentinel for binutils. Remove | |
627 | * the function and filename lines. | |
628 | */ | |
629 | zfree(&line); | |
630 | do { | |
631 | ch = io__get_char(io); | |
632 | } while (ch > 0 && ch != '\n'); | |
633 | do { | |
634 | ch = io__get_char(io); | |
635 | } while (ch > 0 && ch != '\n'); | |
636 | return 0; | |
8dc26b6f | 637 | } |
8dc26b6f | 638 | } |
2c4b9280 | 639 | } |
e90208e9 IR |
640 | /* Read the second function name line (if inline data then this is the first line). */ |
641 | if (first && (io__getline(io, &line, &line_len) < 0 || !line_len)) | |
8dc26b6f IR |
642 | goto error; |
643 | ||
e90208e9 | 644 | pr_debug("%s %s: addr2line read line: %s", __func__, dso_name, line); |
be8ecc57 TGJ |
645 | if (function != NULL) |
646 | *function = strdup(strim(line)); | |
647 | ||
648 | zfree(&line); | |
649 | line_len = 0; | |
650 | ||
8dc26b6f | 651 | /* Read the third filename and line number line. */ |
b3801e79 | 652 | if (io__getline(io, &line, &line_len) < 0 || !line_len) |
be8ecc57 TGJ |
653 | goto error; |
654 | ||
e90208e9 | 655 | pr_debug("%s %s: addr2line filename:number : %s", __func__, dso_name, line); |
2c4b9280 IR |
656 | if (filename_split(line, line_nr == NULL ? &dummy_line_nr : line_nr) == 0 && |
657 | style == GNU_BINUTILS) { | |
be8ecc57 TGJ |
658 | ret = 0; |
659 | goto error; | |
660 | } | |
661 | ||
662 | if (filename != NULL) | |
663 | *filename = strdup(line); | |
664 | ||
665 | zfree(&line); | |
666 | line_len = 0; | |
667 | ||
668 | return 1; | |
669 | ||
670 | error: | |
671 | free(line); | |
672 | if (function != NULL) | |
673 | zfree(function); | |
674 | if (filename != NULL) | |
675 | zfree(filename); | |
676 | return ret; | |
454ff00f AH |
677 | } |
678 | ||
be8ecc57 TGJ |
679 | static int inline_list__append_record(struct dso *dso, |
680 | struct inline_node *node, | |
681 | struct symbol *sym, | |
682 | const char *function, | |
683 | const char *filename, | |
684 | unsigned int line_nr) | |
a64489c5 | 685 | { |
be8ecc57 | 686 | struct symbol *inline_sym = new_inline_sym(dso, sym, function); |
a64489c5 | 687 | |
be8ecc57 TGJ |
688 | return inline_list__append(inline_sym, srcline_from_fileline(filename, line_nr), node); |
689 | } | |
a64489c5 | 690 | |
be8ecc57 TGJ |
691 | static int addr2line(const char *dso_name, u64 addr, |
692 | char **file, unsigned int *line_nr, | |
693 | struct dso *dso, | |
694 | bool unwind_inlines, | |
695 | struct inline_node *node, | |
696 | struct symbol *sym __maybe_unused) | |
697 | { | |
ee756ef7 | 698 | struct child_process *a2l = dso__a2l(dso); |
be8ecc57 TGJ |
699 | char *record_function = NULL; |
700 | char *record_filename = NULL; | |
701 | unsigned int record_line_nr = 0; | |
702 | int record_status = -1; | |
703 | int ret = 0; | |
704 | size_t inline_count = 0; | |
b3801e79 IR |
705 | int len; |
706 | char buf[128]; | |
707 | ssize_t written; | |
701677b9 | 708 | struct io io = { .eof = false }; |
2c4b9280 | 709 | enum a2l_style a2l_style; |
be8ecc57 TGJ |
710 | |
711 | if (!a2l) { | |
3b27222d NK |
712 | if (!filename__has_section(dso_name, ".debug_line")) |
713 | goto out; | |
714 | ||
ee756ef7 IR |
715 | dso__set_a2l(dso, |
716 | addr2line_subprocess_init(symbol_conf.addr2line_path, dso_name)); | |
717 | a2l = dso__a2l(dso); | |
a64489c5 JY |
718 | } |
719 | ||
be8ecc57 TGJ |
720 | if (a2l == NULL) { |
721 | if (!symbol_conf.disable_add2line_warn) | |
722 | pr_warning("%s %s: addr2line_subprocess_init failed\n", __func__, dso_name); | |
a64489c5 JY |
723 | goto out; |
724 | } | |
c7a0023a IR |
725 | a2l_style = addr2line_configure(a2l, dso_name); |
726 | if (a2l_style == BROKEN) | |
2c4b9280 | 727 | goto out; |
a64489c5 | 728 | |
be8ecc57 | 729 | /* |
e90208e9 IR |
730 | * Send our request and then *deliberately* send something that can't be |
731 | * interpreted as a valid address to ask addr2line about (namely, | |
732 | * ","). This causes addr2line to first write out the answer to our | |
733 | * request, in an unbounded/unknown number of records, and then to write | |
734 | * out the lines "0x0...0", "??" and "??:0", for GNU binutils, or "," | |
735 | * for llvm-addr2line, so that we can detect when it has finished giving | |
736 | * us anything useful. | |
be8ecc57 | 737 | */ |
b3801e79 IR |
738 | len = snprintf(buf, sizeof(buf), "%016"PRIx64"\n,\n", addr); |
739 | written = len > 0 ? write(a2l->in, buf, len) : -1; | |
740 | if (written != len) { | |
d5e33ce0 NK |
741 | if (!symbol_conf.disable_add2line_warn) |
742 | pr_warning("%s %s: could not send request\n", __func__, dso_name); | |
be8ecc57 TGJ |
743 | goto out; |
744 | } | |
b3801e79 | 745 | io__init(&io, a2l->out, buf, sizeof(buf)); |
701677b9 | 746 | io.timeout_ms = addr2line_timeout_ms; |
e90208e9 | 747 | switch (read_addr2line_record(&io, a2l_style, dso_name, addr, /*first=*/true, |
2c4b9280 | 748 | &record_function, &record_filename, &record_line_nr)) { |
be8ecc57 | 749 | case -1: |
d5e33ce0 NK |
750 | if (!symbol_conf.disable_add2line_warn) |
751 | pr_warning("%s %s: could not read first record\n", __func__, dso_name); | |
be8ecc57 TGJ |
752 | goto out; |
753 | case 0: | |
754 | /* | |
8dc26b6f IR |
755 | * The first record was invalid, so return failure, but first |
756 | * read another record, since we sent a sentinel ',' for the | |
e90208e9 IR |
757 | * sake of detected the last inlined function. Treat this as the |
758 | * first of a record as the ',' generates a new start with GNU | |
759 | * binutils, also force a non-zero address as we're no longer | |
760 | * reading that record. | |
be8ecc57 | 761 | */ |
e90208e9 IR |
762 | switch (read_addr2line_record(&io, a2l_style, dso_name, |
763 | /*addr=*/1, /*first=*/true, | |
764 | NULL, NULL, NULL)) { | |
be8ecc57 | 765 | case -1: |
d5e33ce0 | 766 | if (!symbol_conf.disable_add2line_warn) |
e90208e9 | 767 | pr_warning("%s %s: could not read sentinel record\n", |
d5e33ce0 | 768 | __func__, dso_name); |
be8ecc57 TGJ |
769 | break; |
770 | case 0: | |
e90208e9 | 771 | /* The sentinel as expected. */ |
be8ecc57 TGJ |
772 | break; |
773 | default: | |
d5e33ce0 NK |
774 | if (!symbol_conf.disable_add2line_warn) |
775 | pr_warning("%s %s: unexpected record instead of sentinel", | |
776 | __func__, dso_name); | |
be8ecc57 TGJ |
777 | break; |
778 | } | |
779 | goto out; | |
780 | default: | |
e90208e9 | 781 | /* First record as expected. */ |
be8ecc57 TGJ |
782 | break; |
783 | } | |
7285cf33 | 784 | |
be8ecc57 TGJ |
785 | if (file) { |
786 | *file = strdup(record_filename); | |
787 | ret = 1; | |
788 | } | |
789 | if (line_nr) | |
790 | *line_nr = record_line_nr; | |
fea0cf84 | 791 | |
be8ecc57 TGJ |
792 | if (unwind_inlines) { |
793 | if (node && inline_list__append_record(dso, node, sym, | |
794 | record_function, | |
795 | record_filename, | |
796 | record_line_nr)) { | |
797 | ret = 0; | |
a64489c5 | 798 | goto out; |
be8ecc57 TGJ |
799 | } |
800 | } | |
a64489c5 | 801 | |
e90208e9 IR |
802 | /* |
803 | * We have to read the records even if we don't care about the inline | |
804 | * info. This isn't the first record and force the address to non-zero | |
805 | * as we're reading records beyond the first. | |
806 | */ | |
b3801e79 | 807 | while ((record_status = read_addr2line_record(&io, |
2c4b9280 | 808 | a2l_style, |
e90208e9 IR |
809 | dso_name, |
810 | /*addr=*/1, | |
811 | /*first=*/false, | |
be8ecc57 TGJ |
812 | &record_function, |
813 | &record_filename, | |
814 | &record_line_nr)) == 1) { | |
815 | if (unwind_inlines && node && inline_count++ < MAX_INLINE_NEST) { | |
816 | if (inline_list__append_record(dso, node, sym, | |
817 | record_function, | |
818 | record_filename, | |
819 | record_line_nr)) { | |
820 | ret = 0; | |
821 | goto out; | |
822 | } | |
823 | ret = 1; /* found at least one inline frame */ | |
7285cf33 | 824 | } |
a64489c5 JY |
825 | } |
826 | ||
827 | out: | |
be8ecc57 TGJ |
828 | free(record_function); |
829 | free(record_filename); | |
701677b9 | 830 | if (io.eof) { |
ee756ef7 | 831 | dso__set_a2l(dso, NULL); |
701677b9 IR |
832 | addr2line_subprocess_cleanup(a2l); |
833 | } | |
be8ecc57 TGJ |
834 | return ret; |
835 | } | |
a64489c5 | 836 | |
be8ecc57 TGJ |
837 | void dso__free_a2l(struct dso *dso) |
838 | { | |
ee756ef7 | 839 | struct child_process *a2l = dso__a2l(dso); |
be8ecc57 TGJ |
840 | |
841 | if (!a2l) | |
842 | return; | |
843 | ||
844 | addr2line_subprocess_cleanup(a2l); | |
845 | ||
ee756ef7 | 846 | dso__set_a2l(dso, NULL); |
a64489c5 JY |
847 | } |
848 | ||
2f48fcd8 | 849 | #endif /* HAVE_LIBBFD_SUPPORT */ |
f048d548 | 850 | |
be8ecc57 TGJ |
851 | static struct inline_node *addr2inlines(const char *dso_name, u64 addr, |
852 | struct dso *dso, struct symbol *sym) | |
853 | { | |
854 | struct inline_node *node; | |
855 | ||
856 | node = zalloc(sizeof(*node)); | |
857 | if (node == NULL) { | |
858 | perror("not enough memory for the inline node"); | |
859 | return NULL; | |
860 | } | |
861 | ||
862 | INIT_LIST_HEAD(&node->val); | |
863 | node->addr = addr; | |
864 | ||
865 | addr2line(dso_name, addr, NULL, NULL, dso, true, node, sym); | |
866 | return node; | |
867 | } | |
868 | ||
906049c8 AH |
869 | /* |
870 | * Number of addr2line failures (without success) before disabling it for that | |
871 | * dso. | |
872 | */ | |
873 | #define A2L_FAIL_LIMIT 123 | |
874 | ||
2f84b42b | 875 | char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym, |
935f5a9d JY |
876 | bool show_sym, bool show_addr, bool unwind_inlines, |
877 | u64 ip) | |
f048d548 | 878 | { |
a949fffb DA |
879 | char *file = NULL; |
880 | unsigned line = 0; | |
2cc9d0ef | 881 | char *srcline; |
bf4414ae | 882 | const char *dso_name; |
f048d548 | 883 | |
ee756ef7 | 884 | if (!dso__has_srcline(dso)) |
23f0981b | 885 | goto out; |
2cc9d0ef | 886 | |
ee756ef7 | 887 | dso_name = srcline_dso_name(dso); |
5580338d | 888 | if (dso_name == NULL) |
ee756ef7 | 889 | goto out_err; |
58d91a00 | 890 | |
fea0cf84 MW |
891 | if (!addr2line(dso_name, addr, &file, &line, dso, |
892 | unwind_inlines, NULL, sym)) | |
ee756ef7 | 893 | goto out_err; |
f048d548 | 894 | |
2be8832f MW |
895 | srcline = srcline_from_fileline(file, line); |
896 | free(file); | |
897 | ||
898 | if (!srcline) | |
ee756ef7 | 899 | goto out_err; |
906049c8 | 900 | |
ee756ef7 | 901 | dso__set_a2l_fails(dso, 0); |
f048d548 | 902 | |
f048d548 | 903 | return srcline; |
2cc9d0ef | 904 | |
ee756ef7 IR |
905 | out_err: |
906 | dso__set_a2l_fails(dso, dso__a2l_fails(dso) + 1); | |
907 | if (dso__a2l_fails(dso) > A2L_FAIL_LIMIT) { | |
908 | dso__set_has_srcline(dso, false); | |
906049c8 AH |
909 | dso__free_a2l(dso); |
910 | } | |
ee756ef7 | 911 | out: |
5dfa210e MW |
912 | if (!show_addr) |
913 | return (show_sym && sym) ? | |
ea335ef3 | 914 | strndup(sym->name, sym->namelen) : SRCLINE_UNKNOWN; |
5dfa210e | 915 | |
85c116a6 | 916 | if (sym) { |
ac931f87 | 917 | if (asprintf(&srcline, "%s+%" PRIu64, show_sym ? sym->name : "", |
935f5a9d | 918 | ip - sym->start) < 0) |
85c116a6 | 919 | return SRCLINE_UNKNOWN; |
ee756ef7 | 920 | } else if (asprintf(&srcline, "%s[%" PRIx64 "]", dso__short_name(dso), addr) < 0) |
23f0981b AK |
921 | return SRCLINE_UNKNOWN; |
922 | return srcline; | |
f048d548 NK |
923 | } |
924 | ||
dd2e18e9 AK |
925 | /* Returns filename and fills in line number in line */ |
926 | char *get_srcline_split(struct dso *dso, u64 addr, unsigned *line) | |
927 | { | |
928 | char *file = NULL; | |
929 | const char *dso_name; | |
930 | ||
ee756ef7 IR |
931 | if (!dso__has_srcline(dso)) |
932 | return NULL; | |
dd2e18e9 | 933 | |
ee756ef7 | 934 | dso_name = srcline_dso_name(dso); |
dd2e18e9 | 935 | if (dso_name == NULL) |
ee756ef7 | 936 | goto out_err; |
dd2e18e9 AK |
937 | |
938 | if (!addr2line(dso_name, addr, &file, line, dso, true, NULL, NULL)) | |
ee756ef7 | 939 | goto out_err; |
dd2e18e9 | 940 | |
ee756ef7 | 941 | dso__set_a2l_fails(dso, 0); |
dd2e18e9 AK |
942 | return file; |
943 | ||
ee756ef7 IR |
944 | out_err: |
945 | dso__set_a2l_fails(dso, dso__a2l_fails(dso) + 1); | |
946 | if (dso__a2l_fails(dso) > A2L_FAIL_LIMIT) { | |
947 | dso__set_has_srcline(dso, false); | |
dd2e18e9 AK |
948 | dso__free_a2l(dso); |
949 | } | |
950 | ||
951 | return NULL; | |
952 | } | |
953 | ||
625db36e | 954 | void zfree_srcline(char **srcline) |
f048d548 | 955 | { |
625db36e IR |
956 | if (*srcline == NULL) |
957 | return; | |
958 | ||
922db21d | 959 | if (*srcline != SRCLINE_UNKNOWN) |
625db36e IR |
960 | free(*srcline); |
961 | ||
962 | *srcline = NULL; | |
f048d548 | 963 | } |
2f84b42b AK |
964 | |
965 | char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym, | |
935f5a9d | 966 | bool show_sym, bool show_addr, u64 ip) |
2f84b42b | 967 | { |
935f5a9d | 968 | return __get_srcline(dso, addr, sym, show_sym, show_addr, false, ip); |
2f84b42b | 969 | } |
a64489c5 | 970 | |
21ac9d54 MW |
971 | struct srcline_node { |
972 | u64 addr; | |
973 | char *srcline; | |
974 | struct rb_node rb_node; | |
975 | }; | |
976 | ||
55ecd631 | 977 | void srcline__tree_insert(struct rb_root_cached *tree, u64 addr, char *srcline) |
21ac9d54 | 978 | { |
55ecd631 | 979 | struct rb_node **p = &tree->rb_root.rb_node; |
21ac9d54 MW |
980 | struct rb_node *parent = NULL; |
981 | struct srcline_node *i, *node; | |
55ecd631 | 982 | bool leftmost = true; |
21ac9d54 MW |
983 | |
984 | node = zalloc(sizeof(struct srcline_node)); | |
985 | if (!node) { | |
986 | perror("not enough memory for the srcline node"); | |
987 | return; | |
988 | } | |
989 | ||
990 | node->addr = addr; | |
991 | node->srcline = srcline; | |
992 | ||
993 | while (*p != NULL) { | |
994 | parent = *p; | |
995 | i = rb_entry(parent, struct srcline_node, rb_node); | |
996 | if (addr < i->addr) | |
997 | p = &(*p)->rb_left; | |
55ecd631 | 998 | else { |
21ac9d54 | 999 | p = &(*p)->rb_right; |
55ecd631 DB |
1000 | leftmost = false; |
1001 | } | |
21ac9d54 MW |
1002 | } |
1003 | rb_link_node(&node->rb_node, parent, p); | |
55ecd631 | 1004 | rb_insert_color_cached(&node->rb_node, tree, leftmost); |
21ac9d54 MW |
1005 | } |
1006 | ||
55ecd631 | 1007 | char *srcline__tree_find(struct rb_root_cached *tree, u64 addr) |
21ac9d54 | 1008 | { |
55ecd631 | 1009 | struct rb_node *n = tree->rb_root.rb_node; |
21ac9d54 MW |
1010 | |
1011 | while (n) { | |
1012 | struct srcline_node *i = rb_entry(n, struct srcline_node, | |
1013 | rb_node); | |
1014 | ||
1015 | if (addr < i->addr) | |
1016 | n = n->rb_left; | |
1017 | else if (addr > i->addr) | |
1018 | n = n->rb_right; | |
1019 | else | |
1020 | return i->srcline; | |
1021 | } | |
1022 | ||
1023 | return NULL; | |
1024 | } | |
1025 | ||
55ecd631 | 1026 | void srcline__tree_delete(struct rb_root_cached *tree) |
21ac9d54 MW |
1027 | { |
1028 | struct srcline_node *pos; | |
55ecd631 | 1029 | struct rb_node *next = rb_first_cached(tree); |
21ac9d54 MW |
1030 | |
1031 | while (next) { | |
1032 | pos = rb_entry(next, struct srcline_node, rb_node); | |
1033 | next = rb_next(&pos->rb_node); | |
55ecd631 | 1034 | rb_erase_cached(&pos->rb_node, tree); |
625db36e | 1035 | zfree_srcline(&pos->srcline); |
21ac9d54 MW |
1036 | zfree(&pos); |
1037 | } | |
1038 | } | |
1039 | ||
fea0cf84 MW |
1040 | struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr, |
1041 | struct symbol *sym) | |
a64489c5 JY |
1042 | { |
1043 | const char *dso_name; | |
1044 | ||
ee756ef7 | 1045 | dso_name = srcline_dso_name(dso); |
a64489c5 JY |
1046 | if (dso_name == NULL) |
1047 | return NULL; | |
1048 | ||
fea0cf84 | 1049 | return addr2inlines(dso_name, addr, dso, sym); |
a64489c5 JY |
1050 | } |
1051 | ||
1052 | void inline_node__delete(struct inline_node *node) | |
1053 | { | |
1054 | struct inline_list *ilist, *tmp; | |
1055 | ||
1056 | list_for_each_entry_safe(ilist, tmp, &node->val, list) { | |
1057 | list_del_init(&ilist->list); | |
625db36e | 1058 | zfree_srcline(&ilist->srcline); |
fea0cf84 MW |
1059 | /* only the inlined symbols are owned by the list */ |
1060 | if (ilist->symbol && ilist->symbol->inlined) | |
1061 | symbol__delete(ilist->symbol); | |
a64489c5 JY |
1062 | free(ilist); |
1063 | } | |
1064 | ||
1065 | free(node); | |
1066 | } | |
11ea2515 | 1067 | |
55ecd631 DB |
1068 | void inlines__tree_insert(struct rb_root_cached *tree, |
1069 | struct inline_node *inlines) | |
11ea2515 | 1070 | { |
55ecd631 | 1071 | struct rb_node **p = &tree->rb_root.rb_node; |
11ea2515 MW |
1072 | struct rb_node *parent = NULL; |
1073 | const u64 addr = inlines->addr; | |
1074 | struct inline_node *i; | |
55ecd631 | 1075 | bool leftmost = true; |
11ea2515 MW |
1076 | |
1077 | while (*p != NULL) { | |
1078 | parent = *p; | |
1079 | i = rb_entry(parent, struct inline_node, rb_node); | |
1080 | if (addr < i->addr) | |
1081 | p = &(*p)->rb_left; | |
55ecd631 | 1082 | else { |
11ea2515 | 1083 | p = &(*p)->rb_right; |
55ecd631 DB |
1084 | leftmost = false; |
1085 | } | |
11ea2515 MW |
1086 | } |
1087 | rb_link_node(&inlines->rb_node, parent, p); | |
55ecd631 | 1088 | rb_insert_color_cached(&inlines->rb_node, tree, leftmost); |
11ea2515 MW |
1089 | } |
1090 | ||
55ecd631 | 1091 | struct inline_node *inlines__tree_find(struct rb_root_cached *tree, u64 addr) |
11ea2515 | 1092 | { |
55ecd631 | 1093 | struct rb_node *n = tree->rb_root.rb_node; |
11ea2515 MW |
1094 | |
1095 | while (n) { | |
1096 | struct inline_node *i = rb_entry(n, struct inline_node, | |
1097 | rb_node); | |
1098 | ||
1099 | if (addr < i->addr) | |
1100 | n = n->rb_left; | |
1101 | else if (addr > i->addr) | |
1102 | n = n->rb_right; | |
1103 | else | |
1104 | return i; | |
1105 | } | |
1106 | ||
1107 | return NULL; | |
1108 | } | |
1109 | ||
55ecd631 | 1110 | void inlines__tree_delete(struct rb_root_cached *tree) |
11ea2515 MW |
1111 | { |
1112 | struct inline_node *pos; | |
55ecd631 | 1113 | struct rb_node *next = rb_first_cached(tree); |
11ea2515 MW |
1114 | |
1115 | while (next) { | |
1116 | pos = rb_entry(next, struct inline_node, rb_node); | |
1117 | next = rb_next(&pos->rb_node); | |
55ecd631 | 1118 | rb_erase_cached(&pos->rb_node, tree); |
11ea2515 MW |
1119 | inline_node__delete(pos); |
1120 | } | |
1121 | } |