// SPDX-License-Identifier: GPL-2.0
#include "libunwind-arch.h"
#include "../debug.h"
+#include "../maps.h"
#include <elf.h>
#include <errno.h>
return -EINVAL;
}
}
+
+
+void libunwind_arch__flush_access(struct maps *maps)
+{
+ unsigned int e_machine = maps__e_machine(maps);
+
+ switch (e_machine) {
+ case EM_NONE:
+ break; // No libunwind info on the maps.
+ case EM_ARM:
+ __libunwind_arch__flush_access_arm(maps);
+ break;
+ case EM_AARCH64:
+ __libunwind_arch__flush_access_arm64(maps);
+ break;
+ case EM_LOONGARCH:
+ __libunwind_arch__flush_access_loongarch(maps);
+ break;
+ case EM_MIPS:
+ __libunwind_arch__flush_access_mips(maps);
+ break;
+ case EM_PPC:
+ __libunwind_arch__flush_access_ppc32(maps);
+ break;
+ case EM_PPC64:
+ __libunwind_arch__flush_access_ppc64(maps);
+ break;
+ case EM_S390:
+ __libunwind_arch__flush_access_s390(maps);
+ break;
+ case EM_386:
+ __libunwind_arch__flush_access_i386(maps);
+ break;
+ case EM_X86_64:
+ __libunwind_arch__flush_access_x86_64(maps);
+ break;
+ default:
+ pr_err("ELF MACHINE %x is not supported.\n", e_machine);
+ break;
+ }
+}
+
+void libunwind_arch__finish_access(struct maps *maps)
+{
+ unsigned int e_machine = maps__e_machine(maps);
+
+ switch (e_machine) {
+ case EM_NONE:
+ break; // No libunwind info on the maps.
+ case EM_ARM:
+ __libunwind_arch__finish_access_arm(maps);
+ break;
+ case EM_AARCH64:
+ __libunwind_arch__finish_access_arm64(maps);
+ break;
+ case EM_LOONGARCH:
+ __libunwind_arch__finish_access_loongarch(maps);
+ break;
+ case EM_MIPS:
+ __libunwind_arch__finish_access_mips(maps);
+ break;
+ case EM_PPC:
+ __libunwind_arch__finish_access_ppc32(maps);
+ break;
+ case EM_PPC64:
+ __libunwind_arch__finish_access_ppc64(maps);
+ break;
+ case EM_S390:
+ __libunwind_arch__finish_access_s390(maps);
+ break;
+ case EM_386:
+ __libunwind_arch__finish_access_i386(maps);
+ break;
+ case EM_X86_64:
+ __libunwind_arch__finish_access_x86_64(maps);
+ break;
+ default:
+ pr_err("ELF MACHINE %x is not supported.\n", e_machine);
+ break;
+ }
+}
#ifndef __LIBUNWIND_ARCH_H
#define __LIBUNWIND_ARCH_H
+struct maps;
+
int __get_perf_regnum_for_unw_regnum_arm(int unw_regnum);
int __get_perf_regnum_for_unw_regnum_arm64(int unw_regnum);
int __get_perf_regnum_for_unw_regnum_loongarch(int unw_regnum);
int __get_perf_regnum_for_unw_regnum_x86_64(int unw_regnum);
int get_perf_regnum_for_unw_regnum(unsigned int e_machine, int unw_regnum);
+void __libunwind_arch__flush_access_arm(struct maps *maps);
+void __libunwind_arch__flush_access_arm64(struct maps *maps);
+void __libunwind_arch__flush_access_loongarch(struct maps *maps);
+void __libunwind_arch__flush_access_mips(struct maps *maps);
+void __libunwind_arch__flush_access_ppc32(struct maps *maps);
+void __libunwind_arch__flush_access_ppc64(struct maps *maps);
+void __libunwind_arch__flush_access_s390(struct maps *maps);
+void __libunwind_arch__flush_access_i386(struct maps *maps);
+void __libunwind_arch__flush_access_x86_64(struct maps *maps);
+void libunwind_arch__flush_access(struct maps *maps);
+
+void __libunwind_arch__finish_access_arm(struct maps *maps);
+void __libunwind_arch__finish_access_arm64(struct maps *maps);
+void __libunwind_arch__finish_access_loongarch(struct maps *maps);
+void __libunwind_arch__finish_access_mips(struct maps *maps);
+void __libunwind_arch__finish_access_ppc32(struct maps *maps);
+void __libunwind_arch__finish_access_ppc64(struct maps *maps);
+void __libunwind_arch__finish_access_s390(struct maps *maps);
+void __libunwind_arch__finish_access_i386(struct maps *maps);
+void __libunwind_arch__finish_access_x86_64(struct maps *maps);
+void libunwind_arch__finish_access(struct maps *maps);
+
#endif /* __LIBUNWIND_ARCH_H */
// SPDX-License-Identifier: GPL-2.0
#include "libunwind-arch.h"
#include "../debug.h"
+#include "../maps.h"
#include "../../../arch/arm/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
#include <errno.h>
+#ifdef HAVE_LIBUNWIND_ARM_SUPPORT
+#include <libunwind-arm.h>
+#endif
+
int __get_perf_regnum_for_unw_regnum_arm(int unw_regnum)
{
if (unw_regnum < 0 || unw_regnum >= PERF_REG_ARM_MAX) {
}
return unw_regnum;
}
+
+void __libunwind_arch__flush_access_arm(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_ARM_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_arm(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_ARM_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
// SPDX-License-Identifier: GPL-2.0
#include "libunwind-arch.h"
#include "../debug.h"
+#include "../maps.h"
#include "../../../arch/arm64/include/uapi/asm/perf_regs.h"
+#include <linux/compiler.h>
#include <errno.h>
+#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT
+#include <libunwind-aarch64.h>
+#endif
+
int __get_perf_regnum_for_unw_regnum_arm64(int unw_regnum)
{
if (unw_regnum < 0 || unw_regnum >= PERF_REG_ARM64_EXTENDED_MAX) {
}
return unw_regnum;
}
+
+void __libunwind_arch__flush_access_arm64(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_arm64(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
// SPDX-License-Identifier: GPL-2.0
#include "libunwind-arch.h"
#include "../debug.h"
+#include "../maps.h"
#include "../../../arch/x86/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
#include <linux/kernel.h>
return perf_i386_regnums[unw_regnum];
#endif // HAVE_LIBUNWIND_X86_SUPPORT
}
+
+void __libunwind_arch__flush_access_i386(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_i386(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
// SPDX-License-Identifier: GPL-2.0
#include "libunwind-arch.h"
#include "../debug.h"
+#include "../maps.h"
#include "../../../arch/loongarch/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
#include <errno.h>
}
#endif // HAVE_LIBUNWIND_LOONGARCH64_SUPPORT
}
+
+void __libunwind_arch__flush_access_loongarch(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_loongarch(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
// SPDX-License-Identifier: GPL-2.0
#include "libunwind-arch.h"
#include "../debug.h"
+#include "../maps.h"
#include "../../../arch/mips/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
#include <errno.h>
}
#endif // HAVE_LIBUNWIND_MIPS_SUPPORT
}
+
+void __libunwind_arch__flush_access_mips(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_mips(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
// SPDX-License-Identifier: GPL-2.0-or-later
#include "libunwind-arch.h"
#include "../debug.h"
+#include "../maps.h"
#include "../../../arch/powerpc/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
#include <errno.h>
}
#endif // HAVE_LIBUNWIND_PPC32_SUPPORT
}
+
+void __libunwind_arch__flush_access_ppc32(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_ppc32(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
// SPDX-License-Identifier: GPL-2.0-or-later
#include "libunwind-arch.h"
#include "../debug.h"
+#include "../maps.h"
#include "../../../arch/powerpc/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
#include <errno.h>
}
#endif // HAVE_LIBUNWIND_PPC64_SUPPORT
}
+
+void __libunwind_arch__flush_access_ppc64(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_ppc64(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
// SPDX-License-Identifier: GPL-2.0
#include "libunwind-arch.h"
#include "../debug.h"
+#include "../maps.h"
#include "../../../arch/s390/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
#include <errno.h>
}
#endif // HAVE_LIBUNWIND_S390X_SUPPORT
}
+
+void __libunwind_arch__flush_access_s390(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_S390X_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_s390(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_S390X_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
// SPDX-License-Identifier: GPL-2.0
#include "libunwind-arch.h"
#include "../debug.h"
+#include "../maps.h"
#include "../../../arch/x86/include/uapi/asm/perf_regs.h"
#include <linux/compiler.h>
#include <linux/kernel.h>
return perf_x86_64_regnums[unw_regnum];
#endif // HAVE_LIBUNWIND_X86_64_SUPPORT
}
+
+void __libunwind_arch__flush_access_x86_64(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_x86_64(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
#ifdef HAVE_LIBUNWIND_SUPPORT
void *addr_space;
const struct unwind_libunwind_ops *unwind_libunwind_ops;
+ uint16_t e_machine;
#endif
#ifdef HAVE_LIBDW_SUPPORT
void *libdw_addr_space_dwfl;
{
RC_CHK_ACCESS(maps)->unwind_libunwind_ops = ops;
}
+
+uint16_t maps__e_machine(const struct maps *maps)
+{
+ return RC_CHK_ACCESS(maps)->e_machine;
+}
+
+void maps__set_e_machine(struct maps *maps, uint16_t e_machine)
+{
+ RC_CHK_ACCESS(maps)->e_machine = e_machine;
+}
#endif
#ifdef HAVE_LIBDW_SUPPORT
void *maps__libdw_addr_space_dwfl(const struct maps *maps)
down_write(maps__lock(dest));
down_read(maps__lock(parent));
+#ifdef HAVE_LIBUNWIND_SUPPORT
+ err = unwind__prepare_access(dest, maps__e_machine(parent));
+ if (err) {
+ up_read(maps__lock(parent));
+ up_write(maps__lock(dest));
+ return err;
+ }
+#endif
parent_maps_by_address = maps__maps_by_address(parent);
n = maps__nr_maps(parent);
if (maps__nr_maps(dest) == 0) {
if (!new)
err = -ENOMEM;
else {
- err = unwind__prepare_access(dest, new, NULL);
- if (!err) {
- dest_maps_by_address[i] = new;
- map__set_kmap_maps(new, dest);
- if (dest_maps_by_name)
- dest_maps_by_name[i] = map__get(new);
- RC_CHK_ACCESS(dest)->nr_maps = i + 1;
- }
+ dest_maps_by_address[i] = new;
+ map__set_kmap_maps(new, dest);
+ if (dest_maps_by_name)
+ dest_maps_by_name[i] = map__get(new);
+ RC_CHK_ACCESS(dest)->nr_maps = i + 1;
}
if (err)
map__put(new);
if (!new)
err = -ENOMEM;
else {
- err = unwind__prepare_access(dest, new, NULL);
- if (!err)
- err = __maps__insert(dest, new);
+ err = __maps__insert(dest, new);
}
map__put(new);
}
void maps__set_addr_space(struct maps *maps, void *addr_space);
const struct unwind_libunwind_ops *maps__unwind_libunwind_ops(const struct maps *maps);
void maps__set_unwind_libunwind_ops(struct maps *maps, const struct unwind_libunwind_ops *ops);
+uint16_t maps__e_machine(const struct maps *maps);
+void maps__set_e_machine(struct maps *maps, uint16_t e_machine);
#endif
#ifdef HAVE_LIBDW_SUPPORT
void *maps__libdw_addr_space_dwfl(const struct maps *maps);
int thread__insert_map(struct thread *thread, struct map *map)
{
int ret;
+ uint16_t e_machine;
- ret = unwind__prepare_access(thread__maps(thread), map, NULL);
+ ret = maps__fixup_overlap_and_insert(thread__maps(thread), map);
if (ret)
return ret;
- return maps__fixup_overlap_and_insert(thread__maps(thread), map);
-}
-
-struct thread__prepare_access_maps_cb_args {
- int err;
- struct maps *maps;
-};
-
-static int thread__prepare_access_maps_cb(struct map *map, void *data)
-{
- bool initialized = false;
- struct thread__prepare_access_maps_cb_args *args = data;
-
- args->err = unwind__prepare_access(args->maps, map, &initialized);
-
- return (args->err || initialized) ? 1 : 0;
+ e_machine = thread__e_machine(thread, /*machine=*/NULL, /*e_flags=*/NULL);
+ return unwind__prepare_access(thread__maps(thread), e_machine);
}
static int thread__prepare_access(struct thread *thread)
{
- struct thread__prepare_access_maps_cb_args args = {
- .err = 0,
- };
-
- if (dwarf_callchain_users) {
- args.maps = thread__maps(thread);
- maps__for_each_map(thread__maps(thread), thread__prepare_access_maps_cb, &args);
- }
+ uint16_t e_machine = thread__e_machine(thread, /*machine=*/NULL, /*e_flags=*/NULL);
- return args.err;
+ return unwind__prepare_access(thread__maps(thread), e_machine);
}
static int thread__clone_maps(struct thread *thread, struct thread *parent, bool do_maps_clone)
return 0;
}
-static void _unwind__flush_access(struct maps *maps)
-{
- unw_flush_cache(maps__addr_space(maps), 0, 0);
-}
-
-static void _unwind__finish_access(struct maps *maps)
-{
- unw_destroy_addr_space(maps__addr_space(maps));
-}
-
static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
void *arg, int max_stack)
{
static struct unwind_libunwind_ops
_unwind_libunwind_ops = {
.prepare_access = _unwind__prepare_access,
- .flush_access = _unwind__flush_access,
- .finish_access = _unwind__finish_access,
.get_entries = _unwind__get_entries,
};
#include "debug.h"
#include "env.h"
#include "callchain.h"
+#include "libunwind-arch/libunwind-arch.h"
+#include <dwarf-regs.h>
+#include <elf.h>
struct unwind_libunwind_ops __weak *local_unwind_libunwind_ops;
struct unwind_libunwind_ops __weak *x86_32_unwind_libunwind_ops;
struct unwind_libunwind_ops __weak *arm64_unwind_libunwind_ops;
-int unwind__prepare_access(struct maps *maps, struct map *map, bool *initialized)
+int unwind__prepare_access(struct maps *maps, uint16_t e_machine)
{
- const char *arch;
- enum dso_type dso_type;
struct unwind_libunwind_ops *ops = local_unwind_libunwind_ops;
- struct dso *dso = map__dso(map);
- struct machine *machine;
- int err;
if (!dwarf_callchain_users)
return 0;
if (maps__addr_space(maps)) {
- pr_debug("unwind: thread map already set, dso=%s\n", dso__name(dso));
- if (initialized)
- *initialized = true;
+ pr_debug3("unwind: thread map already set\n");
return 0;
}
- machine = maps__machine(maps);
- /* env->arch is NULL for live-mode (i.e. perf top) */
- if (!machine->env || !machine->env->arch)
- goto out_register;
-
- dso_type = dso__type(dso, machine);
- if (dso_type == DSO__TYPE_UNKNOWN)
+ if (e_machine == EM_NONE)
return 0;
- arch = perf_env__arch(machine->env);
-
- if (!strcmp(arch, "x86")) {
- if (dso_type != DSO__TYPE_64BIT)
+ if (e_machine != EM_HOST) {
+ /* If not live/local mode. */
+ switch (e_machine) {
+ case EM_386:
ops = x86_32_unwind_libunwind_ops;
- } else if (!strcmp(arch, "arm64") || !strcmp(arch, "arm")) {
- if (dso_type == DSO__TYPE_64BIT)
+ break;
+ case EM_AARCH64:
ops = arm64_unwind_libunwind_ops;
+ break;
+ default:
+ pr_warning_once("unwind: ELF machine type %d is not supported\n",
+ e_machine);
+ return 0;
+ }
}
if (!ops) {
- pr_warning_once("unwind: target platform=%s is not supported\n", arch);
+ pr_warning_once("unwind: target platform is not supported\n");
return 0;
}
-out_register:
maps__set_unwind_libunwind_ops(maps, ops);
+ maps__set_e_machine(maps, e_machine);
- err = maps__unwind_libunwind_ops(maps)->prepare_access(maps);
- if (initialized)
- *initialized = err ? false : true;
- return err;
+ return ops->prepare_access(maps);
}
void unwind__flush_access(struct maps *maps)
{
- const struct unwind_libunwind_ops *ops = maps__unwind_libunwind_ops(maps);
-
- if (ops)
- ops->flush_access(maps);
+ libunwind_arch__flush_access(maps);
}
void unwind__finish_access(struct maps *maps)
{
- const struct unwind_libunwind_ops *ops = maps__unwind_libunwind_ops(maps);
-
- if (ops)
- ops->finish_access(maps);
+ libunwind_arch__finish_access(maps);
}
int libunwind__get_entries(unwind_entry_cb_t cb, void *arg,
#ifndef __UNWIND_H
#define __UNWIND_H
+#include <stdint.h>
#include <linux/compiler.h>
#include <linux/types.h>
#include "map_symbol.h"
struct unwind_libunwind_ops {
int (*prepare_access)(struct maps *maps);
- void (*flush_access)(struct maps *maps);
- void (*finish_access)(struct maps *maps);
int (*get_entries)(unwind_entry_cb_t cb, void *arg,
struct thread *thread,
struct perf_sample *data, int max_stack, bool best_effort);
struct thread *thread,
struct perf_sample *data, int max_stack,
bool best_effort);
-int unwind__prepare_access(struct maps *maps, struct map *map, bool *initialized);
+int unwind__prepare_access(struct maps *maps, uint16_t e_machine);
void unwind__flush_access(struct maps *maps);
void unwind__finish_access(struct maps *maps);
#else
}
static inline int unwind__prepare_access(struct maps *maps __maybe_unused,
- struct map *map __maybe_unused,
- bool *initialized __maybe_unused)
+ uint16_t e_machine __maybe_unused)
{
return 0;
}