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