diff --git a/tools/perf/arch/powerpc/util/skip-callchain-idx.c b/tools/perf/arch/powerpc/util/skip-callchain-idx.c index e57f10798fa6..472714cfad38 100644 --- a/tools/perf/arch/powerpc/util/skip-callchain-idx.c +++ b/tools/perf/arch/powerpc/util/skip-callchain-idx.c @@ -152,9 +152,10 @@ static int check_return_addr(struct dso *dso, Dwarf_Addr mapped_pc) Dwarf_Addr end = mapped_pc; bool signalp; + mutex_lock(dso__lock(dso)); dwfl = dso__libdw_dwfl(dso); if (!dwfl) - return -1; + goto out; mod = dwfl_addrmodule(dwfl, mapped_pc); if (!mod) { @@ -183,6 +184,7 @@ static int check_return_addr(struct dso *dso, Dwarf_Addr mapped_pc) rc = check_return_reg(ra_regno, frame); out: + mutex_unlock(dso__lock(dso)); return rc; } diff --git a/tools/perf/util/addr2line.c b/tools/perf/util/addr2line.c index 4b0d349ed334..6f64f6b42442 100644 --- a/tools/perf/util/addr2line.c +++ b/tools/perf/util/addr2line.c @@ -15,6 +15,7 @@ #include #include #include +#include #define MAX_INLINE_NEST 1024 @@ -284,7 +285,7 @@ int cmd__addr2line(const char *dso_name, u64 addr, struct inline_node *node, struct symbol *sym __maybe_unused) { - struct child_process *a2l = dso__a2l(dso); + struct child_process *a2l; char *record_function = NULL; char *record_filename = NULL; unsigned int record_line_nr = 0; @@ -296,24 +297,38 @@ int cmd__addr2line(const char *dso_name, u64 addr, ssize_t written; struct io io = { .eof = false }; enum cmd_a2l_style cmd_a2l_style; + const char *current_dso_name; + + mutex_lock(dso__lock(dso)); + current_dso_name = dso__symsrc_filename(dso) ?: dso_name; + a2l = dso__a2l(dso); if (!a2l) { - if (!filename__has_section(dso_name, ".debug_line")) - goto out; + if (!filename__has_section(current_dso_name, ".debug_line")) + goto out_unlock; dso__set_a2l(dso, - addr2line_subprocess_init(symbol_conf.addr2line_path, dso_name)); + addr2line_subprocess_init(symbol_conf.addr2line_path, + current_dso_name)); a2l = dso__a2l(dso); } if (a2l == NULL) { if (!symbol_conf.addr2line_disable_warn) pr_warning("%s %s: addr2line_subprocess_init failed\n", __func__, dso_name); - goto out; + goto out_unlock; } cmd_a2l_style = cmd_addr2line_configure(a2l, dso_name); if (cmd_a2l_style == BROKEN) - goto out; + goto out_unlock; + + /* + * Take ownership of the a2l subprocess so we can safely perform + * blocking IPC without holding the dso lock. If another thread + * resolves a symbol concurrently, it will spawn a new a2l process. + */ + dso__set_a2l(dso, NULL); + mutex_unlock(dso__lock(dso)); /* * Send our request and then *deliberately* send something that can't be @@ -414,12 +429,21 @@ int cmd__addr2line(const char *dso_name, u64 addr, } out: + mutex_lock(dso__lock(dso)); free(record_function); free(record_filename); - if (io.eof) { - dso__set_a2l(dso, NULL); + + current_dso_name = dso__symsrc_filename(dso) ?: dso__long_name(dso); + if (!io.eof && dso__a2l(dso) == NULL && current_dso_name && + !strcmp(current_dso_name, dso_name)) + dso__set_a2l(dso, a2l); + else addr2line_subprocess_cleanup(a2l); - } + mutex_unlock(dso__lock(dso)); + return ret; + +out_unlock: + mutex_unlock(dso__lock(dso)); return ret; } diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 42bfe30a3b51..df39e6ca88e6 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -2075,3 +2075,12 @@ struct debuginfo *dso__debuginfo(struct dso *dso) free(name); return dinfo; } + +void dso__set_symsrc_filename(struct dso *dso, char *val) +{ + RC_CHK_ACCESS(dso)->symsrc_filename = val; + dso__free_libdw(dso); + dso__free_a2l(dso); + dso__set_has_srcline(dso, true); + dso__set_a2l_fails(dso, 0); +} diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index 55c4aaa53c38..7966c7048c85 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -735,10 +735,7 @@ static inline const char *dso__symsrc_filename(const struct dso *dso) return RC_CHK_ACCESS(dso)->symsrc_filename; } -static inline void dso__set_symsrc_filename(struct dso *dso, char *val) -{ - RC_CHK_ACCESS(dso)->symsrc_filename = val; -} +void dso__set_symsrc_filename(struct dso *dso, char *val); static inline void dso__free_symsrc_filename(struct dso *dso) { diff --git a/tools/perf/util/libbfd.c b/tools/perf/util/libbfd.c index 33dc6158b2b1..35aa9d98f2f8 100644 --- a/tools/perf/util/libbfd.c +++ b/tools/perf/util/libbfd.c @@ -226,7 +226,11 @@ int libbfd__addr2line(const char *dso_name, u64 addr, struct symbol *sym) { int ret = 0; - struct a2l_data *a2l = dso__a2l(dso); + struct a2l_data *a2l; + + mutex_lock(dso__lock(dso)); + dso_name = dso__symsrc_filename(dso) ?: dso_name; + a2l = dso__a2l(dso); if (!a2l) { a2l = addr2line_init(dso_name); @@ -236,7 +240,8 @@ int libbfd__addr2line(const char *dso_name, u64 addr, if (a2l == NULL) { if (!symbol_conf.addr2line_disable_warn) pr_warning("addr2line_init failed for %s\n", dso_name); - return 0; + ret = -1; + goto out; } a2l->addr = addr; @@ -244,14 +249,18 @@ int libbfd__addr2line(const char *dso_name, u64 addr, bfd_map_over_sections(a2l->abfd, find_address_in_section, a2l); - if (!a2l->found) - return 0; + if (!a2l->found) { + ret = 0; + goto out; + } if (unwind_inlines) { int cnt = 0; - if (node && inline_list__append_dso_a2l(dso, node, sym)) - return 0; + if (node && inline_list__append_dso_a2l(dso, node, sym)) { + ret = 0; + goto out; + } while (bfd_find_inliner_info(a2l->abfd, &a2l->filename, &a2l->funcname, &a2l->line) && @@ -261,8 +270,10 @@ int libbfd__addr2line(const char *dso_name, u64 addr, a2l->filename = NULL; if (node != NULL) { - if (inline_list__append_dso_a2l(dso, node, sym)) - return 0; + if (inline_list__append_dso_a2l(dso, node, sym)) { + ret = 0; + goto out; + } // found at least one inline frame ret = 1; } @@ -277,6 +288,8 @@ int libbfd__addr2line(const char *dso_name, u64 addr, if (line) *line = a2l->line; +out: + mutex_unlock(dso__lock(dso)); return ret; } diff --git a/tools/perf/util/libdw.c b/tools/perf/util/libdw.c index 4ca7e7e4fbe9..c4504cceb313 100644 --- a/tools/perf/util/libdw.c +++ b/tools/perf/util/libdw.c @@ -35,7 +35,7 @@ struct Dwfl *dso__libdw_dwfl(struct dso *dso) if (dwfl) return dwfl; - dso_name = dso__long_name(dso); + dso_name = dso__symsrc_filename(dso) ?: dso__long_name(dso); /* * Initialize Dwfl session. * We need to open the DSO file to report it to libdw. @@ -167,32 +167,41 @@ int libdw__addr2line(u64 addr, char **file, unsigned int *line_nr, struct dso *dso, bool unwind_inlines, struct inline_node *node, struct symbol *sym) { - Dwfl *dwfl = dso__libdw_dwfl(dso); + Dwfl *dwfl; Dwfl_Module *mod; Dwfl_Line *dwline; Dwarf_Addr bias; const char *src; int lineno = 0; + int ret = 0; + mutex_lock(dso__lock(dso)); + dwfl = dso__libdw_dwfl(dso); if (!dwfl) - return 0; + goto out; mod = dwfl_addrmodule(dwfl, addr); - if (!mod) - return 0; + if (!mod) { + ret = 0; + goto out; + } /* * Get/ignore the dwarf information. Determine the bias, difference * between the regular ELF addr2line addresses and those to use with * libdw. */ - if (!dwfl_module_getdwarf(mod, &bias)) - return 0; + if (!dwfl_module_getdwarf(mod, &bias)) { + ret = -1; + goto out; + } /* Find source line information for the address. */ dwline = dwfl_module_getsrc(mod, addr + bias); - if (!dwline) - return 0; + if (!dwline) { + ret = -1; + goto out; + } /* Get line information. */ src = dwfl_lineinfo(dwline, /*addr=*/NULL, &lineno, /*col=*/NULL, /*mtime=*/NULL, @@ -219,7 +228,8 @@ int libdw__addr2line(u64 addr, char **file, unsigned int *line_nr, free(*file); *file = NULL; } - return 0; + ret = 0; + goto out; } /* Walk from the parent down to the leaf. */ @@ -235,8 +245,13 @@ int libdw__addr2line(u64 addr, char **file, unsigned int *line_nr, *file = NULL; } inline_node__clear_frames(node); - return 0; + ret = 0; + goto out; } } - return 1; + ret = 1; + +out: + mutex_unlock(dso__lock(dso)); + return ret; } diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c index b082178c279b..5ed89f97ff11 100644 --- a/tools/perf/util/srcline.c +++ b/tools/perf/util/srcline.c @@ -1,40 +1,49 @@ // SPDX-License-Identifier: GPL-2.0 #include "srcline.h" -#include "addr2line.h" -#include "dso.h" -#include "callchain.h" -#include "libbfd.h" -#include "llvm.h" -#include "symbol.h" -#include "libdw.h" -#include "debug.h" -#include "util.h" #include +#include +#include #include + #include #include +#include "addr2line.h" +#include "callchain.h" +#include "debug.h" +#include "dso.h" +#include "libbfd.h" +#include "libdw.h" +#include "llvm.h" +#include "symbol.h" +#include "util.h" + bool srcline_full_filename; char *srcline__unknown = (char *)"??:0"; -static const char *srcline_dso_name(struct dso *dso) +static char *srcline_dso_name(struct dso *dso) { const char *dso_name; + char *ret = NULL; + mutex_lock(dso__lock(dso)); if (dso__symsrc_filename(dso)) dso_name = dso__symsrc_filename(dso); else dso_name = dso__long_name(dso); if (dso_name[0] == '[') - return NULL; + goto out; if (is_perf_pid_map_name(dso_name)) - return NULL; + goto out; - return dso_name; + ret = strdup(dso_name); +out: + mutex_unlock(dso__lock(dso)); + return ret; } int inline_list__append(struct symbol *symbol, char *srcline, struct inline_node *node) @@ -258,7 +267,7 @@ char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym, char *file = NULL; unsigned line = 0; char *srcline; - const char *dso_name; + char *dso_name; if (!dso__has_srcline(dso)) goto out; @@ -268,8 +277,11 @@ char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym, goto out_err; if (!addr2line(dso_name, addr, &file, &line, dso, - unwind_inlines, /*node=*/NULL, sym)) + unwind_inlines, /*node=*/NULL, sym)) { + free(dso_name); goto out_err; + } + free(dso_name); srcline = srcline_from_fileline(file, line); free(file); @@ -277,16 +289,20 @@ char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym, if (!srcline) goto out_err; + mutex_lock(dso__lock(dso)); dso__set_a2l_fails(dso, 0); + mutex_unlock(dso__lock(dso)); return srcline; out_err: + mutex_lock(dso__lock(dso)); dso__set_a2l_fails(dso, dso__a2l_fails(dso) + 1); if (dso__a2l_fails(dso) > A2L_FAIL_LIMIT) { dso__set_has_srcline(dso, false); dso__free_a2l(dso); } + mutex_unlock(dso__lock(dso)); out: if (!show_addr) return (show_sym && sym) ? @@ -305,7 +321,7 @@ char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym, char *get_srcline_split(struct dso *dso, u64 addr, unsigned *line) { char *file = NULL; - const char *dso_name; + char *dso_name; if (!dso__has_srcline(dso)) return NULL; @@ -315,18 +331,25 @@ char *get_srcline_split(struct dso *dso, u64 addr, unsigned *line) goto out_err; if (!addr2line(dso_name, addr, &file, line, dso, /*unwind_inlines=*/true, - /*node=*/NULL, /*sym=*/NULL)) + /*node=*/NULL, /*sym=*/NULL)) { + free(dso_name); goto out_err; + } + free(dso_name); + mutex_lock(dso__lock(dso)); dso__set_a2l_fails(dso, 0); + mutex_unlock(dso__lock(dso)); return file; out_err: + mutex_lock(dso__lock(dso)); dso__set_a2l_fails(dso, dso__a2l_fails(dso) + 1); if (dso__a2l_fails(dso) > A2L_FAIL_LIMIT) { dso__set_has_srcline(dso, false); dso__free_a2l(dso); } + mutex_unlock(dso__lock(dso)); return NULL; } @@ -420,13 +443,16 @@ void srcline__tree_delete(struct rb_root_cached *tree) struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr, struct symbol *sym) { - const char *dso_name; + char *dso_name; + struct inline_node *node; dso_name = srcline_dso_name(dso); if (dso_name == NULL) return NULL; - return addr2inlines(dso_name, addr, dso, sym); + node = addr2inlines(dso_name, addr, dso, sym); + free(dso_name); + return node; } void inline_node__clear_frames(struct inline_node *node) diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c index ebea101e1001..4ccfcc7c2dfc 100644 --- a/tools/perf/util/unwind-libdw.c +++ b/tools/perf/util/unwind-libdw.c @@ -42,8 +42,10 @@ static int __find_debuginfo(Dwfl_Module *mod __maybe_unused, void **userdata, const struct dso *dso = *userdata; assert(dso); + mutex_lock(dso__lock((struct dso *)dso)); if (dso__symsrc_filename(dso) && strcmp(file_name, dso__symsrc_filename(dso))) *debuginfo_file_name = strdup(dso__symsrc_filename(dso)); + mutex_unlock(dso__lock((struct dso *)dso)); return -1; } diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c index 73d191ce51a5..6052383e862b 100644 --- a/tools/perf/util/unwind-libunwind.c +++ b/tools/perf/util/unwind-libunwind.c @@ -1,20 +1,28 @@ // SPDX-License-Identifier: GPL-2.0 +#include "unwind.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + #include "callchain.h" #include "debug.h" #include "dso.h" #include "env.h" +#include "libunwind-arch/libunwind-arch.h" #include "map.h" #include "perf_regs.h" #include "session.h" #include "symbol.h" #include "thread.h" -#include "unwind.h" -#include "libunwind-arch/libunwind-arch.h" -#include -#include -#include -#include -#include #define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */ #define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */ @@ -293,10 +301,19 @@ static int read_unwind_spec_debug_frame(struct dso *dso, } if (ofs <= 0) { - fd = open(dso__symsrc_filename(dso), O_RDONLY); - if (fd >= 0) { - ofs = elf_section_offset(fd, ".debug_frame"); - close(fd); + char *alloc_name; + + mutex_lock(dso__lock(dso)); + alloc_name = dso__symsrc_filename(dso) ? + strdup(dso__symsrc_filename(dso)) : NULL; + mutex_unlock(dso__lock(dso)); + if (alloc_name) { + fd = open(alloc_name, O_RDONLY); + if (fd >= 0) { + ofs = elf_section_offset(fd, ".debug_frame"); + close(fd); + } + free(alloc_name); } } @@ -321,6 +338,7 @@ static int read_unwind_spec_debug_frame(struct dso *dso, } } if (ofs > 0) { + mutex_lock(dso__lock(dso)); if (dso__symsrc_filename(dso) != NULL) { pr_warning( "%s: overwrite symsrc(%s,%s)\n", @@ -330,6 +348,7 @@ static int read_unwind_spec_debug_frame(struct dso *dso, dso__free_symsrc_filename(dso); } dso__set_symsrc_filename(dso, debuglink); + mutex_unlock(dso__lock(dso)); } else { free(debuglink); } @@ -429,12 +448,23 @@ int __libunwind__find_proc_info(void *as, uint64_t ip, void *pi, int need_unwind dso__data_put_fd(dso); } - symfile = dso__symsrc_filename(dso) ?: dso__name(dso); + mutex_lock(dso__lock(dso)); + symfile = dso__symsrc_filename(dso) ? + strdup(dso__symsrc_filename(dso)) : + strdup(dso__name(dso)); + mutex_unlock(dso__lock(dso)); - if (libunwind_arch__dwarf_find_debug_frame(ui->e_machine, /*found=*/0, &di, ip, - base, symfile, start, map__end(map))) { - ret = libunwind_arch__dwarf_search_unwind_table(ui->e_machine, as, ip, &di, pi, - need_unwind_info, arg); + if (symfile) { + if (libunwind_arch__dwarf_find_debug_frame(ui->e_machine, + /*found=*/0, &di, ip, + base, symfile, start, + map__end(map))) { + ret = libunwind_arch__dwarf_search_unwind_table(ui->e_machine, as, + ip, &di, pi, + need_unwind_info, + arg); + } + free((char *)symfile); } } map__put(map);