* Re: [PATCH v4 12/53] perf bpf: Don't synthesize BPF events when disabled
From: Arnaldo Carvalho de Melo @ 2023-11-09 16:10 UTC (permalink / raw)
To: Song Liu
Cc: Ian Rogers, Song Liu, Peter Zijlstra, Ingo Molnar, Mark Rutland,
Alexander Shishkin, Jiri Olsa, Namhyung Kim, Adrian Hunter,
Nick Terrell, Kan Liang, Andi Kleen, Kajol Jain, Athira Rajeev,
Huacai Chen, Masami Hiramatsu, Vincent Whitchurch,
Steinar H. Gunderson, Liam Howlett, Miguel Ojeda, Colin Ian King,
Dmitrii Dolgov, Yang Jihong, Ming Wang, James Clark,
K Prateek Nayak, Sean Christopherson, Leo Yan, Ravi Bangoria,
German Gomez, Changbin Du, Paolo Bonzini, Li Dong, Sandipan Das,
liuwenyu, linux-kernel, linux-perf-users
In-Reply-To: <CAPhsuW4ftvoUFnNfcZgBg7=SeaHmev7roFnix=+c+zSq3LawFQ@mail.gmail.com>
Em Wed, Nov 08, 2023 at 03:03:15PM -0800, Song Liu escreveu:
> On Wed, Nov 8, 2023 at 8:15 AM Arnaldo Carvalho de Melo <acme@kernel.org> wrote:
> >
> > Em Thu, Nov 02, 2023 at 10:56:54AM -0700, Ian Rogers escreveu:
> > > If BPF sideband events are disabled on the command line, don't
> > > synthesize BPF events too.
> >
> >
> > Interesting, in 71184c6ab7e60fd5 ("perf record: Replace option
> > --bpf-event with --no-bpf-event") we checked that, but only down at
> > perf_event__synthesize_one_bpf_prog(), where we have:
> >
> > if (!opts->no_bpf_event) {
> > /* Synthesize PERF_RECORD_BPF_EVENT */
> > *bpf_event = (struct perf_record_bpf_event)
> >
> >
> > So we better remove that, now redundant check? I'll apply your patch as
> > is and then we can remove that other check.
> >
> > Song, can I have your Acked-by or Reviewed-by, please?
> >
> > - Arnaldo
> >
> > > Signed-off-by: Ian Rogers <irogers@google.com>
>
> Good catch!
>
> Acked-by: Song Liu <song@kernel.org>
Thanks, applied the patch with your Acked-by, will revisit this after
this gets published.
- Arnaldo
^ permalink raw reply
* Re: [PATCH v2] perf report: Add s390 raw data interpretation for PAI counters
From: Arnaldo Carvalho de Melo @ 2023-11-09 16:12 UTC (permalink / raw)
To: Thomas Richter
Cc: linux-kernel, linux-perf-users, namhyung, svens, gor, sumanthk,
hca
In-Reply-To: <20231109124105.3845355-1-tmricht@linux.ibm.com>
Em Thu, Nov 09, 2023 at 01:41:05PM +0100, Thomas Richter escreveu:
> commit 1bf54f32f525 ("s390/pai: Add support for cryptography counters")
> added support for Processor Activity Instrumentation Facility (PAI)
> counters. These counters values are added as raw data with the perf
> sample during perf record.
> Now add support to display these counters in perf report command.
> The counter number, its assigned name and value is now printed in
> addition to the hexadecimal output.
Can someone with access to a machine where these counters are available
try this patch to provide a Tested-by or Reviewed-by tag?
- Arnaldo
^ permalink raw reply
* Re: [PATCH v2 2/2] perf test: Add support for setting objdump binary via perf config
From: Arnaldo Carvalho de Melo @ 2023-11-09 16:14 UTC (permalink / raw)
To: James Clark
Cc: linux-perf-users, irogers, Peter Zijlstra, Ingo Molnar,
Mark Rutland, Alexander Shishkin, Jiri Olsa, Namhyung Kim,
Adrian Hunter, Nathan Chancellor, Nick Desaulniers, Tom Rix,
Yonghong Song, Fangrui Song, Kan Liang, Yang Jihong,
Athira Rajeev, Ravi Bangoria, linux-kernel, llvm
In-Reply-To: <2e0fb2d4-1410-6a7e-902d-8249a9ea523a@arm.com>
Em Thu, Nov 09, 2023 at 10:26:34AM +0000, James Clark escreveu:
>
>
> On 08/11/2023 20:53, Arnaldo Carvalho de Melo wrote:
> > Em Mon, Nov 06, 2023 at 03:10:49PM +0000, James Clark escreveu:
> >> Add a perf config variable that does the same thing as "perf test
> >> --objdump <x>".
> >>
> >> Also update the man page.
> >
> > That is ok, if one wants to change objdump just for testing, as a
> > followup improvement it may be interesting to allow that for the other
> > tools that have --objdump as well as to add this as a global option,
> > that affects all tools, no?
>
> For the tools they already all share annotate.objdump in the config. Do
> you mean that the tests could share the same config instead?
Probably.
> Maybe I could have used annotate.objdump for the tests, but it was used
> in a slightly different way, and I thought it would be easier for people
> to find if it started with "test."
Well, we can have both, with "test." overriding "annotate."?
Anyway, I applied this patch locally, now trying to fix some unrelated
problem that is making 'perf test' fail so that I can push
perf-tools-next publicly.
- Arnaldo
> >
> > Anyway, applied both patches.
> >
> > - Arnaldo
> >
> >> Signed-off-by: James Clark <james.clark@arm.com>
> >> ---
> >> tools/perf/Documentation/perf-config.txt | 4 ++++
> >> tools/perf/tests/builtin-test.c | 12 ++++++++++++
> >> 2 files changed, 16 insertions(+)
> >>
> >> diff --git a/tools/perf/Documentation/perf-config.txt b/tools/perf/Documentation/perf-config.txt
> >> index 0b4e79dbd3f6..16398babd1ef 100644
> >> --- a/tools/perf/Documentation/perf-config.txt
> >> +++ b/tools/perf/Documentation/perf-config.txt
> >> @@ -722,6 +722,10 @@ session-<NAME>.*::
> >> Defines new record session for daemon. The value is record's
> >> command line without the 'record' keyword.
> >>
> >> +test.*::
> >> +
> >> + test.objdump::
> >> + objdump binary to use for disassembly and annotations.
> >>
> >> SEE ALSO
> >> --------
> >> diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
> >> index a8d17dd50588..113e92119e1d 100644
> >> --- a/tools/perf/tests/builtin-test.c
> >> +++ b/tools/perf/tests/builtin-test.c
> >> @@ -14,6 +14,7 @@
> >> #include <sys/wait.h>
> >> #include <sys/stat.h>
> >> #include "builtin.h"
> >> +#include "config.h"
> >> #include "hist.h"
> >> #include "intlist.h"
> >> #include "tests.h"
> >> @@ -514,6 +515,15 @@ static int run_workload(const char *work, int argc, const char **argv)
> >> return -1;
> >> }
> >>
> >> +static int perf_test__config(const char *var, const char *value,
> >> + void *data __maybe_unused)
> >> +{
> >> + if (!strcmp(var, "test.objdump"))
> >> + test_objdump_path = value;
> >> +
> >> + return 0;
> >> +}
> >> +
> >> int cmd_test(int argc, const char **argv)
> >> {
> >> const char *test_usage[] = {
> >> @@ -541,6 +551,8 @@ int cmd_test(int argc, const char **argv)
> >> if (ret < 0)
> >> return ret;
> >>
> >> + perf_config(perf_test__config, NULL);
> >> +
> >> /* Unbuffered output */
> >> setvbuf(stdout, NULL, _IONBF, 0);
> >>
> >> --
> >> 2.34.1
> >>
> >
--
- Arnaldo
^ permalink raw reply
* [PATCH] perf evsel: Ignore the non-group case for branch counters
From: kan.liang @ 2023-11-09 16:40 UTC (permalink / raw)
To: acme, irogers, linux-kernel, linux-perf-users
Cc: peterz, mingo, jolsa, namhyung, adrian.hunter, tinghao.zhang,
Kan Liang, Arnaldo Carvalho de Melo
From: Kan Liang <kan.liang@linux.intel.com>
The perf test 27: Sample parsing fails with the branch counters support
introduced.
The branch counters feature requires all the events to belong to a
group. There is no problem with the normal perf usage which usually
initializes an evlist even for a single evsel.
But the perf test is special, which may not initialize an evlist. The
Sample parsing test case is one of the examples. The existing code
crashes with the !evsel->evlist.
Non-group means the evsel doesn't have branch counters support.
Reported-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Closes: https://lore.kernel.org/lkml/ZUv+G+w5EvJgQS45@kernel.org/
Signed-off-by: Kan Liang <kan.liang@linux.intel.com>
---
tools/perf/util/evsel.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 58a9b8c82790..7a6a2d1f96db 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -2355,6 +2355,10 @@ static inline bool evsel__has_branch_counters(const struct evsel *evsel)
{
struct evsel *cur, *leader = evsel__leader(evsel);
+ /* The branch counters feature only supports group */
+ if (!leader || !evsel->evlist)
+ return false;
+
evlist__for_each_entry(evsel->evlist, cur) {
if ((leader == evsel__leader(cur)) &&
(cur->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_COUNTERS))
--
2.35.1
^ permalink raw reply related
* Re: [PATCH RFC 09/10] unwind: Introduce SFrame user space unwinding
From: Indu Bhagat @ 2023-11-09 19:31 UTC (permalink / raw)
To: Josh Poimboeuf, Peter Zijlstra, Steven Rostedt, Ingo Molnar,
Arnaldo Carvalho de Melo
Cc: linux-kernel, x86, Mark Rutland, Alexander Shishkin, Jiri Olsa,
Namhyung Kim, Ian Rogers, Adrian Hunter, linux-perf-users,
Mark Brown, linux-toolchains
In-Reply-To: <09460e60dd1c2f8ea1abb8d9188195db699ce76f.1699487758.git.jpoimboe@kernel.org>
On 11/8/23 16:41, Josh Poimboeuf wrote:
> Some distros have started compiling frame pointers into all their
> packages to enable the kernel to do system-wide profiling of user space.
> Unfortunately that creates a runtime performance penalty across the
> entire system. Using DWARF instead isn't feasible due to complexity and
> slowness.
>
> For in-kernel unwinding we solved this problem with the creation of the
> ORC unwinder for x86_64. Similarly, for user space the GNU assembler
> has created the SFrame format starting with binutils 2.40. SFrame is a
> simpler version of .eh_frame which gets placed in the .sframe section.
>
> Add support for unwinding user space using SFrame.
>
> More information about SFrame can be found here:
>
> - https://lwn.net/Articles/932209/
> - https://lwn.net/Articles/940686/
> - https://sourceware.org/binutils/docs/sframe-spec.html
>
> Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
> ---
> arch/Kconfig | 3 +
> arch/x86/include/asm/mmu.h | 2 +-
> fs/binfmt_elf.c | 46 +++-
> include/linux/mm_types.h | 3 +
> include/linux/sframe.h | 46 ++++
> include/linux/user_unwind.h | 1 +
> include/uapi/linux/elf.h | 1 +
> include/uapi/linux/prctl.h | 3 +
> kernel/fork.c | 10 +
> kernel/sys.c | 11 +
> kernel/unwind/Makefile | 1 +
> kernel/unwind/sframe.c | 414 ++++++++++++++++++++++++++++++++++++
> kernel/unwind/sframe.h | 217 +++++++++++++++++++
> kernel/unwind/user.c | 15 +-
> mm/init-mm.c | 2 +
> 15 files changed, 768 insertions(+), 7 deletions(-)
> create mode 100644 include/linux/sframe.h
> create mode 100644 kernel/unwind/sframe.c
> create mode 100644 kernel/unwind/sframe.h
>
Thanks for working on this.
> diff --git a/kernel/unwind/sframe.c b/kernel/unwind/sframe.c
> new file mode 100644
> index 000000000000..b167c19497e5
> --- /dev/null
> +++ b/kernel/unwind/sframe.c
> @@ -0,0 +1,414 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/srcu.h>
> +#include <linux/uaccess.h>
> +#include <linux/mm.h>
> +#include <linux/sframe.h>
> +#include <linux/user_unwind.h>
> +
> +#include "sframe.h"
> +
> +#define SFRAME_FILENAME_LEN 32
> +
> +struct sframe_section {
> + struct rcu_head rcu;
> +
> + unsigned long sframe_addr;
> + unsigned long text_addr;
> +
> + unsigned long fdes_addr;
> + unsigned long fres_addr;
> + unsigned int fdes_num;
> + signed char ra_off, fp_off;
> +};
> +
> +DEFINE_STATIC_SRCU(sframe_srcu);
> +
> +#define __SFRAME_GET_USER(out, user_ptr, type) \
> +({ \
> + type __tmp; \
> + if (get_user(__tmp, (type *)user_ptr)) \
> + return -EFAULT; \
> + user_ptr += sizeof(__tmp); \
> + out = __tmp; \
> +})
> +
> +#define SFRAME_GET_USER_SIGNED(out, user_ptr, size) \
> +({ \
> + switch (size) { \
> + case 1: \
> + __SFRAME_GET_USER(out, user_ptr, s8); \
> + break; \
> + case 2: \
> + __SFRAME_GET_USER(out, user_ptr, s16); \
> + break; \
> + case 4: \
> + __SFRAME_GET_USER(out, user_ptr, s32); \
> + break; \
> + default: \
> + return -EINVAL; \
> + } \
> +})
> +
> +#define SFRAME_GET_USER_UNSIGNED(out, user_ptr, size) \
> +({ \
> + switch (size) { \
> + case 1: \
> + __SFRAME_GET_USER(out, user_ptr, u8); \
> + break; \
> + case 2: \
> + __SFRAME_GET_USER(out, user_ptr, u16); \
> + break; \
> + case 4: \
> + __SFRAME_GET_USER(out, user_ptr, u32); \
> + break; \
> + default: \
> + return -EINVAL; \
> + } \
> +})
> +
> +static unsigned char fre_type_to_size(unsigned char fre_type)
> +{
> + if (fre_type > 2)
> + return 0;
> + return 1 << fre_type;
> +}
> +
> +static unsigned char offset_size_enum_to_size(unsigned char off_size)
> +{
> + if (off_size > 2)
> + return 0;
> + return 1 << off_size;
> +}
> +
> +static int find_fde(struct sframe_section *sec, unsigned long ip,
> + struct sframe_fde *fde)
> +{
> + s32 func_off, ip_off;
> + struct sframe_fde __user *first, *last, *mid, *found;
> +
> + ip_off = ip - sec->sframe_addr;
> +
> + first = (void *)sec->fdes_addr;
> + last = first + sec->fdes_num;
> + while (first <= last) {
> + mid = first + ((last - first) / 2);
> + if (get_user(func_off, (s32 *)mid))
> + return -EFAULT;
> + if (ip_off >= func_off) {
> + found = mid;
> + first = mid + 1;
> + } else
> + last = mid - 1;
> + }
> +
> + if (!found)
> + return -EINVAL;
> +
> + if (copy_from_user(fde, found, sizeof(*fde)))
> + return -EFAULT;
> +
> + return 0;
> +}
> +
> +static int find_fre(struct sframe_section *sec, struct sframe_fde *fde,
> + unsigned long ip, struct user_unwind_frame *frame)
> +{
> + unsigned char fde_type = SFRAME_FUNC_FDE_TYPE(fde->info);
> + unsigned char fre_type = SFRAME_FUNC_FRE_TYPE(fde->info);
> + s32 fre_ip_off, cfa_off, ra_off, fp_off, ip_off;
> + unsigned char offset_count, offset_size;
> + unsigned char addr_size;
> + void __user *f, *last_f;
> + u8 fre_info;
> + int i;
> +
> + addr_size = fre_type_to_size(fre_type);
> + if (!addr_size)
> + return -EINVAL;
> +
> + ip_off = ip - sec->sframe_addr - fde->start_addr;
> +
> + f = (void *)sec->fres_addr + fde->fres_off;
> +
> + for (i = 0; i < fde->fres_num; i++) {
> +
> + SFRAME_GET_USER_UNSIGNED(fre_ip_off, f, addr_size);
> +
> + if (fde_type == SFRAME_FDE_TYPE_PCINC) {
> + if (fre_ip_off > ip_off)
> + break;
> + } else {
> + /* SFRAME_FDE_TYPE_PCMASK */
> +#if 0 /* sframe v2 */
> + if (ip_off % fde->rep_size < fre_ip_off)
> + break;
> +#endif
> + }
> +
> + SFRAME_GET_USER_UNSIGNED(fre_info, f, 1);
> +
> + offset_count = SFRAME_FRE_OFFSET_COUNT(fre_info);
> + offset_size = offset_size_enum_to_size(SFRAME_FRE_OFFSET_SIZE(fre_info));
> +
> + if (!offset_count || !offset_size)
> + return -EINVAL;
> +
> + last_f = f;
> + f += offset_count * offset_size;
> + }
> +
> + if (!last_f)
> + return -EINVAL;
> +
> + f = last_f;
> +
> + SFRAME_GET_USER_UNSIGNED(cfa_off, f, offset_size);
> + offset_count--;
> +
> + ra_off = sec->ra_off;
> + if (!ra_off) {
> + if (!offset_count--)
> + return -EINVAL;
> + SFRAME_GET_USER_SIGNED(ra_off, f, offset_size);
> + }
> +
> + fp_off = sec->fp_off;
> + if (!fp_off && offset_count) {
> + offset_count--;
> + SFRAME_GET_USER_SIGNED(fp_off, f, offset_size);
> + }
> +
> + if (offset_count)
> + return -EINVAL;
> +
> + frame->cfa_off = cfa_off;
> + frame->ra_off = ra_off;
> + frame->fp_off = fp_off;
> + frame->use_fp = SFRAME_FRE_CFA_BASE_REG_ID(fre_info) == SFRAME_BASE_REG_FP;
> +
> + return 0;
> +}
> +
> +int sframe_find(unsigned long ip, struct user_unwind_frame *frame)
> +{
> + struct mm_struct *mm = current->mm;
> + struct sframe_section *sec;
> + struct sframe_fde fde;
> + int srcu_idx;
> + int ret = -EINVAL;
> +
> + srcu_idx = srcu_read_lock(&sframe_srcu);
> +
> + sec = mtree_load(&mm->sframe_mt, ip);
> + if (!sec) {
> + srcu_read_unlock(&sframe_srcu, srcu_idx);
> + return -EINVAL;
> + }
> +
> +
> + ret = find_fde(sec, ip, &fde);
> + if (ret)
> + goto err_unlock;
> +
> + ret = find_fre(sec, &fde, ip, frame);
> + if (ret)
> + goto err_unlock;
> +
> + srcu_read_unlock(&sframe_srcu, srcu_idx);
> + return 0;
> +
> +err_unlock:
> + srcu_read_unlock(&sframe_srcu, srcu_idx);
> + return ret;
> +}
> +
> +static int get_sframe_file(unsigned long sframe_addr, struct sframe_file *file)
> +{
> + struct mm_struct *mm = current->mm;
> + struct vm_area_struct *sframe_vma, *text_vma, *vma;
> + VMA_ITERATOR(vmi, mm, 0);
> +
> + mmap_read_lock(mm);
> +
> + sframe_vma = vma_lookup(mm, sframe_addr);
> + if (!sframe_vma || !sframe_vma->vm_file)
> + goto err_unlock;
> +
> + text_vma = NULL;
> +
> + for_each_vma(vmi, vma) {
> + if (vma->vm_file != sframe_vma->vm_file)
> + continue;
> + if (vma->vm_flags & VM_EXEC) {
> + if (text_vma) {
> + /*
> + * Multiple EXEC segments in a single file
> + * aren't currently supported, is that a thing?
> + */
> + WARN_ON_ONCE(1);
> + goto err_unlock;
> + }
> + text_vma = vma;
> + }
> + }
> +
> + file->sframe_addr = sframe_addr;
> + file->text_start = text_vma->vm_start;
> + file->text_end = text_vma->vm_end;
> +
> + mmap_read_unlock(mm);
> + return 0;
> +
> +err_unlock:
> + mmap_read_unlock(mm);
> + return -EINVAL;
> +}
> +
> +static int validate_sframe_addrs(struct sframe_file *file)
> +{
> + struct mm_struct *mm = current->mm;
> + struct vm_area_struct *text_vma;
> +
> + mmap_read_lock(mm);
> +
> + if (!vma_lookup(mm, file->sframe_addr))
> + goto err_unlock;
> +
> + text_vma = vma_lookup(mm, file->text_start);
> + if (!(text_vma->vm_flags & VM_EXEC))
> + goto err_unlock;
> +
> + if (vma_lookup(mm, file->text_end-1) != text_vma)
> + goto err_unlock;
> +
> + mmap_read_unlock(mm);
> + return 0;
> +
> +err_unlock:
> + mmap_read_unlock(mm);
> + return -EINVAL;
> +}
> +
> +int __sframe_add_section(struct sframe_file *file)
> +{
> + struct maple_tree *sframe_mt = ¤t->mm->sframe_mt;
> + struct sframe_section *sec;
> + struct sframe_header shdr;
> + unsigned long header_end;
> + int ret;
> +
> + if (copy_from_user(&shdr, (void *)file->sframe_addr, sizeof(shdr)))
> + return -EFAULT;
> +
> + if (shdr.preamble.magic != SFRAME_MAGIC ||
> + shdr.preamble.version != SFRAME_VERSION_1 ||
> + (!shdr.preamble.flags & SFRAME_F_FDE_SORTED) ||
> + shdr.auxhdr_len || !shdr.num_fdes || !shdr.num_fres ||
> + shdr.fdes_off > shdr.fres_off) {
> + return -EINVAL;
> + }
> +
I would say that it will be ideal to start the support with
SFRAME_VERSION_2 onwards, if we have a choice.
The structure SFrame FDE in SFRAME_VERSION_1 was unaligned on-disk. We
fixed that in SFRAME_VERSION_2 (Binutils 2.41) by adding some padding as
you have already noted. For x86_64, its not an issue though, yes.
^ permalink raw reply
* Re: [PATCH 1/5] perf tools: Add get_unaligned_leNN()
From: Arnaldo Carvalho de Melo @ 2023-11-09 19:34 UTC (permalink / raw)
To: Adrian Hunter
Cc: Jiri Olsa, Namhyung Kim, Ian Rogers, linux-kernel,
linux-perf-users
In-Reply-To: <7c5b626c-1de9-4c12-a781-e44985b4a797@intel.com>
Em Thu, Oct 26, 2023 at 07:09:26PM +0300, Adrian Hunter escreveu:
> On 26/10/23 16:44, Arnaldo Carvalho de Melo wrote:
> > Em Thu, Oct 05, 2023 at 04:38:05PM -0300, Arnaldo Carvalho de Melo escreveu:
> >> Em Thu, Oct 05, 2023 at 10:04:47PM +0300, Adrian Hunter escreveu:
> >>> Add get_unaligned_le16(), get_unaligned_le32 and get_unaligned_le64, same
> >>> as include/asm-generic/unaligned.h.
> >>>
> >>> Use diagnostic pragmas to ignore -Wpacked used by perf build.
> >>
> >> Can we get the tools copy of include/asm-generic/unaligned.h closer and
> >> have it in check-headers.sh?
> >
> > And this is not building when cross building to mips, mips64 and mipsel
> > on debian:experimental:
> >
> > In file included from util/intel-pt-decoder/intel-pt-pkt-decoder.c:10:
> > /git/perf-6.6.0-rc1/tools/include/asm-generic/unaligned.h: In function 'get_unaligned_le16':
> > /git/perf-6.6.0-rc1/tools/include/asm-generic/unaligned.h:13:29: error: packed attribute causes inefficient alignment for 'x' [-Werror=attributes]
>
> So I guess another diagnostic pragma is needed, perhaps the following works?
>
> #pragma GCC diagnostic ignored "-Wattributes"
Yeah, I tested with this and it works, I'll add a patch with your
Suggested-by, ok?
- Arnaldo
>
> > 13 | const struct { type x; } __packed *__pptr = (typeof(__pptr))(ptr); \
> > | ^
> > /git/perf-6.6.0-rc1/tools/include/asm-generic/unaligned.h:27:28: note: in expansion of macro '__get_unaligned_t'
> > 27 | return le16_to_cpu(__get_unaligned_t(__le16, p));
> > | ^~~~~~~~~~~~~~~~~
> > /git/perf-6.6.0-rc1/tools/include/asm-generic/unaligned.h: In function 'get_unaligned_le32':
> > /git/perf-6.6.0-rc1/tools/include/asm-generic/unaligned.h:13:29: error: packed attribute causes inefficient alignment for 'x' [-Werror=attributes]
> > 13 | const struct { type x; } __packed *__pptr = (typeof(__pptr))(ptr); \
> > | ^
> > /git/perf-6.6.0-rc1/tools/include/asm-generic/unaligned.h:32:28: note: in expansion of macro '__get_unaligned_t'
> > 32 | return le32_to_cpu(__get_unaligned_t(__le32, p));
> > | ^~~~~~~~~~~~~~~~~
> >
> >
> > Ditto for some other distros when cross building on ubuntu:18.04
> >
> > MKDIR /tmp/build/perf/util/perf-regs-arch/
> > In file included from /usr/sparc64-linux-gnu/include/bits/byteswap.h:34:0,
> > from /usr/sparc64-linux-gnu/include/endian.h:60,
> > from util/intel-pt-decoder/intel-pt-pkt-decoder.c:9:
> > /git/perf-6.6.0-rc1/tools/include/asm-generic/unaligned.h: In function 'get_unaligned_le16':
> > /git/perf-6.6.0-rc1/tools/include/asm-generic/unaligned.h:13:22: error: packed attribute causes inefficient alignment for 'x' [-Werror=attributes]
> > const struct { type x; } __packed *__pptr = (typeof(__pptr))(ptr); \
> > ^
> > /git/perf-6.6.0-rc1/tools/include/asm-generic/unaligned.h:27:21: note: in expansion of macro '__get_unaligned_t'
> > return le16_to_cpu(__get_unaligned_t(__le16, p));
> > ^~~~~~~~~~~~~~~~~
> >
> > 37 14.17 ubuntu:18.04-x-arm : FAIL gcc version 7.5.0 (Ubuntu/Linaro 7.5.0-3ubuntu1~18.04)
> > 38 13.56 ubuntu:18.04-x-arm64 : FAIL gcc version 7.5.0 (Ubuntu/Linaro 7.5.0-3ubuntu1~18.04)
> > 42 12.70 ubuntu:18.04-x-riscv64 : FAIL gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)
> > 44 13.95 ubuntu:18.04-x-sh4 : FAIL gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)
> > 45 13.08 ubuntu:18.04-x-sparc64 : FAIL gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)
> >
> >> - Arnaldo
> >>
> >>> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
> >>> ---
> >>> tools/include/asm-generic/unaligned.h | 20 ++++++++++++++++++++
> >>> 1 file changed, 20 insertions(+)
> >>>
> >>> diff --git a/tools/include/asm-generic/unaligned.h b/tools/include/asm-generic/unaligned.h
> >>> index 47387c607035..9140bb4e16c6 100644
> >>> --- a/tools/include/asm-generic/unaligned.h
> >>> +++ b/tools/include/asm-generic/unaligned.h
> >>> @@ -6,6 +6,9 @@
> >>> #ifndef __TOOLS_LINUX_ASM_GENERIC_UNALIGNED_H
> >>> #define __TOOLS_LINUX_ASM_GENERIC_UNALIGNED_H
> >>>
> >>> +#pragma GCC diagnostic push
> >>> +#pragma GCC diagnostic ignored "-Wpacked"
> >>> +
> >>> #define __get_unaligned_t(type, ptr) ({ \
> >>> const struct { type x; } __packed *__pptr = (typeof(__pptr))(ptr); \
> >>> __pptr->x; \
> >>> @@ -19,5 +22,22 @@
> >>> #define get_unaligned(ptr) __get_unaligned_t(typeof(*(ptr)), (ptr))
> >>> #define put_unaligned(val, ptr) __put_unaligned_t(typeof(*(ptr)), (val), (ptr))
> >>>
> >>> +static inline u16 get_unaligned_le16(const void *p)
> >>> +{
> >>> + return le16_to_cpu(__get_unaligned_t(__le16, p));
> >>> +}
> >>> +
> >>> +static inline u32 get_unaligned_le32(const void *p)
> >>> +{
> >>> + return le32_to_cpu(__get_unaligned_t(__le32, p));
> >>> +}
> >>> +
> >>> +static inline u64 get_unaligned_le64(const void *p)
> >>> +{
> >>> + return le64_to_cpu(__get_unaligned_t(__le64, p));
> >>> +}
> >>> +
> >>> +#pragma GCC diagnostic pop
> >>> +
> >>> #endif /* __TOOLS_LINUX_ASM_GENERIC_UNALIGNED_H */
> >>>
> >>> --
> >>> 2.34.1
> >>>
> >>
> >> --
> >>
> >> - Arnaldo
> >
>
--
- Arnaldo
^ permalink raw reply
* Re: [PATCH RFC 09/10] unwind: Introduce SFrame user space unwinding
From: Josh Poimboeuf @ 2023-11-09 19:37 UTC (permalink / raw)
To: Indu Bhagat
Cc: Peter Zijlstra, Steven Rostedt, Ingo Molnar,
Arnaldo Carvalho de Melo, linux-kernel, x86, Mark Rutland,
Alexander Shishkin, Jiri Olsa, Namhyung Kim, Ian Rogers,
Adrian Hunter, linux-perf-users, Mark Brown, linux-toolchains
In-Reply-To: <24e46e1c-e9fa-4d44-97f2-068bda6e54b4@oracle.com>
On Thu, Nov 09, 2023 at 11:31:59AM -0800, Indu Bhagat wrote:
> > + if (shdr.preamble.magic != SFRAME_MAGIC ||
> > + shdr.preamble.version != SFRAME_VERSION_1 ||
> > + (!shdr.preamble.flags & SFRAME_F_FDE_SORTED) ||
> > + shdr.auxhdr_len || !shdr.num_fdes || !shdr.num_fres ||
> > + shdr.fdes_off > shdr.fres_off) {
> > + return -EINVAL;
> > + }
> > +
>
> I would say that it will be ideal to start the support with SFRAME_VERSION_2
> onwards, if we have a choice.
>
> The structure SFrame FDE in SFRAME_VERSION_1 was unaligned on-disk. We
> fixed that in SFRAME_VERSION_2 (Binutils 2.41) by adding some padding as you
> have already noted. For x86_64, its not an issue though, yes.
Agreed. I actually had v2 implemented but then realized my binutils was
older so I changed to v1 for testing. But yeah, we can make v2 the
minimum.
--
Josh
^ permalink raw reply
* Re: [PATCH RFC 09/10] unwind: Introduce SFrame user space unwinding
From: Steven Rostedt @ 2023-11-09 19:49 UTC (permalink / raw)
To: Josh Poimboeuf
Cc: Indu Bhagat, Peter Zijlstra, Ingo Molnar,
Arnaldo Carvalho de Melo, linux-kernel, x86, Mark Rutland,
Alexander Shishkin, Jiri Olsa, Namhyung Kim, Ian Rogers,
Adrian Hunter, linux-perf-users, Mark Brown, linux-toolchains
In-Reply-To: <20231109193759.6wugcdpucoilnncl@treble>
On Thu, 9 Nov 2023 11:37:59 -0800
Josh Poimboeuf <jpoimboe@kernel.org> wrote:
> > The structure SFrame FDE in SFRAME_VERSION_1 was unaligned on-disk. We
> > fixed that in SFRAME_VERSION_2 (Binutils 2.41) by adding some padding as you
> > have already noted. For x86_64, its not an issue though, yes.
>
> Agreed. I actually had v2 implemented but then realized my binutils was
> older so I changed to v1 for testing. But yeah, we can make v2 the
> minimum.
Cool, my test box has binutils 2.41, so it should be good to go ;-)
-- Steve
^ permalink raw reply
* Re: [PATCH RFC 09/10] unwind: Introduce SFrame user space unwinding
From: Josh Poimboeuf @ 2023-11-09 19:53 UTC (permalink / raw)
To: Steven Rostedt
Cc: Indu Bhagat, Peter Zijlstra, Ingo Molnar,
Arnaldo Carvalho de Melo, linux-kernel, x86, Mark Rutland,
Alexander Shishkin, Jiri Olsa, Namhyung Kim, Ian Rogers,
Adrian Hunter, linux-perf-users, Mark Brown, linux-toolchains
In-Reply-To: <20231109144956.0c20fd98@gandalf.local.home>
On Thu, Nov 09, 2023 at 02:49:56PM -0500, Steven Rostedt wrote:
> On Thu, 9 Nov 2023 11:37:59 -0800
> Josh Poimboeuf <jpoimboe@kernel.org> wrote:
>
> > > The structure SFrame FDE in SFRAME_VERSION_1 was unaligned on-disk. We
> > > fixed that in SFRAME_VERSION_2 (Binutils 2.41) by adding some padding as you
> > > have already noted. For x86_64, its not an issue though, yes.
> >
> > Agreed. I actually had v2 implemented but then realized my binutils was
> > older so I changed to v1 for testing. But yeah, we can make v2 the
> > minimum.
>
> Cool, my test box has binutils 2.41, so it should be good to go ;-)
You'll need something like this (untested!)
diff --git a/kernel/unwind/sframe.c b/kernel/unwind/sframe.c
index b167c19497e5..f8b2a520a689 100644
--- a/kernel/unwind/sframe.c
+++ b/kernel/unwind/sframe.c
@@ -142,10 +142,8 @@ static int find_fre(struct sframe_section *sec, struct sframe_fde *fde,
break;
} else {
/* SFRAME_FDE_TYPE_PCMASK */
-#if 0 /* sframe v2 */
if (ip_off % fde->rep_size < fre_ip_off)
break;
-#endif
}
SFRAME_GET_USER_UNSIGNED(fre_info, f, 1);
@@ -304,7 +302,7 @@ int __sframe_add_section(struct sframe_file *file)
return -EFAULT;
if (shdr.preamble.magic != SFRAME_MAGIC ||
- shdr.preamble.version != SFRAME_VERSION_1 ||
+ shdr.preamble.version != SFRAME_VERSION_2 ||
(!shdr.preamble.flags & SFRAME_F_FDE_SORTED) ||
shdr.auxhdr_len || !shdr.num_fdes || !shdr.num_fres ||
shdr.fdes_off > shdr.fres_off) {
diff --git a/kernel/unwind/sframe.h b/kernel/unwind/sframe.h
index 1f91b696daf5..1adfc88b784d 100644
--- a/kernel/unwind/sframe.h
+++ b/kernel/unwind/sframe.h
@@ -125,10 +125,8 @@ struct sframe_fde {
u32 fres_off;
u32 fres_num;
u8 info;
-#if 0 /* TODO sframe v2 */
u8 rep_size;
u16 padding;
-#endif
} __packed;
/*
^ permalink raw reply related
* [PATCH 1/1] tools: Disable __packed attribute compiler warning due to -Werror=attributes
From: Arnaldo Carvalho de Melo @ 2023-11-09 19:53 UTC (permalink / raw)
To: Adrian Hunter
Cc: Ian Rogers, Jiri Olsa, Namhyung Kim, linux-kernel,
linux-perf-users
Noticed on several perf tools cross build test containers:
[perfbuilder@five ~]$ grep FAIL ~/dm.log/summary
19 10.18 debian:experimental-x-mips : FAIL gcc version 12.3.0 (Debian 12.3.0-6)
20 11.21 debian:experimental-x-mips64 : FAIL gcc version 12.3.0 (Debian 12.3.0-6)
21 11.30 debian:experimental-x-mipsel : FAIL gcc version 12.3.0 (Debian 12.3.0-6)
37 12.07 ubuntu:18.04-x-arm : FAIL gcc version 7.5.0 (Ubuntu/Linaro 7.5.0-3ubuntu1~18.04)
42 11.91 ubuntu:18.04-x-riscv64 : FAIL gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)
44 13.17 ubuntu:18.04-x-sh4 : FAIL gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)
45 12.09 ubuntu:18.04-x-sparc64 : FAIL gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)
[perfbuilder@five ~]$
In file included from util/intel-pt-decoder/intel-pt-pkt-decoder.c:10:
/tmp/perf-6.6.0-rc1/tools/include/asm-generic/unaligned.h: In function 'get_unaligned_le16':
/tmp/perf-6.6.0-rc1/tools/include/asm-generic/unaligned.h:13:29: error: packed attribute causes inefficient alignment for 'x' [-Werror=attributes]
13 | const struct { type x; } __packed *__pptr = (typeof(__pptr))(ptr); \
| ^
/tmp/perf-6.6.0-rc1/tools/include/asm-generic/unaligned.h:27:28: note: in expansion of macro '__get_unaligned_t'
27 | return le16_to_cpu(__get_unaligned_t(__le16, p));
| ^~~~~~~~~~~~~~~~~
This comes from the kernel, where the -Wattributes and -Wpacked isn't
used, -Wpacked is already disabled, do it for the attributes as well.
Fixes: a91c987254651443 ("perf tools: Add get_unaligned_leNN()")
Suggested-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Link: https://lore.kernel.org/lkml/7c5b626c-1de9-4c12-a781-e44985b4a797@intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
tools/include/asm-generic/unaligned.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/tools/include/asm-generic/unaligned.h b/tools/include/asm-generic/unaligned.h
index 156743d399aed223..2fd551915c2025ee 100644
--- a/tools/include/asm-generic/unaligned.h
+++ b/tools/include/asm-generic/unaligned.h
@@ -8,6 +8,7 @@
*/
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpacked"
+#pragma GCC diagnostic ignored "-Wattributes"
#define __get_unaligned_t(type, ptr) ({ \
const struct { type x; } __packed *__pptr = (typeof(__pptr))(ptr); \
--
2.41.0
^ permalink raw reply related
* Re: [PATCH 2/2] perf header: Additional note on AMD IBS for max_precise pmu cap
From: Arnaldo Carvalho de Melo @ 2023-11-09 19:56 UTC (permalink / raw)
To: Ravi Bangoria
Cc: namhyung, kim.phillips, peterz, mingo, mark.rutland,
alexander.shishkin, jolsa, irogers, adrian.hunter, kan.liang,
changbin.du, yangjihong1, zwisler, wangming01, chenhuacai,
kprateek.nayak, linux-perf-users, linux-kernel, sandipan.das,
ananth.narayan, santosh.shukla
In-Reply-To: <20231107083331.901-2-ravi.bangoria@amd.com>
Em Tue, Nov 07, 2023 at 02:03:31PM +0530, Ravi Bangoria escreveu:
> From: Arnaldo Carvalho de Melo <acme@kernel.org>
Applied this one, waiting for some more time to address Ian comments,
- Arnaldo
> x86 core pmu exposes supported maximum precision level via max_precise
> pmu capability. Although, AMD core pmu does not support precise mode,
> certain core pmu events with precise_ip > 0 are allowed and forwarded
> to IBS OP pmu. Display a note about this in perf report header and
> document the details in the perf-list man page.
>
> Signed-off-by: Arnaldo Carvalho de Melo <acme@kernel.org>
> Signed-off-by: Ravi Bangoria <ravi.bangoria@amd.com>
> ---
> tools/perf/Documentation/perf-list.txt | 12 +++++++-----
> tools/perf/util/env.c | 18 ++++++++++++++++++
> tools/perf/util/env.h | 2 ++
> tools/perf/util/header.c | 8 ++++++++
> 4 files changed, 35 insertions(+), 5 deletions(-)
>
> diff --git a/tools/perf/Documentation/perf-list.txt b/tools/perf/Documentation/perf-list.txt
> index d5f78e125efe..1b90575ee3c8 100644
> --- a/tools/perf/Documentation/perf-list.txt
> +++ b/tools/perf/Documentation/perf-list.txt
> @@ -81,11 +81,13 @@ For Intel systems precise event sampling is implemented with PEBS
> which supports up to precise-level 2, and precise level 3 for
> some special cases
>
> -On AMD systems it is implemented using IBS (up to precise-level 2).
> -The precise modifier works with event types 0x76 (cpu-cycles, CPU
> -clocks not halted) and 0xC1 (micro-ops retired). Both events map to
> -IBS execution sampling (IBS op) with the IBS Op Counter Control bit
> -(IbsOpCntCtl) set respectively (see the
> +On AMD systems it is implemented using IBS OP (up to precise-level 2).
> +Unlike Intel PEBS which provides levels of precision, AMD core pmu is
> +inherently non-precise and IBS is inherently precise. (i.e. ibs_op//,
> +ibs_op//p, ibs_op//pp and ibs_op//ppp are all same). The precise modifier
> +works with event types 0x76 (cpu-cycles, CPU clocks not halted) and 0xC1
> +(micro-ops retired). Both events map to IBS execution sampling (IBS op)
> +with the IBS Op Counter Control bit (IbsOpCntCtl) set respectively (see the
> Core Complex (CCX) -> Processor x86 Core -> Instruction Based Sampling (IBS)
> section of the [AMD Processor Programming Reference (PPR)] relevant to the
> family, model and stepping of the processor being used).
> diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c
> index 44140b7f596a..cbc18b22ace5 100644
> --- a/tools/perf/util/env.c
> +++ b/tools/perf/util/env.c
> @@ -531,6 +531,24 @@ int perf_env__numa_node(struct perf_env *env, struct perf_cpu cpu)
> return cpu.cpu >= 0 && cpu.cpu < env->nr_numa_map ? env->numa_map[cpu.cpu] : -1;
> }
>
> +bool perf_env__has_pmu_mapping(struct perf_env *env, const char *pmu_name)
> +{
> + char *pmu_mapping = env->pmu_mappings, *colon;
> +
> + for (int i = 0; i < env->nr_pmu_mappings; ++i) {
> + if (strtoul(pmu_mapping, &colon, 0) == ULONG_MAX || *colon != ':')
> + goto out_error;
> +
> + pmu_mapping = colon + 1;
> + if (strcmp(pmu_mapping, pmu_name) == 0)
> + return true;
> +
> + pmu_mapping += strlen(pmu_mapping) + 1;
> + }
> +out_error:
> + return false;
> +}
> +
> char *perf_env__find_pmu_cap(struct perf_env *env, const char *pmu_name,
> const char *cap)
> {
> diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h
> index 4566c51f2fd9..56aea562c61b 100644
> --- a/tools/perf/util/env.h
> +++ b/tools/perf/util/env.h
> @@ -174,4 +174,6 @@ struct btf_node *perf_env__find_btf(struct perf_env *env, __u32 btf_id);
> int perf_env__numa_node(struct perf_env *env, struct perf_cpu cpu);
> char *perf_env__find_pmu_cap(struct perf_env *env, const char *pmu_name,
> const char *cap);
> +
> +bool perf_env__has_pmu_mapping(struct perf_env *env, const char *pmu_name);
> #endif /* __PERF_ENV_H */
> diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
> index e86b9439ffee..3cc288d14002 100644
> --- a/tools/perf/util/header.c
> +++ b/tools/perf/util/header.c
> @@ -2145,6 +2145,14 @@ static void print_pmu_caps(struct feat_fd *ff, FILE *fp)
> __print_pmu_caps(fp, pmu_caps->nr_caps, pmu_caps->caps,
> pmu_caps->pmu_name);
> }
> +
> + if (strcmp(perf_env__arch(&ff->ph->env), "x86") == 0 &&
> + perf_env__has_pmu_mapping(&ff->ph->env, "ibs_op")) {
> + char *max_precise = perf_env__find_pmu_cap(&ff->ph->env, "cpu", "max_precise");
> +
> + if (max_precise != NULL && atoi(max_precise) == 0)
> + fprintf(fp, "# AMD systems uses ibs_op// PMU for some precise events, e.g.: cycles:p, see the 'perf list' man page for further details.\n");
> + }
> }
>
> static void print_pmu_mappings(struct feat_fd *ff, FILE *fp)
> --
> 2.41.0
>
--
- Arnaldo
^ permalink raw reply
* Re: [PATCH 1/2] perf tool AMD: Use non-precise cycles as default event on certain Zen2 processors
From: Namhyung Kim @ 2023-11-09 21:53 UTC (permalink / raw)
To: Ravi Bangoria
Cc: acme, kim.phillips, peterz, mingo, mark.rutland,
alexander.shishkin, jolsa, irogers, adrian.hunter, kan.liang,
changbin.du, yangjihong1, zwisler, wangming01, chenhuacai,
kprateek.nayak, linux-perf-users, linux-kernel, sandipan.das,
ananth.narayan, santosh.shukla
In-Reply-To: <20231107083331.901-1-ravi.bangoria@amd.com>
Hi Ravi,
On Tue, Nov 7, 2023 at 12:34 AM Ravi Bangoria <ravi.bangoria@amd.com> wrote:
>
> By default, Perf uses precise cycles event when no explicit event is
> specified by user. Precise cycles event is forwarded to ibs_op// pmu
> on AMD. However, IBS has hw issue on certain Zen2 processors where
> it might raise NMI without sample_valid bit set, which causes Unknown
> NMI warnings. (Erratum #1215: IBS (Instruction Based Sampling) Counter
> Valid Value May be Incorrect After Exit From Core C6 (CC6) State.) So,
> use non-precise cycles as default event on affected processors.
It seems like a kernel issue, do we have a kernel patch not to forward
precise cycles or instructions events to IBS on the affected CPUs?
Thanks,
Namhyung
>
> This does not prevent user to use explicit precise cycles event or
> ibs_op// pmu directly.
>
> Suggested-by: Arnaldo Carvalho de Melo <acme@kernel.org>
> Signed-off-by: Ravi Bangoria <ravi.bangoria@amd.com>
> ---
^ permalink raw reply
* [PATCH v1] perf vendor events: Add skx, clx, icx and spr upi bandwidth metric
From: Ian Rogers @ 2023-11-09 23:27 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
Mark Rutland, Alexander Shishkin, Jiri Olsa, Namhyung Kim,
Adrian Hunter, Kan Liang, linux-perf-users, linux-kernel,
Perry Taylor, Caleb Biggers
Cc: Ian Rogers
Add upi_data_receive_bw metric for skylakex, cascadelakex, icelakex
and sapphirerapids. The metric was added to perfmon metrics in:
https://github.com/intel/perfmon/pull/119
Signed-off-by: Ian Rogers <irogers@google.com>
---
.../perf/pmu-events/arch/x86/cascadelakex/clx-metrics.json | 6 ++++++
tools/perf/pmu-events/arch/x86/icelakex/icx-metrics.json | 6 ++++++
.../pmu-events/arch/x86/sapphirerapids/spr-metrics.json | 6 ++++++
tools/perf/pmu-events/arch/x86/skylakex/skx-metrics.json | 6 ++++++
4 files changed, 24 insertions(+)
diff --git a/tools/perf/pmu-events/arch/x86/cascadelakex/clx-metrics.json b/tools/perf/pmu-events/arch/x86/cascadelakex/clx-metrics.json
index 84c132af3dfa..8bc6c0707856 100644
--- a/tools/perf/pmu-events/arch/x86/cascadelakex/clx-metrics.json
+++ b/tools/perf/pmu-events/arch/x86/cascadelakex/clx-metrics.json
@@ -1862,6 +1862,12 @@
"MetricName": "uncore_frequency",
"ScaleUnit": "1GHz"
},
+ {
+ "BriefDescription": "Intel(R) Ultra Path Interconnect (UPI) data receive bandwidth (MB/sec)",
+ "MetricExpr": "UNC_UPI_RxL_FLITS.ALL_DATA * 7.111111111111111 / 1e6 / duration_time",
+ "MetricName": "upi_data_receive_bw",
+ "ScaleUnit": "1MB/s"
+ },
{
"BriefDescription": "Intel(R) Ultra Path Interconnect (UPI) data transmit bandwidth (MB/sec)",
"MetricExpr": "UNC_UPI_TxL_FLITS.ALL_DATA * 7.111111111111111 / 1e6 / duration_time",
diff --git a/tools/perf/pmu-events/arch/x86/icelakex/icx-metrics.json b/tools/perf/pmu-events/arch/x86/icelakex/icx-metrics.json
index e98602c66707..71d78a7841ea 100644
--- a/tools/perf/pmu-events/arch/x86/icelakex/icx-metrics.json
+++ b/tools/perf/pmu-events/arch/x86/icelakex/icx-metrics.json
@@ -1846,6 +1846,12 @@
"MetricName": "uncore_frequency",
"ScaleUnit": "1GHz"
},
+ {
+ "BriefDescription": "Intel(R) Ultra Path Interconnect (UPI) data receive bandwidth (MB/sec)",
+ "MetricExpr": "UNC_UPI_RxL_FLITS.ALL_DATA * 7.111111111111111 / 1e6 / duration_time",
+ "MetricName": "upi_data_receive_bw",
+ "ScaleUnit": "1MB/s"
+ },
{
"BriefDescription": "Intel(R) Ultra Path Interconnect (UPI) data transmit bandwidth (MB/sec)",
"MetricExpr": "UNC_UPI_TxL_FLITS.ALL_DATA * 7.111111111111111 / 1e6 / duration_time",
diff --git a/tools/perf/pmu-events/arch/x86/sapphirerapids/spr-metrics.json b/tools/perf/pmu-events/arch/x86/sapphirerapids/spr-metrics.json
index 06c6d67cb76b..e31a4aac9f20 100644
--- a/tools/perf/pmu-events/arch/x86/sapphirerapids/spr-metrics.json
+++ b/tools/perf/pmu-events/arch/x86/sapphirerapids/spr-metrics.json
@@ -1964,6 +1964,12 @@
"MetricName": "uncore_frequency",
"ScaleUnit": "1GHz"
},
+ {
+ "BriefDescription": "Intel(R) Ultra Path Interconnect (UPI) data receive bandwidth (MB/sec)",
+ "MetricExpr": "UNC_UPI_RxL_FLITS.ALL_DATA * 7.111111111111111 / 1e6 / duration_time",
+ "MetricName": "upi_data_receive_bw",
+ "ScaleUnit": "1MB/s"
+ },
{
"BriefDescription": "Intel(R) Ultra Path Interconnect (UPI) data transmit bandwidth (MB/sec)",
"MetricExpr": "UNC_UPI_TxL_FLITS.ALL_DATA * 7.111111111111111 / 1e6 / duration_time",
diff --git a/tools/perf/pmu-events/arch/x86/skylakex/skx-metrics.json b/tools/perf/pmu-events/arch/x86/skylakex/skx-metrics.json
index 4a8f8eeb7525..ec3aa5ef00a3 100644
--- a/tools/perf/pmu-events/arch/x86/skylakex/skx-metrics.json
+++ b/tools/perf/pmu-events/arch/x86/skylakex/skx-metrics.json
@@ -1806,6 +1806,12 @@
"MetricName": "uncore_frequency",
"ScaleUnit": "1GHz"
},
+ {
+ "BriefDescription": "Intel(R) Ultra Path Interconnect (UPI) data receive bandwidth (MB/sec)",
+ "MetricExpr": "UNC_UPI_RxL_FLITS.ALL_DATA * 7.111111111111111 / 1e6 / duration_time",
+ "MetricName": "upi_data_receive_bw",
+ "ScaleUnit": "1MB/s"
+ },
{
"BriefDescription": "Intel(R) Ultra Path Interconnect (UPI) data transmit bandwidth (MB/sec)",
"MetricExpr": "UNC_UPI_TxL_FLITS.ALL_DATA * 7.111111111111111 / 1e6 / duration_time",
--
2.43.0.rc0.421.g78406f8d94-goog
^ permalink raw reply related
* [RFC 00/52] perf tools: Introduce data type profiling (v2)
From: Namhyung Kim @ 2023-11-09 23:59 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo, Jiri Olsa, Peter Zijlstra
Cc: Ian Rogers, Adrian Hunter, Ingo Molnar, LKML, linux-perf-users,
Linus Torvalds, Stephane Eranian, Masami Hiramatsu, Andi Kleen,
linux-trace-devel, linux-toolchains, Ben Woodard, Joe Mario,
Kees Cook, David Blaikie, Xu Liu, Kan Liang, Ravi Bangoria,
Mark Wielaard, Jason Merrill
Hello,
I'm happy to share my work on data type profiling. This is to associate
PMU samples to data types they refer using DWARF debug information. So
basically it depends on quality of PMU events and compiler for producing
DWARF info. But it doesn't require any changes in the target program.
As it's an early stage, I've targeted the kernel on x86 to reduce the
amount of work but IIUC there's no fundamental blocker to apply it to
other architectures and applications.
* v2 changes
- speed up analysis by not asking (unused) line number info to objdump
- support annotate a specific data type only by passing a type name like
`perf annotate --data-type=<TYPENAME>` (PeterZ)
- allow event group view to see multiple event results together like
`perf annotate --data-type --group` (PeterZ)
- rename to `die_get_typename_from_type()` (Masami)
- add a feature check for HAVE_DWARF_CFI_SUPPORT (Masami)
- add Acked-by tags from Masami
* How to use it
To get precise memory access samples, users can use `perf mem record`
command to utilize those events supported by their architecture. Intel
machines would work best as they have dedicated memory access events but
they would have a filter to ignore low latency loads like less than 30
cycles (use --ldlat option to change the default value).
# To get memory access samples in kernel for 1 second (on Intel)
$ sudo perf mem record -a -K --ldlat=4 -- sleep 1
# Similar for the AMD (but it requires 6.3+ kernel for BPF filters)
$ sudo perf mem record -a --filter 'mem_op == load || mem_op == store, ip > 0x8000000000000000' -- sleep 1
Note that it used 'sudo' command because it's collecting the event in
system wide mode. Actually it would depend on the sysctl setting of
kernel.perf_event_paranoid. AMD still needs root due to the BPF filter
though.
Actually users can use a different event as long as it gives precise
instruction addresses in samples. But the perf mem record will pick
up the available events which will give more information like data
source or latency (for advanced usage).
After getting a profile data, you would run perf report or perf
annotate as usual to see the result. Make sure that you have a kernel
debug package installed or vmlinux with DWARF info.
I've added new options and sort keys to enable the data type profiling.
Probably I need to add it to perf mem or perf c2c command for better
user experience. I'm open to discussion how we can make it simpler and
intuitive for regular users. But let's talk about the lower level
interface for now.
In perf report, it's just a matter of selecting new sort keys: 'type'
and 'typeoff'. The 'type' shows name of the data type as a whole while
'typeoff' shows name of the field in the data type. I found it useful
to use it with --hierarchy option to group relevant entries in the same
level.
Also, if you have both load and store events, pass --group option to
see the result together. This would give 3 output columns with a dummy
event. I think we should get rid of the dummy event after recording or
discard from the output at least.
$ sudo perf report -s type,typeoff --hierarchy --group --stdio
...
#
# Samples: 10K of events 'cpu/mem-loads,ldlat=4/P, cpu/mem-stores/P, dummy:u'
# Event count (approx.): 602758064
#
# Overhead Data Type / Data Type Offset
# ........................... ............................
#
26.09% 3.28% 0.00% long unsigned int
26.09% 3.28% 0.00% long unsigned int +0 (no field)
18.48% 0.73% 0.00% struct page
10.83% 0.02% 0.00% struct page +8 (lru.next)
3.90% 0.28% 0.00% struct page +0 (flags)
3.45% 0.06% 0.00% struct page +24 (mapping)
0.25% 0.28% 0.00% struct page +48 (_mapcount.counter)
0.02% 0.06% 0.00% struct page +32 (index)
0.02% 0.00% 0.00% struct page +52 (_refcount.counter)
0.02% 0.01% 0.00% struct page +56 (memcg_data)
0.00% 0.01% 0.00% struct page +16 (lru.prev)
15.37% 17.54% 0.00% (stack operation)
15.37% 17.54% 0.00% (stack operation) +0 (no field)
11.71% 50.27% 0.00% (unknown)
11.71% 50.27% 0.00% (unknown) +0 (no field)
...
The most frequently accessed type was long unsigned int and then the
struct page and you can see the second field (lru.next) at offset
8 was accessed mostly.
The (stack operation) and (unknown) have no type and field info. FYI,
the stack operations are samples in PUSH, POP or RET instructions which
save or restore registers from/to the stack. They are usually parts of
function prologue and epilogue and have no type info.
In perf annotate, new --data-type option was added to enable data
field level annotation. Now it only shows number of samples for each
field but we can improve it. The --data-type option optionally takes an
argument to specify the name of data type to display. Otherwise, it'd
display all data types having samples.
$ sudo perf annotate --data-type=page --group
Annotate type: 'struct page' in [kernel.kallsyms] (480 samples):
event[0] = cpu/mem-loads,ldlat=4/P
event[1] = cpu/mem-stores/P
event[2] = dummy:u
============================================================================
samples offset size field
447 33 0 0 64 struct page {
108 8 0 0 8 long unsigned int flags;
319 13 0 8 40 union {
319 13 0 8 40 struct {
236 2 0 8 16 union {
236 2 0 8 16 struct list_head lru {
236 1 0 8 8 struct list_head* next;
0 1 0 16 8 struct list_head* prev;
};
236 2 0 8 16 struct {
236 1 0 8 8 void* __filler;
0 1 0 16 4 unsigned int mlock_count;
};
236 2 0 8 16 struct list_head buddy_list {
236 1 0 8 8 struct list_head* next;
0 1 0 16 8 struct list_head* prev;
};
236 2 0 8 16 struct list_head pcp_list {
236 1 0 8 8 struct list_head* next;
0 1 0 16 8 struct list_head* prev;
};
};
82 4 0 24 8 struct address_space* mapping;
1 7 0 32 8 union {
1 7 0 32 8 long unsigned int index;
1 7 0 32 8 long unsigned int share;
};
0 0 0 40 8 long unsigned int private;
};
...
This shows each struct one by one and field-level access info in C-like
style. The number of samples for the outer struct is a sum of number of
samples in every field in the struct. In unions, each field is placed
in the same offset so they will have the same number of samples.
No TUI support yet.
* How it works
The basic idea is to use DWARF location expression in debug entries for
variables. Say we got a sample in the instruction below:
0x123456: mov 0x18(%rdi), %rcx
Then we know the instruction at 0x123456 is accessing to a memory region
where %rdi register has a base address and offset 0x18 from the base.
DWARF would have a debug info entry for a function or a block which
covers that address. For example, we might have something like this:
<1><100>: Abbrev Number: 10 (DW_TAG_subroutine_type)
<101> DW_AT_name : (indirect string, offset: 0x184e6): foo
<105> DW_AT_type : <0x29ad7>
<106> DW_AT_low_pc : 0x123400
<10e> DW_AT_high_pc : 0x1234ff
<2><116>: Abbrev Number: 8 (DW_TAG_formal_parameter)
<117> DW_AT_name : (indirect string, offset: 0x18527): bar
<11b> DW_AT_type : <0x29b3a>
<11c> DW_AT_location : 1 byte block: 55 (DW_OP_reg2 (rdi))
So the function 'foo' covers the instruction from 0x123400 to 0x1234ff
and we know the sample instruction belongs to the function. And it has
a parameter called 'bar' and it's located at the %rdi register. Then we
know the instruction is using the variable bar and its type would be a
pointer (to a struct). We can follow the type info of bar and verify
its access by checking the size of the (struct) type and offset in the
instruction (0x18).
Well.. this is a simple example that the 'bar' has a single location.
Other variables might be located in various places over time but it
should be covered by the location list of the debug entry. Therefore,
as long as DWARF produces a correct location expression for a variable,
it should be able to find the variable using the location info.
Global variables and local variables are different as they can be
accessed directly without a pointer. They are located in an absolute
address or relative position from the current stack frame. So it needs
to handle such location expressions as well.
However, some memory accesses don't have a variable in some cases. For
example, you have a pointer variable for a struct which contains another
pointers. And then you can directly dereference it without using a
variable. Consider the following source code.
int foo(struct baz *bar) {
...
if (bar->p->q == 0)
return 1;
...
}
This can generate instructions like below.
...
0x123456: mov 0x18(%rdi), %rcx
0x12345a: mov 0x10(%rcx), %rax <=== sample
0x12345e: test %rax, %rax
0x123461: je <...>
...
And imagine we have a sample at 0x12345a. Then it cannot find a
variable for %rcx since DWARF didn't generate one (it only knows about
'bar'). Without compiler support, all it can do is to track the code
execution in each instruction and propagate the type info in each
register and stack location by following the memory access.
Actually I found a discussion in the DWARF mailing list to support
"inverted location lists" and it seems a perfect fit for this project.
It'd be great if new DWARF would provide a way to lookup variable and
type info using a concrete location info (like a register number).
https://lists.dwarfstd.org/pipermail/dwarf-discuss/2023-June/002278.html
* Patch structure
The patch 1-5 are cleanups and a fix that can be applied separately.
The patch 6-25 are the main changes in perf report and perf annotate to
support simple cases with a pointer variable. The patch 26-36 are to
improve it by handling global and local variables (without a pointer)
and some edge cases. The patch 37-43 implemented instruction tracking
to infer data type when there's no variable for that. The patch 47-51
handles kernel-specific per-cpu variables (only for current CPU). The
patch 52 is to help debugging and is not intended for merge.
* Limitations and future work
As I said earlier, this work is in a very early shape and has many
limitations or rooms for improvement. Basically it uses objdump tool to
extract location information from the sample instruction. And the
parsing code and instruction tracking work on x86 only.
In the previous version, I mentioned a performance issue on objdump but
it turned out that it was because of bad usage. I realized it passed
"-l" option to display line numbers along with the disassembly. But it
doesn't use the line numbers for data type profiling. So I just got rid
of it and it gives a huge speedup (285s -> 9.5s).
$ time ./perf.v1 report -s type > output1
real 4m45.248s
user 4m0.714s
sys 0m44.338s
$ time ./perf.v2 report -s type > output2
real 0m9.464s
user 0m3.271s
sys 0m6.254s
$ md5sum output*
1489f96658bfaee61812df9a42fa7812 output1
1489f96658bfaee61812df9a42fa7812 output2
Interestingly, now GNU objdump outperforms llvm-objdump for some reason.
If you set the perf config annotate.objdump=llvm-objdump as I said in
the v1 cover letter, please remove it now. :)
Even with this change, still the most processing time was spent on the
objdump to get the disassembly. It'd be nice if we can get the result
without using objdump at all.
Also I only tested it with C programs (mostly vmlinux) and I believe
there are many issues on handling C++ applications. Probably other
languages (like Rust?) could be supported too. But even for C programs,
it could improve things like better supporting union and array types and
dealing with type casts and so on.
I think compiler could generate more DWARF information to help this kind
of analysis. Like I mentioned, it doesn't have a variable for
intermediate pointers when they are chained: a->b->c. This chain could
be longer and hard to track the type from the previous variable. If
compiler could generate (artificial) debug entries for the intermediate
pointers with a precise location expression and type info, it would be
really helpful.
And I plan to improve the analysis in perf tools with better integration
to the existing command like perf mem and/or perf c2c. It'd be pretty
interesting to see per-struct or per-field access patterns both for load
and store event at the same time. Also using data-source or snoop info
for each struct/field would give some insights on optimizing memory
usage or layout.
There are kernel specific issues too. Some per-cpu variable accesses
created complex instruction patterns so it was hard to determine which
data/type it accessed. For now, it just parsed simple patterns for
this-cpu access using %gs segment register. Also it should handle
self-modifying codes like kprobe, ftrace, live patch and so on. I guess
they would usually create an out-of-line copy of modified instructions
but needs more checking. And I have no idea about the status of struct
layout randomization and the DWARF info of the resulting struct. Maybe
there are more issues I'm not aware of, please let me know if you notice
something.
* Summary
Despite all the issues, I believe this would be a good addition to our
performance toolset. It would help to observe memory overheads in a
different angle and to optimize the memory usage. I'm really looking
forward to hearing any feedback.
The code is available at 'perf/data-profile-v2' branch in
git://git.kernel.org/pub/scm/linux/kernel/git/namhyung/linux-perf.git
Enjoy,
Namhyung
Cc: Ben Woodard <woodard@redhat.com>
Cc: Joe Mario <jmario@redhat.com>
CC: Kees Cook <keescook@chromium.org>
Cc: David Blaikie <blaikie@google.com>
Cc: Xu Liu <xliuprof@google.com>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Ravi Bangoria <ravi.bangoria@amd.com>
Cc: Mark Wielaard <mark@klomp.org>
Cc: Jason Merrill <jason@redhat.com>
Namhyung Kim (52):
perf annotate: Pass "-l" option to objdump conditionally
perf annotate: Move raw_comment and raw_func_start
perf tools: Add util/debuginfo.[ch] files
perf dwarf-aux: Fix die_get_typename() for void *
perf dwarf-aux: Move #ifdef code to the header file
perf dwarf-aux: Add die_get_scopes() helper
perf dwarf-aux: Add die_find_variable_by_reg() helper
perf build: Add feature check for dwarf_getcfi()
perf probe: Convert to check dwarf_getcfi feature
perf dwarf-aux: Factor out die_get_typename_from_type()
perf dwarf-regs: Add get_dwarf_regnum()
perf annotate-data: Add find_data_type()
perf annotate-data: Add dso->data_types tree
perf annotate: Factor out evsel__get_arch()
perf annotate: Check if operand has multiple regs
perf annotate: Add annotate_get_insn_location()
perf annotate: Implement hist_entry__get_data_type()
perf report: Add 'type' sort key
perf report: Support data type profiling
perf annotate-data: Add member field in the data type
perf annotate-data: Update sample histogram for type
perf report: Add 'typeoff' sort key
perf report: Add 'symoff' sort key
perf annotate: Add --data-type option
perf annotate: Support event group display
perf annotate: Add --type-stat option for debugging
perf annotate: Add --insn-stat option for debugging
perf annotate-data: Parse 'lock' prefix from llvm-objdump
perf annotate-data: Handle macro fusion on x86
perf annotate-data: Handle array style accesses
perf annotate-data: Add stack operation pseudo type
perf dwarf-aux: Add die_find_variable_by_addr()
perf annotate-data: Handle PC-relative addressing
perf annotate-data: Support global variables
perf dwarf-aux: Add die_get_cfa()
perf annotate-data: Support stack variables
perf dwarf-aux: Check allowed DWARF Ops
perf dwarf-aux: Add die_collect_vars()
perf dwarf-aux: Handle type transfer for memory access
perf annotate-data: Introduce struct data_loc_info
perf map: Add map__objdump_2rip()
perf annotate: Add annotate_get_basic_blocks()
perf annotate-data: Maintain variable type info
perf annotate-data: Add update_insn_state()
perf annotate-data: Handle global variable access
perf annotate-data: Handle call instructions
perf annotate-data: Implement instruction tracking
perf annotate: Parse x86 segment register location
perf annotate-data: Handle this-cpu variables in kernel
perf annotate-data: Track instructions with a this-cpu variable
perf annotate-data: Add stack canary type
perf annotate-data: Add debug message
tools/build/Makefile.feature | 1 +
tools/build/feature/Makefile | 4 +
tools/build/feature/test-dwarf_getcfi.c | 9 +
tools/perf/Documentation/perf-annotate.txt | 11 +
tools/perf/Documentation/perf-report.txt | 3 +
tools/perf/Makefile.config | 5 +
.../arch/loongarch/annotate/instructions.c | 6 +-
tools/perf/arch/x86/util/dwarf-regs.c | 38 +
tools/perf/builtin-annotate.c | 217 ++-
tools/perf/builtin-report.c | 21 +-
tools/perf/util/Build | 2 +
tools/perf/util/annotate-data.c | 1221 +++++++++++++++++
tools/perf/util/annotate-data.h | 222 +++
tools/perf/util/annotate.c | 786 ++++++++++-
tools/perf/util/annotate.h | 104 +-
tools/perf/util/debuginfo.c | 205 +++
tools/perf/util/debuginfo.h | 64 +
tools/perf/util/dso.c | 4 +
tools/perf/util/dso.h | 2 +
tools/perf/util/dwarf-aux.c | 566 +++++++-
tools/perf/util/dwarf-aux.h | 92 +-
tools/perf/util/dwarf-regs.c | 34 +
tools/perf/util/hist.h | 3 +
tools/perf/util/include/dwarf-regs.h | 19 +
tools/perf/util/map.c | 20 +
tools/perf/util/map.h | 3 +
tools/perf/util/probe-finder.c | 201 +--
tools/perf/util/probe-finder.h | 19 +-
tools/perf/util/sort.c | 202 ++-
tools/perf/util/sort.h | 7 +
tools/perf/util/symbol_conf.h | 4 +-
31 files changed, 3830 insertions(+), 265 deletions(-)
create mode 100644 tools/build/feature/test-dwarf_getcfi.c
create mode 100644 tools/perf/util/annotate-data.c
create mode 100644 tools/perf/util/annotate-data.h
create mode 100644 tools/perf/util/debuginfo.c
create mode 100644 tools/perf/util/debuginfo.h
base-commit: 6512b6aa237db36d881a81cc312db39668e61853
--
2.42.0.869.gea05f2083d-goog
^ permalink raw reply
* [PATCH 01/52] perf annotate: Pass "-l" option to objdump conditionally
From: Namhyung Kim @ 2023-11-09 23:59 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo, Jiri Olsa, Peter Zijlstra
Cc: Ian Rogers, Adrian Hunter, Ingo Molnar, LKML, linux-perf-users,
Linus Torvalds, Stephane Eranian, Masami Hiramatsu, Andi Kleen,
linux-trace-devel, linux-toolchains
In-Reply-To: <20231110000012.3538610-1-namhyung@kernel.org>
The "-l" option is to print line numbers in the objdump output.
perf annotate TUI only can show the line numbers later but it
causes big slow downs for the kernel binary.
Similarly, showing source code also takes a long time and it
already has an option to control it.
$ time objdump ... -d -S -C vmlinux > /dev/null
real 0m3.474s
user 0m3.047s
sys 0m0.428s
$ time objdump ... -d -l -C vmlinux > /dev/null
real 0m1.796s
user 0m1.459s
sys 0m0.338s
$ time objdump ... -d -C vmlinux > /dev/null
real 0m0.051s
user 0m0.036s
sys 0m0.016s
As it's not needed for data type profiling, let's make it conditional
so that it can skip the unnecessary work.
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/util/annotate.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 9b68b8e3791c..118195c787b9 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -2144,12 +2144,13 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
err = asprintf(&command,
"%s %s%s --start-address=0x%016" PRIx64
" --stop-address=0x%016" PRIx64
- " -l -d %s %s %s %c%s%c %s%s -C \"$1\"",
+ " %s -d %s %s %s %c%s%c %s%s -C \"$1\"",
opts->objdump_path ?: "objdump",
opts->disassembler_style ? "-M " : "",
opts->disassembler_style ?: "",
map__rip_2objdump(map, sym->start),
map__rip_2objdump(map, sym->end),
+ opts->show_linenr ? "-l" : "",
opts->show_asm_raw ? "" : "--no-show-raw-insn",
opts->annotate_src ? "-S" : "",
opts->prefix ? "--prefix " : "",
--
2.42.0.869.gea05f2083d-goog
^ permalink raw reply related
* [PATCH 02/52] perf annotate: Move raw_comment and raw_func_start
From: Namhyung Kim @ 2023-11-09 23:59 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo, Jiri Olsa, Peter Zijlstra
Cc: Ian Rogers, Adrian Hunter, Ingo Molnar, LKML, linux-perf-users,
Linus Torvalds, Stephane Eranian, Masami Hiramatsu, Andi Kleen,
linux-trace-devel, linux-toolchains, Huacai Chen, WANG Rui
In-Reply-To: <20231110000012.3538610-1-namhyung@kernel.org>
Thoese two fields are used only for the jump_ops, so move them into the
union to save some bytes. Also add jump__delete() callback not to free
the fields as they didn't allocate new strings.
Cc: Huacai Chen <chenhuacai@kernel.org>
Cc: WANG Rui <wangrui@loongson.cn>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
.../perf/arch/loongarch/annotate/instructions.c | 6 +++---
tools/perf/util/annotate.c | 17 +++++++++++++----
tools/perf/util/annotate.h | 6 ++++--
3 files changed, 20 insertions(+), 9 deletions(-)
diff --git a/tools/perf/arch/loongarch/annotate/instructions.c b/tools/perf/arch/loongarch/annotate/instructions.c
index 98e19c5366ac..21cc7e4149f7 100644
--- a/tools/perf/arch/loongarch/annotate/instructions.c
+++ b/tools/perf/arch/loongarch/annotate/instructions.c
@@ -61,10 +61,10 @@ static int loongarch_jump__parse(struct arch *arch, struct ins_operands *ops, st
const char *c = strchr(ops->raw, '#');
u64 start, end;
- ops->raw_comment = strchr(ops->raw, arch->objdump.comment_char);
- ops->raw_func_start = strchr(ops->raw, '<');
+ ops->jump.raw_comment = strchr(ops->raw, arch->objdump.comment_char);
+ ops->jump.raw_func_start = strchr(ops->raw, '<');
- if (ops->raw_func_start && c > ops->raw_func_start)
+ if (ops->jump.raw_func_start && c > ops->jump.raw_func_start)
c = NULL;
if (c++ != NULL)
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 118195c787b9..3364edf30f50 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -340,10 +340,10 @@ bool ins__is_call(const struct ins *ins)
*/
static inline const char *validate_comma(const char *c, struct ins_operands *ops)
{
- if (ops->raw_comment && c > ops->raw_comment)
+ if (ops->jump.raw_comment && c > ops->jump.raw_comment)
return NULL;
- if (ops->raw_func_start && c > ops->raw_func_start)
+ if (ops->jump.raw_func_start && c > ops->jump.raw_func_start)
return NULL;
return c;
@@ -359,8 +359,8 @@ static int jump__parse(struct arch *arch, struct ins_operands *ops, struct map_s
const char *c = strchr(ops->raw, ',');
u64 start, end;
- ops->raw_comment = strchr(ops->raw, arch->objdump.comment_char);
- ops->raw_func_start = strchr(ops->raw, '<');
+ ops->jump.raw_comment = strchr(ops->raw, arch->objdump.comment_char);
+ ops->jump.raw_func_start = strchr(ops->raw, '<');
c = validate_comma(c, ops);
@@ -462,7 +462,16 @@ static int jump__scnprintf(struct ins *ins, char *bf, size_t size,
ops->target.offset);
}
+static void jump__delete(struct ins_operands *ops __maybe_unused)
+{
+ /*
+ * The ops->jump.raw_comment and ops->jump.raw_func_start belong to the
+ * raw string, don't free them.
+ */
+}
+
static struct ins_ops jump_ops = {
+ .free = jump__delete,
.parse = jump__parse,
.scnprintf = jump__scnprintf,
};
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h
index de59c1aff08e..bc8b95e8b1be 100644
--- a/tools/perf/util/annotate.h
+++ b/tools/perf/util/annotate.h
@@ -31,8 +31,6 @@ struct ins {
struct ins_operands {
char *raw;
- char *raw_comment;
- char *raw_func_start;
struct {
char *raw;
char *name;
@@ -52,6 +50,10 @@ struct ins_operands {
struct ins ins;
struct ins_operands *ops;
} locked;
+ struct {
+ char *raw_comment;
+ char *raw_func_start;
+ } jump;
};
};
--
2.42.0.869.gea05f2083d-goog
^ permalink raw reply related
* [PATCH 03/52] perf tools: Add util/debuginfo.[ch] files
From: Namhyung Kim @ 2023-11-09 23:59 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo, Jiri Olsa, Peter Zijlstra
Cc: Ian Rogers, Adrian Hunter, Ingo Molnar, LKML, linux-perf-users,
Linus Torvalds, Stephane Eranian, Masami Hiramatsu, Andi Kleen,
linux-trace-devel, linux-toolchains
In-Reply-To: <20231110000012.3538610-1-namhyung@kernel.org>
Split debuginfo data structure and related functions into a separate
file so that it can be used other than the probe-finder.
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/util/Build | 1 +
tools/perf/util/debuginfo.c | 205 +++++++++++++++++++++++++++++++++
tools/perf/util/debuginfo.h | 64 ++++++++++
tools/perf/util/probe-finder.c | 193 +------------------------------
tools/perf/util/probe-finder.h | 19 +--
5 files changed, 272 insertions(+), 210 deletions(-)
create mode 100644 tools/perf/util/debuginfo.c
create mode 100644 tools/perf/util/debuginfo.h
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 96058f949ec9..73e3f194f949 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -195,6 +195,7 @@ endif
perf-$(CONFIG_DWARF) += probe-finder.o
perf-$(CONFIG_DWARF) += dwarf-aux.o
perf-$(CONFIG_DWARF) += dwarf-regs.o
+perf-$(CONFIG_DWARF) += debuginfo.o
perf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
perf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind-local.o
diff --git a/tools/perf/util/debuginfo.c b/tools/perf/util/debuginfo.c
new file mode 100644
index 000000000000..19acf4775d35
--- /dev/null
+++ b/tools/perf/util/debuginfo.c
@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * DWARF debug information handling code. Copied from probe-finder.c.
+ *
+ * Written by Masami Hiramatsu <mhiramat@redhat.com>
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <linux/zalloc.h>
+
+#include "build-id.h"
+#include "dso.h"
+#include "debug.h"
+#include "debuginfo.h"
+#include "symbol.h"
+
+#ifdef HAVE_DEBUGINFOD_SUPPORT
+#include <elfutils/debuginfod.h>
+#endif
+
+/* Dwarf FL wrappers */
+static char *debuginfo_path; /* Currently dummy */
+
+static const Dwfl_Callbacks offline_callbacks = {
+ .find_debuginfo = dwfl_standard_find_debuginfo,
+ .debuginfo_path = &debuginfo_path,
+
+ .section_address = dwfl_offline_section_address,
+
+ /* We use this table for core files too. */
+ .find_elf = dwfl_build_id_find_elf,
+};
+
+/* Get a Dwarf from offline image */
+static int debuginfo__init_offline_dwarf(struct debuginfo *dbg,
+ const char *path)
+{
+ GElf_Addr dummy;
+ int fd;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return fd;
+
+ dbg->dwfl = dwfl_begin(&offline_callbacks);
+ if (!dbg->dwfl)
+ goto error;
+
+ dwfl_report_begin(dbg->dwfl);
+ dbg->mod = dwfl_report_offline(dbg->dwfl, "", "", fd);
+ if (!dbg->mod)
+ goto error;
+
+ dbg->dbg = dwfl_module_getdwarf(dbg->mod, &dbg->bias);
+ if (!dbg->dbg)
+ goto error;
+
+ dwfl_module_build_id(dbg->mod, &dbg->build_id, &dummy);
+
+ dwfl_report_end(dbg->dwfl, NULL, NULL);
+
+ return 0;
+error:
+ if (dbg->dwfl)
+ dwfl_end(dbg->dwfl);
+ else
+ close(fd);
+ memset(dbg, 0, sizeof(*dbg));
+
+ return -ENOENT;
+}
+
+static struct debuginfo *__debuginfo__new(const char *path)
+{
+ struct debuginfo *dbg = zalloc(sizeof(*dbg));
+ if (!dbg)
+ return NULL;
+
+ if (debuginfo__init_offline_dwarf(dbg, path) < 0)
+ zfree(&dbg);
+ if (dbg)
+ pr_debug("Open Debuginfo file: %s\n", path);
+ return dbg;
+}
+
+enum dso_binary_type distro_dwarf_types[] = {
+ DSO_BINARY_TYPE__FEDORA_DEBUGINFO,
+ DSO_BINARY_TYPE__UBUNTU_DEBUGINFO,
+ DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO,
+ DSO_BINARY_TYPE__BUILDID_DEBUGINFO,
+ DSO_BINARY_TYPE__MIXEDUP_UBUNTU_DEBUGINFO,
+ DSO_BINARY_TYPE__NOT_FOUND,
+};
+
+struct debuginfo *debuginfo__new(const char *path)
+{
+ enum dso_binary_type *type;
+ char buf[PATH_MAX], nil = '\0';
+ struct dso *dso;
+ struct debuginfo *dinfo = NULL;
+ struct build_id bid;
+
+ /* Try to open distro debuginfo files */
+ dso = dso__new(path);
+ if (!dso)
+ goto out;
+
+ /* Set the build id for DSO_BINARY_TYPE__BUILDID_DEBUGINFO */
+ if (is_regular_file(path) && filename__read_build_id(path, &bid) > 0)
+ dso__set_build_id(dso, &bid);
+
+ for (type = distro_dwarf_types;
+ !dinfo && *type != DSO_BINARY_TYPE__NOT_FOUND;
+ type++) {
+ if (dso__read_binary_type_filename(dso, *type, &nil,
+ buf, PATH_MAX) < 0)
+ continue;
+ dinfo = __debuginfo__new(buf);
+ }
+ dso__put(dso);
+
+out:
+ /* if failed to open all distro debuginfo, open given binary */
+ return dinfo ? : __debuginfo__new(path);
+}
+
+void debuginfo__delete(struct debuginfo *dbg)
+{
+ if (dbg) {
+ if (dbg->dwfl)
+ dwfl_end(dbg->dwfl);
+ free(dbg);
+ }
+}
+
+/* For the kernel module, we need a special code to get a DIE */
+int debuginfo__get_text_offset(struct debuginfo *dbg, Dwarf_Addr *offs,
+ bool adjust_offset)
+{
+ int n, i;
+ Elf32_Word shndx;
+ Elf_Scn *scn;
+ Elf *elf;
+ GElf_Shdr mem, *shdr;
+ const char *p;
+
+ elf = dwfl_module_getelf(dbg->mod, &dbg->bias);
+ if (!elf)
+ return -EINVAL;
+
+ /* Get the number of relocations */
+ n = dwfl_module_relocations(dbg->mod);
+ if (n < 0)
+ return -ENOENT;
+ /* Search the relocation related .text section */
+ for (i = 0; i < n; i++) {
+ p = dwfl_module_relocation_info(dbg->mod, i, &shndx);
+ if (strcmp(p, ".text") == 0) {
+ /* OK, get the section header */
+ scn = elf_getscn(elf, shndx);
+ if (!scn)
+ return -ENOENT;
+ shdr = gelf_getshdr(scn, &mem);
+ if (!shdr)
+ return -ENOENT;
+ *offs = shdr->sh_addr;
+ if (adjust_offset)
+ *offs -= shdr->sh_offset;
+ }
+ }
+ return 0;
+}
+
+#ifdef HAVE_DEBUGINFOD_SUPPORT
+int get_source_from_debuginfod(const char *raw_path,
+ const char *sbuild_id, char **new_path)
+{
+ debuginfod_client *c = debuginfod_begin();
+ const char *p = raw_path;
+ int fd;
+
+ if (!c)
+ return -ENOMEM;
+
+ fd = debuginfod_find_source(c, (const unsigned char *)sbuild_id,
+ 0, p, new_path);
+ pr_debug("Search %s from debuginfod -> %d\n", p, fd);
+ if (fd >= 0)
+ close(fd);
+ debuginfod_end(c);
+ if (fd < 0) {
+ pr_debug("Failed to find %s in debuginfod (%s)\n",
+ raw_path, sbuild_id);
+ return -ENOENT;
+ }
+ pr_debug("Got a source %s\n", *new_path);
+
+ return 0;
+}
+#endif /* HAVE_DEBUGINFOD_SUPPORT */
diff --git a/tools/perf/util/debuginfo.h b/tools/perf/util/debuginfo.h
new file mode 100644
index 000000000000..4d65b8c605fc
--- /dev/null
+++ b/tools/perf/util/debuginfo.h
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef _PERF_DEBUGINFO_H
+#define _PERF_DEBUGINFO_H
+
+#include <errno.h>
+#include <linux/compiler.h>
+
+#ifdef HAVE_DWARF_SUPPORT
+
+#include "dwarf-aux.h"
+
+/* debug information structure */
+struct debuginfo {
+ Dwarf *dbg;
+ Dwfl_Module *mod;
+ Dwfl *dwfl;
+ Dwarf_Addr bias;
+ const unsigned char *build_id;
+};
+
+/* This also tries to open distro debuginfo */
+struct debuginfo *debuginfo__new(const char *path);
+void debuginfo__delete(struct debuginfo *dbg);
+
+int debuginfo__get_text_offset(struct debuginfo *dbg, Dwarf_Addr *offs,
+ bool adjust_offset);
+
+#else /* HAVE_DWARF_SUPPORT */
+
+/* dummy debug information structure */
+struct debuginfo {
+};
+
+static inline struct debuginfo *debuginfo__new(const char *path __maybe_unused)
+{
+ return NULL;
+}
+
+static inline void debuginfo__delete(struct debuginfo *dbg __maybe_unused)
+{
+}
+
+static inline int debuginfo__get_text_offset(struct debuginfo *dbg __maybe_unused,
+ Dwarf_Addr *offs __maybe_unused,
+ bool adjust_offset __maybe_unused)
+{
+ return -EINVAL;
+}
+
+#endif /* HAVE_DWARF_SUPPORT */
+
+#ifdef HAVE_DEBUGINFOD_SUPPORT
+int get_source_from_debuginfod(const char *raw_path, const char *sbuild_id,
+ char **new_path);
+#else /* HAVE_DEBUGINFOD_SUPPORT */
+static inline int get_source_from_debuginfod(const char *raw_path __maybe_unused,
+ const char *sbuild_id __maybe_unused,
+ char **new_path __maybe_unused)
+{
+ return -ENOTSUP;
+}
+#endif /* HAVE_DEBUGINFOD_SUPPORT */
+
+#endif /* _PERF_DEBUGINFO_H */
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index f171360b0ef4..8d3dd85f9ff4 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -23,6 +23,7 @@
#include "event.h"
#include "dso.h"
#include "debug.h"
+#include "debuginfo.h"
#include "intlist.h"
#include "strbuf.h"
#include "strlist.h"
@@ -31,128 +32,9 @@
#include "probe-file.h"
#include "string2.h"
-#ifdef HAVE_DEBUGINFOD_SUPPORT
-#include <elfutils/debuginfod.h>
-#endif
-
/* Kprobe tracer basic type is up to u64 */
#define MAX_BASIC_TYPE_BITS 64
-/* Dwarf FL wrappers */
-static char *debuginfo_path; /* Currently dummy */
-
-static const Dwfl_Callbacks offline_callbacks = {
- .find_debuginfo = dwfl_standard_find_debuginfo,
- .debuginfo_path = &debuginfo_path,
-
- .section_address = dwfl_offline_section_address,
-
- /* We use this table for core files too. */
- .find_elf = dwfl_build_id_find_elf,
-};
-
-/* Get a Dwarf from offline image */
-static int debuginfo__init_offline_dwarf(struct debuginfo *dbg,
- const char *path)
-{
- GElf_Addr dummy;
- int fd;
-
- fd = open(path, O_RDONLY);
- if (fd < 0)
- return fd;
-
- dbg->dwfl = dwfl_begin(&offline_callbacks);
- if (!dbg->dwfl)
- goto error;
-
- dwfl_report_begin(dbg->dwfl);
- dbg->mod = dwfl_report_offline(dbg->dwfl, "", "", fd);
- if (!dbg->mod)
- goto error;
-
- dbg->dbg = dwfl_module_getdwarf(dbg->mod, &dbg->bias);
- if (!dbg->dbg)
- goto error;
-
- dwfl_module_build_id(dbg->mod, &dbg->build_id, &dummy);
-
- dwfl_report_end(dbg->dwfl, NULL, NULL);
-
- return 0;
-error:
- if (dbg->dwfl)
- dwfl_end(dbg->dwfl);
- else
- close(fd);
- memset(dbg, 0, sizeof(*dbg));
-
- return -ENOENT;
-}
-
-static struct debuginfo *__debuginfo__new(const char *path)
-{
- struct debuginfo *dbg = zalloc(sizeof(*dbg));
- if (!dbg)
- return NULL;
-
- if (debuginfo__init_offline_dwarf(dbg, path) < 0)
- zfree(&dbg);
- if (dbg)
- pr_debug("Open Debuginfo file: %s\n", path);
- return dbg;
-}
-
-enum dso_binary_type distro_dwarf_types[] = {
- DSO_BINARY_TYPE__FEDORA_DEBUGINFO,
- DSO_BINARY_TYPE__UBUNTU_DEBUGINFO,
- DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO,
- DSO_BINARY_TYPE__BUILDID_DEBUGINFO,
- DSO_BINARY_TYPE__MIXEDUP_UBUNTU_DEBUGINFO,
- DSO_BINARY_TYPE__NOT_FOUND,
-};
-
-struct debuginfo *debuginfo__new(const char *path)
-{
- enum dso_binary_type *type;
- char buf[PATH_MAX], nil = '\0';
- struct dso *dso;
- struct debuginfo *dinfo = NULL;
- struct build_id bid;
-
- /* Try to open distro debuginfo files */
- dso = dso__new(path);
- if (!dso)
- goto out;
-
- /* Set the build id for DSO_BINARY_TYPE__BUILDID_DEBUGINFO */
- if (is_regular_file(path) && filename__read_build_id(path, &bid) > 0)
- dso__set_build_id(dso, &bid);
-
- for (type = distro_dwarf_types;
- !dinfo && *type != DSO_BINARY_TYPE__NOT_FOUND;
- type++) {
- if (dso__read_binary_type_filename(dso, *type, &nil,
- buf, PATH_MAX) < 0)
- continue;
- dinfo = __debuginfo__new(buf);
- }
- dso__put(dso);
-
-out:
- /* if failed to open all distro debuginfo, open given binary */
- return dinfo ? : __debuginfo__new(path);
-}
-
-void debuginfo__delete(struct debuginfo *dbg)
-{
- if (dbg) {
- if (dbg->dwfl)
- dwfl_end(dbg->dwfl);
- free(dbg);
- }
-}
-
/*
* Probe finder related functions
*/
@@ -1677,44 +1559,6 @@ int debuginfo__find_available_vars_at(struct debuginfo *dbg,
return (ret < 0) ? ret : af.nvls;
}
-/* For the kernel module, we need a special code to get a DIE */
-int debuginfo__get_text_offset(struct debuginfo *dbg, Dwarf_Addr *offs,
- bool adjust_offset)
-{
- int n, i;
- Elf32_Word shndx;
- Elf_Scn *scn;
- Elf *elf;
- GElf_Shdr mem, *shdr;
- const char *p;
-
- elf = dwfl_module_getelf(dbg->mod, &dbg->bias);
- if (!elf)
- return -EINVAL;
-
- /* Get the number of relocations */
- n = dwfl_module_relocations(dbg->mod);
- if (n < 0)
- return -ENOENT;
- /* Search the relocation related .text section */
- for (i = 0; i < n; i++) {
- p = dwfl_module_relocation_info(dbg->mod, i, &shndx);
- if (strcmp(p, ".text") == 0) {
- /* OK, get the section header */
- scn = elf_getscn(elf, shndx);
- if (!scn)
- return -ENOENT;
- shdr = gelf_getshdr(scn, &mem);
- if (!shdr)
- return -ENOENT;
- *offs = shdr->sh_addr;
- if (adjust_offset)
- *offs -= shdr->sh_offset;
- }
- }
- return 0;
-}
-
/* Reverse search */
int debuginfo__find_probe_point(struct debuginfo *dbg, u64 addr,
struct perf_probe_point *ppt)
@@ -2009,41 +1853,6 @@ int debuginfo__find_line_range(struct debuginfo *dbg, struct line_range *lr)
return (ret < 0) ? ret : lf.found;
}
-#ifdef HAVE_DEBUGINFOD_SUPPORT
-/* debuginfod doesn't require the comp_dir but buildid is required */
-static int get_source_from_debuginfod(const char *raw_path,
- const char *sbuild_id, char **new_path)
-{
- debuginfod_client *c = debuginfod_begin();
- const char *p = raw_path;
- int fd;
-
- if (!c)
- return -ENOMEM;
-
- fd = debuginfod_find_source(c, (const unsigned char *)sbuild_id,
- 0, p, new_path);
- pr_debug("Search %s from debuginfod -> %d\n", p, fd);
- if (fd >= 0)
- close(fd);
- debuginfod_end(c);
- if (fd < 0) {
- pr_debug("Failed to find %s in debuginfod (%s)\n",
- raw_path, sbuild_id);
- return -ENOENT;
- }
- pr_debug("Got a source %s\n", *new_path);
-
- return 0;
-}
-#else
-static inline int get_source_from_debuginfod(const char *raw_path __maybe_unused,
- const char *sbuild_id __maybe_unused,
- char **new_path __maybe_unused)
-{
- return -ENOTSUP;
-}
-#endif
/*
* Find a src file from a DWARF tag path. Prepend optional source path prefix
* and chop off leading directories that do not exist. Result is passed back as
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
index 8bc1c80d3c1c..3add5ff516e1 100644
--- a/tools/perf/util/probe-finder.h
+++ b/tools/perf/util/probe-finder.h
@@ -24,21 +24,7 @@ static inline int is_c_varname(const char *name)
#ifdef HAVE_DWARF_SUPPORT
#include "dwarf-aux.h"
-
-/* TODO: export debuginfo data structure even if no dwarf support */
-
-/* debug information structure */
-struct debuginfo {
- Dwarf *dbg;
- Dwfl_Module *mod;
- Dwfl *dwfl;
- Dwarf_Addr bias;
- const unsigned char *build_id;
-};
-
-/* This also tries to open distro debuginfo */
-struct debuginfo *debuginfo__new(const char *path);
-void debuginfo__delete(struct debuginfo *dbg);
+#include "debuginfo.h"
/* Find probe_trace_events specified by perf_probe_event from debuginfo */
int debuginfo__find_trace_events(struct debuginfo *dbg,
@@ -49,9 +35,6 @@ int debuginfo__find_trace_events(struct debuginfo *dbg,
int debuginfo__find_probe_point(struct debuginfo *dbg, u64 addr,
struct perf_probe_point *ppt);
-int debuginfo__get_text_offset(struct debuginfo *dbg, Dwarf_Addr *offs,
- bool adjust_offset);
-
/* Find a line range */
int debuginfo__find_line_range(struct debuginfo *dbg, struct line_range *lr);
--
2.42.0.869.gea05f2083d-goog
^ permalink raw reply related
* [PATCH 04/52] perf dwarf-aux: Fix die_get_typename() for void *
From: Namhyung Kim @ 2023-11-09 23:59 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo, Jiri Olsa, Peter Zijlstra
Cc: Ian Rogers, Adrian Hunter, Ingo Molnar, LKML, linux-perf-users,
Linus Torvalds, Stephane Eranian, Masami Hiramatsu, Andi Kleen,
linux-trace-devel, linux-toolchains
In-Reply-To: <20231110000012.3538610-1-namhyung@kernel.org>
The die_get_typename() is to return a C-like type name from DWARF debug
entry and it follows data type if the target entry is a pointer type.
But I found void pointers don't have the type attribte to follow and
then the function returns an error for that case. This results in a
broken type string for void pointer types.
For example, the following type entries are pointer types.
<1><48c>: Abbrev Number: 4 (DW_TAG_pointer_type)
<48d> DW_AT_byte_size : 8
<48d> DW_AT_type : <0x481>
<1><491>: Abbrev Number: 211 (DW_TAG_pointer_type)
<493> DW_AT_byte_size : 8
<1><494>: Abbrev Number: 4 (DW_TAG_pointer_type)
<495> DW_AT_byte_size : 8
<495> DW_AT_type : <0x49e>
The first one at offset 48c and the third one at offset 494 have type
information. Then they are pointer types for the referenced types.
But the second one at offset 491 doesn't have the type attribute.
Acked-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/util/dwarf-aux.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c
index 2941d88f2199..4849c3bbfd95 100644
--- a/tools/perf/util/dwarf-aux.c
+++ b/tools/perf/util/dwarf-aux.c
@@ -1090,7 +1090,14 @@ int die_get_typename(Dwarf_Die *vr_die, struct strbuf *buf)
return strbuf_addf(buf, "%s%s", tmp, name ?: "");
}
ret = die_get_typename(&type, buf);
- return ret ? ret : strbuf_addstr(buf, tmp);
+ if (ret < 0) {
+ /* void pointer has no type attribute */
+ if (tag == DW_TAG_pointer_type && ret == -ENOENT)
+ return strbuf_addf(buf, "void*");
+
+ return ret;
+ }
+ return strbuf_addstr(buf, tmp);
}
/**
--
2.42.0.869.gea05f2083d-goog
^ permalink raw reply related
* [PATCH 05/52] perf dwarf-aux: Move #ifdef code to the header file
From: Namhyung Kim @ 2023-11-09 23:59 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo, Jiri Olsa, Peter Zijlstra
Cc: Ian Rogers, Adrian Hunter, Ingo Molnar, LKML, linux-perf-users,
Linus Torvalds, Stephane Eranian, Masami Hiramatsu, Andi Kleen,
linux-trace-devel, linux-toolchains
In-Reply-To: <20231110000012.3538610-1-namhyung@kernel.org>
It's a usual convention that the conditional code is handled in a header
file. As I'm planning to add some more of them, let's move the current
code to the header first.
Acked-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/util/dwarf-aux.c | 7 -------
tools/perf/util/dwarf-aux.h | 19 +++++++++++++++++--
2 files changed, 17 insertions(+), 9 deletions(-)
diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c
index 4849c3bbfd95..adef2635587d 100644
--- a/tools/perf/util/dwarf-aux.c
+++ b/tools/perf/util/dwarf-aux.c
@@ -1245,13 +1245,6 @@ int die_get_var_range(Dwarf_Die *sp_die, Dwarf_Die *vr_die, struct strbuf *buf)
out:
return ret;
}
-#else
-int die_get_var_range(Dwarf_Die *sp_die __maybe_unused,
- Dwarf_Die *vr_die __maybe_unused,
- struct strbuf *buf __maybe_unused)
-{
- return -ENOTSUP;
-}
#endif
/*
diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h
index 7ec8bc1083bb..4f5d0211ee4f 100644
--- a/tools/perf/util/dwarf-aux.h
+++ b/tools/perf/util/dwarf-aux.h
@@ -121,7 +121,6 @@ int die_get_typename(Dwarf_Die *vr_die, struct strbuf *buf);
/* Get the name and type of given variable DIE, stored as "type\tname" */
int die_get_varname(Dwarf_Die *vr_die, struct strbuf *buf);
-int die_get_var_range(Dwarf_Die *sp_die, Dwarf_Die *vr_die, struct strbuf *buf);
/* Check if target program is compiled with optimization */
bool die_is_optimized_target(Dwarf_Die *cu_die);
@@ -130,4 +129,20 @@ bool die_is_optimized_target(Dwarf_Die *cu_die);
void die_skip_prologue(Dwarf_Die *sp_die, Dwarf_Die *cu_die,
Dwarf_Addr *entrypc);
-#endif
+#ifdef HAVE_DWARF_GETLOCATIONS_SUPPORT
+
+/* Get byte offset range of given variable DIE */
+int die_get_var_range(Dwarf_Die *sp_die, Dwarf_Die *vr_die, struct strbuf *buf);
+
+#else /* HAVE_DWARF_GETLOCATIONS_SUPPORT */
+
+static inline int die_get_var_range(Dwarf_Die *sp_die __maybe_unused,
+ Dwarf_Die *vr_die __maybe_unused,
+ struct strbuf *buf __maybe_unused)
+{
+ return -ENOTSUP;
+}
+
+#endif /* HAVE_DWARF_GETLOCATIONS_SUPPORT */
+
+#endif /* _DWARF_AUX_H */
--
2.42.0.869.gea05f2083d-goog
^ permalink raw reply related
* [PATCH 06/52] perf dwarf-aux: Add die_get_scopes() helper
From: Namhyung Kim @ 2023-11-09 23:59 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo, Jiri Olsa, Peter Zijlstra
Cc: Ian Rogers, Adrian Hunter, Ingo Molnar, LKML, linux-perf-users,
Linus Torvalds, Stephane Eranian, Masami Hiramatsu, Andi Kleen,
linux-trace-devel, linux-toolchains
In-Reply-To: <20231110000012.3538610-1-namhyung@kernel.org>
The die_get_scopes() would return the number of enclosing DIEs for the
given address and it fills an array of DIEs like dwarf_getscopes().
But it doesn't follow the abstract origin of inlined functions as we
want information of the concrete instance. This is needed to check the
location of parameters and local variables properly. Users can check
the origin separately if needed.
Acked-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/util/dwarf-aux.c | 53 +++++++++++++++++++++++++++++++++++++
tools/perf/util/dwarf-aux.h | 3 +++
2 files changed, 56 insertions(+)
diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c
index adef2635587d..10aa32334d6f 100644
--- a/tools/perf/util/dwarf-aux.c
+++ b/tools/perf/util/dwarf-aux.c
@@ -1425,3 +1425,56 @@ void die_skip_prologue(Dwarf_Die *sp_die, Dwarf_Die *cu_die,
*entrypc = postprologue_addr;
}
+
+/* Internal parameters for __die_find_scope_cb() */
+struct find_scope_data {
+ /* Target instruction address */
+ Dwarf_Addr pc;
+ /* Number of scopes found [output] */
+ int nr;
+ /* Array of scopes found, 0 for the outermost one. [output] */
+ Dwarf_Die *scopes;
+};
+
+static int __die_find_scope_cb(Dwarf_Die *die_mem, void *arg)
+{
+ struct find_scope_data *data = arg;
+
+ if (dwarf_haspc(die_mem, data->pc)) {
+ Dwarf_Die *tmp;
+
+ tmp = realloc(data->scopes, (data->nr + 1) * sizeof(*tmp));
+ if (tmp == NULL)
+ return DIE_FIND_CB_END;
+
+ memcpy(tmp + data->nr, die_mem, sizeof(*die_mem));
+ data->scopes = tmp;
+ data->nr++;
+ return DIE_FIND_CB_CHILD;
+ }
+ return DIE_FIND_CB_SIBLING;
+}
+
+/**
+ * die_get_scopes - Return a list of scopes including the address
+ * @cu_die: a compile unit DIE
+ * @pc: the address to find
+ * @scopes: the array of DIEs for scopes (result)
+ *
+ * This function does the same as the dwarf_getscopes() but doesn't follow
+ * the origins of inlined functions. It returns the number of scopes saved
+ * in the @scopes argument. The outer scope will be saved first (index 0) and
+ * the last one is the innermost scope at the @pc.
+ */
+int die_get_scopes(Dwarf_Die *cu_die, Dwarf_Addr pc, Dwarf_Die **scopes)
+{
+ struct find_scope_data data = {
+ .pc = pc,
+ };
+ Dwarf_Die die_mem;
+
+ die_find_child(cu_die, __die_find_scope_cb, &data, &die_mem);
+
+ *scopes = data.scopes;
+ return data.nr;
+}
diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h
index 4f5d0211ee4f..f9d765f80fb0 100644
--- a/tools/perf/util/dwarf-aux.h
+++ b/tools/perf/util/dwarf-aux.h
@@ -129,6 +129,9 @@ bool die_is_optimized_target(Dwarf_Die *cu_die);
void die_skip_prologue(Dwarf_Die *sp_die, Dwarf_Die *cu_die,
Dwarf_Addr *entrypc);
+/* Get the list of including scopes */
+int die_get_scopes(Dwarf_Die *cu_die, Dwarf_Addr pc, Dwarf_Die **scopes);
+
#ifdef HAVE_DWARF_GETLOCATIONS_SUPPORT
/* Get byte offset range of given variable DIE */
--
2.42.0.869.gea05f2083d-goog
^ permalink raw reply related
* [PATCH 07/52] perf dwarf-aux: Add die_find_variable_by_reg() helper
From: Namhyung Kim @ 2023-11-09 23:59 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo, Jiri Olsa, Peter Zijlstra
Cc: Ian Rogers, Adrian Hunter, Ingo Molnar, LKML, linux-perf-users,
Linus Torvalds, Stephane Eranian, Masami Hiramatsu, Andi Kleen,
linux-trace-devel, linux-toolchains
In-Reply-To: <20231110000012.3538610-1-namhyung@kernel.org>
The die_find_variable_by_reg() will search for a variable or a parameter
sub-DIE in the given scope DIE where the location matches to the given
register.
For the simpliest and most common case, memory access usually happens
with a base register and an offset to the field so the register would
hold a pointer in a variable or function parameter. Then we can find
one if it has a location expression at the (instruction) address. So
this function only handles such a simple case for now.
In this case, the expression would have a DW_OP_regN operation where
N < 32. If the register index (N) is greater than or equal to 32,
DW_OP_regx operation with an operand which saves the value for the N
would be used. It would reject expressions with more operations.
Acked-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/util/dwarf-aux.c | 67 +++++++++++++++++++++++++++++++++++++
tools/perf/util/dwarf-aux.h | 12 +++++++
2 files changed, 79 insertions(+)
diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c
index 10aa32334d6f..652e6e7368a2 100644
--- a/tools/perf/util/dwarf-aux.c
+++ b/tools/perf/util/dwarf-aux.c
@@ -1245,6 +1245,73 @@ int die_get_var_range(Dwarf_Die *sp_die, Dwarf_Die *vr_die, struct strbuf *buf)
out:
return ret;
}
+
+/* Interval parameters for __die_find_var_reg_cb() */
+struct find_var_data {
+ /* Target instruction address */
+ Dwarf_Addr pc;
+ /* Target register */
+ unsigned reg;
+};
+
+/* Max number of registers DW_OP_regN supports */
+#define DWARF_OP_DIRECT_REGS 32
+
+/* Only checks direct child DIEs in the given scope. */
+static int __die_find_var_reg_cb(Dwarf_Die *die_mem, void *arg)
+{
+ struct find_var_data *data = arg;
+ int tag = dwarf_tag(die_mem);
+ ptrdiff_t off = 0;
+ Dwarf_Attribute attr;
+ Dwarf_Addr base, start, end;
+ Dwarf_Op *ops;
+ size_t nops;
+
+ if (tag != DW_TAG_variable && tag != DW_TAG_formal_parameter)
+ return DIE_FIND_CB_SIBLING;
+
+ if (dwarf_attr(die_mem, DW_AT_location, &attr) == NULL)
+ return DIE_FIND_CB_SIBLING;
+
+ while ((off = dwarf_getlocations(&attr, off, &base, &start, &end, &ops, &nops)) > 0) {
+ /* Assuming the location list is sorted by address */
+ if (end < data->pc)
+ continue;
+ if (start > data->pc)
+ break;
+
+ /* Only match with a simple case */
+ if (data->reg < DWARF_OP_DIRECT_REGS) {
+ if (ops->atom == (DW_OP_reg0 + data->reg) && nops == 1)
+ return DIE_FIND_CB_END;
+ } else {
+ if (ops->atom == DW_OP_regx && ops->number == data->reg &&
+ nops == 1)
+ return DIE_FIND_CB_END;
+ }
+ }
+ return DIE_FIND_CB_SIBLING;
+}
+
+/**
+ * die_find_variable_by_reg - Find a variable saved in a register
+ * @sc_die: a scope DIE
+ * @pc: the program address to find
+ * @reg: the register number to find
+ * @die_mem: a buffer to save the resulting DIE
+ *
+ * Find the variable DIE accessed by the given register.
+ */
+Dwarf_Die *die_find_variable_by_reg(Dwarf_Die *sc_die, Dwarf_Addr pc, int reg,
+ Dwarf_Die *die_mem)
+{
+ struct find_var_data data = {
+ .pc = pc,
+ .reg = reg,
+ };
+ return die_find_child(sc_die, __die_find_var_reg_cb, &data, die_mem);
+}
#endif
/*
diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h
index f9d765f80fb0..b6f430730bd1 100644
--- a/tools/perf/util/dwarf-aux.h
+++ b/tools/perf/util/dwarf-aux.h
@@ -137,6 +137,10 @@ int die_get_scopes(Dwarf_Die *cu_die, Dwarf_Addr pc, Dwarf_Die **scopes);
/* Get byte offset range of given variable DIE */
int die_get_var_range(Dwarf_Die *sp_die, Dwarf_Die *vr_die, struct strbuf *buf);
+/* Find a variable saved in the 'reg' at given address */
+Dwarf_Die *die_find_variable_by_reg(Dwarf_Die *sc_die, Dwarf_Addr pc, int reg,
+ Dwarf_Die *die_mem);
+
#else /* HAVE_DWARF_GETLOCATIONS_SUPPORT */
static inline int die_get_var_range(Dwarf_Die *sp_die __maybe_unused,
@@ -146,6 +150,14 @@ static inline int die_get_var_range(Dwarf_Die *sp_die __maybe_unused,
return -ENOTSUP;
}
+static inline Dwarf_Die *die_find_variable_by_reg(Dwarf_Die *sc_die __maybe_unused,
+ Dwarf_Addr pc __maybe_unused,
+ int reg __maybe_unused,
+ Dwarf_Die *die_mem __maybe_unused)
+{
+ return NULL;
+}
+
#endif /* HAVE_DWARF_GETLOCATIONS_SUPPORT */
#endif /* _DWARF_AUX_H */
--
2.42.0.869.gea05f2083d-goog
^ permalink raw reply related
* [PATCH 08/52] perf build: Add feature check for dwarf_getcfi()
From: Namhyung Kim @ 2023-11-09 23:59 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo, Jiri Olsa, Peter Zijlstra
Cc: Ian Rogers, Adrian Hunter, Ingo Molnar, LKML, linux-perf-users,
Linus Torvalds, Stephane Eranian, Masami Hiramatsu, Andi Kleen,
linux-trace-devel, linux-toolchains
In-Reply-To: <20231110000012.3538610-1-namhyung@kernel.org>
The dwarf_getcfi() is available on libdw 0.142+. Instead of just
checking the version number, it'd be nice to have a config item to check
the feature at build time.
Suggested-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/build/Makefile.feature | 1 +
tools/build/feature/Makefile | 4 ++++
tools/build/feature/test-dwarf_getcfi.c | 9 +++++++++
3 files changed, 14 insertions(+)
create mode 100644 tools/build/feature/test-dwarf_getcfi.c
diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature
index 934e2777a2db..64df118376df 100644
--- a/tools/build/Makefile.feature
+++ b/tools/build/Makefile.feature
@@ -32,6 +32,7 @@ FEATURE_TESTS_BASIC := \
backtrace \
dwarf \
dwarf_getlocations \
+ dwarf_getcfi \
eventfd \
fortify-source \
get_current_dir_name \
diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile
index dad79ede4e0a..37722e509eb9 100644
--- a/tools/build/feature/Makefile
+++ b/tools/build/feature/Makefile
@@ -7,6 +7,7 @@ FILES= \
test-bionic.bin \
test-dwarf.bin \
test-dwarf_getlocations.bin \
+ test-dwarf_getcfi.bin \
test-eventfd.bin \
test-fortify-source.bin \
test-get_current_dir_name.bin \
@@ -154,6 +155,9 @@ endif
$(OUTPUT)test-dwarf_getlocations.bin:
$(BUILD) $(DWARFLIBS)
+$(OUTPUT)test-dwarf_getcfi.bin:
+ $(BUILD) $(DWARFLIBS)
+
$(OUTPUT)test-libelf-getphdrnum.bin:
$(BUILD) -lelf
diff --git a/tools/build/feature/test-dwarf_getcfi.c b/tools/build/feature/test-dwarf_getcfi.c
new file mode 100644
index 000000000000..50e7d7cb7bdf
--- /dev/null
+++ b/tools/build/feature/test-dwarf_getcfi.c
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <stdio.h>
+#include <elfutils/libdw.h>
+
+int main(void)
+{
+ Dwarf *dwarf = NULL;
+ return dwarf_getcfi(dwarf) == NULL;
+}
--
2.42.0.869.gea05f2083d-goog
^ permalink raw reply related
* [PATCH 09/52] perf probe: Convert to check dwarf_getcfi feature
From: Namhyung Kim @ 2023-11-09 23:59 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo, Jiri Olsa, Peter Zijlstra
Cc: Ian Rogers, Adrian Hunter, Ingo Molnar, LKML, linux-perf-users,
Linus Torvalds, Stephane Eranian, Masami Hiramatsu, Andi Kleen,
linux-trace-devel, linux-toolchains
In-Reply-To: <20231110000012.3538610-1-namhyung@kernel.org>
Now it has a feature check for the dwarf_getcfi(), use it and convert
the code to check HAVE_DWARF_CFI_SUPPORT definition.
Suggested-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/Makefile.config | 5 +++++
tools/perf/util/probe-finder.c | 8 ++++----
2 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config
index 8b6cffbc4858..aa55850fbc21 100644
--- a/tools/perf/Makefile.config
+++ b/tools/perf/Makefile.config
@@ -476,6 +476,11 @@ else
else
CFLAGS += -DHAVE_DWARF_GETLOCATIONS_SUPPORT
endif # dwarf_getlocations
+ ifneq ($(feature-dwarf_getcfi), 1)
+ msg := $(warning Old libdw.h, finding variables at given 'perf probe' point will not work, install elfutils-devel/libdw-dev >= 0.142);
+ else
+ CFLAGS += -DHAVE_DWARF_CFI_SUPPORT
+ endif # dwarf_getcfi
endif # Dwarf support
endif # libelf support
endif # NO_LIBELF
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 8d3dd85f9ff4..c8923375e30d 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -604,7 +604,7 @@ static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf)
ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1);
if (ret <= 0 || nops == 0) {
pf->fb_ops = NULL;
-#if _ELFUTILS_PREREQ(0, 142)
+#ifdef HAVE_DWARF_CFI_SUPPORT
} else if (nops == 1 && pf->fb_ops[0].atom == DW_OP_call_frame_cfa &&
(pf->cfi_eh != NULL || pf->cfi_dbg != NULL)) {
if ((dwarf_cfi_addrframe(pf->cfi_eh, pf->addr, &frame) != 0 &&
@@ -615,7 +615,7 @@ static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf)
free(frame);
return -ENOENT;
}
-#endif
+#endif /* HAVE_DWARF_CFI_SUPPORT */
}
/* Call finder's callback handler */
@@ -1140,7 +1140,7 @@ static int debuginfo__find_probes(struct debuginfo *dbg,
pf->machine = ehdr.e_machine;
-#if _ELFUTILS_PREREQ(0, 142)
+#ifdef HAVE_DWARF_CFI_SUPPORT
do {
GElf_Shdr shdr;
@@ -1150,7 +1150,7 @@ static int debuginfo__find_probes(struct debuginfo *dbg,
pf->cfi_dbg = dwarf_getcfi(dbg->dbg);
} while (0);
-#endif
+#endif /* HAVE_DWARF_CFI_SUPPORT */
ret = debuginfo__find_probe_location(dbg, pf);
return ret;
--
2.42.0.869.gea05f2083d-goog
^ permalink raw reply related
* [PATCH 10/52] perf dwarf-aux: Factor out die_get_typename_from_type()
From: Namhyung Kim @ 2023-11-09 23:59 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo, Jiri Olsa, Peter Zijlstra
Cc: Ian Rogers, Adrian Hunter, Ingo Molnar, LKML, linux-perf-users,
Linus Torvalds, Stephane Eranian, Masami Hiramatsu, Andi Kleen,
linux-trace-devel, linux-toolchains
In-Reply-To: <20231110000012.3538610-1-namhyung@kernel.org>
The die_get_typename_from_type() is to get the name of the given DIE in
C-style type name. The difference from the die_get_typename() is that
it does not retrieve the DW_AT_type and use the given DIE directly.
This will be used when users know the type DIE already.
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/util/dwarf-aux.c | 38 ++++++++++++++++++++++++++-----------
tools/perf/util/dwarf-aux.h | 3 +++
2 files changed, 30 insertions(+), 11 deletions(-)
diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c
index 652e6e7368a2..4bdcd3dea28f 100644
--- a/tools/perf/util/dwarf-aux.c
+++ b/tools/perf/util/dwarf-aux.c
@@ -1051,32 +1051,28 @@ Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
}
/**
- * die_get_typename - Get the name of given variable DIE
- * @vr_die: a variable DIE
+ * die_get_typename_from_type - Get the name of given type DIE
+ * @type_die: a type DIE
* @buf: a strbuf for result type name
*
- * Get the name of @vr_die and stores it to @buf. Return 0 if succeeded.
+ * Get the name of @type_die and stores it to @buf. Return 0 if succeeded.
* and Return -ENOENT if failed to find type name.
* Note that the result will stores typedef name if possible, and stores
* "*(function_type)" if the type is a function pointer.
*/
-int die_get_typename(Dwarf_Die *vr_die, struct strbuf *buf)
+int die_get_typename_from_type(Dwarf_Die *type_die, struct strbuf *buf)
{
- Dwarf_Die type;
int tag, ret;
const char *tmp = "";
- if (__die_get_real_type(vr_die, &type) == NULL)
- return -ENOENT;
-
- tag = dwarf_tag(&type);
+ tag = dwarf_tag(type_die);
if (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)
tmp = "*";
else if (tag == DW_TAG_subroutine_type) {
/* Function pointer */
return strbuf_add(buf, "(function_type)", 15);
} else {
- const char *name = dwarf_diename(&type);
+ const char *name = dwarf_diename(type_die);
if (tag == DW_TAG_union_type)
tmp = "union ";
@@ -1089,7 +1085,7 @@ int die_get_typename(Dwarf_Die *vr_die, struct strbuf *buf)
/* Write a base name */
return strbuf_addf(buf, "%s%s", tmp, name ?: "");
}
- ret = die_get_typename(&type, buf);
+ ret = die_get_typename(type_die, buf);
if (ret < 0) {
/* void pointer has no type attribute */
if (tag == DW_TAG_pointer_type && ret == -ENOENT)
@@ -1100,6 +1096,26 @@ int die_get_typename(Dwarf_Die *vr_die, struct strbuf *buf)
return strbuf_addstr(buf, tmp);
}
+/**
+ * die_get_typename - Get the name of given variable DIE
+ * @vr_die: a variable DIE
+ * @buf: a strbuf for result type name
+ *
+ * Get the name of @vr_die and stores it to @buf. Return 0 if succeeded.
+ * and Return -ENOENT if failed to find type name.
+ * Note that the result will stores typedef name if possible, and stores
+ * "*(function_type)" if the type is a function pointer.
+ */
+int die_get_typename(Dwarf_Die *vr_die, struct strbuf *buf)
+{
+ Dwarf_Die type;
+
+ if (__die_get_real_type(vr_die, &type) == NULL)
+ return -ENOENT;
+
+ return die_get_typename_from_type(&type, buf);
+}
+
/**
* die_get_varname - Get the name and type of given variable DIE
* @vr_die: a variable DIE
diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h
index b6f430730bd1..f9763d3b7572 100644
--- a/tools/perf/util/dwarf-aux.h
+++ b/tools/perf/util/dwarf-aux.h
@@ -116,6 +116,9 @@ Dwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name,
Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
Dwarf_Die *die_mem);
+/* Get the name of given type DIE */
+int die_get_typename_from_type(Dwarf_Die *type_die, struct strbuf *buf);
+
/* Get the name of given variable DIE */
int die_get_typename(Dwarf_Die *vr_die, struct strbuf *buf);
--
2.42.0.869.gea05f2083d-goog
^ permalink raw reply related
* [PATCH 11/52] perf dwarf-regs: Add get_dwarf_regnum()
From: Namhyung Kim @ 2023-11-09 23:59 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo, Jiri Olsa, Peter Zijlstra
Cc: Ian Rogers, Adrian Hunter, Ingo Molnar, LKML, linux-perf-users,
Linus Torvalds, Stephane Eranian, Masami Hiramatsu, Andi Kleen,
linux-trace-devel, linux-toolchains
In-Reply-To: <20231110000012.3538610-1-namhyung@kernel.org>
The get_dwarf_regnum() returns a DWARF register number from a register
name string according to the psABI. Also add two pseudo encodings of
DWARF_REG_PC which is a register that are used by PC-relative addressing
and DWARF_REG_FB which is a frame base register. They need to be
handled in a special way.
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/arch/x86/util/dwarf-regs.c | 38 +++++++++++++++++++++++++++
tools/perf/util/dwarf-regs.c | 34 ++++++++++++++++++++++++
tools/perf/util/include/dwarf-regs.h | 19 ++++++++++++++
3 files changed, 91 insertions(+)
diff --git a/tools/perf/arch/x86/util/dwarf-regs.c b/tools/perf/arch/x86/util/dwarf-regs.c
index 530934805710..399c4a0a29d8 100644
--- a/tools/perf/arch/x86/util/dwarf-regs.c
+++ b/tools/perf/arch/x86/util/dwarf-regs.c
@@ -113,3 +113,41 @@ int regs_query_register_offset(const char *name)
return roff->offset;
return -EINVAL;
}
+
+struct dwarf_regs_idx {
+ const char *name;
+ int idx;
+};
+
+static const struct dwarf_regs_idx x86_regidx_table[] = {
+ { "rax", 0 }, { "eax", 0 }, { "ax", 0 }, { "al", 0 },
+ { "rdx", 1 }, { "edx", 1 }, { "dx", 1 }, { "dl", 1 },
+ { "rcx", 2 }, { "ecx", 2 }, { "cx", 2 }, { "cl", 2 },
+ { "rbx", 3 }, { "edx", 3 }, { "bx", 3 }, { "bl", 3 },
+ { "rsi", 4 }, { "esi", 4 }, { "si", 4 }, { "sil", 4 },
+ { "rdi", 5 }, { "edi", 5 }, { "di", 5 }, { "dil", 5 },
+ { "rbp", 6 }, { "ebp", 6 }, { "bp", 6 }, { "bpl", 6 },
+ { "rsp", 7 }, { "esp", 7 }, { "sp", 7 }, { "spl", 7 },
+ { "r8", 8 }, { "r8d", 8 }, { "r8w", 8 }, { "r8b", 8 },
+ { "r9", 9 }, { "r9d", 9 }, { "r9w", 9 }, { "r9b", 9 },
+ { "r10", 10 }, { "r10d", 10 }, { "r10w", 10 }, { "r10b", 10 },
+ { "r11", 11 }, { "r11d", 11 }, { "r11w", 11 }, { "r11b", 11 },
+ { "r12", 12 }, { "r12d", 12 }, { "r12w", 12 }, { "r12b", 12 },
+ { "r13", 13 }, { "r13d", 13 }, { "r13w", 13 }, { "r13b", 13 },
+ { "r14", 14 }, { "r14d", 14 }, { "r14w", 14 }, { "r14b", 14 },
+ { "r15", 15 }, { "r15d", 15 }, { "r15w", 15 }, { "r15b", 15 },
+ { "rip", DWARF_REG_PC },
+};
+
+int get_arch_regnum(const char *name)
+{
+ unsigned int i;
+
+ if (*name != '%')
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(x86_regidx_table); i++)
+ if (!strcmp(x86_regidx_table[i].name, name + 1))
+ return x86_regidx_table[i].idx;
+ return -ENOENT;
+}
diff --git a/tools/perf/util/dwarf-regs.c b/tools/perf/util/dwarf-regs.c
index 69cfaa5953bf..5b7f86c0063f 100644
--- a/tools/perf/util/dwarf-regs.c
+++ b/tools/perf/util/dwarf-regs.c
@@ -5,9 +5,12 @@
* Written by: Masami Hiramatsu <mhiramat@kernel.org>
*/
+#include <stdlib.h>
+#include <string.h>
#include <debug.h>
#include <dwarf-regs.h>
#include <elf.h>
+#include <errno.h>
#include <linux/kernel.h>
#ifndef EM_AARCH64
@@ -68,3 +71,34 @@ const char *get_dwarf_regstr(unsigned int n, unsigned int machine)
}
return NULL;
}
+
+__weak int get_arch_regnum(const char *name __maybe_unused)
+{
+ return -ENOTSUP;
+}
+
+/* Return DWARF register number from architecture register name */
+int get_dwarf_regnum(const char *name, unsigned int machine)
+{
+ char *regname = strdup(name);
+ int reg = -1;
+ char *p;
+
+ if (regname == NULL)
+ return -EINVAL;
+
+ /* For convenience, remove trailing characters */
+ p = strpbrk(regname, " ,)");
+ if (p)
+ *p = '\0';
+
+ switch (machine) {
+ case EM_NONE: /* Generic arch - use host arch */
+ reg = get_arch_regnum(regname);
+ break;
+ default:
+ pr_err("ELF MACHINE %x is not supported.\n", machine);
+ }
+ free(regname);
+ return reg;
+}
diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h
index 7d99a084e82d..01fb25a1150a 100644
--- a/tools/perf/util/include/dwarf-regs.h
+++ b/tools/perf/util/include/dwarf-regs.h
@@ -2,6 +2,9 @@
#ifndef _PERF_DWARF_REGS_H_
#define _PERF_DWARF_REGS_H_
+#define DWARF_REG_PC 0xd3af9c /* random number */
+#define DWARF_REG_FB 0xd3affb /* random number */
+
#ifdef HAVE_DWARF_SUPPORT
const char *get_arch_regstr(unsigned int n);
/*
@@ -10,6 +13,22 @@ const char *get_arch_regstr(unsigned int n);
* machine: ELF machine signature (EM_*)
*/
const char *get_dwarf_regstr(unsigned int n, unsigned int machine);
+
+int get_arch_regnum(const char *name);
+/*
+ * get_dwarf_regnum - Returns DWARF regnum from register name
+ * name: architecture register name
+ * machine: ELF machine signature (EM_*)
+ */
+int get_dwarf_regnum(const char *name, unsigned int machine);
+
+#else /* HAVE_DWARF_SUPPORT */
+
+static inline int get_dwarf_regnum(const char *name __maybe_unused,
+ unsigned int machine __maybe_unused)
+{
+ return -1;
+}
#endif
#ifdef HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET
--
2.42.0.869.gea05f2083d-goog
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox