diff options
| author | Kees Cook <kees+treewide@kernel.org> | 2026-09-02 15:31:14 -0700 |
|---|---|---|
| committer | Kees Cook <kees@kernel.org> | 2026-09-04 21:37:00 -0700 |
| commit | 3a2c4d55e32ad65efebdb6de44eef3bfa08bb49d (patch) | |
| tree | c65086f9bdcd48c6360fb7cb4598bca084da1f32 /tools/perf/util/srcline.c | |
| download | linux-stable-3a2c4d55e32ad65efebdb6de44eef3bfa08bb49d.tar.gz linux-stable-3a2c4d55e32ad65efebdb6de44eef3bfa08bb49d.zip | |
treewide: refresh kmalloc_obj() conversionsgrafted
This is another run of the Coccinelle script for converting kmalloc()
family of allocations to kmalloc_obj() via the existing rules in
scripts/coccinelle/api/kmalloc_objs.cocci
This catches both the set of kmalloc() uses added since the first
kmalloc_obj() conversions in v7.0 and adds a large group missed in the
first pass due to Coccinelle not interacting well with the cleanup.h
scoped_...() family of macros[1]. I worked around this with spatch's
"--macro-file" argument to a file with all the scoped_...() macros mapped
to Coccinelle's YACFE_ITERATOR[2] as that was the closest viable control
flow indicator I could find.
Build tested allmodconfig on x86, arm64, arm, loongarch, mips, powerpc,
riscv, and s390 with no new warnings.
Link: https://lore.kernel.org/lkml/202609021314.8A9C0B8@keescook/ [1]
Link: https://github.com/coccinelle/coccinelle/blob/master/standard.h [2]
Signed-off-by: Kees Cook <kees+treewide@kernel.org>
Diffstat (limited to 'tools/perf/util/srcline.c')
| -rw-r--r-- | tools/perf/util/srcline.c | 508 |
1 files changed, 508 insertions, 0 deletions
diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c new file mode 100644 index 000000000..b082178c2 --- /dev/null +++ b/tools/perf/util/srcline.c @@ -0,0 +1,508 @@ +// 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 <inttypes.h> +#include <string.h> +#include <linux/string.h> +#include <linux/zalloc.h> + +bool srcline_full_filename; + +char *srcline__unknown = (char *)"??:0"; + +static const char *srcline_dso_name(struct dso *dso) +{ + const char *dso_name; + + if (dso__symsrc_filename(dso)) + dso_name = dso__symsrc_filename(dso); + else + dso_name = dso__long_name(dso); + + if (dso_name[0] == '[') + return NULL; + + if (is_perf_pid_map_name(dso_name)) + return NULL; + + return dso_name; +} + +int inline_list__append(struct symbol *symbol, char *srcline, struct inline_node *node) +{ + struct inline_list *ilist; + + ilist = zalloc(sizeof(*ilist)); + if (ilist == NULL) + return -1; + + ilist->symbol = symbol; + ilist->srcline = srcline; + + if (callchain_param.order == ORDER_CALLEE) + list_add_tail(&ilist->list, &node->val); + else + list_add(&ilist->list, &node->val); + + return 0; +} + +int inline_list__append_tail(struct symbol *symbol, char *srcline, struct inline_node *node) +{ + struct inline_list *ilist; + + ilist = zalloc(sizeof(*ilist)); + if (ilist == NULL) + return -1; + + ilist->symbol = symbol; + ilist->srcline = srcline; + + if (callchain_param.order == ORDER_CALLEE) + list_add(&ilist->list, &node->val); + else + list_add_tail(&ilist->list, &node->val); + + return 0; +} + +char *srcline_from_fileline(const char *file, unsigned int line) +{ + char *srcline; + + if (!file) + return NULL; + + if (!srcline_full_filename) + file = perf_basename(file); + + if (asprintf(&srcline, "%s:%u", file, line) < 0) + return NULL; + + return srcline; +} + +struct symbol *new_inline_sym(struct dso *dso, + struct symbol *base_sym, + const char *funcname) +{ + struct symbol *inline_sym; + char *demangled = NULL; + + if (!funcname) + funcname = "??"; + + if (dso) { + demangled = dso__demangle_sym(dso, 0, funcname); + if (demangled) + funcname = demangled; + } + + if (base_sym && strcmp(funcname, base_sym->name) == 0) { + /* reuse the real, existing symbol */ + inline_sym = base_sym; + /* ensure that we don't alias an inlined symbol, which could + * lead to double frees in inline_node__delete + */ + assert(!symbol__inlined(base_sym)); + } else { + /* create a fake symbol for the inline frame */ + inline_sym = symbol__new(base_sym ? base_sym->start : 0, + base_sym ? (base_sym->end - base_sym->start) : 0, + base_sym ? symbol__binding(base_sym) : 0, + base_sym ? symbol__type(base_sym) : 0, + funcname); + if (inline_sym) + symbol__set_inlined(inline_sym, true); + } + + free(demangled); + + return inline_sym; +} + +static int addr2line(const char *dso_name, u64 addr, char **file, unsigned int *line_nr, + struct dso *dso, bool unwind_inlines, struct inline_node *node, + struct symbol *sym) +{ + int ret = 0; + + if (symbol_conf.addr2line_style[0] == A2L_STYLE_UNKNOWN) { + int i = 0; + + /* Default addr2line fallback order. */ +#ifdef HAVE_LIBDW_SUPPORT + symbol_conf.addr2line_style[i++] = A2L_STYLE_LIBDW; +#endif +#ifdef HAVE_LIBLLVM_SUPPORT + symbol_conf.addr2line_style[i++] = A2L_STYLE_LLVM; +#endif +#ifdef HAVE_LIBBFD_SUPPORT + symbol_conf.addr2line_style[i++] = A2L_STYLE_LIBBFD; +#endif + symbol_conf.addr2line_style[i++] = A2L_STYLE_CMD; + } + + for (size_t i = 0; i < ARRAY_SIZE(symbol_conf.addr2line_style); i++) { + switch (symbol_conf.addr2line_style[i]) { + case A2L_STYLE_LIBDW: + ret = libdw__addr2line(addr, file, line_nr, dso, unwind_inlines, + node, sym); + break; + case A2L_STYLE_LLVM: + ret = llvm__addr2line(dso_name, addr, file, line_nr, dso, unwind_inlines, + node, sym); + break; + case A2L_STYLE_LIBBFD: + ret = libbfd__addr2line(dso_name, addr, file, line_nr, dso, unwind_inlines, + node, sym); + break; + case A2L_STYLE_CMD: + ret = cmd__addr2line(dso_name, addr, file, line_nr, dso, unwind_inlines, + node, sym); + break; + case A2L_STYLE_UNKNOWN: + default: + break; + } + if (ret > 0) + return ret; + } + + return 0; +} + +int addr2line_configure(const char *var, const char *value, void *cb __maybe_unused) +{ + static const char * const a2l_style_names[] = { + [A2L_STYLE_LIBDW] = "libdw", + [A2L_STYLE_LLVM] = "llvm", + [A2L_STYLE_LIBBFD] = "libbfd", + [A2L_STYLE_CMD] = "addr2line", + NULL + }; + + char *s, *p, *saveptr; + size_t i = 0; + + if (strcmp(var, "addr2line.style")) + return 0; + + if (!value) + return -1; + + s = strdup(value); + if (!s) + return -1; + + p = strtok_r(s, ",", &saveptr); + while (p && i < ARRAY_SIZE(symbol_conf.addr2line_style)) { + bool found = false; + char *q = strim(p); + + for (size_t j = A2L_STYLE_LIBDW; j < MAX_A2L_STYLE; j++) { + if (!strcasecmp(q, a2l_style_names[j])) { + symbol_conf.addr2line_style[i++] = j; + found = true; + break; + } + } + if (!found) + pr_warning("Unknown addr2line style: %s\n", q); + p = strtok_r(NULL, ",", &saveptr); + } + + free(s); + return 0; +} + +static struct inline_node *addr2inlines(const char *dso_name, u64 addr, + struct dso *dso, struct symbol *sym) +{ + struct inline_node *node; + + node = zalloc(sizeof(*node)); + if (node == NULL) { + perror("not enough memory for the inline node"); + return NULL; + } + + INIT_LIST_HEAD(&node->val); + node->addr = addr; + + addr2line(dso_name, addr, /*file=*/NULL, /*line_nr=*/NULL, dso, + /*unwind_inlines=*/true, node, sym); + + return node; +} + +/* + * Number of addr2line failures (without success) before disabling it for that + * dso. + */ +#define A2L_FAIL_LIMIT 123 + +char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym, + bool show_sym, bool show_addr, bool unwind_inlines, + u64 ip) +{ + char *file = NULL; + unsigned line = 0; + char *srcline; + const char *dso_name; + + if (!dso__has_srcline(dso)) + goto out; + + dso_name = srcline_dso_name(dso); + if (dso_name == NULL) + goto out_err; + + if (!addr2line(dso_name, addr, &file, &line, dso, + unwind_inlines, /*node=*/NULL, sym)) + goto out_err; + + srcline = srcline_from_fileline(file, line); + free(file); + + if (!srcline) + goto out_err; + + dso__set_a2l_fails(dso, 0); + + return srcline; + +out_err: + 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); + } +out: + if (!show_addr) + return (show_sym && sym) ? + strndup(sym->name, sym->namelen) : SRCLINE_UNKNOWN; + + if (sym) { + if (asprintf(&srcline, "%s+%" PRIu64, show_sym ? sym->name : "", + ip - sym->start) < 0) + return SRCLINE_UNKNOWN; + } else if (asprintf(&srcline, "%s[%" PRIx64 "]", dso__short_name(dso), addr) < 0) + return SRCLINE_UNKNOWN; + return srcline; +} + +/* Returns filename and fills in line number in line */ +char *get_srcline_split(struct dso *dso, u64 addr, unsigned *line) +{ + char *file = NULL; + const char *dso_name; + + if (!dso__has_srcline(dso)) + return NULL; + + dso_name = srcline_dso_name(dso); + if (dso_name == NULL) + goto out_err; + + if (!addr2line(dso_name, addr, &file, line, dso, /*unwind_inlines=*/true, + /*node=*/NULL, /*sym=*/NULL)) + goto out_err; + + dso__set_a2l_fails(dso, 0); + return file; + +out_err: + 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); + } + + return NULL; +} + +void zfree_srcline(char **srcline) +{ + if (*srcline == NULL) + return; + + if (*srcline != SRCLINE_UNKNOWN) + free(*srcline); + + *srcline = NULL; +} + +char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym, + bool show_sym, bool show_addr, u64 ip) +{ + return __get_srcline(dso, addr, sym, show_sym, show_addr, false, ip); +} + +struct srcline_node { + u64 addr; + char *srcline; + struct rb_node rb_node; +}; + +void srcline__tree_insert(struct rb_root_cached *tree, u64 addr, char *srcline) +{ + struct rb_node **p = &tree->rb_root.rb_node; + struct rb_node *parent = NULL; + struct srcline_node *i, *node; + bool leftmost = true; + + node = zalloc(sizeof(struct srcline_node)); + if (!node) { + perror("not enough memory for the srcline node"); + return; + } + + node->addr = addr; + node->srcline = srcline; + + while (*p != NULL) { + parent = *p; + i = rb_entry(parent, struct srcline_node, rb_node); + if (addr < i->addr) + p = &(*p)->rb_left; + else { + p = &(*p)->rb_right; + leftmost = false; + } + } + rb_link_node(&node->rb_node, parent, p); + rb_insert_color_cached(&node->rb_node, tree, leftmost); +} + +char *srcline__tree_find(struct rb_root_cached *tree, u64 addr) +{ + struct rb_node *n = tree->rb_root.rb_node; + + while (n) { + struct srcline_node *i = rb_entry(n, struct srcline_node, + rb_node); + + if (addr < i->addr) + n = n->rb_left; + else if (addr > i->addr) + n = n->rb_right; + else + return i->srcline; + } + + return NULL; +} + +void srcline__tree_delete(struct rb_root_cached *tree) +{ + struct srcline_node *pos; + struct rb_node *next = rb_first_cached(tree); + + while (next) { + pos = rb_entry(next, struct srcline_node, rb_node); + next = rb_next(&pos->rb_node); + rb_erase_cached(&pos->rb_node, tree); + zfree_srcline(&pos->srcline); + zfree(&pos); + } +} + +struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr, + struct symbol *sym) +{ + const char *dso_name; + + dso_name = srcline_dso_name(dso); + if (dso_name == NULL) + return NULL; + + return addr2inlines(dso_name, addr, dso, sym); +} + +void inline_node__clear_frames(struct inline_node *node) +{ + struct inline_list *ilist, *tmp; + + if (node == NULL) + return; + + list_for_each_entry_safe(ilist, tmp, &node->val, list) { + list_del_init(&ilist->list); + zfree_srcline(&ilist->srcline); + /* only the inlined symbols are owned by the list */ + if (ilist->symbol && symbol__inlined(ilist->symbol)) + symbol__delete(ilist->symbol); + free(ilist); + } +} + +void inline_node__delete(struct inline_node *node) +{ + inline_node__clear_frames(node); + free(node); +} + +void inlines__tree_insert(struct rb_root_cached *tree, + struct inline_node *inlines) +{ + struct rb_node **p = &tree->rb_root.rb_node; + struct rb_node *parent = NULL; + const u64 addr = inlines->addr; + struct inline_node *i; + bool leftmost = true; + + while (*p != NULL) { + parent = *p; + i = rb_entry(parent, struct inline_node, rb_node); + if (addr < i->addr) + p = &(*p)->rb_left; + else { + p = &(*p)->rb_right; + leftmost = false; + } + } + rb_link_node(&inlines->rb_node, parent, p); + rb_insert_color_cached(&inlines->rb_node, tree, leftmost); +} + +struct inline_node *inlines__tree_find(struct rb_root_cached *tree, u64 addr) +{ + struct rb_node *n = tree->rb_root.rb_node; + + while (n) { + struct inline_node *i = rb_entry(n, struct inline_node, + rb_node); + + if (addr < i->addr) + n = n->rb_left; + else if (addr > i->addr) + n = n->rb_right; + else + return i; + } + + return NULL; +} + +void inlines__tree_delete(struct rb_root_cached *tree) +{ + struct inline_node *pos; + struct rb_node *next = rb_first_cached(tree); + + while (next) { + pos = rb_entry(next, struct inline_node, rb_node); + next = rb_next(&pos->rb_node); + rb_erase_cached(&pos->rb_node, tree); + inline_node__delete(pos); + } +} |
