162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <inttypes.h>
362306a36Sopenharmony_ci#include <signal.h>
462306a36Sopenharmony_ci#include <stdio.h>
562306a36Sopenharmony_ci#include <stdlib.h>
662306a36Sopenharmony_ci#include <string.h>
762306a36Sopenharmony_ci#include <sys/types.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/string.h>
1162306a36Sopenharmony_ci#include <linux/zalloc.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <api/io.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "util/dso.h"
1662306a36Sopenharmony_ci#include "util/debug.h"
1762306a36Sopenharmony_ci#include "util/callchain.h"
1862306a36Sopenharmony_ci#include "util/symbol_conf.h"
1962306a36Sopenharmony_ci#include "srcline.h"
2062306a36Sopenharmony_ci#include "string2.h"
2162306a36Sopenharmony_ci#include "symbol.h"
2262306a36Sopenharmony_ci#include "subcmd/run-command.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/* If addr2line doesn't return data for 1 second then timeout. */
2562306a36Sopenharmony_ciint addr2line_timeout_ms = 1 * 1000;
2662306a36Sopenharmony_cibool srcline_full_filename;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cichar *srcline__unknown = (char *)"??:0";
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic const char *dso__name(struct dso *dso)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	const char *dso_name;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	if (dso->symsrc_filename)
3562306a36Sopenharmony_ci		dso_name = dso->symsrc_filename;
3662306a36Sopenharmony_ci	else
3762306a36Sopenharmony_ci		dso_name = dso->long_name;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	if (dso_name[0] == '[')
4062306a36Sopenharmony_ci		return NULL;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	if (!strncmp(dso_name, "/tmp/perf-", 10))
4362306a36Sopenharmony_ci		return NULL;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	return dso_name;
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic int inline_list__append(struct symbol *symbol, char *srcline,
4962306a36Sopenharmony_ci			       struct inline_node *node)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	struct inline_list *ilist;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	ilist = zalloc(sizeof(*ilist));
5462306a36Sopenharmony_ci	if (ilist == NULL)
5562306a36Sopenharmony_ci		return -1;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	ilist->symbol = symbol;
5862306a36Sopenharmony_ci	ilist->srcline = srcline;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	if (callchain_param.order == ORDER_CALLEE)
6162306a36Sopenharmony_ci		list_add_tail(&ilist->list, &node->val);
6262306a36Sopenharmony_ci	else
6362306a36Sopenharmony_ci		list_add(&ilist->list, &node->val);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	return 0;
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/* basename version that takes a const input string */
6962306a36Sopenharmony_cistatic const char *gnu_basename(const char *path)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	const char *base = strrchr(path, '/');
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	return base ? base + 1 : path;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic char *srcline_from_fileline(const char *file, unsigned int line)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	char *srcline;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if (!file)
8162306a36Sopenharmony_ci		return NULL;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	if (!srcline_full_filename)
8462306a36Sopenharmony_ci		file = gnu_basename(file);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	if (asprintf(&srcline, "%s:%u", file, line) < 0)
8762306a36Sopenharmony_ci		return NULL;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	return srcline;
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic struct symbol *new_inline_sym(struct dso *dso,
9362306a36Sopenharmony_ci				     struct symbol *base_sym,
9462306a36Sopenharmony_ci				     const char *funcname)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	struct symbol *inline_sym;
9762306a36Sopenharmony_ci	char *demangled = NULL;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	if (!funcname)
10062306a36Sopenharmony_ci		funcname = "??";
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	if (dso) {
10362306a36Sopenharmony_ci		demangled = dso__demangle_sym(dso, 0, funcname);
10462306a36Sopenharmony_ci		if (demangled)
10562306a36Sopenharmony_ci			funcname = demangled;
10662306a36Sopenharmony_ci	}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	if (base_sym && strcmp(funcname, base_sym->name) == 0) {
10962306a36Sopenharmony_ci		/* reuse the real, existing symbol */
11062306a36Sopenharmony_ci		inline_sym = base_sym;
11162306a36Sopenharmony_ci		/* ensure that we don't alias an inlined symbol, which could
11262306a36Sopenharmony_ci		 * lead to double frees in inline_node__delete
11362306a36Sopenharmony_ci		 */
11462306a36Sopenharmony_ci		assert(!base_sym->inlined);
11562306a36Sopenharmony_ci	} else {
11662306a36Sopenharmony_ci		/* create a fake symbol for the inline frame */
11762306a36Sopenharmony_ci		inline_sym = symbol__new(base_sym ? base_sym->start : 0,
11862306a36Sopenharmony_ci					 base_sym ? (base_sym->end - base_sym->start) : 0,
11962306a36Sopenharmony_ci					 base_sym ? base_sym->binding : 0,
12062306a36Sopenharmony_ci					 base_sym ? base_sym->type : 0,
12162306a36Sopenharmony_ci					 funcname);
12262306a36Sopenharmony_ci		if (inline_sym)
12362306a36Sopenharmony_ci			inline_sym->inlined = 1;
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	free(demangled);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	return inline_sym;
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci#define MAX_INLINE_NEST 1024
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci#ifdef HAVE_LIBBFD_SUPPORT
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci/*
13662306a36Sopenharmony_ci * Implement addr2line using libbfd.
13762306a36Sopenharmony_ci */
13862306a36Sopenharmony_ci#define PACKAGE "perf"
13962306a36Sopenharmony_ci#include <bfd.h>
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistruct a2l_data {
14262306a36Sopenharmony_ci	const char 	*input;
14362306a36Sopenharmony_ci	u64	 	addr;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	bool 		found;
14662306a36Sopenharmony_ci	const char 	*filename;
14762306a36Sopenharmony_ci	const char 	*funcname;
14862306a36Sopenharmony_ci	unsigned 	line;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	bfd 		*abfd;
15162306a36Sopenharmony_ci	asymbol 	**syms;
15262306a36Sopenharmony_ci};
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic int bfd_error(const char *string)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	const char *errmsg;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	errmsg = bfd_errmsg(bfd_get_error());
15962306a36Sopenharmony_ci	fflush(stdout);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	if (string)
16262306a36Sopenharmony_ci		pr_debug("%s: %s\n", string, errmsg);
16362306a36Sopenharmony_ci	else
16462306a36Sopenharmony_ci		pr_debug("%s\n", errmsg);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	return -1;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic int slurp_symtab(bfd *abfd, struct a2l_data *a2l)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	long storage;
17262306a36Sopenharmony_ci	long symcount;
17362306a36Sopenharmony_ci	asymbol **syms;
17462306a36Sopenharmony_ci	bfd_boolean dynamic = FALSE;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	if ((bfd_get_file_flags(abfd) & HAS_SYMS) == 0)
17762306a36Sopenharmony_ci		return bfd_error(bfd_get_filename(abfd));
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	storage = bfd_get_symtab_upper_bound(abfd);
18062306a36Sopenharmony_ci	if (storage == 0L) {
18162306a36Sopenharmony_ci		storage = bfd_get_dynamic_symtab_upper_bound(abfd);
18262306a36Sopenharmony_ci		dynamic = TRUE;
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci	if (storage < 0L)
18562306a36Sopenharmony_ci		return bfd_error(bfd_get_filename(abfd));
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	syms = malloc(storage);
18862306a36Sopenharmony_ci	if (dynamic)
18962306a36Sopenharmony_ci		symcount = bfd_canonicalize_dynamic_symtab(abfd, syms);
19062306a36Sopenharmony_ci	else
19162306a36Sopenharmony_ci		symcount = bfd_canonicalize_symtab(abfd, syms);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	if (symcount < 0) {
19462306a36Sopenharmony_ci		free(syms);
19562306a36Sopenharmony_ci		return bfd_error(bfd_get_filename(abfd));
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	a2l->syms = syms;
19962306a36Sopenharmony_ci	return 0;
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic void find_address_in_section(bfd *abfd, asection *section, void *data)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	bfd_vma pc, vma;
20562306a36Sopenharmony_ci	bfd_size_type size;
20662306a36Sopenharmony_ci	struct a2l_data *a2l = data;
20762306a36Sopenharmony_ci	flagword flags;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	if (a2l->found)
21062306a36Sopenharmony_ci		return;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci#ifdef bfd_get_section_flags
21362306a36Sopenharmony_ci	flags = bfd_get_section_flags(abfd, section);
21462306a36Sopenharmony_ci#else
21562306a36Sopenharmony_ci	flags = bfd_section_flags(section);
21662306a36Sopenharmony_ci#endif
21762306a36Sopenharmony_ci	if ((flags & SEC_ALLOC) == 0)
21862306a36Sopenharmony_ci		return;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	pc = a2l->addr;
22162306a36Sopenharmony_ci#ifdef bfd_get_section_vma
22262306a36Sopenharmony_ci	vma = bfd_get_section_vma(abfd, section);
22362306a36Sopenharmony_ci#else
22462306a36Sopenharmony_ci	vma = bfd_section_vma(section);
22562306a36Sopenharmony_ci#endif
22662306a36Sopenharmony_ci#ifdef bfd_get_section_size
22762306a36Sopenharmony_ci	size = bfd_get_section_size(section);
22862306a36Sopenharmony_ci#else
22962306a36Sopenharmony_ci	size = bfd_section_size(section);
23062306a36Sopenharmony_ci#endif
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	if (pc < vma || pc >= vma + size)
23362306a36Sopenharmony_ci		return;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	a2l->found = bfd_find_nearest_line(abfd, section, a2l->syms, pc - vma,
23662306a36Sopenharmony_ci					   &a2l->filename, &a2l->funcname,
23762306a36Sopenharmony_ci					   &a2l->line);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	if (a2l->filename && !strlen(a2l->filename))
24062306a36Sopenharmony_ci		a2l->filename = NULL;
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic struct a2l_data *addr2line_init(const char *path)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	bfd *abfd;
24662306a36Sopenharmony_ci	struct a2l_data *a2l = NULL;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	abfd = bfd_openr(path, NULL);
24962306a36Sopenharmony_ci	if (abfd == NULL)
25062306a36Sopenharmony_ci		return NULL;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	if (!bfd_check_format(abfd, bfd_object))
25362306a36Sopenharmony_ci		goto out;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	a2l = zalloc(sizeof(*a2l));
25662306a36Sopenharmony_ci	if (a2l == NULL)
25762306a36Sopenharmony_ci		goto out;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	a2l->abfd = abfd;
26062306a36Sopenharmony_ci	a2l->input = strdup(path);
26162306a36Sopenharmony_ci	if (a2l->input == NULL)
26262306a36Sopenharmony_ci		goto out;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	if (slurp_symtab(abfd, a2l))
26562306a36Sopenharmony_ci		goto out;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	return a2l;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ciout:
27062306a36Sopenharmony_ci	if (a2l) {
27162306a36Sopenharmony_ci		zfree((char **)&a2l->input);
27262306a36Sopenharmony_ci		free(a2l);
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci	bfd_close(abfd);
27562306a36Sopenharmony_ci	return NULL;
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistatic void addr2line_cleanup(struct a2l_data *a2l)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	if (a2l->abfd)
28162306a36Sopenharmony_ci		bfd_close(a2l->abfd);
28262306a36Sopenharmony_ci	zfree((char **)&a2l->input);
28362306a36Sopenharmony_ci	zfree(&a2l->syms);
28462306a36Sopenharmony_ci	free(a2l);
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic int inline_list__append_dso_a2l(struct dso *dso,
28862306a36Sopenharmony_ci				       struct inline_node *node,
28962306a36Sopenharmony_ci				       struct symbol *sym)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	struct a2l_data *a2l = dso->a2l;
29262306a36Sopenharmony_ci	struct symbol *inline_sym = new_inline_sym(dso, sym, a2l->funcname);
29362306a36Sopenharmony_ci	char *srcline = NULL;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	if (a2l->filename)
29662306a36Sopenharmony_ci		srcline = srcline_from_fileline(a2l->filename, a2l->line);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	return inline_list__append(inline_sym, srcline, node);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cistatic int addr2line(const char *dso_name, u64 addr,
30262306a36Sopenharmony_ci		     char **file, unsigned int *line, struct dso *dso,
30362306a36Sopenharmony_ci		     bool unwind_inlines, struct inline_node *node,
30462306a36Sopenharmony_ci		     struct symbol *sym)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	int ret = 0;
30762306a36Sopenharmony_ci	struct a2l_data *a2l = dso->a2l;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	if (!a2l) {
31062306a36Sopenharmony_ci		dso->a2l = addr2line_init(dso_name);
31162306a36Sopenharmony_ci		a2l = dso->a2l;
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	if (a2l == NULL) {
31562306a36Sopenharmony_ci		if (!symbol_conf.disable_add2line_warn)
31662306a36Sopenharmony_ci			pr_warning("addr2line_init failed for %s\n", dso_name);
31762306a36Sopenharmony_ci		return 0;
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	a2l->addr = addr;
32162306a36Sopenharmony_ci	a2l->found = false;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	bfd_map_over_sections(a2l->abfd, find_address_in_section, a2l);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	if (!a2l->found)
32662306a36Sopenharmony_ci		return 0;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	if (unwind_inlines) {
32962306a36Sopenharmony_ci		int cnt = 0;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci		if (node && inline_list__append_dso_a2l(dso, node, sym))
33262306a36Sopenharmony_ci			return 0;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci		while (bfd_find_inliner_info(a2l->abfd, &a2l->filename,
33562306a36Sopenharmony_ci					     &a2l->funcname, &a2l->line) &&
33662306a36Sopenharmony_ci		       cnt++ < MAX_INLINE_NEST) {
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci			if (a2l->filename && !strlen(a2l->filename))
33962306a36Sopenharmony_ci				a2l->filename = NULL;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci			if (node != NULL) {
34262306a36Sopenharmony_ci				if (inline_list__append_dso_a2l(dso, node, sym))
34362306a36Sopenharmony_ci					return 0;
34462306a36Sopenharmony_ci				// found at least one inline frame
34562306a36Sopenharmony_ci				ret = 1;
34662306a36Sopenharmony_ci			}
34762306a36Sopenharmony_ci		}
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	if (file) {
35162306a36Sopenharmony_ci		*file = a2l->filename ? strdup(a2l->filename) : NULL;
35262306a36Sopenharmony_ci		ret = *file ? 1 : 0;
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	if (line)
35662306a36Sopenharmony_ci		*line = a2l->line;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	return ret;
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_civoid dso__free_a2l(struct dso *dso)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	struct a2l_data *a2l = dso->a2l;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	if (!a2l)
36662306a36Sopenharmony_ci		return;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	addr2line_cleanup(a2l);
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	dso->a2l = NULL;
37162306a36Sopenharmony_ci}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci#else /* HAVE_LIBBFD_SUPPORT */
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_cistatic int filename_split(char *filename, unsigned int *line_nr)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	char *sep;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	sep = strchr(filename, '\n');
38062306a36Sopenharmony_ci	if (sep)
38162306a36Sopenharmony_ci		*sep = '\0';
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	if (!strcmp(filename, "??:0"))
38462306a36Sopenharmony_ci		return 0;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	sep = strchr(filename, ':');
38762306a36Sopenharmony_ci	if (sep) {
38862306a36Sopenharmony_ci		*sep++ = '\0';
38962306a36Sopenharmony_ci		*line_nr = strtoul(sep, NULL, 0);
39062306a36Sopenharmony_ci		return 1;
39162306a36Sopenharmony_ci	}
39262306a36Sopenharmony_ci	pr_debug("addr2line missing ':' in filename split\n");
39362306a36Sopenharmony_ci	return 0;
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_cistatic void addr2line_subprocess_cleanup(struct child_process *a2l)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	if (a2l->pid != -1) {
39962306a36Sopenharmony_ci		kill(a2l->pid, SIGKILL);
40062306a36Sopenharmony_ci		finish_command(a2l); /* ignore result, we don't care */
40162306a36Sopenharmony_ci		a2l->pid = -1;
40262306a36Sopenharmony_ci		close(a2l->in);
40362306a36Sopenharmony_ci		close(a2l->out);
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	free(a2l);
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cistatic struct child_process *addr2line_subprocess_init(const char *addr2line_path,
41062306a36Sopenharmony_ci							const char *binary_path)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	const char *argv[] = {
41362306a36Sopenharmony_ci		addr2line_path ?: "addr2line",
41462306a36Sopenharmony_ci		"-e", binary_path,
41562306a36Sopenharmony_ci		"-a", "-i", "-f", NULL
41662306a36Sopenharmony_ci	};
41762306a36Sopenharmony_ci	struct child_process *a2l = zalloc(sizeof(*a2l));
41862306a36Sopenharmony_ci	int start_command_status = 0;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	if (a2l == NULL) {
42162306a36Sopenharmony_ci		pr_err("Failed to allocate memory for addr2line");
42262306a36Sopenharmony_ci		return NULL;
42362306a36Sopenharmony_ci	}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	a2l->pid = -1;
42662306a36Sopenharmony_ci	a2l->in = -1;
42762306a36Sopenharmony_ci	a2l->out = -1;
42862306a36Sopenharmony_ci	a2l->no_stderr = 1;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	a2l->argv = argv;
43162306a36Sopenharmony_ci	start_command_status = start_command(a2l);
43262306a36Sopenharmony_ci	a2l->argv = NULL; /* it's not used after start_command; avoid dangling pointers */
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	if (start_command_status != 0) {
43562306a36Sopenharmony_ci		pr_warning("could not start addr2line (%s) for %s: start_command return code %d\n",
43662306a36Sopenharmony_ci			addr2line_path, binary_path, start_command_status);
43762306a36Sopenharmony_ci		addr2line_subprocess_cleanup(a2l);
43862306a36Sopenharmony_ci		return NULL;
43962306a36Sopenharmony_ci	}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	return a2l;
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_cienum a2l_style {
44562306a36Sopenharmony_ci	BROKEN,
44662306a36Sopenharmony_ci	GNU_BINUTILS,
44762306a36Sopenharmony_ci	LLVM,
44862306a36Sopenharmony_ci};
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_cistatic enum a2l_style addr2line_configure(struct child_process *a2l, const char *dso_name)
45162306a36Sopenharmony_ci{
45262306a36Sopenharmony_ci	static bool cached;
45362306a36Sopenharmony_ci	static enum a2l_style style;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	if (!cached) {
45662306a36Sopenharmony_ci		char buf[128];
45762306a36Sopenharmony_ci		struct io io;
45862306a36Sopenharmony_ci		int ch;
45962306a36Sopenharmony_ci		int lines;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci		if (write(a2l->in, ",\n", 2) != 2)
46262306a36Sopenharmony_ci			return BROKEN;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci		io__init(&io, a2l->out, buf, sizeof(buf));
46562306a36Sopenharmony_ci		ch = io__get_char(&io);
46662306a36Sopenharmony_ci		if (ch == ',') {
46762306a36Sopenharmony_ci			style = LLVM;
46862306a36Sopenharmony_ci			cached = true;
46962306a36Sopenharmony_ci			lines = 1;
47062306a36Sopenharmony_ci			pr_debug("Detected LLVM addr2line style\n");
47162306a36Sopenharmony_ci		} else if (ch == '0') {
47262306a36Sopenharmony_ci			style = GNU_BINUTILS;
47362306a36Sopenharmony_ci			cached = true;
47462306a36Sopenharmony_ci			lines = 3;
47562306a36Sopenharmony_ci			pr_debug("Detected binutils addr2line style\n");
47662306a36Sopenharmony_ci		} else {
47762306a36Sopenharmony_ci			if (!symbol_conf.disable_add2line_warn) {
47862306a36Sopenharmony_ci				char *output = NULL;
47962306a36Sopenharmony_ci				size_t output_len;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci				io__getline(&io, &output, &output_len);
48262306a36Sopenharmony_ci				pr_warning("%s %s: addr2line configuration failed\n",
48362306a36Sopenharmony_ci					   __func__, dso_name);
48462306a36Sopenharmony_ci				pr_warning("\t%c%s", ch, output);
48562306a36Sopenharmony_ci			}
48662306a36Sopenharmony_ci			pr_debug("Unknown/broken addr2line style\n");
48762306a36Sopenharmony_ci			return BROKEN;
48862306a36Sopenharmony_ci		}
48962306a36Sopenharmony_ci		while (lines) {
49062306a36Sopenharmony_ci			ch = io__get_char(&io);
49162306a36Sopenharmony_ci			if (ch <= 0)
49262306a36Sopenharmony_ci				break;
49362306a36Sopenharmony_ci			if (ch == '\n')
49462306a36Sopenharmony_ci				lines--;
49562306a36Sopenharmony_ci		}
49662306a36Sopenharmony_ci		/* Ignore SIGPIPE in the event addr2line exits. */
49762306a36Sopenharmony_ci		signal(SIGPIPE, SIG_IGN);
49862306a36Sopenharmony_ci	}
49962306a36Sopenharmony_ci	return style;
50062306a36Sopenharmony_ci}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_cistatic int read_addr2line_record(struct io *io,
50362306a36Sopenharmony_ci				 enum a2l_style style,
50462306a36Sopenharmony_ci				 const char *dso_name,
50562306a36Sopenharmony_ci				 u64 addr,
50662306a36Sopenharmony_ci				 bool first,
50762306a36Sopenharmony_ci				 char **function,
50862306a36Sopenharmony_ci				 char **filename,
50962306a36Sopenharmony_ci				 unsigned int *line_nr)
51062306a36Sopenharmony_ci{
51162306a36Sopenharmony_ci	/*
51262306a36Sopenharmony_ci	 * Returns:
51362306a36Sopenharmony_ci	 * -1 ==> error
51462306a36Sopenharmony_ci	 * 0 ==> sentinel (or other ill-formed) record read
51562306a36Sopenharmony_ci	 * 1 ==> a genuine record read
51662306a36Sopenharmony_ci	 */
51762306a36Sopenharmony_ci	char *line = NULL;
51862306a36Sopenharmony_ci	size_t line_len = 0;
51962306a36Sopenharmony_ci	unsigned int dummy_line_nr = 0;
52062306a36Sopenharmony_ci	int ret = -1;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	if (function != NULL)
52362306a36Sopenharmony_ci		zfree(function);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	if (filename != NULL)
52662306a36Sopenharmony_ci		zfree(filename);
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	if (line_nr != NULL)
52962306a36Sopenharmony_ci		*line_nr = 0;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	/*
53262306a36Sopenharmony_ci	 * Read the first line. Without an error this will be:
53362306a36Sopenharmony_ci	 * - for the first line an address like 0x1234,
53462306a36Sopenharmony_ci	 * - the binutils sentinel 0x0000000000000000,
53562306a36Sopenharmony_ci	 * - the llvm-addr2line the sentinel ',' character,
53662306a36Sopenharmony_ci	 * - the function name line for an inlined function.
53762306a36Sopenharmony_ci	 */
53862306a36Sopenharmony_ci	if (io__getline(io, &line, &line_len) < 0 || !line_len)
53962306a36Sopenharmony_ci		goto error;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	pr_debug("%s %s: addr2line read address for sentinel: %s", __func__, dso_name, line);
54262306a36Sopenharmony_ci	if (style == LLVM && line_len == 2 && line[0] == ',') {
54362306a36Sopenharmony_ci		/* Found the llvm-addr2line sentinel character. */
54462306a36Sopenharmony_ci		zfree(&line);
54562306a36Sopenharmony_ci		return 0;
54662306a36Sopenharmony_ci	} else if (style == GNU_BINUTILS && (!first || addr != 0)) {
54762306a36Sopenharmony_ci		int zero_count = 0, non_zero_count = 0;
54862306a36Sopenharmony_ci		/*
54962306a36Sopenharmony_ci		 * Check for binutils sentinel ignoring it for the case the
55062306a36Sopenharmony_ci		 * requested address is 0.
55162306a36Sopenharmony_ci		 */
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci		/* A given address should always start 0x. */
55462306a36Sopenharmony_ci		if (line_len >= 2 || line[0] != '0' || line[1] != 'x') {
55562306a36Sopenharmony_ci			for (size_t i = 2; i < line_len; i++) {
55662306a36Sopenharmony_ci				if (line[i] == '0')
55762306a36Sopenharmony_ci					zero_count++;
55862306a36Sopenharmony_ci				else if (line[i] != '\n')
55962306a36Sopenharmony_ci					non_zero_count++;
56062306a36Sopenharmony_ci			}
56162306a36Sopenharmony_ci			if (!non_zero_count) {
56262306a36Sopenharmony_ci				int ch;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci				if (first && !zero_count) {
56562306a36Sopenharmony_ci					/* Line was erroneous just '0x'. */
56662306a36Sopenharmony_ci					goto error;
56762306a36Sopenharmony_ci				}
56862306a36Sopenharmony_ci				/*
56962306a36Sopenharmony_ci				 * Line was 0x0..0, the sentinel for binutils. Remove
57062306a36Sopenharmony_ci				 * the function and filename lines.
57162306a36Sopenharmony_ci				 */
57262306a36Sopenharmony_ci				zfree(&line);
57362306a36Sopenharmony_ci				do {
57462306a36Sopenharmony_ci					ch = io__get_char(io);
57562306a36Sopenharmony_ci				} while (ch > 0 && ch != '\n');
57662306a36Sopenharmony_ci				do {
57762306a36Sopenharmony_ci					ch = io__get_char(io);
57862306a36Sopenharmony_ci				} while (ch > 0 && ch != '\n');
57962306a36Sopenharmony_ci				return 0;
58062306a36Sopenharmony_ci			}
58162306a36Sopenharmony_ci		}
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ci	/* Read the second function name line (if inline data then this is the first line). */
58462306a36Sopenharmony_ci	if (first && (io__getline(io, &line, &line_len) < 0 || !line_len))
58562306a36Sopenharmony_ci		goto error;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	pr_debug("%s %s: addr2line read line: %s", __func__, dso_name, line);
58862306a36Sopenharmony_ci	if (function != NULL)
58962306a36Sopenharmony_ci		*function = strdup(strim(line));
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	zfree(&line);
59262306a36Sopenharmony_ci	line_len = 0;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	/* Read the third filename and line number line. */
59562306a36Sopenharmony_ci	if (io__getline(io, &line, &line_len) < 0 || !line_len)
59662306a36Sopenharmony_ci		goto error;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	pr_debug("%s %s: addr2line filename:number : %s", __func__, dso_name, line);
59962306a36Sopenharmony_ci	if (filename_split(line, line_nr == NULL ? &dummy_line_nr : line_nr) == 0 &&
60062306a36Sopenharmony_ci	    style == GNU_BINUTILS) {
60162306a36Sopenharmony_ci		ret = 0;
60262306a36Sopenharmony_ci		goto error;
60362306a36Sopenharmony_ci	}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	if (filename != NULL)
60662306a36Sopenharmony_ci		*filename = strdup(line);
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	zfree(&line);
60962306a36Sopenharmony_ci	line_len = 0;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	return 1;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_cierror:
61462306a36Sopenharmony_ci	free(line);
61562306a36Sopenharmony_ci	if (function != NULL)
61662306a36Sopenharmony_ci		zfree(function);
61762306a36Sopenharmony_ci	if (filename != NULL)
61862306a36Sopenharmony_ci		zfree(filename);
61962306a36Sopenharmony_ci	return ret;
62062306a36Sopenharmony_ci}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_cistatic int inline_list__append_record(struct dso *dso,
62362306a36Sopenharmony_ci				      struct inline_node *node,
62462306a36Sopenharmony_ci				      struct symbol *sym,
62562306a36Sopenharmony_ci				      const char *function,
62662306a36Sopenharmony_ci				      const char *filename,
62762306a36Sopenharmony_ci				      unsigned int line_nr)
62862306a36Sopenharmony_ci{
62962306a36Sopenharmony_ci	struct symbol *inline_sym = new_inline_sym(dso, sym, function);
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	return inline_list__append(inline_sym, srcline_from_fileline(filename, line_nr), node);
63262306a36Sopenharmony_ci}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_cistatic int addr2line(const char *dso_name, u64 addr,
63562306a36Sopenharmony_ci		     char **file, unsigned int *line_nr,
63662306a36Sopenharmony_ci		     struct dso *dso,
63762306a36Sopenharmony_ci		     bool unwind_inlines,
63862306a36Sopenharmony_ci		     struct inline_node *node,
63962306a36Sopenharmony_ci		     struct symbol *sym __maybe_unused)
64062306a36Sopenharmony_ci{
64162306a36Sopenharmony_ci	struct child_process *a2l = dso->a2l;
64262306a36Sopenharmony_ci	char *record_function = NULL;
64362306a36Sopenharmony_ci	char *record_filename = NULL;
64462306a36Sopenharmony_ci	unsigned int record_line_nr = 0;
64562306a36Sopenharmony_ci	int record_status = -1;
64662306a36Sopenharmony_ci	int ret = 0;
64762306a36Sopenharmony_ci	size_t inline_count = 0;
64862306a36Sopenharmony_ci	int len;
64962306a36Sopenharmony_ci	char buf[128];
65062306a36Sopenharmony_ci	ssize_t written;
65162306a36Sopenharmony_ci	struct io io = { .eof = false };
65262306a36Sopenharmony_ci	enum a2l_style a2l_style;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	if (!a2l) {
65562306a36Sopenharmony_ci		if (!filename__has_section(dso_name, ".debug_line"))
65662306a36Sopenharmony_ci			goto out;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci		dso->a2l = addr2line_subprocess_init(symbol_conf.addr2line_path, dso_name);
65962306a36Sopenharmony_ci		a2l = dso->a2l;
66062306a36Sopenharmony_ci	}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	if (a2l == NULL) {
66362306a36Sopenharmony_ci		if (!symbol_conf.disable_add2line_warn)
66462306a36Sopenharmony_ci			pr_warning("%s %s: addr2line_subprocess_init failed\n", __func__, dso_name);
66562306a36Sopenharmony_ci		goto out;
66662306a36Sopenharmony_ci	}
66762306a36Sopenharmony_ci	a2l_style = addr2line_configure(a2l, dso_name);
66862306a36Sopenharmony_ci	if (a2l_style == BROKEN)
66962306a36Sopenharmony_ci		goto out;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	/*
67262306a36Sopenharmony_ci	 * Send our request and then *deliberately* send something that can't be
67362306a36Sopenharmony_ci	 * interpreted as a valid address to ask addr2line about (namely,
67462306a36Sopenharmony_ci	 * ","). This causes addr2line to first write out the answer to our
67562306a36Sopenharmony_ci	 * request, in an unbounded/unknown number of records, and then to write
67662306a36Sopenharmony_ci	 * out the lines "0x0...0", "??" and "??:0", for GNU binutils, or ","
67762306a36Sopenharmony_ci	 * for llvm-addr2line, so that we can detect when it has finished giving
67862306a36Sopenharmony_ci	 * us anything useful.
67962306a36Sopenharmony_ci	 */
68062306a36Sopenharmony_ci	len = snprintf(buf, sizeof(buf), "%016"PRIx64"\n,\n", addr);
68162306a36Sopenharmony_ci	written = len > 0 ? write(a2l->in, buf, len) : -1;
68262306a36Sopenharmony_ci	if (written != len) {
68362306a36Sopenharmony_ci		if (!symbol_conf.disable_add2line_warn)
68462306a36Sopenharmony_ci			pr_warning("%s %s: could not send request\n", __func__, dso_name);
68562306a36Sopenharmony_ci		goto out;
68662306a36Sopenharmony_ci	}
68762306a36Sopenharmony_ci	io__init(&io, a2l->out, buf, sizeof(buf));
68862306a36Sopenharmony_ci	io.timeout_ms = addr2line_timeout_ms;
68962306a36Sopenharmony_ci	switch (read_addr2line_record(&io, a2l_style, dso_name, addr, /*first=*/true,
69062306a36Sopenharmony_ci				      &record_function, &record_filename, &record_line_nr)) {
69162306a36Sopenharmony_ci	case -1:
69262306a36Sopenharmony_ci		if (!symbol_conf.disable_add2line_warn)
69362306a36Sopenharmony_ci			pr_warning("%s %s: could not read first record\n", __func__, dso_name);
69462306a36Sopenharmony_ci		goto out;
69562306a36Sopenharmony_ci	case 0:
69662306a36Sopenharmony_ci		/*
69762306a36Sopenharmony_ci		 * The first record was invalid, so return failure, but first
69862306a36Sopenharmony_ci		 * read another record, since we sent a sentinel ',' for the
69962306a36Sopenharmony_ci		 * sake of detected the last inlined function. Treat this as the
70062306a36Sopenharmony_ci		 * first of a record as the ',' generates a new start with GNU
70162306a36Sopenharmony_ci		 * binutils, also force a non-zero address as we're no longer
70262306a36Sopenharmony_ci		 * reading that record.
70362306a36Sopenharmony_ci		 */
70462306a36Sopenharmony_ci		switch (read_addr2line_record(&io, a2l_style, dso_name,
70562306a36Sopenharmony_ci					      /*addr=*/1, /*first=*/true,
70662306a36Sopenharmony_ci					      NULL, NULL, NULL)) {
70762306a36Sopenharmony_ci		case -1:
70862306a36Sopenharmony_ci			if (!symbol_conf.disable_add2line_warn)
70962306a36Sopenharmony_ci				pr_warning("%s %s: could not read sentinel record\n",
71062306a36Sopenharmony_ci					   __func__, dso_name);
71162306a36Sopenharmony_ci			break;
71262306a36Sopenharmony_ci		case 0:
71362306a36Sopenharmony_ci			/* The sentinel as expected. */
71462306a36Sopenharmony_ci			break;
71562306a36Sopenharmony_ci		default:
71662306a36Sopenharmony_ci			if (!symbol_conf.disable_add2line_warn)
71762306a36Sopenharmony_ci				pr_warning("%s %s: unexpected record instead of sentinel",
71862306a36Sopenharmony_ci					   __func__, dso_name);
71962306a36Sopenharmony_ci			break;
72062306a36Sopenharmony_ci		}
72162306a36Sopenharmony_ci		goto out;
72262306a36Sopenharmony_ci	default:
72362306a36Sopenharmony_ci		/* First record as expected. */
72462306a36Sopenharmony_ci		break;
72562306a36Sopenharmony_ci	}
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	if (file) {
72862306a36Sopenharmony_ci		*file = strdup(record_filename);
72962306a36Sopenharmony_ci		ret = 1;
73062306a36Sopenharmony_ci	}
73162306a36Sopenharmony_ci	if (line_nr)
73262306a36Sopenharmony_ci		*line_nr = record_line_nr;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	if (unwind_inlines) {
73562306a36Sopenharmony_ci		if (node && inline_list__append_record(dso, node, sym,
73662306a36Sopenharmony_ci						       record_function,
73762306a36Sopenharmony_ci						       record_filename,
73862306a36Sopenharmony_ci						       record_line_nr)) {
73962306a36Sopenharmony_ci			ret = 0;
74062306a36Sopenharmony_ci			goto out;
74162306a36Sopenharmony_ci		}
74262306a36Sopenharmony_ci	}
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	/*
74562306a36Sopenharmony_ci	 * We have to read the records even if we don't care about the inline
74662306a36Sopenharmony_ci	 * info. This isn't the first record and force the address to non-zero
74762306a36Sopenharmony_ci	 * as we're reading records beyond the first.
74862306a36Sopenharmony_ci	 */
74962306a36Sopenharmony_ci	while ((record_status = read_addr2line_record(&io,
75062306a36Sopenharmony_ci						      a2l_style,
75162306a36Sopenharmony_ci						      dso_name,
75262306a36Sopenharmony_ci						      /*addr=*/1,
75362306a36Sopenharmony_ci						      /*first=*/false,
75462306a36Sopenharmony_ci						      &record_function,
75562306a36Sopenharmony_ci						      &record_filename,
75662306a36Sopenharmony_ci						      &record_line_nr)) == 1) {
75762306a36Sopenharmony_ci		if (unwind_inlines && node && inline_count++ < MAX_INLINE_NEST) {
75862306a36Sopenharmony_ci			if (inline_list__append_record(dso, node, sym,
75962306a36Sopenharmony_ci						       record_function,
76062306a36Sopenharmony_ci						       record_filename,
76162306a36Sopenharmony_ci						       record_line_nr)) {
76262306a36Sopenharmony_ci				ret = 0;
76362306a36Sopenharmony_ci				goto out;
76462306a36Sopenharmony_ci			}
76562306a36Sopenharmony_ci			ret = 1; /* found at least one inline frame */
76662306a36Sopenharmony_ci		}
76762306a36Sopenharmony_ci	}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ciout:
77062306a36Sopenharmony_ci	free(record_function);
77162306a36Sopenharmony_ci	free(record_filename);
77262306a36Sopenharmony_ci	if (io.eof) {
77362306a36Sopenharmony_ci		dso->a2l = NULL;
77462306a36Sopenharmony_ci		addr2line_subprocess_cleanup(a2l);
77562306a36Sopenharmony_ci	}
77662306a36Sopenharmony_ci	return ret;
77762306a36Sopenharmony_ci}
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_civoid dso__free_a2l(struct dso *dso)
78062306a36Sopenharmony_ci{
78162306a36Sopenharmony_ci	struct child_process *a2l = dso->a2l;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	if (!a2l)
78462306a36Sopenharmony_ci		return;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	addr2line_subprocess_cleanup(a2l);
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	dso->a2l = NULL;
78962306a36Sopenharmony_ci}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci#endif /* HAVE_LIBBFD_SUPPORT */
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_cistatic struct inline_node *addr2inlines(const char *dso_name, u64 addr,
79462306a36Sopenharmony_ci					struct dso *dso, struct symbol *sym)
79562306a36Sopenharmony_ci{
79662306a36Sopenharmony_ci	struct inline_node *node;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	node = zalloc(sizeof(*node));
79962306a36Sopenharmony_ci	if (node == NULL) {
80062306a36Sopenharmony_ci		perror("not enough memory for the inline node");
80162306a36Sopenharmony_ci		return NULL;
80262306a36Sopenharmony_ci	}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	INIT_LIST_HEAD(&node->val);
80562306a36Sopenharmony_ci	node->addr = addr;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	addr2line(dso_name, addr, NULL, NULL, dso, true, node, sym);
80862306a36Sopenharmony_ci	return node;
80962306a36Sopenharmony_ci}
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci/*
81262306a36Sopenharmony_ci * Number of addr2line failures (without success) before disabling it for that
81362306a36Sopenharmony_ci * dso.
81462306a36Sopenharmony_ci */
81562306a36Sopenharmony_ci#define A2L_FAIL_LIMIT 123
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_cichar *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
81862306a36Sopenharmony_ci		  bool show_sym, bool show_addr, bool unwind_inlines,
81962306a36Sopenharmony_ci		  u64 ip)
82062306a36Sopenharmony_ci{
82162306a36Sopenharmony_ci	char *file = NULL;
82262306a36Sopenharmony_ci	unsigned line = 0;
82362306a36Sopenharmony_ci	char *srcline;
82462306a36Sopenharmony_ci	const char *dso_name;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	if (!dso->has_srcline)
82762306a36Sopenharmony_ci		goto out;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	dso_name = dso__name(dso);
83062306a36Sopenharmony_ci	if (dso_name == NULL)
83162306a36Sopenharmony_ci		goto out;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	if (!addr2line(dso_name, addr, &file, &line, dso,
83462306a36Sopenharmony_ci		       unwind_inlines, NULL, sym))
83562306a36Sopenharmony_ci		goto out;
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	srcline = srcline_from_fileline(file, line);
83862306a36Sopenharmony_ci	free(file);
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	if (!srcline)
84162306a36Sopenharmony_ci		goto out;
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	dso->a2l_fails = 0;
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	return srcline;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ciout:
84862306a36Sopenharmony_ci	if (dso->a2l_fails && ++dso->a2l_fails > A2L_FAIL_LIMIT) {
84962306a36Sopenharmony_ci		dso->has_srcline = 0;
85062306a36Sopenharmony_ci		dso__free_a2l(dso);
85162306a36Sopenharmony_ci	}
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	if (!show_addr)
85462306a36Sopenharmony_ci		return (show_sym && sym) ?
85562306a36Sopenharmony_ci			    strndup(sym->name, sym->namelen) : SRCLINE_UNKNOWN;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	if (sym) {
85862306a36Sopenharmony_ci		if (asprintf(&srcline, "%s+%" PRIu64, show_sym ? sym->name : "",
85962306a36Sopenharmony_ci					ip - sym->start) < 0)
86062306a36Sopenharmony_ci			return SRCLINE_UNKNOWN;
86162306a36Sopenharmony_ci	} else if (asprintf(&srcline, "%s[%" PRIx64 "]", dso->short_name, addr) < 0)
86262306a36Sopenharmony_ci		return SRCLINE_UNKNOWN;
86362306a36Sopenharmony_ci	return srcline;
86462306a36Sopenharmony_ci}
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci/* Returns filename and fills in line number in line */
86762306a36Sopenharmony_cichar *get_srcline_split(struct dso *dso, u64 addr, unsigned *line)
86862306a36Sopenharmony_ci{
86962306a36Sopenharmony_ci	char *file = NULL;
87062306a36Sopenharmony_ci	const char *dso_name;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	if (!dso->has_srcline)
87362306a36Sopenharmony_ci		goto out;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	dso_name = dso__name(dso);
87662306a36Sopenharmony_ci	if (dso_name == NULL)
87762306a36Sopenharmony_ci		goto out;
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	if (!addr2line(dso_name, addr, &file, line, dso, true, NULL, NULL))
88062306a36Sopenharmony_ci		goto out;
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	dso->a2l_fails = 0;
88362306a36Sopenharmony_ci	return file;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ciout:
88662306a36Sopenharmony_ci	if (dso->a2l_fails && ++dso->a2l_fails > A2L_FAIL_LIMIT) {
88762306a36Sopenharmony_ci		dso->has_srcline = 0;
88862306a36Sopenharmony_ci		dso__free_a2l(dso);
88962306a36Sopenharmony_ci	}
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	return NULL;
89262306a36Sopenharmony_ci}
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_civoid zfree_srcline(char **srcline)
89562306a36Sopenharmony_ci{
89662306a36Sopenharmony_ci	if (*srcline == NULL)
89762306a36Sopenharmony_ci		return;
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	if (*srcline != SRCLINE_UNKNOWN)
90062306a36Sopenharmony_ci		free(*srcline);
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	*srcline = NULL;
90362306a36Sopenharmony_ci}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_cichar *get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
90662306a36Sopenharmony_ci		  bool show_sym, bool show_addr, u64 ip)
90762306a36Sopenharmony_ci{
90862306a36Sopenharmony_ci	return __get_srcline(dso, addr, sym, show_sym, show_addr, false, ip);
90962306a36Sopenharmony_ci}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_cistruct srcline_node {
91262306a36Sopenharmony_ci	u64			addr;
91362306a36Sopenharmony_ci	char			*srcline;
91462306a36Sopenharmony_ci	struct rb_node		rb_node;
91562306a36Sopenharmony_ci};
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_civoid srcline__tree_insert(struct rb_root_cached *tree, u64 addr, char *srcline)
91862306a36Sopenharmony_ci{
91962306a36Sopenharmony_ci	struct rb_node **p = &tree->rb_root.rb_node;
92062306a36Sopenharmony_ci	struct rb_node *parent = NULL;
92162306a36Sopenharmony_ci	struct srcline_node *i, *node;
92262306a36Sopenharmony_ci	bool leftmost = true;
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	node = zalloc(sizeof(struct srcline_node));
92562306a36Sopenharmony_ci	if (!node) {
92662306a36Sopenharmony_ci		perror("not enough memory for the srcline node");
92762306a36Sopenharmony_ci		return;
92862306a36Sopenharmony_ci	}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	node->addr = addr;
93162306a36Sopenharmony_ci	node->srcline = srcline;
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	while (*p != NULL) {
93462306a36Sopenharmony_ci		parent = *p;
93562306a36Sopenharmony_ci		i = rb_entry(parent, struct srcline_node, rb_node);
93662306a36Sopenharmony_ci		if (addr < i->addr)
93762306a36Sopenharmony_ci			p = &(*p)->rb_left;
93862306a36Sopenharmony_ci		else {
93962306a36Sopenharmony_ci			p = &(*p)->rb_right;
94062306a36Sopenharmony_ci			leftmost = false;
94162306a36Sopenharmony_ci		}
94262306a36Sopenharmony_ci	}
94362306a36Sopenharmony_ci	rb_link_node(&node->rb_node, parent, p);
94462306a36Sopenharmony_ci	rb_insert_color_cached(&node->rb_node, tree, leftmost);
94562306a36Sopenharmony_ci}
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_cichar *srcline__tree_find(struct rb_root_cached *tree, u64 addr)
94862306a36Sopenharmony_ci{
94962306a36Sopenharmony_ci	struct rb_node *n = tree->rb_root.rb_node;
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	while (n) {
95262306a36Sopenharmony_ci		struct srcline_node *i = rb_entry(n, struct srcline_node,
95362306a36Sopenharmony_ci						  rb_node);
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci		if (addr < i->addr)
95662306a36Sopenharmony_ci			n = n->rb_left;
95762306a36Sopenharmony_ci		else if (addr > i->addr)
95862306a36Sopenharmony_ci			n = n->rb_right;
95962306a36Sopenharmony_ci		else
96062306a36Sopenharmony_ci			return i->srcline;
96162306a36Sopenharmony_ci	}
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	return NULL;
96462306a36Sopenharmony_ci}
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_civoid srcline__tree_delete(struct rb_root_cached *tree)
96762306a36Sopenharmony_ci{
96862306a36Sopenharmony_ci	struct srcline_node *pos;
96962306a36Sopenharmony_ci	struct rb_node *next = rb_first_cached(tree);
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	while (next) {
97262306a36Sopenharmony_ci		pos = rb_entry(next, struct srcline_node, rb_node);
97362306a36Sopenharmony_ci		next = rb_next(&pos->rb_node);
97462306a36Sopenharmony_ci		rb_erase_cached(&pos->rb_node, tree);
97562306a36Sopenharmony_ci		zfree_srcline(&pos->srcline);
97662306a36Sopenharmony_ci		zfree(&pos);
97762306a36Sopenharmony_ci	}
97862306a36Sopenharmony_ci}
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_cistruct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr,
98162306a36Sopenharmony_ci					    struct symbol *sym)
98262306a36Sopenharmony_ci{
98362306a36Sopenharmony_ci	const char *dso_name;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	dso_name = dso__name(dso);
98662306a36Sopenharmony_ci	if (dso_name == NULL)
98762306a36Sopenharmony_ci		return NULL;
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	return addr2inlines(dso_name, addr, dso, sym);
99062306a36Sopenharmony_ci}
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_civoid inline_node__delete(struct inline_node *node)
99362306a36Sopenharmony_ci{
99462306a36Sopenharmony_ci	struct inline_list *ilist, *tmp;
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	list_for_each_entry_safe(ilist, tmp, &node->val, list) {
99762306a36Sopenharmony_ci		list_del_init(&ilist->list);
99862306a36Sopenharmony_ci		zfree_srcline(&ilist->srcline);
99962306a36Sopenharmony_ci		/* only the inlined symbols are owned by the list */
100062306a36Sopenharmony_ci		if (ilist->symbol && ilist->symbol->inlined)
100162306a36Sopenharmony_ci			symbol__delete(ilist->symbol);
100262306a36Sopenharmony_ci		free(ilist);
100362306a36Sopenharmony_ci	}
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	free(node);
100662306a36Sopenharmony_ci}
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_civoid inlines__tree_insert(struct rb_root_cached *tree,
100962306a36Sopenharmony_ci			  struct inline_node *inlines)
101062306a36Sopenharmony_ci{
101162306a36Sopenharmony_ci	struct rb_node **p = &tree->rb_root.rb_node;
101262306a36Sopenharmony_ci	struct rb_node *parent = NULL;
101362306a36Sopenharmony_ci	const u64 addr = inlines->addr;
101462306a36Sopenharmony_ci	struct inline_node *i;
101562306a36Sopenharmony_ci	bool leftmost = true;
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	while (*p != NULL) {
101862306a36Sopenharmony_ci		parent = *p;
101962306a36Sopenharmony_ci		i = rb_entry(parent, struct inline_node, rb_node);
102062306a36Sopenharmony_ci		if (addr < i->addr)
102162306a36Sopenharmony_ci			p = &(*p)->rb_left;
102262306a36Sopenharmony_ci		else {
102362306a36Sopenharmony_ci			p = &(*p)->rb_right;
102462306a36Sopenharmony_ci			leftmost = false;
102562306a36Sopenharmony_ci		}
102662306a36Sopenharmony_ci	}
102762306a36Sopenharmony_ci	rb_link_node(&inlines->rb_node, parent, p);
102862306a36Sopenharmony_ci	rb_insert_color_cached(&inlines->rb_node, tree, leftmost);
102962306a36Sopenharmony_ci}
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_cistruct inline_node *inlines__tree_find(struct rb_root_cached *tree, u64 addr)
103262306a36Sopenharmony_ci{
103362306a36Sopenharmony_ci	struct rb_node *n = tree->rb_root.rb_node;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	while (n) {
103662306a36Sopenharmony_ci		struct inline_node *i = rb_entry(n, struct inline_node,
103762306a36Sopenharmony_ci						 rb_node);
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci		if (addr < i->addr)
104062306a36Sopenharmony_ci			n = n->rb_left;
104162306a36Sopenharmony_ci		else if (addr > i->addr)
104262306a36Sopenharmony_ci			n = n->rb_right;
104362306a36Sopenharmony_ci		else
104462306a36Sopenharmony_ci			return i;
104562306a36Sopenharmony_ci	}
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	return NULL;
104862306a36Sopenharmony_ci}
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_civoid inlines__tree_delete(struct rb_root_cached *tree)
105162306a36Sopenharmony_ci{
105262306a36Sopenharmony_ci	struct inline_node *pos;
105362306a36Sopenharmony_ci	struct rb_node *next = rb_first_cached(tree);
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	while (next) {
105662306a36Sopenharmony_ci		pos = rb_entry(next, struct inline_node, rb_node);
105762306a36Sopenharmony_ci		next = rb_next(&pos->rb_node);
105862306a36Sopenharmony_ci		rb_erase_cached(&pos->rb_node, tree);
105962306a36Sopenharmony_ci		inline_node__delete(pos);
106062306a36Sopenharmony_ci	}
106162306a36Sopenharmony_ci}
1062