Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
fd20e811 | 2 | #include <inttypes.h> |
f048d548 NK |
3 | #include <stdio.h> |
4 | #include <stdlib.h> | |
5 | #include <string.h> | |
6 | ||
7 | #include <linux/kernel.h> | |
8 | ||
86c98cab | 9 | #include "util/dso.h" |
f048d548 NK |
10 | #include "util/util.h" |
11 | #include "util/debug.h" | |
a64489c5 | 12 | #include "util/callchain.h" |
632a5cab | 13 | #include "srcline.h" |
7285cf33 | 14 | #include "string2.h" |
85c116a6 AK |
15 | #include "symbol.h" |
16 | ||
a9710ba0 AK |
17 | bool srcline_full_filename; |
18 | ||
5580338d JY |
19 | static const char *dso__name(struct dso *dso) |
20 | { | |
21 | const char *dso_name; | |
22 | ||
23 | if (dso->symsrc_filename) | |
24 | dso_name = dso->symsrc_filename; | |
25 | else | |
26 | dso_name = dso->long_name; | |
27 | ||
28 | if (dso_name[0] == '[') | |
29 | return NULL; | |
30 | ||
31 | if (!strncmp(dso_name, "/tmp/perf-", 10)) | |
32 | return NULL; | |
33 | ||
34 | return dso_name; | |
35 | } | |
36 | ||
2be8832f MW |
37 | static int inline_list__append(struct symbol *symbol, char *srcline, |
38 | struct inline_node *node) | |
a64489c5 JY |
39 | { |
40 | struct inline_list *ilist; | |
a64489c5 JY |
41 | |
42 | ilist = zalloc(sizeof(*ilist)); | |
43 | if (ilist == NULL) | |
44 | return -1; | |
45 | ||
fea0cf84 | 46 | ilist->symbol = symbol; |
2be8832f | 47 | ilist->srcline = srcline; |
a64489c5 | 48 | |
28071f51 MW |
49 | if (callchain_param.order == ORDER_CALLEE) |
50 | list_add_tail(&ilist->list, &node->val); | |
51 | else | |
52 | list_add(&ilist->list, &node->val); | |
a64489c5 JY |
53 | |
54 | return 0; | |
55 | } | |
56 | ||
2be8832f MW |
57 | /* basename version that takes a const input string */ |
58 | static const char *gnu_basename(const char *path) | |
59 | { | |
60 | const char *base = strrchr(path, '/'); | |
61 | ||
62 | return base ? base + 1 : path; | |
63 | } | |
64 | ||
65 | static char *srcline_from_fileline(const char *file, unsigned int line) | |
66 | { | |
67 | char *srcline; | |
68 | ||
69 | if (!file) | |
70 | return NULL; | |
71 | ||
72 | if (!srcline_full_filename) | |
73 | file = gnu_basename(file); | |
74 | ||
75 | if (asprintf(&srcline, "%s:%u", file, line) < 0) | |
76 | return NULL; | |
77 | ||
78 | return srcline; | |
79 | } | |
80 | ||
7285cf33 NK |
81 | static struct symbol *new_inline_sym(struct dso *dso, |
82 | struct symbol *base_sym, | |
83 | const char *funcname) | |
84 | { | |
85 | struct symbol *inline_sym; | |
86 | char *demangled = NULL; | |
87 | ||
88 | if (dso) { | |
89 | demangled = dso__demangle_sym(dso, 0, funcname); | |
90 | if (demangled) | |
91 | funcname = demangled; | |
92 | } | |
93 | ||
94 | if (base_sym && strcmp(funcname, base_sym->name) == 0) { | |
95 | /* reuse the real, existing symbol */ | |
96 | inline_sym = base_sym; | |
97 | /* ensure that we don't alias an inlined symbol, which could | |
98 | * lead to double frees in inline_node__delete | |
99 | */ | |
100 | assert(!base_sym->inlined); | |
101 | } else { | |
102 | /* create a fake symbol for the inline frame */ | |
103 | inline_sym = symbol__new(base_sym ? base_sym->start : 0, | |
104 | base_sym ? base_sym->end : 0, | |
105 | base_sym ? base_sym->binding : 0, | |
af30bffa | 106 | base_sym ? base_sym->type : 0, |
7285cf33 NK |
107 | funcname); |
108 | if (inline_sym) | |
109 | inline_sym->inlined = 1; | |
110 | } | |
111 | ||
112 | free(demangled); | |
113 | ||
114 | return inline_sym; | |
115 | } | |
116 | ||
2f48fcd8 RV |
117 | #ifdef HAVE_LIBBFD_SUPPORT |
118 | ||
119 | /* | |
120 | * Implement addr2line using libbfd. | |
121 | */ | |
122 | #define PACKAGE "perf" | |
123 | #include <bfd.h> | |
124 | ||
125 | struct a2l_data { | |
126 | const char *input; | |
ac931f87 | 127 | u64 addr; |
2f48fcd8 RV |
128 | |
129 | bool found; | |
130 | const char *filename; | |
131 | const char *funcname; | |
132 | unsigned line; | |
133 | ||
134 | bfd *abfd; | |
135 | asymbol **syms; | |
136 | }; | |
137 | ||
138 | static int bfd_error(const char *string) | |
139 | { | |
140 | const char *errmsg; | |
141 | ||
142 | errmsg = bfd_errmsg(bfd_get_error()); | |
143 | fflush(stdout); | |
144 | ||
145 | if (string) | |
146 | pr_debug("%s: %s\n", string, errmsg); | |
147 | else | |
148 | pr_debug("%s\n", errmsg); | |
149 | ||
150 | return -1; | |
151 | } | |
152 | ||
153 | static int slurp_symtab(bfd *abfd, struct a2l_data *a2l) | |
154 | { | |
155 | long storage; | |
156 | long symcount; | |
157 | asymbol **syms; | |
158 | bfd_boolean dynamic = FALSE; | |
159 | ||
160 | if ((bfd_get_file_flags(abfd) & HAS_SYMS) == 0) | |
161 | return bfd_error(bfd_get_filename(abfd)); | |
162 | ||
163 | storage = bfd_get_symtab_upper_bound(abfd); | |
164 | if (storage == 0L) { | |
165 | storage = bfd_get_dynamic_symtab_upper_bound(abfd); | |
166 | dynamic = TRUE; | |
167 | } | |
168 | if (storage < 0L) | |
169 | return bfd_error(bfd_get_filename(abfd)); | |
170 | ||
171 | syms = malloc(storage); | |
172 | if (dynamic) | |
173 | symcount = bfd_canonicalize_dynamic_symtab(abfd, syms); | |
174 | else | |
175 | symcount = bfd_canonicalize_symtab(abfd, syms); | |
176 | ||
177 | if (symcount < 0) { | |
178 | free(syms); | |
179 | return bfd_error(bfd_get_filename(abfd)); | |
180 | } | |
181 | ||
182 | a2l->syms = syms; | |
183 | return 0; | |
184 | } | |
185 | ||
186 | static void find_address_in_section(bfd *abfd, asection *section, void *data) | |
187 | { | |
188 | bfd_vma pc, vma; | |
189 | bfd_size_type size; | |
190 | struct a2l_data *a2l = data; | |
191 | ||
192 | if (a2l->found) | |
193 | return; | |
194 | ||
195 | if ((bfd_get_section_flags(abfd, section) & SEC_ALLOC) == 0) | |
196 | return; | |
197 | ||
198 | pc = a2l->addr; | |
199 | vma = bfd_get_section_vma(abfd, section); | |
200 | size = bfd_get_section_size(section); | |
201 | ||
202 | if (pc < vma || pc >= vma + size) | |
203 | return; | |
204 | ||
205 | a2l->found = bfd_find_nearest_line(abfd, section, a2l->syms, pc - vma, | |
206 | &a2l->filename, &a2l->funcname, | |
207 | &a2l->line); | |
d964b1cd MW |
208 | |
209 | if (a2l->filename && !strlen(a2l->filename)) | |
210 | a2l->filename = NULL; | |
2f48fcd8 RV |
211 | } |
212 | ||
213 | static struct a2l_data *addr2line_init(const char *path) | |
214 | { | |
215 | bfd *abfd; | |
216 | struct a2l_data *a2l = NULL; | |
217 | ||
218 | abfd = bfd_openr(path, NULL); | |
219 | if (abfd == NULL) | |
220 | return NULL; | |
221 | ||
222 | if (!bfd_check_format(abfd, bfd_object)) | |
223 | goto out; | |
224 | ||
225 | a2l = zalloc(sizeof(*a2l)); | |
226 | if (a2l == NULL) | |
227 | goto out; | |
228 | ||
229 | a2l->abfd = abfd; | |
230 | a2l->input = strdup(path); | |
231 | if (a2l->input == NULL) | |
232 | goto out; | |
233 | ||
234 | if (slurp_symtab(abfd, a2l)) | |
235 | goto out; | |
236 | ||
237 | return a2l; | |
238 | ||
239 | out: | |
240 | if (a2l) { | |
7d16c634 | 241 | zfree((char **)&a2l->input); |
2f48fcd8 RV |
242 | free(a2l); |
243 | } | |
244 | bfd_close(abfd); | |
245 | return NULL; | |
246 | } | |
247 | ||
248 | static void addr2line_cleanup(struct a2l_data *a2l) | |
249 | { | |
250 | if (a2l->abfd) | |
251 | bfd_close(a2l->abfd); | |
7d16c634 | 252 | zfree((char **)&a2l->input); |
74cf249d | 253 | zfree(&a2l->syms); |
2f48fcd8 RV |
254 | free(a2l); |
255 | } | |
256 | ||
2f84b42b AK |
257 | #define MAX_INLINE_NEST 1024 |
258 | ||
4d53b9d5 | 259 | static int inline_list__append_dso_a2l(struct dso *dso, |
fea0cf84 MW |
260 | struct inline_node *node, |
261 | struct symbol *sym) | |
4d53b9d5 MW |
262 | { |
263 | struct a2l_data *a2l = dso->a2l; | |
fea0cf84 | 264 | struct symbol *inline_sym = new_inline_sym(dso, sym, a2l->funcname); |
2be8832f | 265 | char *srcline = NULL; |
4d53b9d5 | 266 | |
2be8832f MW |
267 | if (a2l->filename) |
268 | srcline = srcline_from_fileline(a2l->filename, a2l->line); | |
269 | ||
270 | return inline_list__append(inline_sym, srcline, node); | |
4d53b9d5 MW |
271 | } |
272 | ||
ac931f87 | 273 | static int addr2line(const char *dso_name, u64 addr, |
2f84b42b | 274 | char **file, unsigned int *line, struct dso *dso, |
fea0cf84 MW |
275 | bool unwind_inlines, struct inline_node *node, |
276 | struct symbol *sym) | |
2f48fcd8 RV |
277 | { |
278 | int ret = 0; | |
454ff00f AH |
279 | struct a2l_data *a2l = dso->a2l; |
280 | ||
281 | if (!a2l) { | |
282 | dso->a2l = addr2line_init(dso_name); | |
283 | a2l = dso->a2l; | |
284 | } | |
2f48fcd8 | 285 | |
2f48fcd8 RV |
286 | if (a2l == NULL) { |
287 | pr_warning("addr2line_init failed for %s\n", dso_name); | |
288 | return 0; | |
289 | } | |
290 | ||
291 | a2l->addr = addr; | |
454ff00f AH |
292 | a2l->found = false; |
293 | ||
2f48fcd8 RV |
294 | bfd_map_over_sections(a2l->abfd, find_address_in_section, a2l); |
295 | ||
b21cc978 MW |
296 | if (!a2l->found) |
297 | return 0; | |
298 | ||
299 | if (unwind_inlines) { | |
2f84b42b AK |
300 | int cnt = 0; |
301 | ||
fea0cf84 | 302 | if (node && inline_list__append_dso_a2l(dso, node, sym)) |
4d53b9d5 MW |
303 | return 0; |
304 | ||
2f84b42b AK |
305 | while (bfd_find_inliner_info(a2l->abfd, &a2l->filename, |
306 | &a2l->funcname, &a2l->line) && | |
a64489c5 JY |
307 | cnt++ < MAX_INLINE_NEST) { |
308 | ||
d964b1cd MW |
309 | if (a2l->filename && !strlen(a2l->filename)) |
310 | a2l->filename = NULL; | |
311 | ||
a64489c5 | 312 | if (node != NULL) { |
fea0cf84 | 313 | if (inline_list__append_dso_a2l(dso, node, sym)) |
a64489c5 | 314 | return 0; |
b21cc978 MW |
315 | // found at least one inline frame |
316 | ret = 1; | |
a64489c5 JY |
317 | } |
318 | } | |
2f84b42b AK |
319 | } |
320 | ||
b21cc978 MW |
321 | if (file) { |
322 | *file = a2l->filename ? strdup(a2l->filename) : NULL; | |
323 | ret = *file ? 1 : 0; | |
2f48fcd8 RV |
324 | } |
325 | ||
b21cc978 MW |
326 | if (line) |
327 | *line = a2l->line; | |
328 | ||
2f48fcd8 RV |
329 | return ret; |
330 | } | |
331 | ||
454ff00f AH |
332 | void dso__free_a2l(struct dso *dso) |
333 | { | |
334 | struct a2l_data *a2l = dso->a2l; | |
335 | ||
336 | if (!a2l) | |
337 | return; | |
338 | ||
339 | addr2line_cleanup(a2l); | |
340 | ||
341 | dso->a2l = NULL; | |
342 | } | |
343 | ||
a64489c5 | 344 | static struct inline_node *addr2inlines(const char *dso_name, u64 addr, |
fea0cf84 | 345 | struct dso *dso, struct symbol *sym) |
a64489c5 | 346 | { |
a64489c5 JY |
347 | struct inline_node *node; |
348 | ||
349 | node = zalloc(sizeof(*node)); | |
350 | if (node == NULL) { | |
351 | perror("not enough memory for the inline node"); | |
352 | return NULL; | |
353 | } | |
354 | ||
355 | INIT_LIST_HEAD(&node->val); | |
356 | node->addr = addr; | |
357 | ||
b38775cf | 358 | addr2line(dso_name, addr, NULL, NULL, dso, true, node, sym); |
a64489c5 | 359 | return node; |
a64489c5 JY |
360 | } |
361 | ||
2f48fcd8 RV |
362 | #else /* HAVE_LIBBFD_SUPPORT */ |
363 | ||
5580338d JY |
364 | static int filename_split(char *filename, unsigned int *line_nr) |
365 | { | |
366 | char *sep; | |
367 | ||
368 | sep = strchr(filename, '\n'); | |
369 | if (sep) | |
370 | *sep = '\0'; | |
371 | ||
372 | if (!strcmp(filename, "??:0")) | |
373 | return 0; | |
374 | ||
375 | sep = strchr(filename, ':'); | |
376 | if (sep) { | |
377 | *sep++ = '\0'; | |
378 | *line_nr = strtoul(sep, NULL, 0); | |
379 | return 1; | |
380 | } | |
381 | ||
382 | return 0; | |
383 | } | |
384 | ||
ac931f87 | 385 | static int addr2line(const char *dso_name, u64 addr, |
454ff00f | 386 | char **file, unsigned int *line_nr, |
2f84b42b | 387 | struct dso *dso __maybe_unused, |
a64489c5 | 388 | bool unwind_inlines __maybe_unused, |
fea0cf84 MW |
389 | struct inline_node *node __maybe_unused, |
390 | struct symbol *sym __maybe_unused) | |
f048d548 NK |
391 | { |
392 | FILE *fp; | |
393 | char cmd[PATH_MAX]; | |
394 | char *filename = NULL; | |
395 | size_t len; | |
f048d548 NK |
396 | int ret = 0; |
397 | ||
398 | scnprintf(cmd, sizeof(cmd), "addr2line -e %s %016"PRIx64, | |
399 | dso_name, addr); | |
400 | ||
401 | fp = popen(cmd, "r"); | |
402 | if (fp == NULL) { | |
403 | pr_warning("popen failed for %s\n", dso_name); | |
404 | return 0; | |
405 | } | |
406 | ||
407 | if (getline(&filename, &len, fp) < 0 || !len) { | |
408 | pr_warning("addr2line has no output for %s\n", dso_name); | |
409 | goto out; | |
410 | } | |
411 | ||
5580338d JY |
412 | ret = filename_split(filename, line_nr); |
413 | if (ret != 1) { | |
f048d548 NK |
414 | free(filename); |
415 | goto out; | |
416 | } | |
417 | ||
5580338d JY |
418 | *file = filename; |
419 | ||
f048d548 NK |
420 | out: |
421 | pclose(fp); | |
422 | return ret; | |
423 | } | |
454ff00f AH |
424 | |
425 | void dso__free_a2l(struct dso *dso __maybe_unused) | |
426 | { | |
427 | } | |
428 | ||
a64489c5 | 429 | static struct inline_node *addr2inlines(const char *dso_name, u64 addr, |
fea0cf84 MW |
430 | struct dso *dso __maybe_unused, |
431 | struct symbol *sym) | |
a64489c5 JY |
432 | { |
433 | FILE *fp; | |
434 | char cmd[PATH_MAX]; | |
435 | struct inline_node *node; | |
436 | char *filename = NULL; | |
7285cf33 NK |
437 | char *funcname = NULL; |
438 | size_t filelen, funclen; | |
a64489c5 JY |
439 | unsigned int line_nr = 0; |
440 | ||
7285cf33 | 441 | scnprintf(cmd, sizeof(cmd), "addr2line -e %s -i -f %016"PRIx64, |
a64489c5 JY |
442 | dso_name, addr); |
443 | ||
444 | fp = popen(cmd, "r"); | |
445 | if (fp == NULL) { | |
446 | pr_err("popen failed for %s\n", dso_name); | |
447 | return NULL; | |
448 | } | |
449 | ||
450 | node = zalloc(sizeof(*node)); | |
451 | if (node == NULL) { | |
452 | perror("not enough memory for the inline node"); | |
453 | goto out; | |
454 | } | |
455 | ||
456 | INIT_LIST_HEAD(&node->val); | |
457 | node->addr = addr; | |
458 | ||
7285cf33 NK |
459 | /* addr2line -f generates two lines for each inlined functions */ |
460 | while (getline(&funcname, &funclen, fp) != -1) { | |
2be8832f | 461 | char *srcline; |
7285cf33 NK |
462 | struct symbol *inline_sym; |
463 | ||
464 | rtrim(funcname); | |
465 | ||
466 | if (getline(&filename, &filelen, fp) == -1) | |
467 | goto out; | |
fea0cf84 | 468 | |
b7b75a60 | 469 | if (filename_split(filename, &line_nr) != 1) |
a64489c5 | 470 | goto out; |
a64489c5 | 471 | |
2be8832f | 472 | srcline = srcline_from_fileline(filename, line_nr); |
7285cf33 NK |
473 | inline_sym = new_inline_sym(dso, sym, funcname); |
474 | ||
475 | if (inline_list__append(inline_sym, srcline, node) != 0) { | |
476 | free(srcline); | |
477 | if (inline_sym && inline_sym->inlined) | |
478 | symbol__delete(inline_sym); | |
a64489c5 | 479 | goto out; |
7285cf33 | 480 | } |
a64489c5 JY |
481 | } |
482 | ||
483 | out: | |
484 | pclose(fp); | |
b7b75a60 | 485 | free(filename); |
7285cf33 | 486 | free(funcname); |
a64489c5 | 487 | |
a64489c5 JY |
488 | return node; |
489 | } | |
490 | ||
2f48fcd8 | 491 | #endif /* HAVE_LIBBFD_SUPPORT */ |
f048d548 | 492 | |
906049c8 AH |
493 | /* |
494 | * Number of addr2line failures (without success) before disabling it for that | |
495 | * dso. | |
496 | */ | |
497 | #define A2L_FAIL_LIMIT 123 | |
498 | ||
2f84b42b | 499 | char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym, |
935f5a9d JY |
500 | bool show_sym, bool show_addr, bool unwind_inlines, |
501 | u64 ip) | |
f048d548 | 502 | { |
a949fffb DA |
503 | char *file = NULL; |
504 | unsigned line = 0; | |
2cc9d0ef | 505 | char *srcline; |
bf4414ae | 506 | const char *dso_name; |
f048d548 | 507 | |
2cc9d0ef | 508 | if (!dso->has_srcline) |
23f0981b | 509 | goto out; |
2cc9d0ef | 510 | |
5580338d JY |
511 | dso_name = dso__name(dso); |
512 | if (dso_name == NULL) | |
58d91a00 NK |
513 | goto out; |
514 | ||
fea0cf84 MW |
515 | if (!addr2line(dso_name, addr, &file, &line, dso, |
516 | unwind_inlines, NULL, sym)) | |
58d91a00 | 517 | goto out; |
f048d548 | 518 | |
2be8832f MW |
519 | srcline = srcline_from_fileline(file, line); |
520 | free(file); | |
521 | ||
522 | if (!srcline) | |
906049c8 | 523 | goto out; |
906049c8 AH |
524 | |
525 | dso->a2l_fails = 0; | |
f048d548 | 526 | |
f048d548 | 527 | return srcline; |
2cc9d0ef NK |
528 | |
529 | out: | |
906049c8 AH |
530 | if (dso->a2l_fails && ++dso->a2l_fails > A2L_FAIL_LIMIT) { |
531 | dso->has_srcline = 0; | |
532 | dso__free_a2l(dso); | |
533 | } | |
5dfa210e MW |
534 | |
535 | if (!show_addr) | |
536 | return (show_sym && sym) ? | |
537 | strndup(sym->name, sym->namelen) : NULL; | |
538 | ||
85c116a6 | 539 | if (sym) { |
ac931f87 | 540 | if (asprintf(&srcline, "%s+%" PRIu64, show_sym ? sym->name : "", |
935f5a9d | 541 | ip - sym->start) < 0) |
85c116a6 | 542 | return SRCLINE_UNKNOWN; |
ac931f87 | 543 | } else if (asprintf(&srcline, "%s[%" PRIx64 "]", dso->short_name, addr) < 0) |
23f0981b AK |
544 | return SRCLINE_UNKNOWN; |
545 | return srcline; | |
f048d548 NK |
546 | } |
547 | ||
548 | void free_srcline(char *srcline) | |
549 | { | |
550 | if (srcline && strcmp(srcline, SRCLINE_UNKNOWN) != 0) | |
551 | free(srcline); | |
552 | } | |
2f84b42b AK |
553 | |
554 | char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym, | |
935f5a9d | 555 | bool show_sym, bool show_addr, u64 ip) |
2f84b42b | 556 | { |
935f5a9d | 557 | return __get_srcline(dso, addr, sym, show_sym, show_addr, false, ip); |
2f84b42b | 558 | } |
a64489c5 | 559 | |
21ac9d54 MW |
560 | struct srcline_node { |
561 | u64 addr; | |
562 | char *srcline; | |
563 | struct rb_node rb_node; | |
564 | }; | |
565 | ||
566 | void srcline__tree_insert(struct rb_root *tree, u64 addr, char *srcline) | |
567 | { | |
568 | struct rb_node **p = &tree->rb_node; | |
569 | struct rb_node *parent = NULL; | |
570 | struct srcline_node *i, *node; | |
571 | ||
572 | node = zalloc(sizeof(struct srcline_node)); | |
573 | if (!node) { | |
574 | perror("not enough memory for the srcline node"); | |
575 | return; | |
576 | } | |
577 | ||
578 | node->addr = addr; | |
579 | node->srcline = srcline; | |
580 | ||
581 | while (*p != NULL) { | |
582 | parent = *p; | |
583 | i = rb_entry(parent, struct srcline_node, rb_node); | |
584 | if (addr < i->addr) | |
585 | p = &(*p)->rb_left; | |
586 | else | |
587 | p = &(*p)->rb_right; | |
588 | } | |
589 | rb_link_node(&node->rb_node, parent, p); | |
590 | rb_insert_color(&node->rb_node, tree); | |
591 | } | |
592 | ||
593 | char *srcline__tree_find(struct rb_root *tree, u64 addr) | |
594 | { | |
595 | struct rb_node *n = tree->rb_node; | |
596 | ||
597 | while (n) { | |
598 | struct srcline_node *i = rb_entry(n, struct srcline_node, | |
599 | rb_node); | |
600 | ||
601 | if (addr < i->addr) | |
602 | n = n->rb_left; | |
603 | else if (addr > i->addr) | |
604 | n = n->rb_right; | |
605 | else | |
606 | return i->srcline; | |
607 | } | |
608 | ||
609 | return NULL; | |
610 | } | |
611 | ||
612 | void srcline__tree_delete(struct rb_root *tree) | |
613 | { | |
614 | struct srcline_node *pos; | |
615 | struct rb_node *next = rb_first(tree); | |
616 | ||
617 | while (next) { | |
618 | pos = rb_entry(next, struct srcline_node, rb_node); | |
619 | next = rb_next(&pos->rb_node); | |
620 | rb_erase(&pos->rb_node, tree); | |
621 | free_srcline(pos->srcline); | |
622 | zfree(&pos); | |
623 | } | |
624 | } | |
625 | ||
fea0cf84 MW |
626 | struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr, |
627 | struct symbol *sym) | |
a64489c5 JY |
628 | { |
629 | const char *dso_name; | |
630 | ||
631 | dso_name = dso__name(dso); | |
632 | if (dso_name == NULL) | |
633 | return NULL; | |
634 | ||
fea0cf84 | 635 | return addr2inlines(dso_name, addr, dso, sym); |
a64489c5 JY |
636 | } |
637 | ||
638 | void inline_node__delete(struct inline_node *node) | |
639 | { | |
640 | struct inline_list *ilist, *tmp; | |
641 | ||
642 | list_for_each_entry_safe(ilist, tmp, &node->val, list) { | |
643 | list_del_init(&ilist->list); | |
2be8832f | 644 | free_srcline(ilist->srcline); |
fea0cf84 MW |
645 | /* only the inlined symbols are owned by the list */ |
646 | if (ilist->symbol && ilist->symbol->inlined) | |
647 | symbol__delete(ilist->symbol); | |
a64489c5 JY |
648 | free(ilist); |
649 | } | |
650 | ||
651 | free(node); | |
652 | } | |
11ea2515 MW |
653 | |
654 | void inlines__tree_insert(struct rb_root *tree, struct inline_node *inlines) | |
655 | { | |
656 | struct rb_node **p = &tree->rb_node; | |
657 | struct rb_node *parent = NULL; | |
658 | const u64 addr = inlines->addr; | |
659 | struct inline_node *i; | |
660 | ||
661 | while (*p != NULL) { | |
662 | parent = *p; | |
663 | i = rb_entry(parent, struct inline_node, rb_node); | |
664 | if (addr < i->addr) | |
665 | p = &(*p)->rb_left; | |
666 | else | |
667 | p = &(*p)->rb_right; | |
668 | } | |
669 | rb_link_node(&inlines->rb_node, parent, p); | |
670 | rb_insert_color(&inlines->rb_node, tree); | |
671 | } | |
672 | ||
673 | struct inline_node *inlines__tree_find(struct rb_root *tree, u64 addr) | |
674 | { | |
675 | struct rb_node *n = tree->rb_node; | |
676 | ||
677 | while (n) { | |
678 | struct inline_node *i = rb_entry(n, struct inline_node, | |
679 | rb_node); | |
680 | ||
681 | if (addr < i->addr) | |
682 | n = n->rb_left; | |
683 | else if (addr > i->addr) | |
684 | n = n->rb_right; | |
685 | else | |
686 | return i; | |
687 | } | |
688 | ||
689 | return NULL; | |
690 | } | |
691 | ||
692 | void inlines__tree_delete(struct rb_root *tree) | |
693 | { | |
694 | struct inline_node *pos; | |
695 | struct rb_node *next = rb_first(tree); | |
696 | ||
697 | while (next) { | |
698 | pos = rb_entry(next, struct inline_node, rb_node); | |
699 | next = rb_next(&pos->rb_node); | |
700 | rb_erase(&pos->rb_node, tree); | |
701 | inline_node__delete(pos); | |
702 | } | |
703 | } |