| // 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); |
| } |