linux/tools/perf/util/unwind.c
Ian Rogers 4248ae6e79 perf unwind: Refactor get_entries to allow dynamic libdw/libunwind selection
Currently, both libdw and libunwind define 'unwind__get_entries'. This
causes a duplicate symbol build failure when both are compiled into
perf.

This commit refactors the DWARF unwind post-processing to be
configurable at runtime via the .perfconfig file option
'unwind.style', or using the argument '--unwind-style' in the commands
'perf report', 'perf script' and 'perf inject', in a similar manner to
the addr2line or the disassembler style.

The file 'tools/perf/util/unwind.c' adds the top-level dispatch
function 'unwind__get_entries'. The backend implementations are
renamed to 'libdw__get_entries' and 'libunwind__get_entries'. Both are
attempted as fallbacks if not configured, or if the primary backend
fails.

Fixes: 2e9191573a ("perf build: Remove NO_LIBDW_DWARF_UNWIND option")
Signed-off-by: Ian Rogers <irogers@google.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Albert Ou <aou@eecs.berkeley.edu>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Alexandre Ghiti <alex@ghiti.fr>
Cc: Andrew Jones <andrew.jones@oss.qualcomm.com>
Cc: Athira Rajeev <atrajeev@linux.ibm.com>
Cc: Dapeng Mi <dapeng1.mi@linux.intel.com>
Cc: Dmitrii Dolgov <9erthalion6@gmail.com>
Cc: Florian Fainelli <florian.fainelli@broadcom.com>
Cc: Howard Chu <howardchu95@gmail.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@linaro.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: John Garry <john.g.garry@oracle.com>
Cc: Leo Yan <leo.yan@linux.dev>
Cc: libunwind-devel@nongnu.org
Cc: Li Guan <guanli.oerv@isrc.iscas.ac.cn>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Paul Walmsley <pjw@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Shimin Guo <shimin.guo@skydio.com>
Cc: Thomas Richter <tmricht@linux.ibm.com>
Cc: Tomas Glozar <tglozar@redhat.com>
Cc: Will Deacon <will@kernel.org>
[ Don't mix declarations and code, move 'entries' variable to the start of scope ]
[ Use pr_warning_once() instead of pr_err() in stubs for get_entries(), suggested by a local sashiko instance ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2026-05-15 08:25:02 -03:00

105 lines
2.5 KiB
C

// SPDX-License-Identifier: GPL-2.0
#include "debug.h"
#include "symbol_conf.h"
#include "unwind.h"
#include <linux/string.h>
#include <string.h>
#include <stdlib.h>
int unwind__get_entries(unwind_entry_cb_t cb __maybe_unused, void *arg __maybe_unused,
struct thread *thread __maybe_unused,
struct perf_sample *data __maybe_unused,
int max_stack __maybe_unused,
bool best_effort __maybe_unused)
{
int ret = 0;
#if defined(HAVE_LIBDW_SUPPORT) || defined(HAVE_LIBUNWIND_SUPPORT)
if (symbol_conf.unwind_style[0] == UNWIND_STYLE_UNKNOWN) {
int i = 0;
#ifdef HAVE_LIBDW_SUPPORT
symbol_conf.unwind_style[i++] = UNWIND_STYLE_LIBDW;
#endif
#ifdef HAVE_LIBUNWIND_SUPPORT
symbol_conf.unwind_style[i++] = UNWIND_STYLE_LIBUNWIND;
#endif
}
#endif //defined(HAVE_LIBDW_SUPPORT) || defined(HAVE_LIBUNWIND_SUPPORT)
for (size_t i = 0; i < ARRAY_SIZE(symbol_conf.unwind_style); i++) {
switch (symbol_conf.unwind_style[i]) {
case UNWIND_STYLE_LIBDW:
ret = libdw__get_entries(cb, arg, thread, data, max_stack, best_effort);
break;
case UNWIND_STYLE_LIBUNWIND:
ret = libunwind__get_entries(cb, arg, thread, data, max_stack, best_effort);
break;
case UNWIND_STYLE_UNKNOWN:
default:
#if !defined(HAVE_LIBDW_SUPPORT) && !defined(HAVE_LIBUNWIND_SUPPORT)
pr_warning_once(
"Error: dwarf unwinding not supported, build perf with libdw or libunwind.\n");
#endif
ret = 0;
break;
}
if (ret > 0) {
ret = 0;
break;
}
if (ret < 0)
break;
}
return ret;
}
int unwind__configure(const char *var, const char *value, void *cb __maybe_unused)
{
static const char * const unwind_style_names[] = {
[UNWIND_STYLE_LIBDW] = "libdw",
[UNWIND_STYLE_LIBUNWIND] = "libunwind",
NULL
};
char *s, *p, *saveptr;
size_t i = 0;
if (strcmp(var, "unwind.style"))
return 0;
if (!value)
return -1;
s = strdup(value);
if (!s)
return -1;
memset(symbol_conf.unwind_style, 0, sizeof(symbol_conf.unwind_style));
p = strtok_r(s, ",", &saveptr);
while (p && i < ARRAY_SIZE(symbol_conf.unwind_style)) {
bool found = false;
char *q = strim(p);
for (size_t j = UNWIND_STYLE_LIBDW; j < MAX_UNWIND_STYLE; j++) {
if (!strcasecmp(q, unwind_style_names[j])) {
symbol_conf.unwind_style[i++] = j;
found = true;
break;
}
}
if (!found)
pr_warning("Unknown unwind style: %s\n", q);
p = strtok_r(NULL, ",", &saveptr);
}
free(s);
return 0;
}
int unwind__option(const struct option *opt __maybe_unused,
const char *arg,
int unset __maybe_unused)
{
return unwind__configure("unwind.style", arg, NULL);
}