From nobody Thu Apr 9 21:51:27 2026 Received: from mail-dy1-f201.google.com (mail-dy1-f201.google.com [74.125.82.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3A248373BFC for ; Sat, 4 Apr 2026 05:41:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775281269; cv=none; b=irLJ6S5z6fRcrEaP4BXLIR5rnDXg0K03md51bocFIwWzPbxnK1U3IVEWwz7MX4RgRqz4jJn0xZwErR3kX4e1MVXGeIGn0VGECMd8ArclNxfVqxf9HbwvQmTV56j9Q3KJNb2bEysgzDKPnDfTumdHbGe5zgfY1e2WTSbnlxPi16k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775281269; c=relaxed/simple; bh=COBuGpY5ERGwxSP6FoueTOCdx4bzGWh2TQkzXkPBqfM=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=OBz9g6c5PjUosVHSmXRUuwPiRgWXT5SbImL4Yw4xgHenxycg1uGfA7dGTGMRDr+ebbCindVVsziOhjRKT5/lAK0igJkra9ieNRhP55ZRYkAIl9KEdQJBj4jnHF7P/goRjxtoXWmUdIuEiiqZs/qqFeg+nXLUmd0PGGVnxFOa56o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=vlbm/Czw; arc=none smtp.client-ip=74.125.82.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="vlbm/Czw" Received: by mail-dy1-f201.google.com with SMTP id 5a478bee46e88-2bdf6fe90a9so3377983eec.1 for ; Fri, 03 Apr 2026 22:41:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1775281261; x=1775886061; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=Oi5oXu9pk6iDvpcxQBgljxkyamiyBaudRtXU+2mmKBk=; b=vlbm/CzwPXxDCkZb4Ex6cVPttI/U0GY9pFoxulIRM6aqp0PhNzHNLYonyL52HzT+lG nu3vUiUUbD7DmWS8TSI7txeXgczHkD7dc7sv72ss8vWQ+7BlIJZsCkZex9bdeEeH13GC 6te5Gu57fkHvzkqSz6QqQdaCBM1m4ITBne8ElZEFM6ZdNQwV5Iwbg9lXQCWbMjm9R4de FeGlE99zL6uhpTOWex0Pran2z5ewaJ+wArV4R0gv+Hvqp38VVHjupOBuWIZGxdT4ctz/ MrCm7iSzoxChUWkiddbIfaFgUUQ/G4V3blwVLDZPK0hVpXwQiR8daGx+xUhItN6GBkS+ 44xQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775281261; x=1775886061; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=Oi5oXu9pk6iDvpcxQBgljxkyamiyBaudRtXU+2mmKBk=; b=ADovgvti+KpSsWiniFe+4oMhVKTFDR9ECo55Av24Vmqr2nCwfb2ybkBxlyIrC0dP9Y vLfq0a8Xk1k+lDHPKkfpxWD0JSP4b7TbSsIWvqRpjiXx/s0ogaS3XaUR5ctCnhE6mFoo 8n0QGu6T8YrBeo4UHPlNmGWiWuDXX0q3MgEtcBtzMN0pMig8cWq43l7vuLYAu9BdWVTa QddMEGLFtl6BmaNX6E1BCzlIHTTzYz5xvo0ZSTtomj13y0EKVPcz7NWx8S3DUjlFmy5H Bl+ci1G4qOIUGZUTBvpn6JhLUQb/ATRi3UfrJxRACjqjNEL8qyzB5r/Apa7GbQBql6id 7lKw== X-Forwarded-Encrypted: i=1; AJvYcCU8B2sfOoBawLi0tOAndwls0H8pSas+S1Oumi+VA1pThZcPzfwhq0tfqFEET9cHqVSqVvSpgfFn5gkGBsg=@vger.kernel.org X-Gm-Message-State: AOJu0YzmLQzuNSWowTa84kOQ1z3UwW5yLnUeRpj6ImEpG6Q8C7C2A4i4 wf5LdPOJcq7oaWTZ7Q9R86W0DQAoFv1SC7Ah74+NJY6pcogz7+zHgZAtLUszwMpuINi3vyXUCc2 Y20MxI9WB+g== X-Received: from dyblf33.prod.google.com ([2002:a05:7301:a21:b0:2c5:2b09:16ef]) (user=irogers job=prod-delivery.src-stubby-dispatcher) by 2002:a05:7300:a506:b0:2c1:27c:75cd with SMTP id 5a478bee46e88-2cbfa2d0e93mr2713178eec.13.1775281260610; Fri, 03 Apr 2026 22:41:00 -0700 (PDT) Date: Fri, 3 Apr 2026 22:40:31 -0700 In-Reply-To: <20260404054032.1538095-1-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260305221927.3237145-1-irogers@google.com> <20260404054032.1538095-1-irogers@google.com> X-Mailer: git-send-email 2.53.0.1213.gd9a14994de-goog Message-ID: <20260404054032.1538095-8-irogers@google.com> Subject: [PATCH v3 7/8] perf unwind-libunwind: Remove libunwind-local From: Ian Rogers To: acme@kernel.org Cc: irogers@google.com, 9erthalion6@gmail.com, adrian.hunter@intel.com, alex@ghiti.fr, alexander.shishkin@linux.intel.com, andrew.jones@oss.qualcomm.com, aou@eecs.berkeley.edu, atrajeev@linux.ibm.com, blakejones@google.com, ctshao@google.com, dapeng1.mi@linux.intel.com, howardchu95@gmail.com, james.clark@linaro.org, john.g.garry@oracle.com, jolsa@kernel.org, leo.yan@linux.dev, libunwind-devel@nongnu.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, linux-riscv@lists.infradead.org, mingo@redhat.com, namhyung@kernel.org, palmer@dabbelt.com, peterz@infradead.org, pjw@kernel.org, shimin.guo@skydio.com, tglozar@redhat.com, tmricht@linux.ibm.com, will@kernel.org, yuzhuo@google.com Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Local unwinding only works on the machine libunwind is built for, rather than cross platform, the APIs for remote and local unwinding are similar but types like unw_word_t depend on the included header. Place the architecture specific code into the appropriate libunwind-.c file. Put generic code in unwind-libunwind.c and use libunwind-arch to choose the correct implementation based on the thread's e_machine. Structuring the code this way avoids including the unwind-libunwind-local.c for each architecture of remote unwinding. Data is moved into the struct unwind_info to simplify the architecture and generic code, trying to keep as much code as possible generic. Signed-off-by: Ian Rogers --- tools/perf/util/Build | 3 - .../perf/util/libunwind-arch/libunwind-arch.c | 184 ++++ .../perf/util/libunwind-arch/libunwind-arch.h | 234 +++++ .../perf/util/libunwind-arch/libunwind-arm.c | 256 ++++++ .../util/libunwind-arch/libunwind-arm64.c | 255 ++++++ .../perf/util/libunwind-arch/libunwind-i386.c | 254 ++++++ .../util/libunwind-arch/libunwind-loongarch.c | 255 ++++++ .../perf/util/libunwind-arch/libunwind-mips.c | 255 ++++++ .../util/libunwind-arch/libunwind-ppc32.c | 255 ++++++ .../util/libunwind-arch/libunwind-ppc64.c | 255 ++++++ .../perf/util/libunwind-arch/libunwind-s390.c | 255 ++++++ .../util/libunwind-arch/libunwind-x86_64.c | 253 ++++++ tools/perf/util/libunwind/arm64.c | 35 - tools/perf/util/libunwind/x86_32.c | 29 - tools/perf/util/maps.c | 10 - tools/perf/util/maps.h | 2 - tools/perf/util/unwind-libunwind-local.c | 820 ------------------ tools/perf/util/unwind-libunwind.c | 654 +++++++++++++- tools/perf/util/unwind.h | 7 - 19 files changed, 3334 insertions(+), 937 deletions(-) delete mode 100644 tools/perf/util/libunwind/arm64.c delete mode 100644 tools/perf/util/libunwind/x86_32.c delete mode 100644 tools/perf/util/unwind-libunwind-local.c diff --git a/tools/perf/util/Build b/tools/perf/util/Build index bf4204135ccb..2bb60f50f62d 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -226,11 +226,8 @@ perf-util-$(CONFIG_LIBDW) +=3D annotate-data.o perf-util-$(CONFIG_LIBDW) +=3D libdw.o perf-util-$(CONFIG_LIBDW) +=3D unwind-libdw.o =20 -perf-util-$(CONFIG_LOCAL_LIBUNWIND) +=3D unwind-libunwind-local.o perf-util-$(CONFIG_LIBUNWIND) +=3D unwind-libunwind.o perf-util-$(CONFIG_LIBUNWIND) +=3D libunwind-arch/ -perf-util-$(CONFIG_LIBUNWIND_X86) +=3D libunwind/x86_32.o -perf-util-$(CONFIG_LIBUNWIND_AARCH64) +=3D libunwind/arm64.o =20 ifeq ($(CONFIG_LIBTRACEEVENT),y) perf-util-$(CONFIG_LIBBABELTRACE) +=3D data-convert-bt.o diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.c b/tools/perf/u= til/libunwind-arch/libunwind-arch.c index 9692e6c81492..8539b4233df4 100644 --- a/tools/perf/util/libunwind-arch/libunwind-arch.c +++ b/tools/perf/util/libunwind-arch/libunwind-arch.c @@ -112,3 +112,187 @@ void libunwind_arch__finish_access(struct maps *maps) break; } } + +void *libunwind_arch__create_addr_space(unsigned int e_machine) +{ + switch (e_machine) { + case EM_ARM: + return __libunwind_arch__create_addr_space_arm(); + case EM_AARCH64: + return __libunwind_arch__create_addr_space_arm64(); + case EM_LOONGARCH: + return __libunwind_arch__create_addr_space_loongarch(); + case EM_MIPS: + return __libunwind_arch__create_addr_space_mips(); + case EM_PPC: + return __libunwind_arch__create_addr_space_ppc32(); + case EM_PPC64: + return __libunwind_arch__create_addr_space_ppc64(); + case EM_S390: + return __libunwind_arch__create_addr_space_s390(); + case EM_386: + return __libunwind_arch__create_addr_space_i386(); + case EM_X86_64: + return __libunwind_arch__create_addr_space_x86_64(); + default: + pr_err("ELF MACHINE %x is not supported.\n", e_machine); + return NULL; + } +} + +int libunwind_arch__dwarf_search_unwind_table(unsigned int e_machine, + void *as, + uint64_t ip, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg) +{ + switch (e_machine) { + case EM_ARM: + return __libunwind_arch__dwarf_search_unwind_table_arm(as, ip, di, pi, + need_unwind_info, arg); + case EM_AARCH64: + return __libunwind_arch__dwarf_search_unwind_table_arm64(as, ip, di, pi, + need_unwind_info, arg); + case EM_LOONGARCH: + return __libunwind_arch__dwarf_search_unwind_table_loongarch(as, ip, di,= pi, + need_unwind_info, arg); + case EM_MIPS: + return __libunwind_arch__dwarf_search_unwind_table_mips(as, ip, di, pi, + need_unwind_info, arg); + case EM_PPC: + return __libunwind_arch__dwarf_search_unwind_table_ppc32(as, ip, di, pi, + need_unwind_info, arg); + case EM_PPC64: + return __libunwind_arch__dwarf_search_unwind_table_ppc64(as, ip, di, pi, + need_unwind_info, arg); + case EM_S390: + return __libunwind_arch__dwarf_search_unwind_table_s390(as, ip, di, pi, + need_unwind_info, arg); + case EM_386: + return __libunwind_arch__dwarf_search_unwind_table_i386(as, ip, di, pi, + need_unwind_info, arg); + case EM_X86_64: + return __libunwind_arch__dwarf_search_unwind_table_x86_64(as, ip, di, pi, + need_unwind_info, arg); + default: + pr_err("ELF MACHINE %x is not supported.\n", e_machine); + return -EINVAL; + } +} + +int libunwind_arch__dwarf_find_debug_frame(unsigned int e_machine, + int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end) +{ + switch (e_machine) { + case EM_ARM: + return __libunwind_arch__dwarf_find_debug_frame_arm(found, di_debug, ip,= segbase, + obj_name, start, end); + case EM_AARCH64: + return __libunwind_arch__dwarf_find_debug_frame_arm64(found, di_debug, i= p, segbase, + obj_name, start, end); + case EM_LOONGARCH: + return __libunwind_arch__dwarf_find_debug_frame_loongarch(found, di_debu= g, ip, + segbase, obj_name, + start, end); + case EM_MIPS: + return __libunwind_arch__dwarf_find_debug_frame_mips(found, di_debug, ip= , segbase, + obj_name, start, end); + case EM_PPC: + return __libunwind_arch__dwarf_find_debug_frame_ppc32(found, di_debug, i= p, segbase, + obj_name, start, end); + case EM_PPC64: + return __libunwind_arch__dwarf_find_debug_frame_ppc64(found, di_debug, i= p, segbase, + obj_name, start, end); + case EM_S390: + return __libunwind_arch__dwarf_find_debug_frame_s390(found, di_debug, ip= , segbase, + obj_name, start, end); + case EM_386: + return __libunwind_arch__dwarf_find_debug_frame_i386(found, di_debug, ip= , segbase, + obj_name, start, end); + case EM_X86_64: + return __libunwind_arch__dwarf_find_debug_frame_x86_64(found, di_debug, = ip, segbase, + obj_name, start, end); + default: + pr_err("ELF MACHINE %x is not supported.\n", e_machine); + return -EINVAL; + } +} + +struct unwind_info *libunwind_arch_unwind_info__new(struct thread *thread, + struct perf_sample *sample, int max_stack, + bool best_effort, uint16_t e_machine, + uint64_t first_ip) +{ + switch (e_machine) { + case EM_ARM: + return __libunwind_arch_unwind_info__new_arm(thread, sample, max_stack, + best_effort, first_ip); + case EM_AARCH64: + return __libunwind_arch_unwind_info__new_arm64(thread, sample, max_stack, + best_effort, first_ip); + case EM_LOONGARCH: + return __libunwind_arch_unwind_info__new_loongarch(thread, sample, max_s= tack, + best_effort, first_ip); + case EM_MIPS: + return __libunwind_arch_unwind_info__new_mips(thread, sample, max_stack, + best_effort, first_ip); + case EM_PPC: + return __libunwind_arch_unwind_info__new_ppc32(thread, sample, max_stack, + best_effort, first_ip); + case EM_PPC64: + return __libunwind_arch_unwind_info__new_ppc64(thread, sample, max_stack, + best_effort, first_ip); + case EM_S390: + return __libunwind_arch_unwind_info__new_s390(thread, sample, max_stack, + best_effort, first_ip); + case EM_386: + return __libunwind_arch_unwind_info__new_i386(thread, sample, max_stack, + best_effort, first_ip); + case EM_X86_64: + return __libunwind_arch_unwind_info__new_x86_64(thread, sample, max_stac= k, + best_effort, first_ip); + default: + pr_err("ELF MACHINE %x is not supported.\n", e_machine); + return NULL; + } +} + +void libunwind_arch_unwind_info__delete(struct unwind_info *ui) +{ + free(ui); +} + +int libunwind_arch__unwind_step(struct unwind_info *ui) +{ + switch (ui->e_machine) { + case EM_ARM: + return __libunwind_arch__unwind_step_arm(ui); + case EM_AARCH64: + return __libunwind_arch__unwind_step_arm64(ui); + case EM_LOONGARCH: + return __libunwind_arch__unwind_step_loongarch(ui); + case EM_MIPS: + return __libunwind_arch__unwind_step_mips(ui); + case EM_PPC: + return __libunwind_arch__unwind_step_ppc32(ui); + case EM_PPC64: + return __libunwind_arch__unwind_step_ppc64(ui); + case EM_S390: + return __libunwind_arch__unwind_step_s390(ui); + case EM_386: + return __libunwind_arch__unwind_step_i386(ui); + case EM_X86_64: + return __libunwind_arch__unwind_step_x86_64(ui); + default: + pr_err("ELF MACHINE %x is not supported.\n", ui->e_machine); + return -EINVAL; + } +} diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.h b/tools/perf/u= til/libunwind-arch/libunwind-arch.h index c00277a5e914..2bf7fc33313b 100644 --- a/tools/perf/util/libunwind-arch/libunwind-arch.h +++ b/tools/perf/util/libunwind-arch/libunwind-arch.h @@ -2,7 +2,36 @@ #ifndef __LIBUNWIND_ARCH_H #define __LIBUNWIND_ARCH_H =20 +#include +#include +#include + +struct machine; struct maps; +struct perf_sample; +struct thread; + +struct unwind_info { + struct machine *machine; + struct thread *thread; + struct perf_sample *sample; + void *cursor; + uint64_t *ips; + int cur_ip; + int max_ips; + unsigned int unw_word_t_size; + uint16_t e_machine; + bool best_effort; +}; + +struct libarch_unwind__dyn_info { + uint64_t start_ip; + uint64_t end_ip; + uint64_t segbase; + uint64_t table_data; + uint64_t table_len; +}; +struct libarch_unwind__proc_info; =20 int __get_perf_regnum_for_unw_regnum_arm(int unw_regnum); int __get_perf_regnum_for_unw_regnum_arm64(int unw_regnum); @@ -37,4 +66,209 @@ 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); =20 +void *__libunwind_arch__create_addr_space_arm(void); +void *__libunwind_arch__create_addr_space_arm64(void); +void *__libunwind_arch__create_addr_space_loongarch(void); +void *__libunwind_arch__create_addr_space_mips(void); +void *__libunwind_arch__create_addr_space_ppc32(void); +void *__libunwind_arch__create_addr_space_ppc64(void); +void *__libunwind_arch__create_addr_space_s390(void); +void *__libunwind_arch__create_addr_space_i386(void); +void *__libunwind_arch__create_addr_space_x86_64(void); +void *libunwind_arch__create_addr_space(unsigned int e_machine); + +int __libunwind__find_proc_info(void *as, uint64_t ip, void *pi, int need_= unwind_info, void *arg); +int __libunwind__access_mem(void *as, uint64_t addr, void *valp_word, int = __write, void *arg); +int __libunwind__access_reg(void *as, int regnum, void *valp_word, int __w= rite, void *arg); + +int __libunwind_arch__dwarf_search_unwind_table_arm(void *as, uint64_t ip, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg); +int __libunwind_arch__dwarf_search_unwind_table_arm64(void *as, uint64_t i= p, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg); +int __libunwind_arch__dwarf_search_unwind_table_loongarch(void *as, uint64= _t ip, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg); +int __libunwind_arch__dwarf_search_unwind_table_mips(void *as, uint64_t ip, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg); +int __libunwind_arch__dwarf_search_unwind_table_ppc32(void *as, uint64_t i= p, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg); +int __libunwind_arch__dwarf_search_unwind_table_ppc64(void *as, uint64_t i= p, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg); +int __libunwind_arch__dwarf_search_unwind_table_s390(void *as, uint64_t ip, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg); +int __libunwind_arch__dwarf_search_unwind_table_i386(void *as, uint64_t ip, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg); +int __libunwind_arch__dwarf_search_unwind_table_x86_64(void *as, uint64_t = ip, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg); +int libunwind_arch__dwarf_search_unwind_table(unsigned int e_machine, + void *as, + uint64_t ip, + struct libarch_unwind__dyn_info *di, + void *pi, + int need_unwind_info, + void *arg); + +int __libunwind_arch__dwarf_find_debug_frame_arm(int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end); +int __libunwind_arch__dwarf_find_debug_frame_arm64(int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end); +int __libunwind_arch__dwarf_find_debug_frame_loongarch(int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end); +int __libunwind_arch__dwarf_find_debug_frame_mips(int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end); +int __libunwind_arch__dwarf_find_debug_frame_ppc32(int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end); +int __libunwind_arch__dwarf_find_debug_frame_ppc64(int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end); +int __libunwind_arch__dwarf_find_debug_frame_s390(int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end); +int __libunwind_arch__dwarf_find_debug_frame_i386(int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end); +int __libunwind_arch__dwarf_find_debug_frame_x86_64(int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end); +int libunwind_arch__dwarf_find_debug_frame(unsigned int e_machine, + int found, + struct libarch_unwind__dyn_info *di_debug, + uint64_t ip, + uint64_t segbase, + const char *obj_name, + uint64_t start, + uint64_t end); + +struct unwind_info *__libunwind_arch_unwind_info__new_arm(struct thread *t= hread, + struct perf_sample *sample, + int max_stack, + bool best_effort, + uint64_t first_ip); +struct unwind_info *__libunwind_arch_unwind_info__new_arm64(struct thread = *thread, + struct perf_sample *sample, + int max_stack, + bool best_effort, + uint64_t first_ip); +struct unwind_info *__libunwind_arch_unwind_info__new_loongarch(struct thr= ead *thread, + struct perf_sample *sample, + int max_stack, + bool best_effort, + uint64_t first_ip); +struct unwind_info *__libunwind_arch_unwind_info__new_mips(struct thread *= thread, + struct perf_sample *sample, + int max_stack, + bool best_effort, + uint64_t first_ip); +struct unwind_info *__libunwind_arch_unwind_info__new_ppc32(struct thread = *thread, + struct perf_sample *sample, + int max_stack, + bool best_effort, + uint64_t first_ip); +struct unwind_info *__libunwind_arch_unwind_info__new_ppc64(struct thread = *thread, + struct perf_sample *sample, + int max_stack, + bool best_effort, + uint64_t first_ip); +struct unwind_info *__libunwind_arch_unwind_info__new_s390(struct thread *= thread, + struct perf_sample *sample, + int max_stack, + bool best_effort, + uint64_t first_ip); +struct unwind_info *__libunwind_arch_unwind_info__new_i386(struct thread *= thread, + struct perf_sample *sample, + int max_stack, + bool best_effort, + uint64_t first_ip); +struct unwind_info *__libunwind_arch_unwind_info__new_x86_64(struct thread= *thread, + struct perf_sample *sample, + int max_stack, + bool best_effort, + uint64_t first_ip); +struct unwind_info *libunwind_arch_unwind_info__new(struct thread *thread, + struct perf_sample *sample, + int max_stack, + bool best_effort, + uint16_t e_machine, + uint64_t first_ip); + +void libunwind_arch_unwind_info__delete(struct unwind_info *ui); + +int __libunwind_arch__unwind_step_arm(struct unwind_info *ui); +int __libunwind_arch__unwind_step_arm64(struct unwind_info *ui); +int __libunwind_arch__unwind_step_loongarch(struct unwind_info *ui); +int __libunwind_arch__unwind_step_mips(struct unwind_info *ui); +int __libunwind_arch__unwind_step_ppc32(struct unwind_info *ui); +int __libunwind_arch__unwind_step_ppc64(struct unwind_info *ui); +int __libunwind_arch__unwind_step_s390(struct unwind_info *ui); +int __libunwind_arch__unwind_step_i386(struct unwind_info *ui); +int __libunwind_arch__unwind_step_x86_64(struct unwind_info *ui); +int libunwind_arch__unwind_step(struct unwind_info *ui); + #endif /* __LIBUNWIND_ARCH_H */ diff --git a/tools/perf/util/libunwind-arch/libunwind-arm.c b/tools/perf/ut= il/libunwind-arch/libunwind-arm.c index bbaf01406c52..b31aeeab1663 100644 --- a/tools/perf/util/libunwind-arch/libunwind-arm.c +++ b/tools/perf/util/libunwind-arch/libunwind-arm.c @@ -2,8 +2,12 @@ #include "libunwind-arch.h" #include "../debug.h" #include "../maps.h" +#include "../thread.h" #include "../../../arch/arm/include/uapi/asm/perf_regs.h" #include +#include +#include +#include #include =20 #ifdef HAVE_LIBUNWIND_ARM_SUPPORT @@ -32,3 +36,255 @@ void __libunwind_arch__finish_access_arm(struct maps *m= aps __maybe_unused) unw_destroy_addr_space(maps__addr_space(maps)); #endif } + + +#ifdef HAVE_LIBUNWIND_ARM_SUPPORT +static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_inf= o_t *pi, + int need_unwind_info, void *arg) +{ + return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, siz= eof(unw_word_t)); +} + +static void put_unwind_info(unw_addr_space_t __maybe_unused as, + unw_proc_info_t *pi __maybe_unused, + void *arg __maybe_unused) +{ + pr_debug("unwind: put_unwind_info called\n"); +} + +static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused *dil_addr, + void __maybe_unused *arg) +{ + return -UNW_ENOINFO; +} + +static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *va= lp, + int __write, void *arg) +{ + return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_w= ord_t)); +} + +static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t= *valp, + int __write, void *arg) +{ + return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw= _word_t)); +} + +static int access_fpreg(unw_addr_space_t __maybe_unused as, + unw_regnum_t __maybe_unused num, + unw_fpreg_t __maybe_unused *val, + int __maybe_unused __write, + void __maybe_unused *arg) +{ + pr_err("unwind: access_fpreg unsupported\n"); + return -UNW_EINVAL; +} + +static int resume(unw_addr_space_t __maybe_unused as, + unw_cursor_t __maybe_unused *cu, + void __maybe_unused *arg) +{ + pr_err("unwind: resume unsupported\n"); + return -UNW_EINVAL; +} + +static int get_proc_name(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused addr, + char __maybe_unused *bufp, size_t __maybe_unused buf_len, + unw_word_t __maybe_unused *offp, void __maybe_unused *arg) +{ + pr_err("unwind: get_proc_name unsupported\n"); + return -UNW_EINVAL; +} +#endif + +void *__libunwind_arch__create_addr_space_arm(void) +{ +#ifdef HAVE_LIBUNWIND_ARM_SUPPORT + static unw_accessors_t accessors =3D { + .find_proc_info =3D find_proc_info, + .put_unwind_info =3D put_unwind_info, + .get_dyn_info_list_addr =3D get_dyn_info_list_addr, + .access_mem =3D access_mem, + .access_reg =3D access_reg, + .access_fpreg =3D access_fpreg, + .resume =3D resume, + .get_proc_name =3D get_proc_name, + }; + unw_addr_space_t addr_space; + + addr_space =3D unw_create_addr_space(&accessors, /*byte_order=3D*/0); + unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); + return addr_space; +#else + return NULL; +#endif +} + +#ifdef HAVE_LIBUNWIND_ARM_SUPPORT +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +#endif + +int __libunwind_arch__dwarf_search_unwind_table_arm(void *as __maybe_unuse= d, + uint64_t ip __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + void *pi __maybe_unused, + int need_unwind_info __maybe_unused, + void *arg __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_ARM_SUPPORT + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, = arg); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +#if defined(HAVE_LIBUNWIND_ARM_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRA= ME_ARM) +extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_= debug, + unw_word_t ip, + unw_word_t segbase, + const char *obj_name, unw_word_t start, + unw_word_t end); +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) +#endif + +int __libunwind_arch__dwarf_find_debug_frame_arm(int found __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + uint64_t ip __maybe_unused, + uint64_t segbase __maybe_unused, + const char *obj_name __maybe_unused, + uint64_t start __maybe_unused, + uint64_t end __maybe_unused) +{ +#if defined(HAVE_LIBUNWIND_ARM_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRA= ME_ARM) + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, sta= rt, end); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +struct unwind_info *__libunwind_arch_unwind_info__new_arm(struct thread *t= hread __maybe_unused, + struct perf_sample *sample __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused, + uint64_t first_ip __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_ARM_SUPPORT + struct arch_unwind_info { + struct unwind_info ui; + unw_cursor_t _cursor; + uint64_t _ips[]; + }; + + struct maps *maps =3D thread__maps(thread); + void *addr_space =3D maps__addr_space(maps); + struct arch_unwind_info *ui; + int ret; + + if (addr_space =3D=3D NULL) + return NULL; + + ui =3D zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack); + if (!ui) + return NULL; + + ui->ui.machine =3D maps__machine(maps); + ui->ui.thread =3D thread; + ui->ui.sample =3D sample; + ui->ui.cursor =3D &ui->_cursor; + ui->ui.ips =3D &ui->_ips[0]; + ui->ui.ips[0] =3D first_ip; + ui->ui.cur_ip =3D 1; + ui->ui.max_ips =3D max_stack; + ui->ui.unw_word_t_size =3D sizeof(unw_word_t); + ui->ui.e_machine =3D EM_ARM; + ui->ui.best_effort =3D best_effort; + + ret =3D unw_init_remote(&ui->_cursor, addr_space, &ui->ui); + if (ret) { + if (!best_effort) + pr_err("libunwind: %s\n", unw_strerror(ret)); + free(ui); + return NULL; + } + + return &ui->ui; +#else + return NULL; +#endif +} + +int __libunwind_arch__unwind_step_arm(struct unwind_info *ui __maybe_unuse= d) +{ +#ifdef HAVE_LIBUNWIND_ARM_SUPPORT + int ret; + + if (ui->cur_ip >=3D ui->max_ips) + return -1; + + ret =3D unw_step(ui->cursor); + if (ret > 0) { + uint64_t ip; + + unw_get_reg(ui->cursor, UNW_REG_IP, &ip); + + if (unw_is_signal_frame(ui->cursor) <=3D 0) { + /* + * Decrement the IP for any non-activation frames. This + * is required to properly find the srcline for caller + * frames. See also the documentation for + * dwfl_frame_pc(), which this code tries to replicate. + */ + --ip; + } + ui->ips[ui->cur_ip++] =3D ip; + } + return ret; +#else + return -EINVAL; +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-arm64.c b/tools/perf/= util/libunwind-arch/libunwind-arm64.c index 8ba510089736..ceeae2d0f8e9 100644 --- a/tools/perf/util/libunwind-arch/libunwind-arm64.c +++ b/tools/perf/util/libunwind-arch/libunwind-arm64.c @@ -2,8 +2,12 @@ #include "libunwind-arch.h" #include "../debug.h" #include "../maps.h" +#include "../thread.h" #include "../../../arch/arm64/include/uapi/asm/perf_regs.h" #include +#include +#include +#include #include =20 #ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT @@ -32,3 +36,254 @@ void __libunwind_arch__finish_access_arm64(struct maps = *maps __maybe_unused) unw_destroy_addr_space(maps__addr_space(maps)); #endif } + +#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT +static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_inf= o_t *pi, + int need_unwind_info, void *arg) +{ + return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, siz= eof(unw_word_t)); +} + +static void put_unwind_info(unw_addr_space_t __maybe_unused as, + unw_proc_info_t *pi __maybe_unused, + void *arg __maybe_unused) +{ + pr_debug("unwind: put_unwind_info called\n"); +} + +static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused *dil_addr, + void __maybe_unused *arg) +{ + return -UNW_ENOINFO; +} + +static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *va= lp, + int __write, void *arg) +{ + return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_w= ord_t)); +} + +static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t= *valp, + int __write, void *arg) +{ + return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw= _word_t)); +} + +static int access_fpreg(unw_addr_space_t __maybe_unused as, + unw_regnum_t __maybe_unused num, + unw_fpreg_t __maybe_unused *val, + int __maybe_unused __write, + void __maybe_unused *arg) +{ + pr_err("unwind: access_fpreg unsupported\n"); + return -UNW_EINVAL; +} + +static int resume(unw_addr_space_t __maybe_unused as, + unw_cursor_t __maybe_unused *cu, + void __maybe_unused *arg) +{ + pr_err("unwind: resume unsupported\n"); + return -UNW_EINVAL; +} + +static int get_proc_name(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused addr, + char __maybe_unused *bufp, size_t __maybe_unused buf_len, + unw_word_t __maybe_unused *offp, void __maybe_unused *arg) +{ + pr_err("unwind: get_proc_name unsupported\n"); + return -UNW_EINVAL; +} +#endif + +void *__libunwind_arch__create_addr_space_arm64(void) +{ +#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT + static unw_accessors_t accessors =3D { + .find_proc_info =3D find_proc_info, + .put_unwind_info =3D put_unwind_info, + .get_dyn_info_list_addr =3D get_dyn_info_list_addr, + .access_mem =3D access_mem, + .access_reg =3D access_reg, + .access_fpreg =3D access_fpreg, + .resume =3D resume, + .get_proc_name =3D get_proc_name, + }; + unw_addr_space_t addr_space; + + addr_space =3D unw_create_addr_space(&accessors, /*byte_order=3D*/0); + unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); + return addr_space; +#else + return NULL; +#endif +} + +#ifdef HAVE_LIBUNWIND_ARM64_SUPPORT +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +#endif + +int __libunwind_arch__dwarf_search_unwind_table_arm64(void *as __maybe_unu= sed, + uint64_t ip __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + void *pi __maybe_unused, + int need_unwind_info __maybe_unused, + void *arg __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_ARM64_SUPPORT + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, = arg); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +#if defined(HAVE_LIBUNWIND_ARM64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_F= RAME_ARM64) +extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_= debug, + unw_word_t ip, + unw_word_t segbase, + const char *obj_name, unw_word_t start, + unw_word_t end); +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) +#endif + +int __libunwind_arch__dwarf_find_debug_frame_arm64(int found __maybe_unuse= d, + struct libarch_unwind__dyn_info *_di __maybe_unused, + uint64_t ip __maybe_unused, + uint64_t segbase __maybe_unused, + const char *obj_name __maybe_unused, + uint64_t start __maybe_unused, + uint64_t end __maybe_unused) +{ +#if defined(HAVE_LIBUNWIND_ARM64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_F= RAME_ARM64) + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, sta= rt, end); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +struct unwind_info *__libunwind_arch_unwind_info__new_arm64(struct thread = *thread __maybe_unused, + struct perf_sample *sample __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused, + uint64_t first_ip __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_ARM64_SUPPORT + struct arch_unwind_info { + struct unwind_info ui; + unw_cursor_t _cursor; + uint64_t _ips[]; + }; + + struct maps *maps =3D thread__maps(thread); + void *addr_space =3D maps__addr_space(maps); + struct arch_unwind_info *ui; + int ret; + + if (addr_space =3D=3D NULL) + return NULL; + + ui =3D zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack); + if (!ui) + return NULL; + + ui->ui.machine =3D maps__machine(maps); + ui->ui.thread =3D thread; + ui->ui.sample =3D sample; + ui->ui.cursor =3D &ui->_cursor; + ui->ui.ips =3D &ui->_ips[0]; + ui->ui.ips[0] =3D first_ip; + ui->ui.cur_ip =3D 1; + ui->ui.max_ips =3D max_stack; + ui->ui.unw_word_t_size =3D sizeof(unw_word_t); + ui->ui.e_machine =3D EM_AARCH64; + ui->ui.best_effort =3D best_effort; + + ret =3D unw_init_remote(&ui->_cursor, addr_space, &ui->ui); + if (ret) { + if (!best_effort) + pr_err("libunwind: %s\n", unw_strerror(ret)); + free(ui); + return NULL; + } + + return &ui->ui; +#else + return NULL; +#endif +} + +int __libunwind_arch__unwind_step_arm64(struct unwind_info *ui __maybe_unu= sed) +{ +#ifdef HAVE_LIBUNWIND_ARM64_SUPPORT + int ret; + + if (ui->cur_ip >=3D ui->max_ips) + return -1; + + ret =3D unw_step(ui->cursor); + if (ret > 0) { + uint64_t ip; + + unw_get_reg(ui->cursor, UNW_REG_IP, &ip); + + if (unw_is_signal_frame(ui->cursor) <=3D 0) { + /* + * Decrement the IP for any non-activation frames. This + * is required to properly find the srcline for caller + * frames. See also the documentation for + * dwfl_frame_pc(), which this code tries to replicate. + */ + --ip; + } + ui->ips[ui->cur_ip++] =3D ip; + } + return ret; +#else + return -EINVAL; +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-i386.c b/tools/perf/u= til/libunwind-arch/libunwind-i386.c index 45ff30c95c1b..fde40872a610 100644 --- a/tools/perf/util/libunwind-arch/libunwind-i386.c +++ b/tools/perf/util/libunwind-arch/libunwind-i386.c @@ -2,9 +2,12 @@ #include "libunwind-arch.h" #include "../debug.h" #include "../maps.h" +#include "../thread.h" #include "../../../arch/x86/include/uapi/asm/perf_regs.h" #include #include +#include +#include #include =20 #ifdef HAVE_LIBUNWIND_X86_SUPPORT @@ -56,3 +59,254 @@ void __libunwind_arch__finish_access_i386(struct maps *= maps __maybe_unused) unw_destroy_addr_space(maps__addr_space(maps)); #endif } + +#ifdef HAVE_LIBUNWIND_X86_SUPPORT +static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_inf= o_t *pi, + int need_unwind_info, void *arg) +{ + return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, siz= eof(unw_word_t)); +} + +static void put_unwind_info(unw_addr_space_t __maybe_unused as, + unw_proc_info_t *pi __maybe_unused, + void *arg __maybe_unused) +{ + pr_debug("unwind: put_unwind_info called\n"); +} + +static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused *dil_addr, + void __maybe_unused *arg) +{ + return -UNW_ENOINFO; +} + +static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *va= lp, + int __write, void *arg) +{ + return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_w= ord_t)); +} + +static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t= *valp, + int __write, void *arg) +{ + return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw= _word_t)); +} + +static int access_fpreg(unw_addr_space_t __maybe_unused as, + unw_regnum_t __maybe_unused num, + unw_fpreg_t __maybe_unused *val, + int __maybe_unused __write, + void __maybe_unused *arg) +{ + pr_err("unwind: access_fpreg unsupported\n"); + return -UNW_EINVAL; +} + +static int resume(unw_addr_space_t __maybe_unused as, + unw_cursor_t __maybe_unused *cu, + void __maybe_unused *arg) +{ + pr_err("unwind: resume unsupported\n"); + return -UNW_EINVAL; +} + +static int get_proc_name(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused addr, + char __maybe_unused *bufp, size_t __maybe_unused buf_len, + unw_word_t __maybe_unused *offp, void __maybe_unused *arg) +{ + pr_err("unwind: get_proc_name unsupported\n"); + return -UNW_EINVAL; +} +#endif + +void *__libunwind_arch__create_addr_space_i386(void) +{ +#ifdef HAVE_LIBUNWIND_X86_SUPPORT + static unw_accessors_t accessors =3D { + .find_proc_info =3D find_proc_info, + .put_unwind_info =3D put_unwind_info, + .get_dyn_info_list_addr =3D get_dyn_info_list_addr, + .access_mem =3D access_mem, + .access_reg =3D access_reg, + .access_fpreg =3D access_fpreg, + .resume =3D resume, + .get_proc_name =3D get_proc_name, + }; + unw_addr_space_t addr_space; + + addr_space =3D unw_create_addr_space(&accessors, /*byte_order=3D*/0); + unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); + return addr_space; +#else + return NULL; +#endif +} + +#ifdef HAVE_LIBUNWIND_X86_SUPPORT +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +#endif + +int __libunwind_arch__dwarf_search_unwind_table_i386(void *as __maybe_unus= ed, + uint64_t ip __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + void *pi __maybe_unused, + int need_unwind_info __maybe_unused, + void *arg __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_X86_SUPPORT + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, = arg); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +#if defined(HAVE_LIBUNWIND_X86_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRA= ME_X86) +extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_= debug, + unw_word_t ip, + unw_word_t segbase, + const char *obj_name, unw_word_t start, + unw_word_t end); +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) +#endif + +int __libunwind_arch__dwarf_find_debug_frame_i386(int found __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + uint64_t ip __maybe_unused, + uint64_t segbase __maybe_unused, + const char *obj_name __maybe_unused, + uint64_t start __maybe_unused, + uint64_t end __maybe_unused) +{ +#if defined(HAVE_LIBUNWIND_X86_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRA= ME_X86) + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, sta= rt, end); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +struct unwind_info *__libunwind_arch_unwind_info__new_i386(struct thread *= thread __maybe_unused, + struct perf_sample *sample __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused, + uint64_t first_ip __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_X86_SUPPORT + struct arch_unwind_info { + struct unwind_info ui; + unw_cursor_t _cursor; + uint64_t _ips[]; + }; + + struct maps *maps =3D thread__maps(thread); + void *addr_space =3D maps__addr_space(maps); + struct arch_unwind_info *ui; + int ret; + + if (addr_space =3D=3D NULL) + return NULL; + + ui =3D zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack); + if (!ui) + return NULL; + + ui->ui.machine =3D maps__machine(maps); + ui->ui.thread =3D thread; + ui->ui.sample =3D sample; + ui->ui.cursor =3D &ui->_cursor; + ui->ui.ips =3D &ui->_ips[0]; + ui->ui.ips[0] =3D first_ip; + ui->ui.cur_ip =3D 1; + ui->ui.max_ips =3D max_stack; + ui->ui.unw_word_t_size =3D sizeof(unw_word_t); + ui->ui.e_machine =3D EM_I386; + ui->ui.best_effort =3D best_effort; + + ret =3D unw_init_remote(&ui->_cursor, addr_space, &ui->ui); + if (ret) { + if (!best_effort) + pr_err("libunwind: %s\n", unw_strerror(ret)); + free(ui); + return NULL; + } + + return &ui->ui; +#else + return NULL; +#endif +} + +int __libunwind_arch__unwind_step_i386(struct unwind_info *ui __maybe_unus= ed) +{ +#ifdef HAVE_LIBUNWIND_X86_SUPPORT + int ret; + + if (ui->cur_ip >=3D ui->max_ips) + return -1; + + ret =3D unw_step(ui->cursor); + if (ret > 0) { + uint64_t ip; + + unw_get_reg(ui->cursor, UNW_REG_IP, &ip); + + if (unw_is_signal_frame(ui->cursor) <=3D 0) { + /* + * Decrement the IP for any non-activation frames. This + * is required to properly find the srcline for caller + * frames. See also the documentation for + * dwfl_frame_pc(), which this code tries to replicate. + */ + --ip; + } + ui->ips[ui->cur_ip++] =3D ip; + } + return ret; +#else + return -EINVAL; +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-loongarch.c b/tools/p= erf/util/libunwind-arch/libunwind-loongarch.c index 837aa11e2b9f..d86397598a9d 100644 --- a/tools/perf/util/libunwind-arch/libunwind-loongarch.c +++ b/tools/perf/util/libunwind-arch/libunwind-loongarch.c @@ -2,8 +2,12 @@ #include "libunwind-arch.h" #include "../debug.h" #include "../maps.h" +#include "../thread.h" #include "../../../arch/loongarch/include/uapi/asm/perf_regs.h" #include +#include +#include +#include #include =20 #ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT @@ -40,3 +44,254 @@ void __libunwind_arch__finish_access_loongarch(struct m= aps *maps __maybe_unused) unw_destroy_addr_space(maps__addr_space(maps)); #endif } + +#ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT +static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_inf= o_t *pi, + int need_unwind_info, void *arg) +{ + return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, siz= eof(unw_word_t)); +} + +static void put_unwind_info(unw_addr_space_t __maybe_unused as, + unw_proc_info_t *pi __maybe_unused, + void *arg __maybe_unused) +{ + pr_debug("unwind: put_unwind_info called\n"); +} + +static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused *dil_addr, + void __maybe_unused *arg) +{ + return -UNW_ENOINFO; +} + +static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *va= lp, + int __write, void *arg) +{ + return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_w= ord_t)); +} + +static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t= *valp, + int __write, void *arg) +{ + return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw= _word_t)); +} + +static int access_fpreg(unw_addr_space_t __maybe_unused as, + unw_regnum_t __maybe_unused num, + unw_fpreg_t __maybe_unused *val, + int __maybe_unused __write, + void __maybe_unused *arg) +{ + pr_err("unwind: access_fpreg unsupported\n"); + return -UNW_EINVAL; +} + +static int resume(unw_addr_space_t __maybe_unused as, + unw_cursor_t __maybe_unused *cu, + void __maybe_unused *arg) +{ + pr_err("unwind: resume unsupported\n"); + return -UNW_EINVAL; +} + +static int get_proc_name(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused addr, + char __maybe_unused *bufp, size_t __maybe_unused buf_len, + unw_word_t __maybe_unused *offp, void __maybe_unused *arg) +{ + pr_err("unwind: get_proc_name unsupported\n"); + return -UNW_EINVAL; +} +#endif + +void *__libunwind_arch__create_addr_space_loongarch(void) +{ +#ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT + static unw_accessors_t accessors =3D { + .find_proc_info =3D find_proc_info, + .put_unwind_info =3D put_unwind_info, + .get_dyn_info_list_addr =3D get_dyn_info_list_addr, + .access_mem =3D access_mem, + .access_reg =3D access_reg, + .access_fpreg =3D access_fpreg, + .resume =3D resume, + .get_proc_name =3D get_proc_name, + }; + unw_addr_space_t addr_space; + + addr_space =3D unw_create_addr_space(&accessors, /*byte_order=3D*/0); + unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); + return addr_space; +#else + return NULL; +#endif +} + +#ifdef HAVE_LIBUNWIND_LOONGARCH_SUPPORT +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +#endif + +int __libunwind_arch__dwarf_search_unwind_table_loongarch(void *as __maybe= _unused, + uint64_t ip __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + void *pi __maybe_unused, + int need_unwind_info __maybe_unused, + void *arg __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_LOONGARCH_SUPPORT + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, = arg); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +#if defined(HAVE_LIBUNWIND_LOONGARCH_SUPPORT) && !defined(NO_LIBUNWIND_DEB= UG_FRAME_LOONGARCH) +extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_= debug, + unw_word_t ip, + unw_word_t segbase, + const char *obj_name, unw_word_t start, + unw_word_t end); +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) +#endif + +int __libunwind_arch__dwarf_find_debug_frame_loongarch(int found __maybe_u= nused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + uint64_t ip __maybe_unused, + uint64_t segbase __maybe_unused, + const char *obj_name __maybe_unused, + uint64_t start __maybe_unused, + uint64_t end __maybe_unused) +{ +#if defined(HAVE_LIBUNWIND_LOONGARCH_SUPPORT) && !defined(NO_LIBUNWIND_DEB= UG_FRAME_LOONGARCH) + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, sta= rt, end); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +struct unwind_info *__libunwind_arch_unwind_info__new_loongarch(struct thr= ead *thread __maybe_unused, + struct perf_sample *sample __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused, + uint64_t first_ip __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_LOONGARCH_SUPPORT + struct arch_unwind_info { + struct unwind_info ui; + unw_cursor_t _cursor; + uint64_t _ips[]; + }; + + struct maps *maps =3D thread__maps(thread); + void *addr_space =3D maps__addr_space(maps); + struct arch_unwind_info *ui; + int ret; + + if (addr_space =3D=3D NULL) + return NULL; + + ui =3D zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack); + if (!ui) + return NULL; + + ui->ui.machine =3D maps__machine(maps); + ui->ui.thread =3D thread; + ui->ui.sample =3D sample; + ui->ui.cursor =3D &ui->_cursor; + ui->ui.ips =3D &ui->_ips[0]; + ui->ui.ips[0] =3D first_ip; + ui->ui.cur_ip =3D 1; + ui->ui.max_ips =3D max_stack; + ui->ui.unw_word_t_size =3D sizeof(unw_word_t); + ui->ui.e_machine =3D EM_LOONGARCH; + ui->ui.best_effort =3D best_effort; + + ret =3D unw_init_remote(&ui->_cursor, addr_space, &ui->ui); + if (ret) { + if (!best_effort) + pr_err("libunwind: %s\n", unw_strerror(ret)); + free(ui); + return NULL; + } + + return &ui->ui; +#else + return NULL; +#endif +} + +int __libunwind_arch__unwind_step_loongarch(struct unwind_info *ui __maybe= _unused) +{ +#ifdef HAVE_LIBUNWIND_LOONGARCH_SUPPORT + int ret; + + if (ui->cur_ip >=3D ui->max_ips) + return -1; + + ret =3D unw_step(ui->cursor); + if (ret > 0) { + uint64_t ip; + + unw_get_reg(ui->cursor, UNW_REG_IP, &ip); + + if (unw_is_signal_frame(ui->cursor) <=3D 0) { + /* + * Decrement the IP for any non-activation frames. This + * is required to properly find the srcline for caller + * frames. See also the documentation for + * dwfl_frame_pc(), which this code tries to replicate. + */ + --ip; + } + ui->ips[ui->cur_ip++] =3D ip; + } + return ret; +#else + return -EINVAL; +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-mips.c b/tools/perf/u= til/libunwind-arch/libunwind-mips.c index 1fa81742ff4a..91ee3c27d159 100644 --- a/tools/perf/util/libunwind-arch/libunwind-mips.c +++ b/tools/perf/util/libunwind-arch/libunwind-mips.c @@ -2,8 +2,12 @@ #include "libunwind-arch.h" #include "../debug.h" #include "../maps.h" +#include "../thread.h" #include "../../../arch/mips/include/uapi/asm/perf_regs.h" #include +#include +#include +#include #include =20 #ifdef HAVE_LIBUNWIND_MIPS_SUPPORT @@ -42,3 +46,254 @@ void __libunwind_arch__finish_access_mips(struct maps *= maps __maybe_unused) unw_destroy_addr_space(maps__addr_space(maps)); #endif } + +#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT +static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_inf= o_t *pi, + int need_unwind_info, void *arg) +{ + return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, siz= eof(unw_word_t)); +} + +static void put_unwind_info(unw_addr_space_t __maybe_unused as, + unw_proc_info_t *pi __maybe_unused, + void *arg __maybe_unused) +{ + pr_debug("unwind: put_unwind_info called\n"); +} + +static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused *dil_addr, + void __maybe_unused *arg) +{ + return -UNW_ENOINFO; +} + +static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *va= lp, + int __write, void *arg) +{ + return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_w= ord_t)); +} + +static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t= *valp, + int __write, void *arg) +{ + return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw= _word_t)); +} + +static int access_fpreg(unw_addr_space_t __maybe_unused as, + unw_regnum_t __maybe_unused num, + unw_fpreg_t __maybe_unused *val, + int __maybe_unused __write, + void __maybe_unused *arg) +{ + pr_err("unwind: access_fpreg unsupported\n"); + return -UNW_EINVAL; +} + +static int resume(unw_addr_space_t __maybe_unused as, + unw_cursor_t __maybe_unused *cu, + void __maybe_unused *arg) +{ + pr_err("unwind: resume unsupported\n"); + return -UNW_EINVAL; +} + +static int get_proc_name(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused addr, + char __maybe_unused *bufp, size_t __maybe_unused buf_len, + unw_word_t __maybe_unused *offp, void __maybe_unused *arg) +{ + pr_err("unwind: get_proc_name unsupported\n"); + return -UNW_EINVAL; +} +#endif + +void *__libunwind_arch__create_addr_space_mips(void) +{ +#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT + static unw_accessors_t accessors =3D { + .find_proc_info =3D find_proc_info, + .put_unwind_info =3D put_unwind_info, + .get_dyn_info_list_addr =3D get_dyn_info_list_addr, + .access_mem =3D access_mem, + .access_reg =3D access_reg, + .access_fpreg =3D access_fpreg, + .resume =3D resume, + .get_proc_name =3D get_proc_name, + }; + unw_addr_space_t addr_space; + + addr_space =3D unw_create_addr_space(&accessors, /*byte_order=3D*/0); + unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); + return addr_space; +#else + return NULL; +#endif +} + +#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +#endif + +int __libunwind_arch__dwarf_search_unwind_table_mips(void *as __maybe_unus= ed, + uint64_t ip __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + void *pi __maybe_unused, + int need_unwind_info __maybe_unused, + void *arg __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, = arg); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +#if defined(HAVE_LIBUNWIND_MIPS_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FR= AME_MIPS) +extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_= debug, + unw_word_t ip, + unw_word_t segbase, + const char *obj_name, unw_word_t start, + unw_word_t end); +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) +#endif + +int __libunwind_arch__dwarf_find_debug_frame_mips(int found __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + uint64_t ip __maybe_unused, + uint64_t segbase __maybe_unused, + const char *obj_name __maybe_unused, + uint64_t start __maybe_unused, + uint64_t end __maybe_unused) +{ +#if defined(HAVE_LIBUNWIND_MIPS_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FR= AME_MIPS) + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, sta= rt, end); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +struct unwind_info *__libunwind_arch_unwind_info__new_mips(struct thread *= thread __maybe_unused, + struct perf_sample *sample __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused, + uint64_t first_ip __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT + struct arch_unwind_info { + struct unwind_info ui; + unw_cursor_t _cursor; + uint64_t _ips[]; + }; + + struct maps *maps =3D thread__maps(thread); + void *addr_space =3D maps__addr_space(maps); + struct arch_unwind_info *ui; + int ret; + + if (addr_space =3D=3D NULL) + return NULL; + + ui =3D zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack); + if (!ui) + return NULL; + + ui->ui.machine =3D maps__machine(maps); + ui->ui.thread =3D thread; + ui->ui.sample =3D sample; + ui->ui.cursor =3D &ui->_cursor; + ui->ui.ips =3D &ui->_ips[0]; + ui->ui.ips[0] =3D first_ip; + ui->ui.cur_ip =3D 1; + ui->ui.max_ips =3D max_stack; + ui->ui.unw_word_t_size =3D sizeof(unw_word_t); + ui->ui.e_machine =3D EM_MIPS; + ui->ui.best_effort =3D best_effort; + + ret =3D unw_init_remote(&ui->_cursor, addr_space, &ui->ui); + if (ret) { + if (!best_effort) + pr_err("libunwind: %s\n", unw_strerror(ret)); + free(ui); + return NULL; + } + + return &ui->ui; +#else + return NULL; +#endif +} + +int __libunwind_arch__unwind_step_mips(struct unwind_info *ui __maybe_unus= ed) +{ +#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT + int ret; + + if (ui->cur_ip >=3D ui->max_ips) + return -1; + + ret =3D unw_step(ui->cursor); + if (ret > 0) { + uint64_t ip; + + unw_get_reg(ui->cursor, UNW_REG_IP, &ip); + + if (unw_is_signal_frame(ui->cursor) <=3D 0) { + /* + * Decrement the IP for any non-activation frames. This + * is required to properly find the srcline for caller + * frames. See also the documentation for + * dwfl_frame_pc(), which this code tries to replicate. + */ + --ip; + } + ui->ips[ui->cur_ip++] =3D ip; + } + return ret; +#else + return -EINVAL; +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-ppc32.c b/tools/perf/= util/libunwind-arch/libunwind-ppc32.c index fa8471c74bf3..b80d3184a28d 100644 --- a/tools/perf/util/libunwind-arch/libunwind-ppc32.c +++ b/tools/perf/util/libunwind-arch/libunwind-ppc32.c @@ -2,8 +2,12 @@ #include "libunwind-arch.h" #include "../debug.h" #include "../maps.h" +#include "../thread.h" #include "../../../arch/powerpc/include/uapi/asm/perf_regs.h" #include +#include +#include +#include #include =20 #ifdef HAVE_LIBUNWIND_PPC32_SUPPORT @@ -44,3 +48,254 @@ void __libunwind_arch__finish_access_ppc32(struct maps = *maps __maybe_unused) unw_destroy_addr_space(maps__addr_space(maps)); #endif } + +#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT +static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_inf= o_t *pi, + int need_unwind_info, void *arg) +{ + return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, siz= eof(unw_word_t)); +} + +static void put_unwind_info(unw_addr_space_t __maybe_unused as, + unw_proc_info_t *pi __maybe_unused, + void *arg __maybe_unused) +{ + pr_debug("unwind: put_unwind_info called\n"); +} + +static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused *dil_addr, + void __maybe_unused *arg) +{ + return -UNW_ENOINFO; +} + +static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *va= lp, + int __write, void *arg) +{ + return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_w= ord_t)); +} + +static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t= *valp, + int __write, void *arg) +{ + return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw= _word_t)); +} + +static int access_fpreg(unw_addr_space_t __maybe_unused as, + unw_regnum_t __maybe_unused num, + unw_fpreg_t __maybe_unused *val, + int __maybe_unused __write, + void __maybe_unused *arg) +{ + pr_err("unwind: access_fpreg unsupported\n"); + return -UNW_EINVAL; +} + +static int resume(unw_addr_space_t __maybe_unused as, + unw_cursor_t __maybe_unused *cu, + void __maybe_unused *arg) +{ + pr_err("unwind: resume unsupported\n"); + return -UNW_EINVAL; +} + +static int get_proc_name(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused addr, + char __maybe_unused *bufp, size_t __maybe_unused buf_len, + unw_word_t __maybe_unused *offp, void __maybe_unused *arg) +{ + pr_err("unwind: get_proc_name unsupported\n"); + return -UNW_EINVAL; +} +#endif + +void *__libunwind_arch__create_addr_space_ppc32(void) +{ +#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT + static unw_accessors_t accessors =3D { + .find_proc_info =3D find_proc_info, + .put_unwind_info =3D put_unwind_info, + .get_dyn_info_list_addr =3D get_dyn_info_list_addr, + .access_mem =3D access_mem, + .access_reg =3D access_reg, + .access_fpreg =3D access_fpreg, + .resume =3D resume, + .get_proc_name =3D get_proc_name, + }; + unw_addr_space_t addr_space; + + addr_space =3D unw_create_addr_space(&accessors, /*byte_order=3D*/0); + unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); + return addr_space; +#else + return NULL; +#endif +} + +#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +#endif + +int __libunwind_arch__dwarf_search_unwind_table_ppc32(void *as __maybe_unu= sed, + uint64_t ip __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + void *pi __maybe_unused, + int need_unwind_info __maybe_unused, + void *arg __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, = arg); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +#if defined(HAVE_LIBUNWIND_PPC32_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_F= RAME_PPC32) +extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_= debug, + unw_word_t ip, + unw_word_t segbase, + const char *obj_name, unw_word_t start, + unw_word_t end); +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) +#endif + +int __libunwind_arch__dwarf_find_debug_frame_ppc32(int found __maybe_unuse= d, + struct libarch_unwind__dyn_info *_di __maybe_unused, + uint64_t ip __maybe_unused, + uint64_t segbase __maybe_unused, + const char *obj_name __maybe_unused, + uint64_t start __maybe_unused, + uint64_t end __maybe_unused) +{ +#if defined(HAVE_LIBUNWIND_PPC32_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_F= RAME_PPC32) + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, sta= rt, end); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +struct unwind_info *__libunwind_arch_unwind_info__new_ppc32(struct thread = *thread __maybe_unused, + struct perf_sample *sample __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused, + uint64_t first_ip __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT + struct arch_unwind_info { + struct unwind_info ui; + unw_cursor_t _cursor; + uint64_t _ips[]; + }; + + struct maps *maps =3D thread__maps(thread); + void *addr_space =3D maps__addr_space(maps); + struct arch_unwind_info *ui; + int ret; + + if (addr_space =3D=3D NULL) + return NULL; + + ui =3D zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack); + if (!ui) + return NULL; + + ui->ui.machine =3D maps__machine(maps); + ui->ui.thread =3D thread; + ui->ui.sample =3D sample; + ui->ui.cursor =3D &ui->_cursor; + ui->ui.ips =3D &ui->_ips[0]; + ui->ui.ips[0] =3D first_ip; + ui->ui.cur_ip =3D 1; + ui->ui.max_ips =3D max_stack; + ui->ui.unw_word_t_size =3D sizeof(unw_word_t); + ui->ui.e_machine =3D EM_PPC; + ui->ui.best_effort =3D best_effort; + + ret =3D unw_init_remote(&ui->_cursor, addr_space, &ui->ui); + if (ret) { + if (!best_effort) + pr_err("libunwind: %s\n", unw_strerror(ret)); + free(ui); + return NULL; + } + + return &ui->ui; +#else + return NULL; +#endif +} + +int __libunwind_arch__unwind_step_ppc32(struct unwind_info *ui __maybe_unu= sed) +{ +#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT + int ret; + + if (ui->cur_ip >=3D ui->max_ips) + return -1; + + ret =3D unw_step(ui->cursor); + if (ret > 0) { + uint64_t ip; + + unw_get_reg(ui->cursor, UNW_REG_IP, &ip); + + if (unw_is_signal_frame(ui->cursor) <=3D 0) { + /* + * Decrement the IP for any non-activation frames. This + * is required to properly find the srcline for caller + * frames. See also the documentation for + * dwfl_frame_pc(), which this code tries to replicate. + */ + --ip; + } + ui->ips[ui->cur_ip++] =3D ip; + } + return ret; +#else + return -EINVAL; +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-ppc64.c b/tools/perf/= util/libunwind-arch/libunwind-ppc64.c index 2f746e347336..cbe3b476203f 100644 --- a/tools/perf/util/libunwind-arch/libunwind-ppc64.c +++ b/tools/perf/util/libunwind-arch/libunwind-ppc64.c @@ -2,8 +2,12 @@ #include "libunwind-arch.h" #include "../debug.h" #include "../maps.h" +#include "../thread.h" #include "../../../arch/powerpc/include/uapi/asm/perf_regs.h" #include +#include +#include +#include #include =20 #ifdef HAVE_LIBUNWIND_PPC64_SUPPORT @@ -46,3 +50,254 @@ void __libunwind_arch__finish_access_ppc64(struct maps = *maps __maybe_unused) unw_destroy_addr_space(maps__addr_space(maps)); #endif } + +#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT +static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_inf= o_t *pi, + int need_unwind_info, void *arg) +{ + return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, siz= eof(unw_word_t)); +} + +static void put_unwind_info(unw_addr_space_t __maybe_unused as, + unw_proc_info_t *pi __maybe_unused, + void *arg __maybe_unused) +{ + pr_debug("unwind: put_unwind_info called\n"); +} + +static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused *dil_addr, + void __maybe_unused *arg) +{ + return -UNW_ENOINFO; +} + +static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *va= lp, + int __write, void *arg) +{ + return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_w= ord_t)); +} + +static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t= *valp, + int __write, void *arg) +{ + return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw= _word_t)); +} + +static int access_fpreg(unw_addr_space_t __maybe_unused as, + unw_regnum_t __maybe_unused num, + unw_fpreg_t __maybe_unused *val, + int __maybe_unused __write, + void __maybe_unused *arg) +{ + pr_err("unwind: access_fpreg unsupported\n"); + return -UNW_EINVAL; +} + +static int resume(unw_addr_space_t __maybe_unused as, + unw_cursor_t __maybe_unused *cu, + void __maybe_unused *arg) +{ + pr_err("unwind: resume unsupported\n"); + return -UNW_EINVAL; +} + +static int get_proc_name(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused addr, + char __maybe_unused *bufp, size_t __maybe_unused buf_len, + unw_word_t __maybe_unused *offp, void __maybe_unused *arg) +{ + pr_err("unwind: get_proc_name unsupported\n"); + return -UNW_EINVAL; +} +#endif + +void *__libunwind_arch__create_addr_space_ppc64(void) +{ +#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT + static unw_accessors_t accessors =3D { + .find_proc_info =3D find_proc_info, + .put_unwind_info =3D put_unwind_info, + .get_dyn_info_list_addr =3D get_dyn_info_list_addr, + .access_mem =3D access_mem, + .access_reg =3D access_reg, + .access_fpreg =3D access_fpreg, + .resume =3D resume, + .get_proc_name =3D get_proc_name, + }; + unw_addr_space_t addr_space; + + addr_space =3D unw_create_addr_space(&accessors, /*byte_order=3D*/0); + unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); + return addr_space; +#else + return NULL; +#endif +} + +#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +#endif + +int __libunwind_arch__dwarf_search_unwind_table_ppc64(void *as __maybe_unu= sed, + uint64_t ip __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + void *pi __maybe_unused, + int need_unwind_info __maybe_unused, + void *arg __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, = arg); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +#if defined(HAVE_LIBUNWIND_PPC64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_F= RAME_PPC64) +extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_= debug, + unw_word_t ip, + unw_word_t segbase, + const char *obj_name, unw_word_t start, + unw_word_t end); +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) +#endif + +int __libunwind_arch__dwarf_find_debug_frame_ppc64(int found __maybe_unuse= d, + struct libarch_unwind__dyn_info *_di __maybe_unused, + uint64_t ip __maybe_unused, + uint64_t segbase __maybe_unused, + const char *obj_name __maybe_unused, + uint64_t start __maybe_unused, + uint64_t end __maybe_unused) +{ +#if defined(HAVE_LIBUNWIND_PPC64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_F= RAME_PPC64) + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, sta= rt, end); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +struct unwind_info *__libunwind_arch_unwind_info__new_ppc64(struct thread = *thread __maybe_unused, + struct perf_sample *sample __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused, + uint64_t first_ip __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT + struct arch_unwind_info { + struct unwind_info ui; + unw_cursor_t _cursor; + uint64_t _ips[]; + }; + + struct maps *maps =3D thread__maps(thread); + void *addr_space =3D maps__addr_space(maps); + struct arch_unwind_info *ui; + int ret; + + if (addr_space =3D=3D NULL) + return NULL; + + ui =3D zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack); + if (!ui) + return NULL; + + ui->ui.machine =3D maps__machine(maps); + ui->ui.thread =3D thread; + ui->ui.sample =3D sample; + ui->ui.cursor =3D &ui->_cursor; + ui->ui.ips =3D &ui->_ips[0]; + ui->ui.ips[0] =3D first_ip; + ui->ui.cur_ip =3D 1; + ui->ui.max_ips =3D max_stack; + ui->ui.unw_word_t_size =3D sizeof(unw_word_t); + ui->ui.e_machine =3D EM_PPC64; + ui->ui.best_effort =3D best_effort; + + ret =3D unw_init_remote(&ui->_cursor, addr_space, &ui->ui); + if (ret) { + if (!best_effort) + pr_err("libunwind: %s\n", unw_strerror(ret)); + free(ui); + return NULL; + } + + return &ui->ui; +#else + return NULL; +#endif +} + +int __libunwind_arch__unwind_step_ppc64(struct unwind_info *ui __maybe_unu= sed) +{ +#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT + int ret; + + if (ui->cur_ip >=3D ui->max_ips) + return -1; + + ret =3D unw_step(ui->cursor); + if (ret > 0) { + uint64_t ip; + + unw_get_reg(ui->cursor, UNW_REG_IP, &ip); + + if (unw_is_signal_frame(ui->cursor) <=3D 0) { + /* + * Decrement the IP for any non-activation frames. This + * is required to properly find the srcline for caller + * frames. See also the documentation for + * dwfl_frame_pc(), which this code tries to replicate. + */ + --ip; + } + ui->ips[ui->cur_ip++] =3D ip; + } + return ret; +#else + return -EINVAL; +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-s390.c b/tools/perf/u= til/libunwind-arch/libunwind-s390.c index 9f68d15438b2..ce3788e86d1f 100644 --- a/tools/perf/util/libunwind-arch/libunwind-s390.c +++ b/tools/perf/util/libunwind-arch/libunwind-s390.c @@ -2,8 +2,12 @@ #include "libunwind-arch.h" #include "../debug.h" #include "../maps.h" +#include "../thread.h" #include "../../../arch/s390/include/uapi/asm/perf_regs.h" #include +#include +#include +#include #include =20 #ifdef HAVE_LIBUNWIND_S390X_SUPPORT @@ -42,3 +46,254 @@ void __libunwind_arch__finish_access_s390(struct maps *= maps __maybe_unused) unw_destroy_addr_space(maps__addr_space(maps)); #endif } + +#ifdef HAVE_LIBUNWIND_S390X_SUPPORT +static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_inf= o_t *pi, + int need_unwind_info, void *arg) +{ + return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg, siz= eof(unw_word_t)); +} + +static void put_unwind_info(unw_addr_space_t __maybe_unused as, + unw_proc_info_t *pi __maybe_unused, + void *arg __maybe_unused) +{ + pr_debug("unwind: put_unwind_info called\n"); +} + +static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused *dil_addr, + void __maybe_unused *arg) +{ + return -UNW_ENOINFO; +} + +static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *va= lp, + int __write, void *arg) +{ + return __libunwind__access_mem(as, addr, valp, __write, arg, sizeof(unw_w= ord_t)); +} + +static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t= *valp, + int __write, void *arg) +{ + return __libunwind__access_reg(as, regnum, valp, __write, arg, sizeof(unw= _word_t)); +} + +static int access_fpreg(unw_addr_space_t __maybe_unused as, + unw_regnum_t __maybe_unused num, + unw_fpreg_t __maybe_unused *val, + int __maybe_unused __write, + void __maybe_unused *arg) +{ + pr_err("unwind: access_fpreg unsupported\n"); + return -UNW_EINVAL; +} + +static int resume(unw_addr_space_t __maybe_unused as, + unw_cursor_t __maybe_unused *cu, + void __maybe_unused *arg) +{ + pr_err("unwind: resume unsupported\n"); + return -UNW_EINVAL; +} + +static int get_proc_name(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused addr, + char __maybe_unused *bufp, size_t __maybe_unused buf_len, + unw_word_t __maybe_unused *offp, void __maybe_unused *arg) +{ + pr_err("unwind: get_proc_name unsupported\n"); + return -UNW_EINVAL; +} +#endif + +void *__libunwind_arch__create_addr_space_s390(void) +{ +#ifdef HAVE_LIBUNWIND_S390X_SUPPORT + static unw_accessors_t accessors =3D { + .find_proc_info =3D find_proc_info, + .put_unwind_info =3D put_unwind_info, + .get_dyn_info_list_addr =3D get_dyn_info_list_addr, + .access_mem =3D access_mem, + .access_reg =3D access_reg, + .access_fpreg =3D access_fpreg, + .resume =3D resume, + .get_proc_name =3D get_proc_name, + }; + unw_addr_space_t addr_space; + + addr_space =3D unw_create_addr_space(&accessors, /*byte_order=3D*/0); + unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); + return addr_space; +#else + return NULL; +#endif +} + +#ifdef HAVE_LIBUNWIND_S390X_SUPPORT +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +#endif + +int __libunwind_arch__dwarf_search_unwind_table_s390(void *as __maybe_unus= ed, + uint64_t ip __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + void *pi __maybe_unused, + int need_unwind_info __maybe_unused, + void *arg __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_S390X_SUPPORT + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, = arg); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +#if defined(HAVE_LIBUNWIND_S390X_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_F= RAME_S390X) +extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_= debug, + unw_word_t ip, + unw_word_t segbase, + const char *obj_name, unw_word_t start, + unw_word_t end); +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) +#endif + +int __libunwind_arch__dwarf_find_debug_frame_s390(int found __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + uint64_t ip __maybe_unused, + uint64_t segbase __maybe_unused, + const char *obj_name __maybe_unused, + uint64_t start __maybe_unused, + uint64_t end __maybe_unused) +{ +#if defined(HAVE_LIBUNWIND_S390X_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_F= RAME_S390X) + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, sta= rt, end); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +struct unwind_info *__libunwind_arch_unwind_info__new_s390(struct thread *= thread __maybe_unused, + struct perf_sample *sample __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused, + uint64_t first_ip __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_S390X_SUPPORT + struct arch_unwind_info { + struct unwind_info ui; + unw_cursor_t _cursor; + uint64_t _ips[]; + }; + + struct maps *maps =3D thread__maps(thread); + void *addr_space =3D maps__addr_space(maps); + struct arch_unwind_info *ui; + int ret; + + if (addr_space =3D=3D NULL) + return NULL; + + ui =3D zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack); + if (!ui) + return NULL; + + ui->ui.machine =3D maps__machine(maps); + ui->ui.thread =3D thread; + ui->ui.sample =3D sample; + ui->ui.cursor =3D &ui->_cursor; + ui->ui.ips =3D &ui->_ips[0]; + ui->ui.ips[0] =3D first_ip; + ui->ui.cur_ip =3D 1; + ui->ui.max_ips =3D max_stack; + ui->ui.unw_word_t_size =3D sizeof(unw_word_t); + ui->ui.e_machine =3D EM_S390; + ui->ui.best_effort =3D best_effort; + + ret =3D unw_init_remote(&ui->_cursor, addr_space, &ui->ui); + if (ret) { + if (!best_effort) + pr_err("libunwind: %s\n", unw_strerror(ret)); + free(ui); + return NULL; + } + + return &ui->ui; +#else + return NULL; +#endif +} + +int __libunwind_arch__unwind_step_s390(struct unwind_info *ui __maybe_unus= ed) +{ +#ifdef HAVE_LIBUNWIND_S390X_SUPPORT + int ret; + + if (ui->cur_ip >=3D ui->max_ips) + return -1; + + ret =3D unw_step(ui->cursor); + if (ret > 0) { + uint64_t ip; + + unw_get_reg(ui->cursor, UNW_REG_IP, &ip); + + if (unw_is_signal_frame(ui->cursor) <=3D 0) { + /* + * Decrement the IP for any non-activation frames. This + * is required to properly find the srcline for caller + * frames. See also the documentation for + * dwfl_frame_pc(), which this code tries to replicate. + */ + --ip; + } + ui->ips[ui->cur_ip++] =3D ip; + } + return ret; +#else + return -EINVAL; +#endif +} diff --git a/tools/perf/util/libunwind-arch/libunwind-x86_64.c b/tools/perf= /util/libunwind-arch/libunwind-x86_64.c index 25e326bd3e15..8ca66d442ffc 100644 --- a/tools/perf/util/libunwind-arch/libunwind-x86_64.c +++ b/tools/perf/util/libunwind-arch/libunwind-x86_64.c @@ -2,9 +2,12 @@ #include "libunwind-arch.h" #include "../debug.h" #include "../maps.h" +#include "../thread.h" #include "../../../arch/x86/include/uapi/asm/perf_regs.h" #include #include +#include +#include #include =20 #ifdef HAVE_LIBUNWIND_X86_64_SUPPORT @@ -65,3 +68,253 @@ void __libunwind_arch__finish_access_x86_64(struct maps= *maps __maybe_unused) unw_destroy_addr_space(maps__addr_space(maps)); #endif } + +#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT +static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_inf= o_t *pi, + int need_unwind_info, void *arg) +{ + return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg); +} + +static void put_unwind_info(unw_addr_space_t __maybe_unused as, + unw_proc_info_t *pi __maybe_unused, + void *arg __maybe_unused) +{ + pr_debug("unwind: put_unwind_info called\n"); +} + +static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused *dil_addr, + void __maybe_unused *arg) +{ + return -UNW_ENOINFO; +} + +static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *va= lp, + int __write, void *arg) +{ + return __libunwind__access_mem(as, addr, valp, __write, arg); +} + +static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t= *valp, + int __write, void *arg) +{ + return __libunwind__access_reg(as, regnum, valp, __write, arg); +} + +static int access_fpreg(unw_addr_space_t __maybe_unused as, + unw_regnum_t __maybe_unused num, + unw_fpreg_t __maybe_unused *val, + int __maybe_unused __write, + void __maybe_unused *arg) +{ + pr_err("unwind: access_fpreg unsupported\n"); + return -UNW_EINVAL; +} + +static int resume(unw_addr_space_t __maybe_unused as, + unw_cursor_t __maybe_unused *cu, + void __maybe_unused *arg) +{ + pr_err("unwind: resume unsupported\n"); + return -UNW_EINVAL; +} + +static int get_proc_name(unw_addr_space_t __maybe_unused as, + unw_word_t __maybe_unused addr, + char __maybe_unused *bufp, size_t __maybe_unused buf_len, + unw_word_t __maybe_unused *offp, void __maybe_unused *arg) +{ + pr_err("unwind: get_proc_name unsupported\n"); + return -UNW_EINVAL; +} +#endif + +void *__libunwind_arch__create_addr_space_x86_64(void) +{ +#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT + static unw_accessors_t accessors =3D { + .find_proc_info =3D find_proc_info, + .put_unwind_info =3D put_unwind_info, + .get_dyn_info_list_addr =3D get_dyn_info_list_addr, + .access_mem =3D access_mem, + .access_reg =3D access_reg, + .access_fpreg =3D access_fpreg, + .resume =3D resume, + .get_proc_name =3D get_proc_name, + }; + unw_addr_space_t addr_space; + + addr_space =3D unw_create_addr_space(&accessors, /*byte_order=3D*/0); + unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); + return addr_space; +#else + return NULL; +#endif +} + +#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT +extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) +#endif + +int __libunwind_arch__dwarf_search_unwind_table_x86_64(void *as __maybe_un= used, + uint64_t ip __maybe_unused, + struct libarch_unwind__dyn_info *_di __maybe_unused, + void *pi __maybe_unused, + int need_unwind_info __maybe_unused, + void *arg __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, = arg); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +#if defined(HAVE_LIBUNWIND_X86_64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_= FRAME_X86_64) +extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_= debug, + unw_word_t ip, + unw_word_t segbase, + const char *obj_name, unw_word_t start, + unw_word_t end); +#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) +#endif + +int __libunwind_arch__dwarf_find_debug_frame_x86_64(int found __maybe_unus= ed, + struct libarch_unwind__dyn_info *_di __maybe_unused, + uint64_t ip __maybe_unused, + uint64_t segbase __maybe_unused, + const char *obj_name __maybe_unused, + uint64_t start __maybe_unused, + uint64_t end __maybe_unused) +{ +#if defined(HAVE_LIBUNWIND_X86_64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_= FRAME_X86_64) + unw_dyn_info_t di =3D { + .format =3D UNW_INFO_FORMAT_REMOTE_TABLE, + .start_ip =3D _di->start_ip, + .end_ip =3D _di->end_ip, + .u =3D { + .rti =3D { + .segbase =3D _di->segbase, + .table_data =3D _di->table_data, + .table_len =3D _di->table_len, + }, + }, + }; + int ret =3D dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, sta= rt, end); + + _di->start_ip =3D di.start_ip; + _di->end_ip =3D di.end_ip; + _di->segbase =3D di.u.rti.segbase; + _di->table_data =3D di.u.rti.table_data; + _di->table_len =3D di.u.rti.table_len; + return ret; +#else + return -EINVAL; +#endif +} + +struct unwind_info *__libunwind_arch_unwind_info__new_x86_64(struct thread= *thread __maybe_unused, + struct perf_sample *sample __maybe_unused, + int max_stack __maybe_unused, + bool best_effort __maybe_unused, + uint64_t first_ip __maybe_unused) +{ +#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT + struct arch_unwind_info { + struct unwind_info ui; + unw_cursor_t _cursor; + uint64_t _ips[]; + }; + + struct maps *maps =3D thread__maps(thread); + void *addr_space =3D maps__addr_space(maps); + struct arch_unwind_info *ui; + int ret; + + if (addr_space =3D=3D NULL) + return NULL; + + ui =3D zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack); + if (!ui) + return NULL; + + ui->ui.machine =3D maps__machine(maps); + ui->ui.thread =3D thread; + ui->ui.sample =3D sample; + ui->ui.cursor =3D &ui->_cursor; + ui->ui.ips =3D &ui->_ips[0]; + ui->ui.ips[0] =3D first_ip; + ui->ui.cur_ip =3D 1; + ui->ui.max_ips =3D max_stack; + ui->ui.unw_word_t_size =3D sizeof(unw_word_t); + ui->ui.e_machine =3D EM_X86_64; + ui->ui.best_effort =3D best_effort; + + ret =3D unw_init_remote(&ui->_cursor, addr_space, &ui->ui); + if (ret) { + if (!best_effort) + pr_err("libunwind: %s\n", unw_strerror(ret)); + free(ui); + return NULL; + } + return &ui->ui; +#else + return NULL; +#endif +} + +int __libunwind_arch__unwind_step_x86_64(struct unwind_info *ui __maybe_un= used) +{ +#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT + int ret; + + if (ui->cur_ip >=3D ui->max_ips) + return 0; + + ret =3D unw_step(ui->cursor); + if (ret > 0) { + uint64_t ip; + + unw_get_reg(ui->cursor, UNW_REG_IP, &ip); + + if (unw_is_signal_frame(ui->cursor) <=3D 0) { + /* + * Decrement the IP for any non-activation frames. This + * is required to properly find the srcline for caller + * frames. See also the documentation for + * dwfl_frame_pc(), which this code tries to replicate. + */ + --ip; + } + ui->ips[ui->cur_ip++] =3D ip; + } + return ret; +#else + return -EINVAL; +#endif +} diff --git a/tools/perf/util/libunwind/arm64.c b/tools/perf/util/libunwind/= arm64.c deleted file mode 100644 index 15670a964495..000000000000 --- a/tools/perf/util/libunwind/arm64.c +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * This file setups defines to compile arch specific binary from the - * generic one. - * - * The function 'LIBUNWIND__ARCH_REG_ID' name is set according to arch - * name and the definition of this function is included directly from - * 'arch/arm64/util/unwind-libunwind.c', to make sure that this function - * is defined no matter what arch the host is. - * - * Finally, the arch specific unwind methods are exported which will - * be assigned to each arm64 thread. - */ - -#define REMOTE_UNWIND_LIBUNWIND - -#include "unwind.h" -#include "libunwind-aarch64.h" -#define perf_event_arm_regs perf_event_arm64_regs -#include <../../../arch/arm64/include/uapi/asm/perf_regs.h> -#undef perf_event_arm_regs -#include "../../arch/arm64/util/unwind-libunwind.c" - -/* NO_LIBUNWIND_DEBUG_FRAME is a feature flag for local libunwind, - * assign NO_LIBUNWIND_DEBUG_FRAME_AARCH64 to it for compiling arm64 - * unwind methods. - */ -#undef NO_LIBUNWIND_DEBUG_FRAME -#ifdef NO_LIBUNWIND_DEBUG_FRAME_AARCH64 -#define NO_LIBUNWIND_DEBUG_FRAME -#endif -#include "util/unwind-libunwind-local.c" - -struct unwind_libunwind_ops * -arm64_unwind_libunwind_ops =3D &_unwind_libunwind_ops; diff --git a/tools/perf/util/libunwind/x86_32.c b/tools/perf/util/libunwind= /x86_32.c deleted file mode 100644 index 1e9fb8bfec44..000000000000 --- a/tools/perf/util/libunwind/x86_32.c +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * This file setups defines to compile arch specific binary from the - * generic one. - * - * The function 'LIBUNWIND__ARCH_REG_ID' name is set according to arch - * name and the definition of this function is included directly from - * 'arch/x86/util/unwind-libunwind.c', to make sure that this function - * is defined no matter what arch the host is. - * - * Finally, the arch specific unwind methods are exported which will - * be assigned to each x86 thread. - */ - -#define REMOTE_UNWIND_LIBUNWIND - -#include "unwind.h" -#include "libunwind-x86.h" - -/* Explicitly define NO_LIBUNWIND_DEBUG_FRAME, because non-ARM has no - * dwarf_find_debug_frame() function. - */ -#ifndef NO_LIBUNWIND_DEBUG_FRAME -#define NO_LIBUNWIND_DEBUG_FRAME -#endif -#include "util/unwind-libunwind-local.c" - -struct unwind_libunwind_ops * -x86_32_unwind_libunwind_ops =3D &_unwind_libunwind_ops; diff --git a/tools/perf/util/maps.c b/tools/perf/util/maps.c index 8c7b2a1e7642..7ba82024fb11 100644 --- a/tools/perf/util/maps.c +++ b/tools/perf/util/maps.c @@ -198,16 +198,6 @@ void maps__set_addr_space(struct maps *maps, void *add= r_space) RC_CHK_ACCESS(maps)->addr_space =3D addr_space; } =20 -const struct unwind_libunwind_ops *maps__unwind_libunwind_ops(const struct= maps *maps) -{ - return RC_CHK_ACCESS(maps)->unwind_libunwind_ops; -} - -void maps__set_unwind_libunwind_ops(struct maps *maps, const struct unwind= _libunwind_ops *ops) -{ - RC_CHK_ACCESS(maps)->unwind_libunwind_ops =3D ops; -} - uint16_t maps__e_machine(const struct maps *maps) { return RC_CHK_ACCESS(maps)->e_machine; diff --git a/tools/perf/util/maps.h b/tools/perf/util/maps.h index 6469f62c41a8..5b80b199685e 100644 --- a/tools/perf/util/maps.h +++ b/tools/perf/util/maps.h @@ -49,8 +49,6 @@ refcount_t *maps__refcnt(struct maps *maps); /* Test only= . */ #ifdef HAVE_LIBUNWIND_SUPPORT void *maps__addr_space(const struct maps *maps); 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 diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unw= ind-libunwind-local.c deleted file mode 100644 index 8f0128ba05a7..000000000000 --- a/tools/perf/util/unwind-libunwind-local.c +++ /dev/null @@ -1,820 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Post mortem Dwarf CFI based unwinding on top of regs and stack dumps. - * - * Lots of this code have been borrowed or heavily inspired from parts of - * the libunwind 0.99 code which are (amongst other contributors I may have - * forgotten): - * - * Copyright (C) 2002-2007 Hewlett-Packard Co - * Contributed by David Mosberger-Tang - * - * And the bugs have been added by: - * - * Copyright (C) 2010, Frederic Weisbecker - * Copyright (C) 2012, Jiri Olsa - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifndef REMOTE_UNWIND_LIBUNWIND -#include -#include -#endif -#include "callchain.h" -#include "thread.h" -#include "session.h" -#include "perf_regs.h" -#include "unwind.h" -#include "map.h" -#include "symbol.h" -#include "debug.h" -#include "asm/bug.h" -#include "dso.h" -#include "libunwind-arch/libunwind-arch.h" - -extern int -UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, - unw_word_t ip, - unw_dyn_info_t *di, - unw_proc_info_t *pi, - int need_unwind_info, void *arg); - -#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) - -extern int -UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug, - unw_word_t ip, - unw_word_t segbase, - const char *obj_name, unw_word_t start, - unw_word_t end); - -#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) - -#define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */ -#define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */ - -/* Pointer-encoding formats: */ -#define DW_EH_PE_omit 0xff -#define DW_EH_PE_ptr 0x00 /* pointer-sized unsigned value */ -#define DW_EH_PE_udata4 0x03 /* unsigned 32-bit value */ -#define DW_EH_PE_udata8 0x04 /* unsigned 64-bit value */ -#define DW_EH_PE_sdata4 0x0b /* signed 32-bit value */ -#define DW_EH_PE_sdata8 0x0c /* signed 64-bit value */ - -/* Pointer-encoding application: */ -#define DW_EH_PE_absptr 0x00 /* absolute value */ -#define DW_EH_PE_pcrel 0x10 /* rel. to addr. of encoded value */ - -/* - * The following are not documented by LSB v1.3, yet they are used by - * GCC, presumably they aren't documented by LSB since they aren't - * used on Linux: - */ -#define DW_EH_PE_funcrel 0x40 /* start-of-procedure-relative */ -#define DW_EH_PE_aligned 0x50 /* aligned pointer */ - -/* Flags intentionally not handled, since they're not needed: - * #define DW_EH_PE_indirect 0x80 - * #define DW_EH_PE_uleb128 0x01 - * #define DW_EH_PE_udata2 0x02 - * #define DW_EH_PE_sleb128 0x09 - * #define DW_EH_PE_sdata2 0x0a - * #define DW_EH_PE_textrel 0x20 - * #define DW_EH_PE_datarel 0x30 - */ - -struct unwind_info { - struct perf_sample *sample; - struct machine *machine; - struct thread *thread; - uint16_t e_machine; - bool best_effort; -}; - -#define dw_read(ptr, type, end) ({ \ - type *__p =3D (type *) ptr; \ - type __v; \ - if ((__p + 1) > (type *) end) \ - return -EINVAL; \ - __v =3D *__p++; \ - ptr =3D (typeof(ptr)) __p; \ - __v; \ - }) - -static int __dw_read_encoded_value(u8 **p, u8 *end, u64 *val, - u8 encoding) -{ - u8 *cur =3D *p; - *val =3D 0; - - switch (encoding) { - case DW_EH_PE_omit: - *val =3D 0; - goto out; - case DW_EH_PE_ptr: - *val =3D dw_read(cur, unsigned long, end); - goto out; - default: - break; - } - - switch (encoding & DW_EH_PE_APPL_MASK) { - case DW_EH_PE_absptr: - break; - case DW_EH_PE_pcrel: - *val =3D (unsigned long) cur; - break; - default: - return -EINVAL; - } - - if ((encoding & 0x07) =3D=3D 0x00) - encoding |=3D DW_EH_PE_udata4; - - switch (encoding & DW_EH_PE_FORMAT_MASK) { - case DW_EH_PE_sdata4: - *val +=3D dw_read(cur, s32, end); - break; - case DW_EH_PE_udata4: - *val +=3D dw_read(cur, u32, end); - break; - case DW_EH_PE_sdata8: - *val +=3D dw_read(cur, s64, end); - break; - case DW_EH_PE_udata8: - *val +=3D dw_read(cur, u64, end); - break; - default: - return -EINVAL; - } - - out: - *p =3D cur; - return 0; -} - -#define dw_read_encoded_value(ptr, end, enc) ({ \ - u64 __v; \ - if (__dw_read_encoded_value(&ptr, end, &__v, enc)) { \ - return -EINVAL; \ - } \ - __v; \ - }) - -static int elf_section_address_and_offset(int fd, const char *name, u64 *a= ddress, u64 *offset) -{ - Elf *elf; - GElf_Ehdr ehdr; - GElf_Shdr shdr; - int ret =3D -1; - - elf =3D elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); - if (elf =3D=3D NULL) - return -1; - - if (gelf_getehdr(elf, &ehdr) =3D=3D NULL) - goto out_err; - - if (!elf_section_by_name(elf, &ehdr, &shdr, name, NULL)) - goto out_err; - - *address =3D shdr.sh_addr; - *offset =3D shdr.sh_offset; - ret =3D 0; -out_err: - elf_end(elf); - return ret; -} - -#ifndef NO_LIBUNWIND_DEBUG_FRAME -static u64 elf_section_offset(int fd, const char *name) -{ - u64 address, offset =3D 0; - - if (elf_section_address_and_offset(fd, name, &address, &offset)) - return 0; - - return offset; -} -#endif - -static u64 elf_base_address(int fd) -{ - Elf *elf =3D elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); - GElf_Phdr phdr; - u64 retval =3D 0; - size_t i, phdrnum =3D 0; - - if (elf =3D=3D NULL) - return 0; - (void)elf_getphdrnum(elf, &phdrnum); - /* PT_LOAD segments are sorted by p_vaddr, so the first has the minimum p= _vaddr. */ - for (i =3D 0; i < phdrnum; i++) { - if (gelf_getphdr(elf, i, &phdr) && phdr.p_type =3D=3D PT_LOAD) { - retval =3D phdr.p_vaddr & -getpagesize(); - break; - } - } - - elf_end(elf); - return retval; -} - -#ifndef NO_LIBUNWIND_DEBUG_FRAME -static int elf_is_exec(int fd, const char *name) -{ - Elf *elf; - GElf_Ehdr ehdr; - int retval =3D 0; - - elf =3D elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); - if (elf =3D=3D NULL) - return 0; - if (gelf_getehdr(elf, &ehdr) =3D=3D NULL) - goto out; - - retval =3D (ehdr.e_type =3D=3D ET_EXEC); - -out: - elf_end(elf); - pr_debug("unwind: elf_is_exec(%s): %d\n", name, retval); - return retval; -} -#endif - -struct table_entry { - u32 start_ip_offset; - u32 fde_offset; -}; - -struct eh_frame_hdr { - unsigned char version; - unsigned char eh_frame_ptr_enc; - unsigned char fde_count_enc; - unsigned char table_enc; - - /* - * The rest of the header is variable-length and consists of the - * following members: - * - * encoded_t eh_frame_ptr; - * encoded_t fde_count; - */ - - /* A single encoded pointer should not be more than 8 bytes. */ - u64 enc[2]; - - /* - * struct { - * encoded_t start_ip; - * encoded_t fde_addr; - * } binary_search_table[fde_count]; - */ - char data[]; -} __packed; - -static int unwind_spec_ehframe(struct dso *dso, struct machine *machine, - u64 offset, u64 *table_data_offset, u64 *fde_count) -{ - struct eh_frame_hdr hdr; - u8 *enc =3D (u8 *) &hdr.enc; - u8 *end =3D (u8 *) &hdr.data; - ssize_t r; - - r =3D dso__data_read_offset(dso, machine, offset, - (u8 *) &hdr, sizeof(hdr)); - if (r !=3D sizeof(hdr)) - return -EINVAL; - - /* We dont need eh_frame_ptr, just skip it. */ - dw_read_encoded_value(enc, end, hdr.eh_frame_ptr_enc); - - *fde_count =3D dw_read_encoded_value(enc, end, hdr.fde_count_enc); - *table_data_offset =3D enc - (u8 *) &hdr; - return 0; -} - -struct read_unwind_spec_eh_frame_maps_cb_args { - struct dso *dso; - u64 base_addr; -}; - -static int read_unwind_spec_eh_frame_maps_cb(struct map *map, void *data) -{ - - struct read_unwind_spec_eh_frame_maps_cb_args *args =3D data; - - if (map__dso(map) =3D=3D args->dso && map__start(map) - map__pgoff(map) <= args->base_addr) - args->base_addr =3D map__start(map) - map__pgoff(map); - - return 0; -} - - -static int read_unwind_spec_eh_frame(struct dso *dso, struct unwind_info *= ui, - u64 *table_data, u64 *segbase, - u64 *fde_count) -{ - struct read_unwind_spec_eh_frame_maps_cb_args args =3D { - .dso =3D dso, - .base_addr =3D UINT64_MAX, - }; - int ret, fd; - - if (dso__data(dso)->eh_frame_hdr_offset =3D=3D 0) { - if (!dso__data_get_fd(dso, ui->machine, &fd)) - return -EINVAL; - - /* Check the .eh_frame section for unwinding info */ - ret =3D elf_section_address_and_offset(fd, ".eh_frame_hdr", - &dso__data(dso)->eh_frame_hdr_addr, - &dso__data(dso)->eh_frame_hdr_offset); - dso__data(dso)->elf_base_addr =3D elf_base_address(fd); - dso__data_put_fd(dso); - if (ret || dso__data(dso)->eh_frame_hdr_offset =3D=3D 0) - return -EINVAL; - } - - maps__for_each_map(thread__maps(ui->thread), read_unwind_spec_eh_frame_ma= ps_cb, &args); - - args.base_addr -=3D dso__data(dso)->elf_base_addr; - /* Address of .eh_frame_hdr */ - *segbase =3D args.base_addr + dso__data(dso)->eh_frame_hdr_addr; - ret =3D unwind_spec_ehframe(dso, ui->machine, dso__data(dso)->eh_frame_hd= r_offset, - table_data, fde_count); - if (ret) - return ret; - /* binary_search_table offset plus .eh_frame_hdr address */ - *table_data +=3D *segbase; - return 0; -} - -#ifndef NO_LIBUNWIND_DEBUG_FRAME -static int read_unwind_spec_debug_frame(struct dso *dso, - struct machine *machine, u64 *offset) -{ - int fd; - u64 ofs =3D dso__data(dso)->debug_frame_offset; - - /* debug_frame can reside in: - * - dso - * - debug pointed by symsrc_filename - * - gnu_debuglink, which doesn't necessary - * has to be pointed by symsrc_filename - */ - if (ofs =3D=3D 0) { - if (dso__data_get_fd(dso, machine, &fd)) { - ofs =3D elf_section_offset(fd, ".debug_frame"); - dso__data_put_fd(dso); - } - - if (ofs <=3D 0) { - fd =3D open(dso__symsrc_filename(dso), O_RDONLY); - if (fd >=3D 0) { - ofs =3D elf_section_offset(fd, ".debug_frame"); - close(fd); - } - } - - if (ofs <=3D 0) { - char *debuglink =3D malloc(PATH_MAX); - int ret =3D 0; - - if (debuglink =3D=3D NULL) { - pr_err("unwind: Can't read unwind spec debug frame.\n"); - return -ENOMEM; - } - - ret =3D dso__read_binary_type_filename( - dso, DSO_BINARY_TYPE__DEBUGLINK, - machine->root_dir, debuglink, PATH_MAX); - if (!ret) { - fd =3D open(debuglink, O_RDONLY); - if (fd >=3D 0) { - ofs =3D elf_section_offset(fd, - ".debug_frame"); - close(fd); - } - } - if (ofs > 0) { - if (dso__symsrc_filename(dso) !=3D NULL) { - pr_warning( - "%s: overwrite symsrc(%s,%s)\n", - __func__, - dso__symsrc_filename(dso), - debuglink); - dso__free_symsrc_filename(dso); - } - dso__set_symsrc_filename(dso, debuglink); - } else { - free(debuglink); - } - } - - dso__data(dso)->debug_frame_offset =3D ofs; - } - - *offset =3D ofs; - if (*offset) - return 0; - - return -EINVAL; -} -#endif - -static struct map *find_map(unw_word_t ip, struct unwind_info *ui) -{ - struct addr_location al; - struct map *ret; - - addr_location__init(&al); - thread__find_map(ui->thread, PERF_RECORD_MISC_USER, ip, &al); - ret =3D map__get(al.map); - addr_location__exit(&al); - return ret; -} - -static int -find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, - int need_unwind_info, void *arg) -{ - struct unwind_info *ui =3D arg; - struct map *map; - struct dso *dso; - unw_dyn_info_t di; - u64 table_data, segbase, fde_count; - int ret =3D -EINVAL; - - map =3D find_map(ip, ui); - if (!map) - return -EINVAL; - - dso =3D map__dso(map); - if (!dso) { - map__put(map); - return -EINVAL; - } - - pr_debug("unwind: find_proc_info dso %s\n", dso__name(dso)); - - /* Check the .eh_frame section for unwinding info */ - if (!read_unwind_spec_eh_frame(dso, ui, &table_data, &segbase, &fde_count= )) { - memset(&di, 0, sizeof(di)); - di.format =3D UNW_INFO_FORMAT_REMOTE_TABLE; - di.start_ip =3D map__start(map); - di.end_ip =3D map__end(map); - di.u.rti.segbase =3D segbase; - di.u.rti.table_data =3D table_data; - di.u.rti.table_len =3D fde_count * sizeof(struct table_entry) - / sizeof(unw_word_t); - ret =3D dwarf_search_unwind_table(as, ip, &di, pi, - need_unwind_info, arg); - } - -#ifndef NO_LIBUNWIND_DEBUG_FRAME - /* Check the .debug_frame section for unwinding info */ - if (ret < 0 && - !read_unwind_spec_debug_frame(dso, ui->machine, &segbase)) { - int fd; - u64 start =3D map__start(map); - unw_word_t base =3D start; - const char *symfile; - - if (dso__data_get_fd(dso, ui->machine, &fd)) { - if (elf_is_exec(fd, dso__name(dso))) - base =3D 0; - dso__data_put_fd(dso); - } - - symfile =3D dso__symsrc_filename(dso) ?: dso__name(dso); - - memset(&di, 0, sizeof(di)); - if (dwarf_find_debug_frame(0, &di, ip, base, symfile, start, map__end(ma= p))) - ret =3D dwarf_search_unwind_table(as, ip, &di, pi, - need_unwind_info, arg); - } -#endif - map__put(map); - return ret; -} - -static int access_fpreg(unw_addr_space_t __maybe_unused as, - unw_regnum_t __maybe_unused num, - unw_fpreg_t __maybe_unused *val, - int __maybe_unused __write, - void __maybe_unused *arg) -{ - pr_err("unwind: access_fpreg unsupported\n"); - return -UNW_EINVAL; -} - -static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as, - unw_word_t __maybe_unused *dil_addr, - void __maybe_unused *arg) -{ - return -UNW_ENOINFO; -} - -static int resume(unw_addr_space_t __maybe_unused as, - unw_cursor_t __maybe_unused *cu, - void __maybe_unused *arg) -{ - pr_err("unwind: resume unsupported\n"); - return -UNW_EINVAL; -} - -static int -get_proc_name(unw_addr_space_t __maybe_unused as, - unw_word_t __maybe_unused addr, - char __maybe_unused *bufp, size_t __maybe_unused buf_len, - unw_word_t __maybe_unused *offp, void __maybe_unused *arg) -{ - pr_err("unwind: get_proc_name unsupported\n"); - return -UNW_EINVAL; -} - -static int access_dso_mem(struct unwind_info *ui, unw_word_t addr, - unw_word_t *data) -{ - struct map *map; - struct dso *dso; - ssize_t size; - - map =3D find_map(addr, ui); - if (!map) { - pr_debug("unwind: no map for %lx\n", (unsigned long)addr); - return -1; - } - - dso =3D map__dso(map); - - if (!dso) { - map__put(map); - return -1; - } - - size =3D dso__data_read_addr(dso, map, ui->machine, - addr, (u8 *) data, sizeof(*data)); - map__put(map); - return !(size =3D=3D sizeof(*data)); -} - -static int access_mem(unw_addr_space_t __maybe_unused as, - unw_word_t addr, unw_word_t *valp, - int __write, void *arg) -{ - struct unwind_info *ui =3D arg; - struct stack_dump *stack =3D &ui->sample->user_stack; - u64 start, end; - int offset; - int ret; - - /* Don't support write, probably not needed. */ - if (__write || !stack || !ui->sample->user_regs || !ui->sample->user_regs= ->regs) { - *valp =3D 0; - return 0; - } - - ret =3D perf_reg_value(&start, perf_sample__user_regs(ui->sample), - perf_arch_reg_sp(ui->e_machine)); - if (ret) - return ret; - - end =3D start + stack->size; - - /* Check overflow. */ - if (addr + sizeof(unw_word_t) < addr) - return -EINVAL; - - if (addr < start || addr + sizeof(unw_word_t) >=3D end) { - ret =3D access_dso_mem(ui, addr, valp); - if (ret) { - pr_debug("unwind: access_mem %p not inside range" - " 0x%" PRIx64 "-0x%" PRIx64 "\n", - (void *) (uintptr_t) addr, start, end); - *valp =3D 0; - return ret; - } - return 0; - } - - offset =3D addr - start; - *valp =3D *(unw_word_t *)&stack->data[offset]; - pr_debug("unwind: access_mem addr %p val %lx, offset %d\n", - (void *) (uintptr_t) addr, (unsigned long)*valp, offset); - return 0; -} - -static int access_reg(unw_addr_space_t __maybe_unused as, - unw_regnum_t regnum, unw_word_t *valp, - int __write, void *arg) -{ - struct unwind_info *ui =3D arg; - int id, ret; - u64 val; - - /* Don't support write, I suspect we don't need it. */ - if (__write) { - pr_err("unwind: access_reg w %d\n", regnum); - return 0; - } - - if (!ui->sample->user_regs || !ui->sample->user_regs->regs) { - *valp =3D 0; - return 0; - } - - id =3D get_perf_regnum_for_unw_regnum(ui->e_machine, regnum); - if (id < 0) - return -EINVAL; - - ret =3D perf_reg_value(&val, perf_sample__user_regs(ui->sample), id); - if (ret) { - if (!ui->best_effort) - pr_err("unwind: can't read reg %d\n", regnum); - return ret; - } - - *valp =3D (unw_word_t) val; - pr_debug("unwind: reg %d, val %lx\n", regnum, (unsigned long)*valp); - return 0; -} - -static void put_unwind_info(unw_addr_space_t __maybe_unused as, - unw_proc_info_t *pi __maybe_unused, - void *arg __maybe_unused) -{ - pr_debug("unwind: put_unwind_info called\n"); -} - -static int entry(u64 ip, struct thread *thread, - unwind_entry_cb_t cb, void *arg) -{ - struct unwind_entry e; - struct addr_location al; - int ret; - - addr_location__init(&al); - e.ms.sym =3D thread__find_symbol(thread, PERF_RECORD_MISC_USER, ip, &al); - e.ip =3D ip; - e.ms.map =3D al.map; - e.ms.thread =3D thread__get(al.thread); - - pr_debug("unwind: %s:ip =3D 0x%" PRIx64 " (0x%" PRIx64 ")\n", - al.sym ? al.sym->name : "''", - ip, - al.map ? map__map_ip(al.map, ip) : (u64) 0); - - ret =3D cb(&e, arg); - addr_location__exit(&al); - return ret; -} - -static void display_error(int err) -{ - switch (err) { - case UNW_EINVAL: - pr_err("unwind: Only supports local.\n"); - break; - case UNW_EUNSPEC: - pr_err("unwind: Unspecified error.\n"); - break; - case UNW_EBADREG: - pr_err("unwind: Register unavailable.\n"); - break; - default: - break; - } -} - -static unw_accessors_t accessors =3D { - .find_proc_info =3D find_proc_info, - .put_unwind_info =3D put_unwind_info, - .get_dyn_info_list_addr =3D get_dyn_info_list_addr, - .access_mem =3D access_mem, - .access_reg =3D access_reg, - .access_fpreg =3D access_fpreg, - .resume =3D resume, - .get_proc_name =3D get_proc_name, -}; - -static int _unwind__prepare_access(struct maps *maps) -{ - void *addr_space =3D unw_create_addr_space(&accessors, 0); - - maps__set_addr_space(maps, addr_space); - if (!addr_space) { - pr_err("unwind: Can't create unwind address space.\n"); - return -ENOMEM; - } - - unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); - return 0; -} - -static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, - void *arg, int max_stack) -{ - u64 val; - unw_word_t ips[max_stack]; - unw_addr_space_t addr_space; - unw_cursor_t c; - int ret, i =3D 0; - - ret =3D perf_reg_value(&val, perf_sample__user_regs(ui->sample), - perf_arch_reg_ip(ui->e_machine)); - if (ret) - return ret; - - ips[i++] =3D (unw_word_t) val; - - /* - * If we need more than one entry, do the DWARF - * unwind itself. - */ - if (max_stack - 1 > 0) { - WARN_ONCE(!ui->thread, "WARNING: ui->thread is NULL"); - addr_space =3D maps__addr_space(thread__maps(ui->thread)); - - if (addr_space =3D=3D NULL) - return -1; - - ret =3D unw_init_remote(&c, addr_space, ui); - if (ret && !ui->best_effort) - display_error(ret); - - while (!ret && (unw_step(&c) > 0) && i < max_stack) { - unw_get_reg(&c, UNW_REG_IP, &ips[i]); - - /* - * Decrement the IP for any non-activation frames. - * this is required to properly find the srcline - * for caller frames. - * See also the documentation for dwfl_frame_pc(), - * which this code tries to replicate. - */ - if (unw_is_signal_frame(&c) <=3D 0) - --ips[i]; - - ++i; - } - - max_stack =3D i; - } - - /* - * Display what we got based on the order setup. - */ - for (i =3D 0; i < max_stack && !ret; i++) { - int j =3D i; - - if (callchain_param.order =3D=3D ORDER_CALLER) - j =3D max_stack - i - 1; - ret =3D ips[j] ? entry(ips[j], ui->thread, cb, arg) : 0; - } - - return ret; -} - -static int _unwind__get_entries(unwind_entry_cb_t cb, void *arg, - struct thread *thread, - struct perf_sample *data, int max_stack, - bool best_effort) -{ - struct unwind_info ui =3D { - .sample =3D data, - .thread =3D thread, - .machine =3D maps__machine(thread__maps(thread)), - .e_machine =3D thread__e_machine(thread, /*machine=3D*/NULL, /*e_flag= s=3D*/NULL), - .best_effort =3D best_effort - }; - - if (!data->user_regs || !data->user_regs->regs) - return -EINVAL; - - if (max_stack <=3D 0) - return -EINVAL; - - return get_entries(&ui, cb, arg, max_stack); -} - -static struct unwind_libunwind_ops -_unwind_libunwind_ops =3D { - .prepare_access =3D _unwind__prepare_access, - .get_entries =3D _unwind__get_entries, -}; - -#ifndef REMOTE_UNWIND_LIBUNWIND -struct unwind_libunwind_ops * -local_unwind_libunwind_ops =3D &_unwind_libunwind_ops; -#endif diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-li= bunwind.c index eaee7b78d87c..66e7b7a26aad 100644 --- a/tools/perf/util/unwind-libunwind.c +++ b/tools/perf/util/unwind-libunwind.c @@ -1,23 +1,557 @@ // SPDX-License-Identifier: GPL-2.0 -#include "unwind.h" +#include "callchain.h" +#include "debug.h" #include "dso.h" +#include "env.h" #include "map.h" -#include "thread.h" +#include "perf_regs.h" #include "session.h" -#include "debug.h" -#include "env.h" -#include "callchain.h" +#include "symbol.h" +#include "thread.h" +#include "unwind.h" #include "libunwind-arch/libunwind-arch.h" #include #include +#include +#include +#include + +#define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */ +#define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */ + +/* Pointer-encoding formats: */ +#define DW_EH_PE_omit 0xff +#define DW_EH_PE_ptr 0x00 /* pointer-sized unsigned value */ +#define DW_EH_PE_udata4 0x03 /* unsigned 32-bit value */ +#define DW_EH_PE_udata8 0x04 /* unsigned 64-bit value */ +#define DW_EH_PE_sdata4 0x0b /* signed 32-bit value */ +#define DW_EH_PE_sdata8 0x0c /* signed 64-bit value */ + +/* Pointer-encoding application: */ +#define DW_EH_PE_absptr 0x00 /* absolute value */ +#define DW_EH_PE_pcrel 0x10 /* rel. to addr. of encoded value */ + +/* + * The following are not documented by LSB v1.3, yet they are used by + * GCC, presumably they aren't documented by LSB since they aren't + * used on Linux: + */ +#define DW_EH_PE_funcrel 0x40 /* start-of-procedure-relative */ +#define DW_EH_PE_aligned 0x50 /* aligned pointer */ + +/* Flags intentionally not handled, since they're not needed: + * #define DW_EH_PE_indirect 0x80 + * #define DW_EH_PE_uleb128 0x01 + * #define DW_EH_PE_udata2 0x02 + * #define DW_EH_PE_sleb128 0x09 + * #define DW_EH_PE_sdata2 0x0a + * #define DW_EH_PE_textrel 0x20 + * #define DW_EH_PE_datarel 0x30 + */ + +#define dw_read(ptr, type, end) ({ \ + type *__p =3D (type *) ptr; \ + type __v; \ + if ((__p + 1) > (type *) end) \ + return -EINVAL; \ + __v =3D *__p++; \ + ptr =3D (typeof(ptr)) __p; \ + __v; \ + }) + +static int __dw_read_encoded_value(u8 **p, u8 *end, u64 *val, + u8 encoding) +{ + u8 *cur =3D *p; + *val =3D 0; + + switch (encoding) { + case DW_EH_PE_omit: + *val =3D 0; + goto out; + case DW_EH_PE_ptr: + *val =3D dw_read(cur, unsigned long, end); + goto out; + default: + break; + } + + switch (encoding & DW_EH_PE_APPL_MASK) { + case DW_EH_PE_absptr: + break; + case DW_EH_PE_pcrel: + *val =3D (unsigned long) cur; + break; + default: + return -EINVAL; + } + + if ((encoding & 0x07) =3D=3D 0x00) + encoding |=3D DW_EH_PE_udata4; + + switch (encoding & DW_EH_PE_FORMAT_MASK) { + case DW_EH_PE_sdata4: + *val +=3D dw_read(cur, s32, end); + break; + case DW_EH_PE_udata4: + *val +=3D dw_read(cur, u32, end); + break; + case DW_EH_PE_sdata8: + *val +=3D dw_read(cur, s64, end); + break; + case DW_EH_PE_udata8: + *val +=3D dw_read(cur, u64, end); + break; + default: + return -EINVAL; + } + + out: + *p =3D cur; + return 0; +} + +#define dw_read_encoded_value(ptr, end, enc) ({ \ + u64 __v; \ + if (__dw_read_encoded_value(&ptr, end, &__v, enc)) { \ + return -EINVAL; \ + } \ + __v; \ + }) + +static u64 elf_base_address(int fd) +{ + Elf *elf =3D elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); + GElf_Phdr phdr; + u64 retval =3D 0; + size_t i, phdrnum =3D 0; + + if (elf =3D=3D NULL) + return 0; + (void)elf_getphdrnum(elf, &phdrnum); + /* PT_LOAD segments are sorted by p_vaddr, so the first has the minimum p= _vaddr. */ + for (i =3D 0; i < phdrnum; i++) { + if (gelf_getphdr(elf, i, &phdr) && phdr.p_type =3D=3D PT_LOAD) { + retval =3D phdr.p_vaddr & -getpagesize(); + break; + } + } + + elf_end(elf); + return retval; +} + +static int unwind_spec_ehframe(struct dso *dso, struct machine *machine, + u64 offset, u64 *table_data_offset, u64 *fde_count) +{ + struct eh_frame_hdr { + unsigned char version; + unsigned char eh_frame_ptr_enc; + unsigned char fde_count_enc; + unsigned char table_enc; + + /* + * The rest of the header is variable-length and consists of the + * following members: + * + * encoded_t eh_frame_ptr; + * encoded_t fde_count; + */ + + /* A single encoded pointer should not be more than 8 bytes. */ + u64 enc[2]; + + /* + * struct { + * encoded_t start_ip; + * encoded_t fde_addr; + * } binary_search_table[fde_count]; + */ + char data[]; + } __packed hdr; + u8 *enc =3D (u8 *) &hdr.enc; + u8 *end =3D (u8 *) &hdr.data; + ssize_t r; + + r =3D dso__data_read_offset(dso, machine, offset, (u8 *) &hdr, sizeof(hdr= )); + if (r !=3D sizeof(hdr)) + return -EINVAL; + + /* We dont need eh_frame_ptr, just skip it. */ + dw_read_encoded_value(enc, end, hdr.eh_frame_ptr_enc); + + *fde_count =3D dw_read_encoded_value(enc, end, hdr.fde_count_enc); + *table_data_offset =3D enc - (u8 *) &hdr; + return 0; +} + +struct read_unwind_spec_eh_frame_maps_cb_args { + struct dso *dso; + u64 base_addr; +}; + +static int read_unwind_spec_eh_frame_maps_cb(struct map *map, void *data) +{ + + struct read_unwind_spec_eh_frame_maps_cb_args *args =3D data; + + if (map__dso(map) =3D=3D args->dso && map__start(map) - map__pgoff(map) <= args->base_addr) + args->base_addr =3D map__start(map) - map__pgoff(map); + + return 0; +} + +static int elf_section_address_and_offset(int fd, const char *name, u64 *a= ddress, u64 *offset) +{ + Elf *elf; + GElf_Ehdr ehdr; + GElf_Shdr shdr; + int ret =3D -1; + + elf =3D elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); + if (elf =3D=3D NULL) + return -1; =20 -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; + if (gelf_getehdr(elf, &ehdr) =3D=3D NULL) + goto out_err; + + if (!elf_section_by_name(elf, &ehdr, &shdr, name, NULL)) + goto out_err; + + *address =3D shdr.sh_addr; + *offset =3D shdr.sh_offset; + ret =3D 0; +out_err: + elf_end(elf); + return ret; +} + +static int read_unwind_spec_eh_frame(struct dso *dso, struct unwind_info *= ui, + u64 *table_data, u64 *segbase, + u64 *fde_count) +{ + struct read_unwind_spec_eh_frame_maps_cb_args args =3D { + .dso =3D dso, + .base_addr =3D UINT64_MAX, + }; + int ret, fd; + + if (dso__data(dso)->eh_frame_hdr_offset =3D=3D 0) { + if (!dso__data_get_fd(dso, ui->machine, &fd)) + return -EINVAL; + + /* Check the .eh_frame section for unwinding info */ + ret =3D elf_section_address_and_offset(fd, ".eh_frame_hdr", + &dso__data(dso)->eh_frame_hdr_addr, + &dso__data(dso)->eh_frame_hdr_offset); + dso__data(dso)->elf_base_addr =3D elf_base_address(fd); + dso__data_put_fd(dso); + if (ret || dso__data(dso)->eh_frame_hdr_offset =3D=3D 0) + return -EINVAL; + } + + maps__for_each_map(thread__maps(ui->thread), read_unwind_spec_eh_frame_ma= ps_cb, &args); + + args.base_addr -=3D dso__data(dso)->elf_base_addr; + /* Address of .eh_frame_hdr */ + *segbase =3D args.base_addr + dso__data(dso)->eh_frame_hdr_addr; + ret =3D unwind_spec_ehframe(dso, ui->machine, dso__data(dso)->eh_frame_hd= r_offset, + table_data, fde_count); + if (ret) + return ret; + /* binary_search_table offset plus .eh_frame_hdr address */ + *table_data +=3D *segbase; + return 0; +} + +static u64 elf_section_offset(int fd, const char *name) +{ + u64 address, offset =3D 0; + + if (elf_section_address_and_offset(fd, name, &address, &offset)) + return 0; + + return offset; +} + +static int read_unwind_spec_debug_frame(struct dso *dso, + struct machine *machine, u64 *offset) +{ + int fd; + u64 ofs =3D dso__data(dso)->debug_frame_offset; + + /* debug_frame can reside in: + * - dso + * - debug pointed by symsrc_filename + * - gnu_debuglink, which doesn't necessary + * has to be pointed by symsrc_filename + */ + if (ofs =3D=3D 0) { + if (dso__data_get_fd(dso, machine, &fd)) { + ofs =3D elf_section_offset(fd, ".debug_frame"); + dso__data_put_fd(dso); + } + + if (ofs <=3D 0) { + fd =3D open(dso__symsrc_filename(dso), O_RDONLY); + if (fd >=3D 0) { + ofs =3D elf_section_offset(fd, ".debug_frame"); + close(fd); + } + } + + if (ofs <=3D 0) { + char *debuglink =3D malloc(PATH_MAX); + int ret =3D 0; + + if (debuglink =3D=3D NULL) { + pr_err("unwind: Can't read unwind spec debug frame.\n"); + return -ENOMEM; + } + + ret =3D dso__read_binary_type_filename( + dso, DSO_BINARY_TYPE__DEBUGLINK, + machine->root_dir, debuglink, PATH_MAX); + if (!ret) { + fd =3D open(debuglink, O_RDONLY); + if (fd >=3D 0) { + ofs =3D elf_section_offset(fd, + ".debug_frame"); + close(fd); + } + } + if (ofs > 0) { + if (dso__symsrc_filename(dso) !=3D NULL) { + pr_warning( + "%s: overwrite symsrc(%s,%s)\n", + __func__, + dso__symsrc_filename(dso), + debuglink); + dso__free_symsrc_filename(dso); + } + dso__set_symsrc_filename(dso, debuglink); + } else { + free(debuglink); + } + } + + dso__data(dso)->debug_frame_offset =3D ofs; + } + + *offset =3D ofs; + if (*offset) + return 0; + + return -EINVAL; +} + +static struct map *find_map(uint64_t ip, struct unwind_info *ui) +{ + struct addr_location al; + struct map *ret; + + addr_location__init(&al); + thread__find_map(ui->thread, PERF_RECORD_MISC_USER, ip, &al); + ret =3D map__get(al.map); + addr_location__exit(&al); + return ret; +} + +static int elf_is_exec(int fd, const char *name) +{ + Elf *elf; + GElf_Ehdr ehdr; + int retval =3D 0; + + elf =3D elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); + if (elf =3D=3D NULL) + return 0; + if (gelf_getehdr(elf, &ehdr) =3D=3D NULL) + goto out; + + retval =3D (ehdr.e_type =3D=3D ET_EXEC); + +out: + elf_end(elf); + pr_debug3("unwind: elf_is_exec(%s): %d\n", name, retval); + return retval; +} + +int __libunwind__find_proc_info(void *as, uint64_t ip, void *pi, int need_= unwind_info, void *arg) +{ + struct unwind_info *ui =3D arg; + struct map *map; + struct dso *dso; + u64 table_data, segbase, fde_count; + int ret =3D -EINVAL; + + map =3D find_map(ip, ui); + if (!map) + return -EINVAL; + + dso =3D map__dso(map); + if (!dso) { + map__put(map); + return -EINVAL; + } + + pr_debug3("unwind: find_proc_info dso %s\n", dso__name(dso)); + + /* Check the .eh_frame section for unwinding info */ + if (!read_unwind_spec_eh_frame(dso, ui, &table_data, &segbase, &fde_count= )) { + struct table_entry { + u32 start_ip_offset; + u32 fde_offset; + }; + struct libarch_unwind__dyn_info di =3D { + .start_ip =3D map__start(map), + .end_ip =3D map__end(map), + .segbase =3D segbase, + .table_data =3D table_data, + .table_len =3D fde_count * sizeof(struct table_entry) / ui->unw_word_t= _size, + }; + + ret =3D libunwind_arch__dwarf_search_unwind_table(ui->e_machine, as, ip,= &di, pi, + need_unwind_info, arg); + } + + /* Check the .debug_frame section for unwinding info */ + if (ret < 0 && !read_unwind_spec_debug_frame(dso, ui->machine, &segbase))= { + int fd; + u64 start =3D map__start(map); + u64 base =3D start; + const char *symfile; + struct libarch_unwind__dyn_info di =3D {}; + + if (dso__data_get_fd(dso, ui->machine, &fd)) { + if (elf_is_exec(fd, dso__name(dso))) + base =3D 0; + dso__data_put_fd(dso); + } + + symfile =3D dso__symsrc_filename(dso) ?: dso__name(dso); + + if (libunwind_arch__dwarf_find_debug_frame(ui->e_machine, /*found=3D*/0,= &di, ip, + base, symfile, start, map__end(map))) { + ret =3D libunwind_arch__dwarf_search_unwind_table(ui->e_machine, as, ip= , &di, pi, + need_unwind_info, arg); + } + } + map__put(map); + return ret; +} + +static int access_dso_mem(struct unwind_info *ui, uint64_t addr, void *dat= a_word) +{ + struct map *map; + struct dso *dso; + ssize_t size; + + map =3D find_map(addr, ui); + if (!map) { + pr_debug("unwind: no map for %lx\n", (unsigned long)addr); + return -1; + } + + dso =3D map__dso(map); + + if (!dso) { + map__put(map); + return -1; + } + + size =3D dso__data_read_addr(dso, map, ui->machine, + addr, + (u8 *) data_word, + ui->unw_word_t_size); + map__put(map); + return !((size_t)size =3D=3D ui->unw_word_t_size); +} + +int __libunwind__access_mem(void *as __maybe_unused, uint64_t addr, void *= valp_word, + int __write, void *arg) +{ + struct unwind_info *ui =3D arg; + struct stack_dump *stack =3D &ui->sample->user_stack; + u64 start, end; + int offset; + int ret; + + /* Don't support write, probably not needed. */ + if (__write || !stack || !ui->sample->user_regs || !ui->sample->user_regs= ->regs) { + uint64_t zero =3D 0; + + memcpy(valp_word, &zero, ui->unw_word_t_size); + return 0; + } + + ret =3D perf_reg_value(&start, perf_sample__user_regs(ui->sample), + perf_arch_reg_sp(ui->e_machine)); + if (ret) + return ret; + + end =3D start + stack->size; + + /* Check overflow. */ + if (addr + ui->unw_word_t_size < addr) + return -EINVAL; + + if (addr < start || addr + ui->unw_word_t_size >=3D end) { + ret =3D access_dso_mem(ui, addr, valp_word); + if (ret) { + pr_debug3("unwind: access_mem %p not inside range" + " 0x%" PRIx64 "-0x%" PRIx64 "\n", + (void *) (uintptr_t) addr, start, end); + memset(valp_word, 0, ui->unw_word_t_size); + return ret; + } + return 0; + } + + offset =3D addr - start; + memcpy(valp_word, &stack->data[offset], ui->unw_word_t_size); + pr_debug3("unwind: access_mem addr %p val %lx, offset %d\n", + (void *) (uintptr_t) addr, *((unsigned long *)valp_word), offset); + return 0; +} + +int __libunwind__access_reg(void *as __maybe_unused, int regnum, void *val= p_word, int __write, + void *arg) +{ + struct unwind_info *ui =3D arg; + int id, ret; + u64 val; + + /* Don't support write, I suspect we don't need it. */ + if (__write) { + pr_err("unwind: access_reg w %d\n", regnum); + return 0; + } + + if (!ui->sample->user_regs || !ui->sample->user_regs->regs) { + memset(valp_word, 0, ui->unw_word_t_size); + return 0; + } + + id =3D get_perf_regnum_for_unw_regnum(ui->e_machine, regnum); + if (id < 0) + return -EINVAL; + + ret =3D perf_reg_value(&val, perf_sample__user_regs(ui->sample), id); + if (ret) { + if (!ui->best_effort) + pr_err("unwind: can't read reg %d\n", regnum); + return ret; + } + + memcpy(valp_word, &val, ui->unw_word_t_size); + pr_debug3("unwind: reg %d, val %lx\n", regnum, val); + return 0; +} =20 int unwind__prepare_access(struct maps *maps, uint16_t e_machine) { - struct unwind_libunwind_ops *ops =3D local_unwind_libunwind_ops; + void *addr_space; =20 if (!dwarf_callchain_users) return 0; @@ -27,25 +561,16 @@ int unwind__prepare_access(struct maps *maps, uint16_t= e_machine) return 0; } =20 - if (e_machine !=3D EM_HOST) { - /* If not live/local mode. */ - switch (e_machine) { - case EM_386: - ops =3D x86_32_unwind_libunwind_ops; - break; - case EM_AARCH64: - ops =3D arm64_unwind_libunwind_ops; - break; - default: - pr_warning_once("unwind: ELF machine type %d is not supported\n", - e_machine); - return 0; - } - } - maps__set_unwind_libunwind_ops(maps, ops); maps__set_e_machine(maps, e_machine); + addr_space =3D libunwind_arch__create_addr_space(e_machine); + + maps__set_addr_space(maps, addr_space); + if (!addr_space) { + pr_err("unwind: Can't create unwind address space.\n"); + return -ENOMEM; + } =20 - return maps__unwind_libunwind_ops(maps)->prepare_access(maps); + return 0; } =20 void unwind__flush_access(struct maps *maps) @@ -58,14 +583,81 @@ void unwind__finish_access(struct maps *maps) libunwind_arch__finish_access(maps); } =20 +static int entry(uint64_t ip, struct thread *thread, unwind_entry_cb_t cb,= void *arg) +{ + struct unwind_entry e; + struct addr_location al; + int ret; + + addr_location__init(&al); + e.ms.sym =3D thread__find_symbol(thread, PERF_RECORD_MISC_USER, ip, &al); + e.ip =3D ip; + e.ms.map =3D al.map; + e.ms.thread =3D thread__get(al.thread); + + pr_debug("unwind: %s:ip =3D 0x%" PRIx64 " (0x%" PRIx64 ")\n", + al.sym ? al.sym->name : "''", + ip, + al.map ? map__map_ip(al.map, ip) : (u64) 0); + + ret =3D cb(&e, arg); + addr_location__exit(&al); + return ret; +} + int libunwind__get_entries(unwind_entry_cb_t cb, void *arg, struct thread *thread, - struct perf_sample *data, int max_stack, + struct perf_sample *sample, int max_stack, bool best_effort) { - const struct unwind_libunwind_ops *ops =3D maps__unwind_libunwind_ops(thr= ead__maps(thread)); + struct unwind_info *ui; + uint64_t first_ip; + int ret, i =3D 0; + uint16_t e_machine; =20 - if (ops) - return ops->get_entries(cb, arg, thread, data, max_stack, best_effort); - return 0; + if (!sample->user_regs || !sample->user_regs->regs) + return -EINVAL; + + if (max_stack <=3D 0) + return -EINVAL; + + if (!thread) { + pr_warning_once("WARNING: thread is NULL"); + return -EINVAL; + } + + e_machine =3D thread__e_machine(thread, /*machine=3D*/NULL, /*e_flags=3D*= /NULL); + ret =3D perf_reg_value(&first_ip, perf_sample__user_regs(sample), + perf_arch_reg_ip(e_machine)); + if (ret) + return ret; + + if (max_stack =3D=3D 1) { + /* Special case for a single entry. */ + return entry(first_ip, thread, cb, arg); + } + + ui =3D libunwind_arch_unwind_info__new(thread, sample, max_stack, best_ef= fort, e_machine, first_ip); + if (!ui) + return -1; + + do { + ret =3D libunwind_arch__unwind_step(ui); + if (ret < 0) + goto out; + + } while (ret); + + /* + * Display what we got based on the order setup. + */ + for (i =3D 0; i < ui->cur_ip; i++) { + int j =3D callchain_param.order =3D=3D ORDER_CALLEE ? i : ui->cur_ip - i= - 1; + + if (ui->ips[j]) + ret =3D entry(ui->ips[j], thread, cb, arg); + } +out: + libunwind_arch_unwind_info__delete(ui); + return ret; } diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h index ad610c5a241c..47ce12408c66 100644 --- a/tools/perf/util/unwind.h +++ b/tools/perf/util/unwind.h @@ -19,13 +19,6 @@ struct unwind_entry { =20 typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg); =20 -struct unwind_libunwind_ops { - int (*prepare_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); -}; - int unwind__configure(const char *var, const char *value, void *cb); int unwind__option(const struct option *opt, const char *arg, int unset); =20 --=20 2.53.0.1213.gd9a14994de-goog