root/tools/perf/util/unwind.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);
}