summaryrefslogtreecommitdiffstats
path: root/tools/perf/util/srcline.c
diff options
context:
space:
mode:
authorKees Cook <kees+treewide@kernel.org>2026-09-02 15:31:14 -0700
committerKees Cook <kees@kernel.org>2026-09-04 21:37:00 -0700
commit3a2c4d55e32ad65efebdb6de44eef3bfa08bb49d (patch)
treec65086f9bdcd48c6360fb7cb4598bca084da1f32 /tools/perf/util/srcline.c
downloadlinux-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.c508
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);
+ }
+}