162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include "unwind.h"
362306a36Sopenharmony_ci#include "dso.h"
462306a36Sopenharmony_ci#include "map.h"
562306a36Sopenharmony_ci#include "thread.h"
662306a36Sopenharmony_ci#include "session.h"
762306a36Sopenharmony_ci#include "debug.h"
862306a36Sopenharmony_ci#include "env.h"
962306a36Sopenharmony_ci#include "callchain.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cistruct unwind_libunwind_ops __weak *local_unwind_libunwind_ops;
1262306a36Sopenharmony_cistruct unwind_libunwind_ops __weak *x86_32_unwind_libunwind_ops;
1362306a36Sopenharmony_cistruct unwind_libunwind_ops __weak *arm64_unwind_libunwind_ops;
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistatic void unwind__register_ops(struct maps *maps, struct unwind_libunwind_ops *ops)
1662306a36Sopenharmony_ci{
1762306a36Sopenharmony_ci	RC_CHK_ACCESS(maps)->unwind_libunwind_ops = ops;
1862306a36Sopenharmony_ci}
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ciint unwind__prepare_access(struct maps *maps, struct map *map, bool *initialized)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	const char *arch;
2362306a36Sopenharmony_ci	enum dso_type dso_type;
2462306a36Sopenharmony_ci	struct unwind_libunwind_ops *ops = local_unwind_libunwind_ops;
2562306a36Sopenharmony_ci	struct dso *dso = map__dso(map);
2662306a36Sopenharmony_ci	struct machine *machine;
2762306a36Sopenharmony_ci	int err;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	if (!dwarf_callchain_users)
3062306a36Sopenharmony_ci		return 0;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	if (maps__addr_space(maps)) {
3362306a36Sopenharmony_ci		pr_debug("unwind: thread map already set, dso=%s\n", dso->name);
3462306a36Sopenharmony_ci		if (initialized)
3562306a36Sopenharmony_ci			*initialized = true;
3662306a36Sopenharmony_ci		return 0;
3762306a36Sopenharmony_ci	}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	machine = maps__machine(maps);
4062306a36Sopenharmony_ci	/* env->arch is NULL for live-mode (i.e. perf top) */
4162306a36Sopenharmony_ci	if (!machine->env || !machine->env->arch)
4262306a36Sopenharmony_ci		goto out_register;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	dso_type = dso__type(dso, machine);
4562306a36Sopenharmony_ci	if (dso_type == DSO__TYPE_UNKNOWN)
4662306a36Sopenharmony_ci		return 0;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	arch = perf_env__arch(machine->env);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	if (!strcmp(arch, "x86")) {
5162306a36Sopenharmony_ci		if (dso_type != DSO__TYPE_64BIT)
5262306a36Sopenharmony_ci			ops = x86_32_unwind_libunwind_ops;
5362306a36Sopenharmony_ci	} else if (!strcmp(arch, "arm64") || !strcmp(arch, "arm")) {
5462306a36Sopenharmony_ci		if (dso_type == DSO__TYPE_64BIT)
5562306a36Sopenharmony_ci			ops = arm64_unwind_libunwind_ops;
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	if (!ops) {
5962306a36Sopenharmony_ci		pr_warning_once("unwind: target platform=%s is not supported\n", arch);
6062306a36Sopenharmony_ci		return 0;
6162306a36Sopenharmony_ci	}
6262306a36Sopenharmony_ciout_register:
6362306a36Sopenharmony_ci	unwind__register_ops(maps, ops);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	err = maps__unwind_libunwind_ops(maps)->prepare_access(maps);
6662306a36Sopenharmony_ci	if (initialized)
6762306a36Sopenharmony_ci		*initialized = err ? false : true;
6862306a36Sopenharmony_ci	return err;
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_civoid unwind__flush_access(struct maps *maps)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	const struct unwind_libunwind_ops *ops = maps__unwind_libunwind_ops(maps);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	if (ops)
7662306a36Sopenharmony_ci		ops->flush_access(maps);
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_civoid unwind__finish_access(struct maps *maps)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	const struct unwind_libunwind_ops *ops = maps__unwind_libunwind_ops(maps);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	if (ops)
8462306a36Sopenharmony_ci		ops->finish_access(maps);
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ciint unwind__get_entries(unwind_entry_cb_t cb, void *arg,
8862306a36Sopenharmony_ci			 struct thread *thread,
8962306a36Sopenharmony_ci			 struct perf_sample *data, int max_stack,
9062306a36Sopenharmony_ci			 bool best_effort)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	const struct unwind_libunwind_ops *ops = maps__unwind_libunwind_ops(thread__maps(thread));
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	if (ops)
9562306a36Sopenharmony_ci		return ops->get_entries(cb, arg, thread, data, max_stack, best_effort);
9662306a36Sopenharmony_ci	return 0;
9762306a36Sopenharmony_ci}
98