Re: [RFC PATCH 0/4] perf: Correlating user process data to samples

2024-04-11 Thread Ian Rogers
On Thu, Apr 11, 2024 at 5:17 PM Beau Belgrave  wrote:
>
> In the Open Telemetry profiling SIG [1], we are trying to find a way to
> grab a tracing association quickly on a per-sample basis. The team at
> Elastic has a bespoke way to do this [2], however, I'd like to see a
> more general way to achieve this. The folks I've been talking with seem
> open to the idea of just having a TLS value for this we could capture

Presumably TLS == Thread Local Storage.

> upon each sample. We could then just state, Open Telemetry SDKs should
> have a TLS value for span correlation. However, we need a way to sample
> the TLS or other value(s) when a sampling event is generated. This is
> supported today on Windows via EventActivityIdControl() [3]. Since
> Open Telemetry works on both Windows and Linux, ideally we can do
> something as efficient for Linux based workloads.
>
> This series is to explore how it would be best possible to collect
> supporting data from a user process when a profile sample is collected.
> Having a value stored in TLS makes a lot of sense for this however
> there are other ways to explore. Whatever is chosen, kernel samples
> taken in process context should be able to get this supporting data.
> In these patches on X64 the fsbase and gsbase are used for this.
>
> An option to explore suggested by Mathieu Desnoyers is to utilize rseq
> for processes to register a value location that can be included when
> profiling if desired. This would allow a tighter contract between user
> processes and a profiler.  It would allow better labeling/categorizing
> the correlation values.

It is hard to understand this idea. Are you saying stash a cookie in
TLS for samples to capture to indicate an activity? Restartable
sequences are about preemption on a CPU not of a thread, so at least
my intuition is that they feel different. You could stash information
like this today by changing the thread name which generates comm
events. I've wondered about having similar information in some form of
reserved for profiling stack slot, for example, to stash a pointer to
the name of a function being interpreted. Snapshotting all of a stack
is bad performance wise and for security. A stack slot would be able
to deal with nesting.

> An idea flow would look like this:
> User Task   Profile
> do_work();  sample() -> IP + No activity
> ...
> set_activity(123);
> ...
> do_work();  sample() -> IP + activity (123)
> ...
> set_activity(124);
> ...
> do_work();  sample() -> IP + activity (124)
>
> Ideally, the set_activity() method would not be a syscall. It needs to
> be very cheap as this should not bottleneck work. Ideally this is just
> a memcpy of 16-20 bytes as it is on Windows via EventActivityIdControl()
> using EVENT_ACTIVITY_CTRL_SET_ID.
>
> For those not aware, Open Telemetry allows collecting data from multiple
> machines and show where time was spent. The tracing context is already
> available for logs, but not for profiling samples. The idea is to show
> where slowdowns occur and have profile samples to explain why they
> slowed down. This must be possible without having to track context
> switches to do this correlation. This is because the profiling rates
> are typically 20hz - 1Khz, while the context switching rates are much
> higher. We do not want to have to consume high context switch rates
> just to know a correlation for a 20hz signal. Often these 20hz signals
> are always enabled in some environments.
>
> Regardless if TLS, rseq, or other source is used I believe we will need
> a way for perf_events to include it within a sample. The changes in this
> series show how it could be done with TLS. There is some factoring work
> under perf to make it easier to add more dump types using the existing
> ABI. This is mostly to make the patches clearer, certainly the refactor
> parts could get dropped and we could have duplicated/specialized paths.

fs and gs may be used for more than just the C runtime's TLS. For
example, they may be used by emulators or managed runtimes. I'm not
clear why this specific case couldn't be handled through BPF.

Thanks,
Ian

> 1. https://opentelemetry.io/blog/2024/profiling/
> 2. 
> https://www.elastic.co/blog/continuous-profiling-distributed-tracing-correlation
> 3. 
> https://learn.microsoft.com/en-us/windows/win32/api/evntprov/nf-evntprov-eventactivityidcontrol
>
> Beau Belgrave (4):
>   perf/core: Introduce perf_prepare_dump_data()
>   perf: Introduce PERF_SAMPLE_TLS_USER sample type
>   perf/core: Factor perf_output_sample_udump()
>   perf/x86/core: Add tls dump support
>
>  arch/Kconfig  |   7 ++
>  arch/x86/Kconfig  |   1 +
>  arch/x86/events/core.c|  14 +++
>  arch/x86/include/asm/perf_event.h |   5 +
>  include/linux/perf_event.h|   7 ++
>  include/uapi/linux/perf_event.h   |   5 +-
>  kernel/events/core.c  | 166 +++---
>  

Re: [PATCH v1 0/4] perf parse-regs: Cleanup config and building

2024-02-14 Thread Ian Rogers
On Wed, Feb 14, 2024 at 3:40 AM Leo Yan  wrote:
>
> Currently, the perf building enables register parsing based on the
> target architecture has supported register feature.
>
> Furthermore, the perf building system needs to maintain a variable
> 'NO_PERF_REGS' and defines macro 'HAVE_PERF_REGS_SUPPORT' for statically
> compiling the tool.
>
> As a result, the perf has no flexibilty for parsing register if an
> architecture doesn't support it. And the source files use the macro
> 'HAVE_PERF_REGS_SUPPORT' to switch on and off the register parsing
> related code, which is not a good practice.
>
> This series is to remove the static building for register parsing. In
> theory, we should can dynamically detect if an arch has support this
> feature and functions can return errors when the feature is not
> supported.
>
> The first patch is to remove unused build configuration
> CONFIG_PERF_REGS.
>
> The second patch is to build perf register functions, without using the
> macro 'HAVE_PERF_REGS_SUPPORT' to statically turn on or off code.
>
> The third patch is to introduce a weak function arch__sample_reg_masks(),
> this function can allow the target arch to return its sample register
> list.  With this change, we can totally remove the macro
> 'HAVE_PERF_REGS_SUPPORT' in the source file.
>
> The forth patch is to clean up the Makefile for removing relevant
> configuration and macro definition, as they are not useful anymore.
>
> I tested this patch set on Arm64 and x86 for building and did a cross
> register parsing ('perf record' on Arm64 and 'perf report' on x86).
>
>
> Leo Yan (4):
>   perf build: Remove unused CONFIG_PERF_REGS
>   perf parse-regs: Always build perf register functions
>   perf parse-regs: Introduce a weak function arch__sample_reg_masks()
>   perf build: Cleanup perf register configuration

Thanks Leo, this is great cleanup! Series:
Reviewed-by: Ian Rogers 

Ian

>  tools/perf/Makefile.config| 25 --
>  tools/perf/arch/arm/util/perf_regs.c  |  7 +++-
>  tools/perf/arch/arm64/util/machine.c  |  2 ++
>  tools/perf/arch/arm64/util/perf_regs.c|  7 +++-
>  tools/perf/arch/csky/util/perf_regs.c |  7 +++-
>  tools/perf/arch/loongarch/util/perf_regs.c|  7 +++-
>  tools/perf/arch/mips/util/perf_regs.c |  7 +++-
>  tools/perf/arch/powerpc/util/perf_regs.c  |  7 +++-
>  tools/perf/arch/riscv/util/perf_regs.c|  7 +++-
>  tools/perf/arch/s390/util/perf_regs.c |  7 +++-
>  tools/perf/arch/x86/util/perf_regs.c  |  7 +++-
>  tools/perf/util/parse-regs-options.c  |  8 ++---
>  .../util/perf-regs-arch/perf_regs_aarch64.c   |  4 ---
>  .../perf/util/perf-regs-arch/perf_regs_arm.c  |  4 ---
>  .../perf/util/perf-regs-arch/perf_regs_csky.c |  4 ---
>  .../util/perf-regs-arch/perf_regs_loongarch.c |  4 ---
>  .../perf/util/perf-regs-arch/perf_regs_mips.c |  4 ---
>  .../util/perf-regs-arch/perf_regs_powerpc.c   |  4 ---
>  .../util/perf-regs-arch/perf_regs_riscv.c |  4 ---
>  .../perf/util/perf-regs-arch/perf_regs_s390.c |  4 ---
>  .../perf/util/perf-regs-arch/perf_regs_x86.c  |  4 ---
>  tools/perf/util/perf_regs.c   | 11 --
>  tools/perf/util/perf_regs.h   | 34 +--
>  23 files changed, 67 insertions(+), 112 deletions(-)
>
> --
> 2.34.1
>



[PATCH] perf arm64: Fix off-by-one directory paths.

2021-04-16 Thread Ian Rogers
Relative path include works in the regular build due to -I paths but may
break in other situations.

Signed-off-by: Ian Rogers 
---
 tools/perf/arch/arm64/util/kvm-stat.c | 4 ++--
 tools/perf/arch/arm64/util/pmu.c  | 4 ++--
 tools/perf/arch/arm64/util/unwind-libunwind.c | 4 ++--
 3 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/tools/perf/arch/arm64/util/kvm-stat.c 
b/tools/perf/arch/arm64/util/kvm-stat.c
index 50376b9062c1..2303256b7d05 100644
--- a/tools/perf/arch/arm64/util/kvm-stat.c
+++ b/tools/perf/arch/arm64/util/kvm-stat.c
@@ -1,8 +1,8 @@
 // SPDX-License-Identifier: GPL-2.0
 #include 
 #include 
-#include "../../util/evsel.h"
-#include "../../util/kvm-stat.h"
+#include "../../../util/evsel.h"
+#include "../../../util/kvm-stat.h"
 #include "arm64_exception_types.h"
 #include "debug.h"
 
diff --git a/tools/perf/arch/arm64/util/pmu.c b/tools/perf/arch/arm64/util/pmu.c
index d3259d61ca75..2234fbd0a912 100644
--- a/tools/perf/arch/arm64/util/pmu.c
+++ b/tools/perf/arch/arm64/util/pmu.c
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 
-#include "../../util/cpumap.h"
-#include "../../util/pmu.h"
+#include "../../../util/cpumap.h"
+#include "../../../util/pmu.h"
 
 struct pmu_events_map *pmu_events_map__find(void)
 {
diff --git a/tools/perf/arch/arm64/util/unwind-libunwind.c 
b/tools/perf/arch/arm64/util/unwind-libunwind.c
index 1495a9523a23..5aecf88e3de6 100644
--- a/tools/perf/arch/arm64/util/unwind-libunwind.c
+++ b/tools/perf/arch/arm64/util/unwind-libunwind.c
@@ -4,9 +4,9 @@
 #ifndef REMOTE_UNWIND_LIBUNWIND
 #include 
 #include "perf_regs.h"
-#include "../../util/unwind.h"
+#include "../../../util/unwind.h"
 #endif
-#include "../../util/debug.h"
+#include "../../../util/debug.h"
 
 int LIBUNWIND__ARCH_REG_ID(int regnum)
 {
-- 
2.31.1.368.gbe11c130af-goog



Re: [RFC] Improve workload error in 'perf record'

2021-04-14 Thread Ian Rogers
On Wed, Apr 14, 2021 at 6:16 AM Arnaldo Carvalho de Melo
 wrote:
>
> Hi,
>
> Please take a look,
>
> Best regards,

Acked-by: Ian Rogers 

Having been confused by this for a case in the past, thanks! It'd be
nice for code coverage's sake to have a shell test on this.

Thanks,
Ian

> - Arnaldo
>
> Arnaldo Carvalho de Melo (2):
>   perf evlist: Add a method to return the list of evsels as a string
>   perf record: Improve 'Workload failed' message printing events + what
> was exec'ed
>
>  tools/perf/builtin-record.c |  8 ++--
>  tools/perf/util/evlist.c| 19 +++
>  tools/perf/util/evlist.h|  2 ++
>  3 files changed, 27 insertions(+), 2 deletions(-)
>
> --
> 2.26.2
>


[PATCH] perf arm-spe: Avoid potential buffer overrun.

2021-04-07 Thread Ian Rogers
SPE extended headers are >1 byte so ensure the buffer contains at
least this before reading. This issue was detected by fuzzing.

Signed-off-by: Ian Rogers 
---
 tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c 
b/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c
index f3ac9d40cebf..2e5eff4f8f03 100644
--- a/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c
+++ b/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c
@@ -210,8 +210,10 @@ static int arm_spe_do_get_packet(const unsigned char *buf, 
size_t len,
 
if ((hdr & SPE_HEADER0_MASK2) == SPE_HEADER0_EXTENDED) {
/* 16-bit extended format header */
-   ext_hdr = 1;
+   if (len == 1)
+   return ARM_SPE_BAD_PACKET;
 
+   ext_hdr = 1;
hdr = buf[1];
if (hdr == SPE_HEADER1_ALIGNMENT)
return arm_spe_get_alignment(buf, len, packet);
-- 
2.31.0.208.g409f899ff0-goog



Re: [PATCH v2 1/6] perf metricgroup: Make find_metric() public with name change

2021-04-01 Thread Ian Rogers
On Thu, Mar 25, 2021 at 3:38 AM John Garry  wrote:
>
> Function find_metric() is required for the metric processing in the
> pmu-events testcase, so make it public. Also change the name to include
> "metricgroup".

Would it make more sense as "pmu_events_map__find_metric" ?

Thanks,
Ian

> Signed-off-by: John Garry 
> ---
>  tools/perf/util/metricgroup.c | 5 +++--
>  tools/perf/util/metricgroup.h | 3 ++-
>  2 files changed, 5 insertions(+), 3 deletions(-)
>
> diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c
> index 6acb44ad439b..71a13406e0bd 100644
> --- a/tools/perf/util/metricgroup.c
> +++ b/tools/perf/util/metricgroup.c
> @@ -900,7 +900,8 @@ static int __add_metric(struct list_head *metric_list,
> (match_metric(__pe->metric_group, __metric) ||  \
>  match_metric(__pe->metric_name, __metric)))
>
> -static struct pmu_event *find_metric(const char *metric, struct 
> pmu_events_map *map)
> +struct pmu_event *metrcgroup_find_metric(const char *metric,
> +struct pmu_events_map *map)
>  {
> struct pmu_event *pe;
> int i;
> @@ -985,7 +986,7 @@ static int __resolve_metric(struct metric *m,
> struct expr_id *parent;
> struct pmu_event *pe;
>
> -   pe = find_metric(cur->key, map);
> +   pe = metrcgroup_find_metric(cur->key, map);
> if (!pe)
> continue;
>
> diff --git a/tools/perf/util/metricgroup.h b/tools/perf/util/metricgroup.h
> index ed1b9392e624..1674c6a36d74 100644
> --- a/tools/perf/util/metricgroup.h
> +++ b/tools/perf/util/metricgroup.h
> @@ -44,7 +44,8 @@ int metricgroup__parse_groups(const struct option *opt,
>   bool metric_no_group,
>   bool metric_no_merge,
>   struct rblist *metric_events);
> -
> +struct pmu_event *metrcgroup_find_metric(const char *metric,
> +struct pmu_events_map *map);
>  int metricgroup__parse_groups_test(struct evlist *evlist,
>struct pmu_events_map *map,
>const char *str,
> --
> 2.26.2
>


Re: [PATCH v3 04/21] x86/insn: Add an insn_decode() API

2021-03-24 Thread Ian Rogers
On Wed, Mar 24, 2021 at 6:54 AM Borislav Petkov  wrote:
>
> On Wed, Mar 24, 2021 at 10:43:20AM -0300, Arnaldo Carvalho de Melo wrote:
> > Borislav, was this addressed? Ian?
>
> Yap:
>
> https://git.kernel.org/tip/0705ef64d1ff52b817e278ca6e28095585ff31e1

Tested on PPC and ARM64 fwiw. Thanks,
Ian

> --
> Regards/Gruss,
> Boris.
>
> https://people.kernel.org/tglx/notes-about-netiquette


Re: [PATCH RFC v2 8/8] selftests/perf: Add kselftest for remove_on_exec

2021-03-22 Thread Ian Rogers
On Mon, Mar 22, 2021 at 6:24 AM Marco Elver  wrote:
>
> On Wed, Mar 10, 2021 at 11:41AM +0100, Marco Elver wrote:
> > Add kselftest to test that remove_on_exec removes inherited events from
> > child tasks.
> >
> > Signed-off-by: Marco Elver 
>
> To make compatible with more recent libc, we'll need to fixup the tests
> with the below.
>
> Also, I've seen that tools/perf/tests exists, however it seems to be
> primarily about perf-tool related tests. Is this correct?
>
> I'd propose to keep these purely kernel ABI related tests separate, and
> that way we can also make use of the kselftests framework which will
> also integrate into various CI systems such as kernelci.org.

Perhaps there is a way to have both? Having the perf tool spot an
errant kernel feels like a feature. There are also
tools/lib/perf/tests and Vince Weaver's tests [1]. It is possible to
run standalone tests from within perf test by having them be executed
by a shell test.

Thanks,
Ian

[1] https://github.com/deater/perf_event_tests

> Thanks,
> -- Marco
>
> -- >8 --
>
> diff --git a/tools/testing/selftests/perf_events/remove_on_exec.c 
> b/tools/testing/selftests/perf_events/remove_on_exec.c
> index e176b3a74d55..f89d0cfdb81e 100644
> --- a/tools/testing/selftests/perf_events/remove_on_exec.c
> +++ b/tools/testing/selftests/perf_events/remove_on_exec.c
> @@ -13,6 +13,11 @@
>  #define __have_siginfo_t 1
>  #define __have_sigval_t 1
>  #define __have_sigevent_t 1
> +#define __siginfo_t_defined
> +#define __sigval_t_defined
> +#define __sigevent_t_defined
> +#define _BITS_SIGINFO_CONSTS_H 1
> +#define _BITS_SIGEVENT_CONSTS_H 1
>
>  #include 
>  #include 
> diff --git a/tools/testing/selftests/perf_events/sigtrap_threads.c 
> b/tools/testing/selftests/perf_events/sigtrap_threads.c
> index 7ebb9bb34c2e..b9a7d4b64b3c 100644
> --- a/tools/testing/selftests/perf_events/sigtrap_threads.c
> +++ b/tools/testing/selftests/perf_events/sigtrap_threads.c
> @@ -13,6 +13,11 @@
>  #define __have_siginfo_t 1
>  #define __have_sigval_t 1
>  #define __have_sigevent_t 1
> +#define __siginfo_t_defined
> +#define __sigval_t_defined
> +#define __sigevent_t_defined
> +#define _BITS_SIGINFO_CONSTS_H 1
> +#define _BITS_SIGEVENT_CONSTS_H 1
>
>  #include 
>  #include 


Re: linux-next: build failure after merge of the tip tree

2021-03-17 Thread Ian Rogers
On Wed, Mar 17, 2021 at 3:54 AM Borislav Petkov  wrote:
>
> + Ian.
>
> On Wed, Mar 17, 2021 at 03:08:58PM +1100, Stephen Rothwell wrote:
> > Hi all,
> >
> > After merging the tip tree, today's linux-next build (native perf)
> > failed like this:
> >
> > In file included from util/intel-pt-decoder/intel-pt-insn-decoder.c:15:
> > util/intel-pt-decoder/../../../arch/x86/lib/insn.c:14:10: fatal error: 
> > asm/inat.h: No such file or directory
> >14 | #include  /*__ignore_sync_check__ */
> >   |  ^~~~
> >
> > This is a powerpc build of perf.  I can't see what caused this failure,
> > so I have used the version of the tip tree from next-20210316 for today.
>
> Yah, this has come up in the past during review but the wrong version
> somehow snuck in, sorry. ;-\
>
> Can you guys verify this fixes the build issue? I don't have a ppc build
> setup.
>
> Thx.

The  path also needs fixing. With the following
I was able to build for arm64 and powerpc.

Thanks,
Ian

diff --git a/tools/arch/x86/lib/insn.c b/tools/arch/x86/lib/insn.c
index cd4dedde3265..968360bf2150 100644
--- a/tools/arch/x86/lib/insn.c
+++ b/tools/arch/x86/lib/insn.c
@@ -11,13 +11,13 @@
 #else
 #include 
 #endif
-#include  /*__ignore_sync_check__ */
-#include  /* __ignore_sync_check__ */
+#include "../include/asm/inat.h" /*__ignore_sync_check__ */
+#include "../include/asm/insn.h" /* __ignore_sync_check__ */

 #include 
 #include 

-#include  /* __ignore_sync_check__ */
+#include "../include/asm/emulate_prefix.h" /* __ignore_sync_check__ */

 #define leXX_to_cpu(t, r)  \
 ({ \

> ---
> From 50d91054fc421e2a90923706d5ca79e941e28300 Mon Sep 17 00:00:00 2001
> From: Borislav Petkov 
> Date: Wed, 17 Mar 2021 11:33:04 +0100
> Subject: [PATCH] tools/insn: Restore the relative include paths for cross
>  building
>
> Building perf on ppc causes:
>
>   In file included from util/intel-pt-decoder/intel-pt-insn-decoder.c:15:
>   util/intel-pt-decoder/../../../arch/x86/lib/insn.c:14:10: fatal error: 
> asm/inat.h: No such file or directory
>  14 | #include  /*__ignore_sync_check__ */
> |      ^~~~
>
> Restore the relative include paths so that the compiler can find the
> headers.
>
> Fixes: 93281c4a9657 ("x86/insn: Add an insn_decode() API")
> Reported-by: Ian Rogers 
> Reported-by: Stephen Rothwell 
> NOT-Signed-off-by: Borislav Petkov 
> ---
>  tools/arch/x86/lib/insn.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/tools/arch/x86/lib/insn.c b/tools/arch/x86/lib/insn.c
> index cd4dedde3265..999fbd4c9bea 100644
> --- a/tools/arch/x86/lib/insn.c
> +++ b/tools/arch/x86/lib/insn.c
> @@ -11,8 +11,8 @@
>  #else
>  #include 
>  #endif
> -#include  /*__ignore_sync_check__ */
> -#include  /* __ignore_sync_check__ */
> +#include "../include/asm/inat.h" /* __ignore_sync_check__ */
> +#include "../include/asm/insn.h" /* __ignore_sync_check__ */
>
>  #include 
>  #include 
> --
> 2.29.2
>
> --
> Regards/Gruss,
> Boris.
>
> SUSE Software Solutions Germany GmbH, GF: Felix Imendörffer, HRB 36809, AG 
> Nürnberg


Re: [PATCH v3 04/21] x86/insn: Add an insn_decode() API

2021-03-16 Thread Ian Rogers
On Thu, Mar 4, 2021 at 9:56 AM Borislav Petkov  wrote:
>
> From: Borislav Petkov 
>
> Users of the instruction decoder should use this to decode instruction
> bytes. For that, have insn*() helpers return an int value to denote
> success/failure. When there's an error fetching the next insn byte and
> the insn falls short, return -ENODATA to denote that.
>
> While at it, make insn_get_opcode() more stricter as to whether what has
> seen so far is a valid insn and if not.
>
> Copy linux/kconfig.h for the tools-version of the decoder so that it can
> use IS_ENABLED().
>
> Also, cast the INSN_MODE_KERN dummy define value to (enum insn_mode)
> for tools use of the decoder because perf tool builds with -Werror and
> errors out with -Werror=sign-compare otherwise.
>
> Signed-off-by: Borislav Petkov 
> Acked-by: Masami Hiramatsu 
> ---
>  arch/x86/include/asm/insn.h   |  24 +++-
>  arch/x86/lib/insn.c   | 216 +++--
>  tools/arch/x86/include/asm/insn.h |  24 +++-
>  tools/arch/x86/lib/insn.c | 222 +++---
>  tools/include/linux/kconfig.h |  73 ++
>  5 files changed, 452 insertions(+), 107 deletions(-)
>  create mode 100644 tools/include/linux/kconfig.h
>
> diff --git a/arch/x86/include/asm/insn.h b/arch/x86/include/asm/insn.h
> index 93f84600ac2c..de9fe760c1e7 100644
> --- a/arch/x86/include/asm/insn.h
> +++ b/arch/x86/include/asm/insn.h
> @@ -132,13 +132,23 @@ struct insn {
>  #define X86_VEX_M_MAX  0x1f/* VEX3.M Maximum value */
>
>  extern void insn_init(struct insn *insn, const void *kaddr, int buf_len, int 
> x86_64);
> -extern void insn_get_prefixes(struct insn *insn);
> -extern void insn_get_opcode(struct insn *insn);
> -extern void insn_get_modrm(struct insn *insn);
> -extern void insn_get_sib(struct insn *insn);
> -extern void insn_get_displacement(struct insn *insn);
> -extern void insn_get_immediate(struct insn *insn);
> -extern void insn_get_length(struct insn *insn);
> +extern int insn_get_prefixes(struct insn *insn);
> +extern int insn_get_opcode(struct insn *insn);
> +extern int insn_get_modrm(struct insn *insn);
> +extern int insn_get_sib(struct insn *insn);
> +extern int insn_get_displacement(struct insn *insn);
> +extern int insn_get_immediate(struct insn *insn);
> +extern int insn_get_length(struct insn *insn);
> +
> +enum insn_mode {
> +   INSN_MODE_32,
> +   INSN_MODE_64,
> +   /* Mode is determined by the current kernel build. */
> +   INSN_MODE_KERN,
> +   INSN_NUM_MODES,
> +};
> +
> +extern int insn_decode(struct insn *insn, const void *kaddr, int buf_len, 
> enum insn_mode m);
>
>  /* Attribute will be determined after getting ModRM (for opcode groups) */
>  static inline void insn_get_attribute(struct insn *insn)
> diff --git a/arch/x86/lib/insn.c b/arch/x86/lib/insn.c
> index ed1e0335aa14..bb58004497f8 100644
> --- a/arch/x86/lib/insn.c
> +++ b/arch/x86/lib/insn.c
> @@ -14,6 +14,9 @@
>  #include  /*__ignore_sync_check__ */
>  #include  /* __ignore_sync_check__ */
>
> +#include 
> +#include 
> +
>  #include  /* __ignore_sync_check__ */
>
>  #define leXX_to_cpu(t, r)  \
> @@ -112,8 +115,12 @@ static void insn_get_emulate_prefix(struct insn *insn)
>   * Populates the @insn->prefixes bitmap, and updates @insn->next_byte
>   * to point to the (first) opcode.  No effect if @insn->prefixes.got
>   * is already set.
> + *
> + * * Returns:
> + * 0:  on success
> + * < 0: on error
>   */
> -void insn_get_prefixes(struct insn *insn)
> +int insn_get_prefixes(struct insn *insn)
>  {
> struct insn_field *prefixes = >prefixes;
> insn_attr_t attr;
> @@ -121,7 +128,7 @@ void insn_get_prefixes(struct insn *insn)
> int i, nb;
>
> if (prefixes->got)
> -   return;
> +   return 0;
>
> insn_get_emulate_prefix(insn);
>
> @@ -231,8 +238,10 @@ void insn_get_prefixes(struct insn *insn)
>
> prefixes->got = 1;
>
> +   return 0;
> +
>  err_out:
> -   return;
> +   return -ENODATA;
>  }
>
>  /**
> @@ -244,16 +253,25 @@ void insn_get_prefixes(struct insn *insn)
>   * If necessary, first collects any preceding (prefix) bytes.
>   * Sets @insn->opcode.value = opcode1.  No effect if @insn->opcode.got
>   * is already 1.
> + *
> + * Returns:
> + * 0:  on success
> + * < 0: on error
>   */
> -void insn_get_opcode(struct insn *insn)
> +int insn_get_opcode(struct insn *insn)
>  {
> struct insn_field *opcode = >opcode;
> +   int pfx_id, ret;
> insn_byte_t op;
> -   int pfx_id;
> +
> if (opcode->got)
> -   return;
> -   if (!insn->prefixes.got)
> -   insn_get_prefixes(insn);
> +   return 0;
> +
> +   if (!insn->prefixes.got) {
> +   ret = insn_get_prefixes(insn);
> +   if (ret)
> +   return ret;
> +   }
>
> /* Get first opcode */
> op = 

[PATCH v2 1/3] perf test: Remove unused argument

2021-03-16 Thread Ian Rogers
Remove unused argument from daemon_exit.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/shell/daemon.sh | 15 +++
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/tools/perf/tests/shell/daemon.sh b/tools/perf/tests/shell/daemon.sh
index 5ad3ca8d681b..66ad56b4e0a5 100755
--- a/tools/perf/tests/shell/daemon.sh
+++ b/tools/perf/tests/shell/daemon.sh
@@ -115,8 +115,7 @@ daemon_start()
 
 daemon_exit()
 {
-   local base=$1
-   local config=$2
+   local config=$1
 
local line=`perf daemon --config ${config} -x: | head -1`
local pid=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $1 }'`
@@ -171,7 +170,7 @@ EOF
 ${base}/session-time/ack "0"
 
# stop daemon
-   daemon_exit ${base} ${config}
+   daemon_exit ${config}
 
rm -rf ${base}
rm -f ${config}
@@ -288,7 +287,7 @@ EOF
done
 
# stop daemon
-   daemon_exit ${base} ${config}
+   daemon_exit ${config}
 
rm -rf ${base}
rm -f ${config}
@@ -333,7 +332,7 @@ EOF
fi
 
# stop daemon
-   daemon_exit ${base} ${config}
+   daemon_exit ${config}
 
# check that sessions are gone
if [ -d "/proc/${pid_size}" ]; then
@@ -374,7 +373,7 @@ EOF
perf daemon signal --config ${config}
 
# stop daemon
-   daemon_exit ${base} ${config}
+   daemon_exit ${config}
 
# count is 2 perf.data for signals and 1 for perf record finished
count=`ls ${base}/session-test/ | grep perf.data | wc -l`
@@ -420,7 +419,7 @@ EOF
fi
 
# stop daemon
-   daemon_exit ${base} ${config}
+   daemon_exit ${config}
 
rm -rf ${base}
rm -f ${config}
@@ -457,7 +456,7 @@ EOF
fi
 
# stop daemon
-   daemon_exit ${base} ${config}
+   daemon_exit ${config}
 
rm -rf ${base}
rm -f ${config}
-- 
2.31.0.rc2.261.g7f71774620-goog



[PATCH v2 2/3] perf test: Cleanup daemon if test is interrupted.

2021-03-16 Thread Ian Rogers
Reorder daemon_start and daemon_exit as the trap handler is added in
daemon_start referencing daemon_exit.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/shell/daemon.sh | 34 +++-
 1 file changed, 20 insertions(+), 14 deletions(-)

diff --git a/tools/perf/tests/shell/daemon.sh b/tools/perf/tests/shell/daemon.sh
index 66ad56b4e0a5..61d13c4c64b8 100755
--- a/tools/perf/tests/shell/daemon.sh
+++ b/tools/perf/tests/shell/daemon.sh
@@ -98,6 +98,23 @@ check_line_other()
fi
 }
 
+daemon_exit()
+{
+   local config=$1
+
+   local line=`perf daemon --config ${config} -x: | head -1`
+   local pid=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $1 }'`
+
+   # Reset trap handler.
+   trap - SIGINT SIGTERM
+
+   # stop daemon
+   perf daemon stop --config ${config}
+
+   # ... and wait for the pid to go away
+   tail --pid=${pid} -f /dev/null
+}
+
 daemon_start()
 {
local config=$1
@@ -105,6 +122,9 @@ daemon_start()
 
perf daemon start --config ${config}
 
+   # Clean up daemon if interrupted.
+   trap "echo 'FAILED: Signal caught'; daemon_exit ${config}; exit 1" 
SIGINT SIGTERM
+
# wait for the session to ping
local state="FAIL"
while [ "${state}" != "OK" ]; do
@@ -113,20 +133,6 @@ daemon_start()
done
 }
 
-daemon_exit()
-{
-   local config=$1
-
-   local line=`perf daemon --config ${config} -x: | head -1`
-   local pid=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $1 }'`
-
-   # stop daemon
-   perf daemon stop --config ${config}
-
-   # ... and wait for the pid to go away
-   tail --pid=${pid} -f /dev/null
-}
-
 test_list()
 {
echo "test daemon list"
-- 
2.31.0.rc2.261.g7f71774620-goog



[PATCH v2 3/3] perf test: Add 30s timeout for wait for daemon start.

2021-03-16 Thread Ian Rogers
Retry the ping loop upto 600 times, or approximately 30 seconds, to make
sure the test does hang at start up.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/shell/daemon.sh | 7 +++
 1 file changed, 7 insertions(+)

diff --git a/tools/perf/tests/shell/daemon.sh b/tools/perf/tests/shell/daemon.sh
index 61d13c4c64b8..ee4a30ca3f57 100755
--- a/tools/perf/tests/shell/daemon.sh
+++ b/tools/perf/tests/shell/daemon.sh
@@ -127,9 +127,16 @@ daemon_start()
 
# wait for the session to ping
local state="FAIL"
+   local retries=0
while [ "${state}" != "OK" ]; do
state=`perf daemon ping --config ${config} --session ${session} 
| awk '{ print $1 }'`
sleep 0.05
+   retries=$((${retries} +1))
+   if [ ${retries} -ge 600 ]; then
+   echo "FAILED: Timeout waiting for daemon to ping"
+   daemon_exit ${config}
+   exit 1
+   fi
done
 }
 
-- 
2.31.0.rc2.261.g7f71774620-goog



Re: [PATCH 2/3] perf test: Cleanup daemon if test is interrupted.

2021-03-11 Thread Ian Rogers
On Thu, Mar 11, 2021 at 2:38 AM Jiri Olsa  wrote:
>
> On Wed, Mar 10, 2021 at 12:41:17PM -0800, Ian Rogers wrote:
> > Reorder daemon_start and daemon_exit as the trap handler is added in
> > daemon_start referencing daemon_exit.
>
> makes sense, minor comments below
>
> >
> > Signed-off-by: Ian Rogers 
> > ---
> >  tools/perf/tests/shell/daemon.sh | 34 +++-
> >  1 file changed, 20 insertions(+), 14 deletions(-)
> >
> > diff --git a/tools/perf/tests/shell/daemon.sh 
> > b/tools/perf/tests/shell/daemon.sh
> > index 66ad56b4e0a5..a02cedc76de6 100755
> > --- a/tools/perf/tests/shell/daemon.sh
> > +++ b/tools/perf/tests/shell/daemon.sh
> > @@ -98,6 +98,23 @@ check_line_other()
> >   fi
> >  }
> >
> > +daemon_exit()
> > +{
> > + local config=$1
> > +
> > + local line=`perf daemon --config ${config} -x: | head -1`
> > + local pid=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $1 }'`
> > +
> > +# Reset trap handler.
> > +trap - SIGINT SIGTERM
>
> please keep the 'tabs' in here
>
> > +
> > + # stop daemon
> > + perf daemon stop --config ${config}
> > +
> > + # ... and wait for the pid to go away
> > + tail --pid=${pid} -f /dev/null
> > +}
> > +
> >  daemon_start()
> >  {
> >   local config=$1
> > @@ -105,6 +122,9 @@ daemon_start()
> >
> >   perf daemon start --config ${config}
> >
> > +# Clean up daemon if interrupted.
> > +trap "daemon_exit ${config}; exit 4" SIGINT SIGTERM
>
> ditto, plus let's document exit values somewhere in comments,
> I think the next patch is adding exit 62, so that one as well

Thanks, it might be easier to just have these as exit 1 - I pulled
values from the errno list. Do you have any preferences?

Ian

> thanks,
> jirka
>
> > +
> >   # wait for the session to ping
> >   local state="FAIL"
> >   while [ "${state}" != "OK" ]; do
> > @@ -113,20 +133,6 @@ daemon_start()
> >   done
> >  }
> >
> > -daemon_exit()
> > -{
> > - local config=$1
> > -
> > - local line=`perf daemon --config ${config} -x: | head -1`
> > - local pid=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $1 }'`
> > -
> > - # stop daemon
> > - perf daemon stop --config ${config}
> > -
> > - # ... and wait for the pid to go away
> > - tail --pid=${pid} -f /dev/null
> > -}
> > -
> >  test_list()
> >  {
> >   echo "test daemon list"
> > --
> > 2.30.1.766.gb4fecdf3b7-goog
> >
>


[PATCH 3/3] perf test: Add 30s timeout for wait for daemon start.

2021-03-10 Thread Ian Rogers
Retry the ping loop upto 600 times, or approximately 30 seconds, to make
sure the test does hang at start up.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/shell/daemon.sh | 7 +++
 1 file changed, 7 insertions(+)

diff --git a/tools/perf/tests/shell/daemon.sh b/tools/perf/tests/shell/daemon.sh
index a02cedc76de6..cb831ff2c185 100755
--- a/tools/perf/tests/shell/daemon.sh
+++ b/tools/perf/tests/shell/daemon.sh
@@ -127,9 +127,16 @@ daemon_start()
 
# wait for the session to ping
local state="FAIL"
+   local retries=0
while [ "${state}" != "OK" ]; do
state=`perf daemon ping --config ${config} --session ${session} 
| awk '{ print $1 }'`
sleep 0.05
+   retries=$((${retries} +1))
+   if [ ${retries} -ge 600 ]; then
+   echo "Timeout waiting for daemon to ping"
+   daemon_exit ${config}
+   exit 62
+   fi
done
 }
 
-- 
2.30.1.766.gb4fecdf3b7-goog



[PATCH 1/3] perf test: Remove unused argument

2021-03-10 Thread Ian Rogers
Remove unused argument from daemon_exit.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/shell/daemon.sh | 15 +++
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/tools/perf/tests/shell/daemon.sh b/tools/perf/tests/shell/daemon.sh
index 5ad3ca8d681b..66ad56b4e0a5 100755
--- a/tools/perf/tests/shell/daemon.sh
+++ b/tools/perf/tests/shell/daemon.sh
@@ -115,8 +115,7 @@ daemon_start()
 
 daemon_exit()
 {
-   local base=$1
-   local config=$2
+   local config=$1
 
local line=`perf daemon --config ${config} -x: | head -1`
local pid=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $1 }'`
@@ -171,7 +170,7 @@ EOF
 ${base}/session-time/ack "0"
 
# stop daemon
-   daemon_exit ${base} ${config}
+   daemon_exit ${config}
 
rm -rf ${base}
rm -f ${config}
@@ -288,7 +287,7 @@ EOF
done
 
# stop daemon
-   daemon_exit ${base} ${config}
+   daemon_exit ${config}
 
rm -rf ${base}
rm -f ${config}
@@ -333,7 +332,7 @@ EOF
fi
 
# stop daemon
-   daemon_exit ${base} ${config}
+   daemon_exit ${config}
 
# check that sessions are gone
if [ -d "/proc/${pid_size}" ]; then
@@ -374,7 +373,7 @@ EOF
perf daemon signal --config ${config}
 
# stop daemon
-   daemon_exit ${base} ${config}
+   daemon_exit ${config}
 
# count is 2 perf.data for signals and 1 for perf record finished
count=`ls ${base}/session-test/ | grep perf.data | wc -l`
@@ -420,7 +419,7 @@ EOF
fi
 
# stop daemon
-   daemon_exit ${base} ${config}
+   daemon_exit ${config}
 
rm -rf ${base}
rm -f ${config}
@@ -457,7 +456,7 @@ EOF
fi
 
# stop daemon
-   daemon_exit ${base} ${config}
+   daemon_exit ${config}
 
rm -rf ${base}
rm -f ${config}
-- 
2.30.1.766.gb4fecdf3b7-goog



[PATCH 2/3] perf test: Cleanup daemon if test is interrupted.

2021-03-10 Thread Ian Rogers
Reorder daemon_start and daemon_exit as the trap handler is added in
daemon_start referencing daemon_exit.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/shell/daemon.sh | 34 +++-
 1 file changed, 20 insertions(+), 14 deletions(-)

diff --git a/tools/perf/tests/shell/daemon.sh b/tools/perf/tests/shell/daemon.sh
index 66ad56b4e0a5..a02cedc76de6 100755
--- a/tools/perf/tests/shell/daemon.sh
+++ b/tools/perf/tests/shell/daemon.sh
@@ -98,6 +98,23 @@ check_line_other()
fi
 }
 
+daemon_exit()
+{
+   local config=$1
+
+   local line=`perf daemon --config ${config} -x: | head -1`
+   local pid=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $1 }'`
+
+# Reset trap handler.
+trap - SIGINT SIGTERM
+
+   # stop daemon
+   perf daemon stop --config ${config}
+
+   # ... and wait for the pid to go away
+   tail --pid=${pid} -f /dev/null
+}
+
 daemon_start()
 {
local config=$1
@@ -105,6 +122,9 @@ daemon_start()
 
perf daemon start --config ${config}
 
+# Clean up daemon if interrupted.
+trap "daemon_exit ${config}; exit 4" SIGINT SIGTERM
+
# wait for the session to ping
local state="FAIL"
while [ "${state}" != "OK" ]; do
@@ -113,20 +133,6 @@ daemon_start()
done
 }
 
-daemon_exit()
-{
-   local config=$1
-
-   local line=`perf daemon --config ${config} -x: | head -1`
-   local pid=`echo "${line}" | awk 'BEGIN { FS = ":" } ; { print $1 }'`
-
-   # stop daemon
-   perf daemon stop --config ${config}
-
-   # ... and wait for the pid to go away
-   tail --pid=${pid} -f /dev/null
-}
-
 test_list()
 {
echo "test daemon list"
-- 
2.30.1.766.gb4fecdf3b7-goog



[PATCH] perf synthetic events: Avoid write of uninitialized memory.

2021-03-09 Thread Ian Rogers
Account for alignment bytes in the zero-ing memset.

Signed-off-by: Ian Rogers 
---
 tools/perf/util/synthetic-events.c | 9 +
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/tools/perf/util/synthetic-events.c 
b/tools/perf/util/synthetic-events.c
index b698046ec2db..31bf3dd6a1e0 100644
--- a/tools/perf/util/synthetic-events.c
+++ b/tools/perf/util/synthetic-events.c
@@ -424,7 +424,7 @@ int perf_event__synthesize_mmap_events(struct perf_tool 
*tool,
 
while (!io.eof) {
static const char anonstr[] = "//anon";
-   size_t size;
+   size_t size, aligned_size;
 
/* ensure null termination since stack will be reused. */
event->mmap2.filename[0] = '\0';
@@ -484,11 +484,12 @@ int perf_event__synthesize_mmap_events(struct perf_tool 
*tool,
}
 
size = strlen(event->mmap2.filename) + 1;
-   size = PERF_ALIGN(size, sizeof(u64));
+   aligned_size = PERF_ALIGN(size, sizeof(u64));
event->mmap2.len -= event->mmap.start;
event->mmap2.header.size = (sizeof(event->mmap2) -
-   (sizeof(event->mmap2.filename) - size));
-   memset(event->mmap2.filename + size, 0, machine->id_hdr_size);
+   (sizeof(event->mmap2.filename) - 
aligned_size));
+   memset(event->mmap2.filename + size, 0, machine->id_hdr_size +
+   (aligned_size - size));
event->mmap2.header.size += machine->id_hdr_size;
event->mmap2.pid = tgid;
event->mmap2.tid = pid;
-- 
2.30.1.766.gb4fecdf3b7-goog



[PATCH] tools include: Add __sum16 and __wsum definitions.

2021-03-07 Thread Ian Rogers
This adds definitions available in the uapi version.

Explanation:
In the kernel include of types.h the uapi version is included.
In tools the uapi/linux/types.h and linux/types.h are distinct.
For BPF programs a definition of __wsum is needed by the generated
bpf_helpers.h. The definition comes either from a generated vmlinux.h or
from  that may be transitively included from bpf.h. The
perf build prefers linux/types.h over uapi/linux/types.h for
*. To allow tools/perf/util/bpf_skel/bpf_prog_profiler.bpf.c
to compile with the same include path used for perf then these
definitions are necessary.

There is likely a wider conversation about exactly how types.h should be
specified and the include order used by the perf build - it is somewhat
confusing that tools/include/uapi/linux/bpf.h is using the non-uapi
types.h.

*see tools/perf/Makefile.config:
...
INC_FLAGS += -I$(srctree)/tools/include/
INC_FLAGS += -I$(srctree)/tools/arch/$(SRCARCH)/include/uapi
INC_FLAGS += -I$(srctree)/tools/include/uapi
...
The include directories are scanned from left-to-right:
https://gcc.gnu.org/onlinedocs/gcc/Directory-Options.html
As tools/include/linux/types.h appears before
tools/include/uapi/linux/types.h then I say it is preferred.

Signed-off-by: Ian Rogers 
---
 tools/include/linux/types.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/tools/include/linux/types.h b/tools/include/linux/types.h
index e9c5a215837d..6e14a533ab4e 100644
--- a/tools/include/linux/types.h
+++ b/tools/include/linux/types.h
@@ -61,6 +61,9 @@ typedef __u32 __bitwise __be32;
 typedef __u64 __bitwise __le64;
 typedef __u64 __bitwise __be64;
 
+typedef __u16 __bitwise __sum16;
+typedef __u32 __bitwise __wsum;
+
 typedef struct {
int counter;
 } atomic_t;
-- 
2.30.1.766.gb4fecdf3b7-goog



[PATCH 1/3] perf skel: Remove some unused variables.

2021-03-06 Thread Ian Rogers
Fixes -Wall warnings.

Signed-off-by: Ian Rogers 
---
 tools/perf/util/bpf_skel/bpf_prog_profiler.bpf.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/tools/perf/util/bpf_skel/bpf_prog_profiler.bpf.c 
b/tools/perf/util/bpf_skel/bpf_prog_profiler.bpf.c
index c7cec92d0236..ab12b4c4ece2 100644
--- a/tools/perf/util/bpf_skel/bpf_prog_profiler.bpf.c
+++ b/tools/perf/util/bpf_skel/bpf_prog_profiler.bpf.c
@@ -52,7 +52,7 @@ int BPF_PROG(fentry_XXX)
 static inline void
 fexit_update_maps(struct bpf_perf_event_value *after)
 {
-   struct bpf_perf_event_value *before, diff, *accum;
+   struct bpf_perf_event_value *before, diff;
__u32 zero = 0;
 
before = bpf_map_lookup_elem(_readings, );
@@ -78,7 +78,6 @@ int BPF_PROG(fexit_XXX)
 {
struct bpf_perf_event_value reading;
__u32 cpu = bpf_get_smp_processor_id();
-   __u32 one = 1, zero = 0;
int err;
 
/* read all events before updating the maps, to reduce error */
-- 
2.30.1.766.gb4fecdf3b7-goog



[PATCH 2/3] perf tool: Enable warnings when compiling BPF programs

2021-03-06 Thread Ian Rogers
Add -Wall -Werror when compiling BPF skeletons.

Signed-off-by: Ian Rogers 
---
 tools/perf/Makefile.perf | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index 5345ac70cd83..f43d2551f3de 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -1029,7 +1029,7 @@ $(BPFTOOL): | $(SKEL_TMP_OUT)
OUTPUT=$(SKEL_TMP_OUT)/ bootstrap
 
 $(SKEL_TMP_OUT)/%.bpf.o: util/bpf_skel/%.bpf.c $(LIBBPF) | $(SKEL_TMP_OUT)
-   $(QUIET_CLANG)$(CLANG) -g -O2 -target bpf $(BPF_INCLUDE) \
+   $(QUIET_CLANG)$(CLANG) -g -O2 -target bpf -Wall -Werror $(BPF_INCLUDE) \
  -c $(filter util/bpf_skel/%.bpf.c,$^) -o $@ && $(LLVM_STRIP) -g $@
 
 $(SKEL_OUT)/%.skel.h: $(SKEL_TMP_OUT)/%.bpf.o | $(BPFTOOL)
-- 
2.30.1.766.gb4fecdf3b7-goog



[PATCH 3/3] perf bpf: Minor whitespace cleanup.

2021-03-06 Thread Ian Rogers
Missed space after #include.

Signed-off-by: Ian Rogers 
---
 tools/perf/util/bpf_counter.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/perf/util/bpf_counter.h b/tools/perf/util/bpf_counter.h
index 2eca210e5dc1..cb9c532e0a07 100644
--- a/tools/perf/util/bpf_counter.h
+++ b/tools/perf/util/bpf_counter.h
@@ -38,7 +38,7 @@ int bpf_counter__install_pe(struct evsel *evsel, int cpu, int 
fd);
 
 #else /* HAVE_BPF_SKEL */
 
-#include
+#include 
 
 static inline int bpf_counter__load(struct evsel *evsel __maybe_unused,
struct target *target __maybe_unused)
-- 
2.30.1.766.gb4fecdf3b7-goog



[PATCH] perf trace: Ensure read cmdlines are null terminated.

2021-02-26 Thread Ian Rogers
Issue detected by address sanitizer.

Fixes: cd4ceb63438e (perf util: Save pid-cmdline mapping into tracing header)
Signed-off-by: Ian Rogers 
---
 tools/perf/util/trace-event-read.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tools/perf/util/trace-event-read.c 
b/tools/perf/util/trace-event-read.c
index f507dff713c9..8a01af783310 100644
--- a/tools/perf/util/trace-event-read.c
+++ b/tools/perf/util/trace-event-read.c
@@ -361,6 +361,7 @@ static int read_saved_cmdline(struct tep_handle *pevent)
pr_debug("error reading saved cmdlines\n");
goto out;
}
+   buf[ret] = '\0';
 
parse_saved_cmdline(pevent, buf, size);
ret = 0;
-- 
2.30.1.766.gb4fecdf3b7-goog



[PATCH] perf tools: Fix documentation of verbose options

2021-02-26 Thread Ian Rogers
Option doesn't take a value, make sure the man pages agree. For example:

$ perf evlist --verbose=1
 Error: option `verbose' takes no value

Signed-off-by: Ian Rogers 
---
 tools/perf/Documentation/perf-evlist.txt   | 2 +-
 tools/perf/Documentation/perf-ftrace.txt   | 4 ++--
 tools/perf/Documentation/perf-kallsyms.txt | 2 +-
 tools/perf/Documentation/perf-trace.txt| 4 ++--
 4 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/tools/perf/Documentation/perf-evlist.txt 
b/tools/perf/Documentation/perf-evlist.txt
index c0a66400a960..9af8b8dfb7b6 100644
--- a/tools/perf/Documentation/perf-evlist.txt
+++ b/tools/perf/Documentation/perf-evlist.txt
@@ -29,7 +29,7 @@ OPTIONS
Show just the sample frequency used for each event.
 
 -v::
---verbose=::
+--verbose::
Show all fields.
 
 -g::
diff --git a/tools/perf/Documentation/perf-ftrace.txt 
b/tools/perf/Documentation/perf-ftrace.txt
index 1e91121bac0f..6e82b7cc0bf0 100644
--- a/tools/perf/Documentation/perf-ftrace.txt
+++ b/tools/perf/Documentation/perf-ftrace.txt
@@ -28,8 +28,8 @@ OPTIONS
specified: function_graph or function.
 
 -v::
---verbose=::
-Verbosity level.
+--verbose::
+Increase the verbosity level.
 
 -F::
 --funcs::
diff --git a/tools/perf/Documentation/perf-kallsyms.txt 
b/tools/perf/Documentation/perf-kallsyms.txt
index f3c620951f6e..c97527df8ecd 100644
--- a/tools/perf/Documentation/perf-kallsyms.txt
+++ b/tools/perf/Documentation/perf-kallsyms.txt
@@ -20,5 +20,5 @@ modules).
 OPTIONS
 ---
 -v::
---verbose=::
+--verbose::
Increase verbosity level, showing details about symbol table loading, 
etc.
diff --git a/tools/perf/Documentation/perf-trace.txt 
b/tools/perf/Documentation/perf-trace.txt
index abc9b5d83312..f0da8cf63e9a 100644
--- a/tools/perf/Documentation/perf-trace.txt
+++ b/tools/perf/Documentation/perf-trace.txt
@@ -97,8 +97,8 @@ filter out the startup phase of the program, which is often 
very different.
Filter out events for these pids and for 'trace' itself (comma 
separated list).
 
 -v::
---verbose=::
-Verbosity level.
+--verbose::
+Increase the verbosity level.
 
 --no-inherit::
Child tasks do not inherit counters.
-- 
2.30.1.766.gb4fecdf3b7-goog



Re: [PATCH v2] perf docs: Add man pages to see also

2021-02-22 Thread Ian Rogers
On Thu, Nov 19, 2020 at 10:30 PM Ian Rogers  wrote:
>
> Add all other man pages to the "see also" list except for
> perf-script-perl and perf-script-python that are linked to from
> perf-script.
>
> v2. Fix accidentally listing perf-top twice.
>
> Signed-off-by: Ian Rogers 

Ping. I think this might have gotten lost.

Thanks,
Ian

> ---
>  tools/perf/Documentation/perf.txt | 12 
>  1 file changed, 12 insertions(+)
>
> diff --git a/tools/perf/Documentation/perf.txt 
> b/tools/perf/Documentation/perf.txt
> index c130a3c46a90..9c330cdfa973 100644
> --- a/tools/perf/Documentation/perf.txt
> +++ b/tools/perf/Documentation/perf.txt
> @@ -76,3 +76,15 @@ SEE ALSO
>  linkperf:perf-stat[1], linkperf:perf-top[1],
>  linkperf:perf-record[1], linkperf:perf-report[1],
>  linkperf:perf-list[1]
> +
> +linkperf:perf-annotate[1],linkperf:perf-archive[1],
> +linkperf:perf-bench[1], linkperf:perf-buildid-cache[1],
> +linkperf:perf-buildid-list[1], linkperf:perf-c2c[1],
> +linkperf:perf-config[1], linkperf:perf-data[1], linkperf:perf-diff[1],
> +linkperf:perf-evlist[1], linkperf:perf-ftrace[1],
> +linkperf:perf-help[1], linkperf:perf-inject[1],
> +linkperf:perf-intel-pt[1], linkperf:perf-kallsyms[1],
> +linkperf:perf-kmem[1], linkperf:perf-kvm[1], linkperf:perf-lock[1],
> +linkperf:perf-mem[1], linkperf:perf-probe[1], linkperf:perf-sched[1],
> +linkperf:perf-script[1], linkperf:perf-test[1],
> +linkperf:perf-trace[1], linkperf:perf-version[1]
> --
> 2.29.2.454.gaff20da3a2-goog
>


[PATCH] perf libperf: Remove unused xyarray.c

2021-02-11 Thread Ian Rogers
Migrated to libperf in:
commit 4b247fa7314c ("libperf: Adopt xyarray class from perf")

Signed-off-by: Ian Rogers 
---
 tools/perf/util/xyarray.c | 33 -
 1 file changed, 33 deletions(-)
 delete mode 100644 tools/perf/util/xyarray.c

diff --git a/tools/perf/util/xyarray.c b/tools/perf/util/xyarray.c
deleted file mode 100644
index 86889ebc3514..
--- a/tools/perf/util/xyarray.c
+++ /dev/null
@@ -1,33 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include "xyarray.h"
-#include 
-#include 
-#include 
-
-struct xyarray *xyarray__new(int xlen, int ylen, size_t entry_size)
-{
-   size_t row_size = ylen * entry_size;
-   struct xyarray *xy = zalloc(sizeof(*xy) + xlen * row_size);
-
-   if (xy != NULL) {
-   xy->entry_size = entry_size;
-   xy->row_size   = row_size;
-   xy->entries= xlen * ylen;
-   xy->max_x  = xlen;
-   xy->max_y  = ylen;
-   }
-
-   return xy;
-}
-
-void xyarray__reset(struct xyarray *xy)
-{
-   size_t n = xy->entries * xy->entry_size;
-
-   memset(xy->contents, 0, n);
-}
-
-void xyarray__delete(struct xyarray *xy)
-{
-   free(xy);
-}
-- 
2.30.0.478.g8a0d178c01-goog



[PATCH] perf env: Remove unneeded internal/cpumap inclusions

2021-02-11 Thread Ian Rogers
Minor cleanup.

Signed-off-by: Ian Rogers 
---
 tools/perf/bench/epoll-ctl.c   | 1 -
 tools/perf/bench/epoll-wait.c  | 1 -
 tools/perf/bench/futex-hash.c  | 1 -
 tools/perf/bench/futex-lock-pi.c   | 1 -
 tools/perf/bench/futex-requeue.c   | 1 -
 tools/perf/bench/futex-wake-parallel.c | 1 -
 tools/perf/bench/futex-wake.c  | 1 -
 tools/perf/tests/openat-syscall-all-cpus.c | 1 -
 tools/perf/util/synthetic-events.c | 1 -
 9 files changed, 9 deletions(-)

diff --git a/tools/perf/bench/epoll-ctl.c b/tools/perf/bench/epoll-ctl.c
index ca2d591aad8a..ddaca75c3bc0 100644
--- a/tools/perf/bench/epoll-ctl.c
+++ b/tools/perf/bench/epoll-ctl.c
@@ -21,7 +21,6 @@
 #include 
 #include 
 #include 
-#include 
 #include 
 
 #include "../util/stat.h"
diff --git a/tools/perf/bench/epoll-wait.c b/tools/perf/bench/epoll-wait.c
index 75dca9773186..0a0ff1247c83 100644
--- a/tools/perf/bench/epoll-wait.c
+++ b/tools/perf/bench/epoll-wait.c
@@ -76,7 +76,6 @@
 #include 
 #include 
 #include 
-#include 
 #include 
 
 #include "../util/stat.h"
diff --git a/tools/perf/bench/futex-hash.c b/tools/perf/bench/futex-hash.c
index 915bf3da7ce2..b65373ce5c4f 100644
--- a/tools/perf/bench/futex-hash.c
+++ b/tools/perf/bench/futex-hash.c
@@ -20,7 +20,6 @@
 #include 
 #include 
 #include 
-#include 
 #include 
 
 #include "../util/stat.h"
diff --git a/tools/perf/bench/futex-lock-pi.c b/tools/perf/bench/futex-lock-pi.c
index bb25d8beb3b8..89c6d160379c 100644
--- a/tools/perf/bench/futex-lock-pi.c
+++ b/tools/perf/bench/futex-lock-pi.c
@@ -14,7 +14,6 @@
 #include 
 #include 
 #include 
-#include 
 #include 
 #include "bench.h"
 #include "futex.h"
diff --git a/tools/perf/bench/futex-requeue.c b/tools/perf/bench/futex-requeue.c
index 7a15c2e61022..5fa23295ee5f 100644
--- a/tools/perf/bench/futex-requeue.c
+++ b/tools/perf/bench/futex-requeue.c
@@ -20,7 +20,6 @@
 #include 
 #include 
 #include 
-#include 
 #include 
 #include "bench.h"
 #include "futex.h"
diff --git a/tools/perf/bench/futex-wake-parallel.c 
b/tools/perf/bench/futex-wake-parallel.c
index cd2b81a845ac..6e6f5247e1fe 100644
--- a/tools/perf/bench/futex-wake-parallel.c
+++ b/tools/perf/bench/futex-wake-parallel.c
@@ -29,7 +29,6 @@ int bench_futex_wake_parallel(int argc __maybe_unused, const 
char **argv __maybe
 #include 
 #include 
 #include "futex.h"
-#include 
 #include 
 
 #include 
diff --git a/tools/perf/bench/futex-wake.c b/tools/perf/bench/futex-wake.c
index 2dfcef3e371e..6d217868f53c 100644
--- a/tools/perf/bench/futex-wake.c
+++ b/tools/perf/bench/futex-wake.c
@@ -20,7 +20,6 @@
 #include 
 #include 
 #include 
-#include 
 #include 
 #include "bench.h"
 #include "futex.h"
diff --git a/tools/perf/tests/openat-syscall-all-cpus.c 
b/tools/perf/tests/openat-syscall-all-cpus.c
index 71f85e2cc127..f7dd6c463f04 100644
--- a/tools/perf/tests/openat-syscall-all-cpus.c
+++ b/tools/perf/tests/openat-syscall-all-cpus.c
@@ -15,7 +15,6 @@
 #include "tests.h"
 #include "thread_map.h"
 #include 
-#include 
 #include "debug.h"
 #include "stat.h"
 #include "util/counts.h"
diff --git a/tools/perf/util/synthetic-events.c 
b/tools/perf/util/synthetic-events.c
index c6f9db3faf83..0b767233ae1f 100644
--- a/tools/perf/util/synthetic-events.c
+++ b/tools/perf/util/synthetic-events.c
@@ -24,7 +24,6 @@
 #include 
 #include 
 #include 
-#include 
 #include 
 #include  // page_size
 #include 
-- 
2.30.0.478.g8a0d178c01-goog



Re: [PATCH 2/2] perf parse-events: Break out tracepoint and printing.

2021-02-03 Thread Ian Rogers
On Wed, Feb 3, 2021 at 4:58 PM Arnaldo Carvalho de Melo  wrote:
>
> Em Wed, Feb 03, 2021 at 05:50:24PM -0300, Arnaldo Carvalho de Melo escreveu:
> > Em Wed, Feb 03, 2021 at 10:38:41AM -0800, Ian Rogers escreveu:
> > > On Wed, Feb 3, 2021 at 8:21 AM Arnaldo Carvalho de Melo 
> > > wrote:
> > >
> > > > Em Tue, Feb 02, 2021 at 09:26:59PM -0800, Ian Rogers escreveu:
> > > > > Move print_*_events functions out of parse-events.c into a new
> > > > > print-events.c. Move tracepoint code into tracepoint.c or
> > > > > trace-event-info.c (sole user). This reduces the dependencies of
> > > > > parse-events.c and makes it more amenable to being a library in the
> > > > > future.
> > > > > Remove some unnecessary definitions from parse-events.h. Fix a
> > > > > checkpatch.pl warning on using unsigned rather than unsigned int.
> > > >
> > > > Thanks, applied.
> > > >
> > >
> > > Wow, cool :-) I wasn't sure about the macros in the headers. Are you
> > > staging these patches before pushing to your perf/core or using
> > > tmp.perf/core ? I know you mentioned a different approach. When I look at:
> > >
> > > https://git.kernel.org/pub/scm/linux/kernel/git/acme/linux.git/
> > >
> > > I don't see anything newer than 9 days.
> >
> > Right, I'm collecting some more patches, will do build tests and push,
> > till then, lemme push tmp.perf/core with what I have.
>
> Strange, some intermediary versions of Alpine Linux are complaining, I'll 
> continue tomorrow morning :-\
>
> [perfbuilder@five ~]$ time dm
> Wed Feb  3 09:32:47 PM -03 2021
> # export PERF_TARBALL=http://192.168.86.5/perf/perf-5.11.0-rc6.tar.xz
> # dm
>172.90 alpine:3.4: Ok   gcc (Alpine 5.3.0) 5.3.0, 
> clang version 3.8.0 (tags/RELEASE_380/final)
>274.62 alpine:3.5: Ok   gcc (Alpine 6.2.1) 6.2.1 
> 20160822, clang version 3.8.1 (tags/RELEASE_381/final)
>376.69 alpine:3.6: Ok   gcc (Alpine 6.3.0) 6.3.0, 
> clang version 4.0.0 (tags/RELEASE_400/final)
>484.43 alpine:3.7: Ok   gcc (Alpine 6.4.0) 6.4.0, 
> Alpine clang version 5.0.0 (tags/RELEASE_500/final) (based on LLVM 5.0.0)
>584.17 alpine:3.8: Ok   gcc (Alpine 6.4.0) 6.4.0, 
> Alpine clang version 5.0.1 (tags/RELEASE_501/final) (based on LLVM 5.0.1)
>616.71 alpine:3.9: FAIL gcc (Alpine 8.3.0) 8.3.0, 
> Alpine clang version 5.0.1 (tags/RELEASE_502/final) (based on LLVM 5.0.1)
>  from util/print-events.c:10:
> /usr/include/linux/swab.h:161:8: error: unknown type name 
> '__always_inline'
>  static __always_inline __u16 __swab16p(const __u16 *p)
> ^~~
> /usr/include/linux/swab.h:161:30: error: expected '=', ',', ';', 'asm' or 
> '__attribute__' before '__swab16p'
>  static __always_inline __u16 __swab16p(const __u16 *p)
>   ^
> /usr/include/linux/swab.h:174:8: error: unknown type name 
> '__always_inline'
>  static __always_inline __u32 __swab32p(const __u32 *p)
> ^~~
> /usr/include/linux/swab.h:174:30: error: expected '=', ',', ';', 'asm' or 
> '__attribute__' before '__swab32p'
>  static __always_inline __u32 __swab32p(const __u32 *p)
>   ^
> /usr/include/linux/swab.h:187:8: error: unknown type name 
> '__always_inline'
>  static __always_inline __u64 __swab64p(const __u64 *p)
> ^~~
> /usr/include/linux/swab.h:187:30: error: expected '=', ',', ';', 'asm' or 
> '__attribute__' before '__swab64p'
>  static __always_inline __u64 __swab64p(const __u64 *p)
>   ^
> /usr/include/linux/swab.h:242:23: error: expected ';' before 'void'
>  static __always_inline void __swab32s(__u32 *p)
>^
>;
> /usr/include/linux/swab.h:255:23: error: expected ';' before 'void'
>  static __always_inline void __swab64s(__u64 *p)
>^
>;
> In file included from /usr/include/asm/byteorder.h:5,
>  from /git/linux/tools/include/uapi/linux/perf_event.h:20,
>  from util/print-events.c:10:
> /usr/include/linux/byteorder/little_endian.h:44:8: error: unknown type 
> name '__always_inline'
>  static __always_inline __le64 __cpu_to_le64p(const __u64 *p)
> ^~~
> /

Re: [PATCH 2/2] perf parse-events: Break out tracepoint and printing.

2021-02-03 Thread Ian Rogers
On Wed, Feb 3, 2021 at 8:21 AM Arnaldo Carvalho de Melo  wrote:
>
> Em Tue, Feb 02, 2021 at 09:26:59PM -0800, Ian Rogers escreveu:
> > Move print_*_events functions out of parse-events.c into a new
> > print-events.c. Move tracepoint code into tracepoint.c or
> > trace-event-info.c (sole user). This reduces the dependencies of
> > parse-events.c and makes it more amenable to being a library in the
> > future.
> > Remove some unnecessary definitions from parse-events.h. Fix a
> > checkpatch.pl warning on using unsigned rather than unsigned int.
>
> Thanks, applied.

(Re-send enabling plain text mode)

Wow, cool :-) I wasn't sure about the macros in the headers. Are you
staging these patches before pushing to your perf/core or using
tmp.perf/core ? I know you mentioned a different approach. When I look
at:

https://git.kernel.org/pub/scm/linux/kernel/git/acme/linux.git/

I don't see anything newer than 9 days.

Thanks,
Ian

>
> - Arnaldo
>
>
> > Signed-off-by: Ian Rogers 
> > ---
> >  tools/perf/builtin-list.c  |   2 +-
> >  tools/perf/builtin-lock.c  |   1 +
> >  tools/perf/builtin-timechart.c |   1 +
> >  tools/perf/builtin-trace.c |   1 +
> >  tools/perf/util/Build  |   2 +
> >  tools/perf/util/parse-events.c | 620 +
> >  tools/perf/util/parse-events.h |  29 --
> >  tools/perf/util/print-events.c | 472 ++
> >  tools/perf/util/print-events.h |  21 +
> >  tools/perf/util/trace-event-info.c |  94 +
> >  tools/perf/util/tracepoint.c   |  63 +++
> >  tools/perf/util/tracepoint.h   |  25 ++
> >  12 files changed, 687 insertions(+), 644 deletions(-)
> >  create mode 100644 tools/perf/util/print-events.c
> >  create mode 100644 tools/perf/util/print-events.h
> >  create mode 100644 tools/perf/util/tracepoint.c
> >  create mode 100644 tools/perf/util/tracepoint.h
> >
> > diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c
> > index 10ab5e40a34f..91327a321c36 100644
> > --- a/tools/perf/builtin-list.c
> > +++ b/tools/perf/builtin-list.c
> > @@ -10,7 +10,7 @@
> >   */
> >  #include "builtin.h"
> >
> > -#include "util/parse-events.h"
> > +#include "util/print-events.h"
> >  #include "util/pmu.h"
> >  #include "util/debug.h"
> >  #include "util/metricgroup.h"
> > diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c
> > index a2f1e53f37a7..486123cb106e 100644
> > --- a/tools/perf/builtin-lock.c
> > +++ b/tools/perf/builtin-lock.c
> > @@ -13,6 +13,7 @@
> >  #include 
> >  #include 
> >  #include "util/trace-event.h"
> > +#include "util/tracepoint.h"
> >
> >  #include "util/debug.h"
> >  #include "util/session.h"
> > diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
> > index 4e380e7b5230..cdebcf26f408 100644
> > --- a/tools/perf/builtin-timechart.c
> > +++ b/tools/perf/builtin-timechart.c
> > @@ -35,6 +35,7 @@
> >  #include "util/tool.h"
> >  #include "util/data.h"
> >  #include "util/debug.h"
> > +#include "util/tracepoint.h"
> >  #include 
> >
> >  #ifdef LACKS_OPEN_MEMSTREAM_PROTOTYPE
> > diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
> > index 85b6a46e85b6..b3b9fa1c7731 100644
> > --- a/tools/perf/builtin-trace.c
> > +++ b/tools/perf/builtin-trace.c
> > @@ -53,6 +53,7 @@
> >  #include "trace-event.h"
> >  #include "util/parse-events.h"
> >  #include "util/bpf-loader.h"
> > +#include "util/tracepoint.h"
> >  #include "callchain.h"
> >  #include "print_binary.h"
> >  #include "string2.h"
> > diff --git a/tools/perf/util/Build b/tools/perf/util/Build
> > index 188521f34347..c2c9f3f490e8 100644
> > --- a/tools/perf/util/Build
> > +++ b/tools/perf/util/Build
> > @@ -23,6 +23,8 @@ perf-y += llvm-utils.o
> >  perf-y += mmap.o
> >  perf-y += memswap.o
> >  perf-y += parse-events.o
> > +perf-y += print-events.o
> > +perf-y += tracepoint.o
> >  perf-y += perf_regs.o
> >  perf-y += path.o
> >  perf-y += print_binary.o
> > diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
> > index 42c84adeb2fb..5d5ebb700ef2 100644
> > --- a/tools/perf/util/parse-events.c
> > +++ b/tools/perf/util/parse-events.c
> > @@ -5,42 +5,34 @@
> >

[PATCH 2/2] perf parse-events: Break out tracepoint and printing.

2021-02-02 Thread Ian Rogers
Move print_*_events functions out of parse-events.c into a new
print-events.c. Move tracepoint code into tracepoint.c or
trace-event-info.c (sole user). This reduces the dependencies of
parse-events.c and makes it more amenable to being a library in the
future.
Remove some unnecessary definitions from parse-events.h. Fix a
checkpatch.pl warning on using unsigned rather than unsigned int.

Signed-off-by: Ian Rogers 
---
 tools/perf/builtin-list.c  |   2 +-
 tools/perf/builtin-lock.c  |   1 +
 tools/perf/builtin-timechart.c |   1 +
 tools/perf/builtin-trace.c |   1 +
 tools/perf/util/Build  |   2 +
 tools/perf/util/parse-events.c | 620 +
 tools/perf/util/parse-events.h |  29 --
 tools/perf/util/print-events.c | 472 ++
 tools/perf/util/print-events.h |  21 +
 tools/perf/util/trace-event-info.c |  94 +
 tools/perf/util/tracepoint.c   |  63 +++
 tools/perf/util/tracepoint.h   |  25 ++
 12 files changed, 687 insertions(+), 644 deletions(-)
 create mode 100644 tools/perf/util/print-events.c
 create mode 100644 tools/perf/util/print-events.h
 create mode 100644 tools/perf/util/tracepoint.c
 create mode 100644 tools/perf/util/tracepoint.h

diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c
index 10ab5e40a34f..91327a321c36 100644
--- a/tools/perf/builtin-list.c
+++ b/tools/perf/builtin-list.c
@@ -10,7 +10,7 @@
  */
 #include "builtin.h"
 
-#include "util/parse-events.h"
+#include "util/print-events.h"
 #include "util/pmu.h"
 #include "util/debug.h"
 #include "util/metricgroup.h"
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c
index a2f1e53f37a7..486123cb106e 100644
--- a/tools/perf/builtin-lock.c
+++ b/tools/perf/builtin-lock.c
@@ -13,6 +13,7 @@
 #include 
 #include 
 #include "util/trace-event.h"
+#include "util/tracepoint.h"
 
 #include "util/debug.h"
 #include "util/session.h"
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index 4e380e7b5230..cdebcf26f408 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -35,6 +35,7 @@
 #include "util/tool.h"
 #include "util/data.h"
 #include "util/debug.h"
+#include "util/tracepoint.h"
 #include 
 
 #ifdef LACKS_OPEN_MEMSTREAM_PROTOTYPE
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 85b6a46e85b6..b3b9fa1c7731 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -53,6 +53,7 @@
 #include "trace-event.h"
 #include "util/parse-events.h"
 #include "util/bpf-loader.h"
+#include "util/tracepoint.h"
 #include "callchain.h"
 #include "print_binary.h"
 #include "string2.h"
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 188521f34347..c2c9f3f490e8 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -23,6 +23,8 @@ perf-y += llvm-utils.o
 perf-y += mmap.o
 perf-y += memswap.o
 perf-y += parse-events.o
+perf-y += print-events.o
+perf-y += tracepoint.o
 perf-y += perf_regs.o
 perf-y += path.o
 perf-y += print_binary.o
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 42c84adeb2fb..5d5ebb700ef2 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -5,42 +5,34 @@
 #include 
 #include 
 #include 
-#include 
-#include 
-#include 
 #include 
 #include "term.h"
-#include "build-id.h"
 #include "evlist.h"
 #include "evsel.h"
-#include 
 #include 
 #include "parse-events.h"
-#include 
 #include "string2.h"
-#include "strlist.h"
-#include "symbol.h"
-#include "header.h"
 #include "bpf-loader.h"
 #include "debug.h"
 #include 
 #include 
 #include "parse-events-bison.h"
-#define YY_EXTRA_TYPE void*
 #include "parse-events-flex.h"
 #include "pmu.h"
-#include "thread_map.h"
-#include "probe-file.h"
 #include "asm/bug.h"
 #include "util/parse-branch-options.h"
-#include "metricgroup.h"
 #include "util/evsel_config.h"
 #include "util/event.h"
-#include "util/pfm.h"
 #include "perf.h"
+#include "tracepoint.h"
 
 #define MAX_NAME_LEN 100
 
+struct perf_pmu_event_symbol {
+   char*symbol;
+   enum perf_pmu_event_symbol_type type;
+};
+
 #ifdef PARSER_DEBUG
 extern int parse_events_debug;
 #endif
@@ -155,35 +147,6 @@ struct event_symbol event_symbols_sw[PERF_COUNT_SW_MAX] = {
 #define PERF_EVENT_TYPE(config)__PERF_EVENT_FIELD(config, TYPE)
 #define PERF_EVENT_ID(config)  __PERF_EVENT_FIELD(config, EVENT)
 
-#define for_each_subsystem(sys_dir, sys_dirent) 

[PATCH 1/2] perf trace-event-info: Rename for_each_event.

2021-02-02 Thread Ian Rogers
Avoid a naming conflict with for_each_event with similar code in
parse-events.c, rename to for_each_event_tps.

Signed-off-by: Ian Rogers 
---
 tools/perf/util/trace-event-info.c | 10 +-
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/tools/perf/util/trace-event-info.c 
b/tools/perf/util/trace-event-info.c
index 0e5c4786f296..a65f65d0857e 100644
--- a/tools/perf/util/trace-event-info.c
+++ b/tools/perf/util/trace-event-info.c
@@ -152,7 +152,7 @@ static bool name_in_tp_list(char *sys, struct 
tracepoint_path *tps)
return false;
 }
 
-#define for_each_event(dir, dent, tps) \
+#define for_each_event_tps(dir, dent, tps) \
while ((dent = readdir(dir)))   \
if (dent->d_type == DT_DIR &&   \
(strcmp(dent->d_name, ".")) &&  \
@@ -174,7 +174,7 @@ static int copy_event_system(const char *sys, struct 
tracepoint_path *tps)
return -errno;
}
 
-   for_each_event(dir, dent, tps) {
+   for_each_event_tps(dir, dent, tps) {
if (!name_in_tp_list(dent->d_name, tps))
continue;
 
@@ -196,7 +196,7 @@ static int copy_event_system(const char *sys, struct 
tracepoint_path *tps)
}
 
rewinddir(dir);
-   for_each_event(dir, dent, tps) {
+   for_each_event_tps(dir, dent, tps) {
if (!name_in_tp_list(dent->d_name, tps))
continue;
 
@@ -274,7 +274,7 @@ static int record_event_files(struct tracepoint_path *tps)
goto out;
}
 
-   for_each_event(dir, dent, tps) {
+   for_each_event_tps(dir, dent, tps) {
if (strcmp(dent->d_name, "ftrace") == 0 ||
!system_in_tp_list(dent->d_name, tps))
continue;
@@ -289,7 +289,7 @@ static int record_event_files(struct tracepoint_path *tps)
}
 
rewinddir(dir);
-   for_each_event(dir, dent, tps) {
+   for_each_event_tps(dir, dent, tps) {
if (strcmp(dent->d_name, "ftrace") == 0 ||
!system_in_tp_list(dent->d_name, tps))
continue;
-- 
2.30.0.365.g02bc693789-goog



[PATCH] perf parse-events: Remove unnecessary #includes

2021-01-27 Thread Ian Rogers
Minor cleanup motivated by trying to separately fuzz test parse-events.

Signed-off-by: Ian Rogers 
---
 tools/perf/util/parse-events.c | 2 --
 tools/perf/util/parse-events.l | 1 -
 2 files changed, 3 deletions(-)

diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 42c84adeb2fb..8982b3c8014c 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -19,8 +19,6 @@
 #include 
 #include "string2.h"
 #include "strlist.h"
-#include "symbol.h"
-#include "header.h"
 #include "bpf-loader.h"
 #include "debug.h"
 #include 
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l
index 9db5097317f4..03d2ac04edb3 100644
--- a/tools/perf/util/parse-events.l
+++ b/tools/perf/util/parse-events.l
@@ -12,7 +12,6 @@
 #include 
 #include 
 #include 
-#include "../perf.h"
 #include "parse-events.h"
 #include "parse-events-bison.h"
 #include "evsel.h"
-- 
2.30.0.280.ga3ce27912f-goog



[PATCH] libperf tests: Avoid uninitialized variable warning.

2021-01-14 Thread Ian Rogers
The variable bf is read (for a write call) without being initialized
triggering a memory sanitizer warning. Use bf in the read and switch the
write to reading from a string.

Signed-off-by: Ian Rogers 
---
 tools/lib/perf/tests/test-evlist.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/tools/lib/perf/tests/test-evlist.c 
b/tools/lib/perf/tests/test-evlist.c
index 6d8ebe0c2504..1b225fe34a72 100644
--- a/tools/lib/perf/tests/test-evlist.c
+++ b/tools/lib/perf/tests/test-evlist.c
@@ -208,7 +208,6 @@ static int test_mmap_thread(void)
char path[PATH_MAX];
int id, err, pid, go_pipe[2];
union perf_event *event;
-   char bf;
int count = 0;
 
snprintf(path, PATH_MAX, 
"%s/kernel/debug/tracing/events/syscalls/sys_enter_prctl/id",
@@ -229,6 +228,7 @@ static int test_mmap_thread(void)
pid = fork();
if (!pid) {
int i;
+   char bf;
 
read(go_pipe[0], , 1);
 
@@ -266,7 +266,7 @@ static int test_mmap_thread(void)
perf_evlist__enable(evlist);
 
/* kick the child and wait for it to finish */
-   write(go_pipe[1], , 1);
+   write(go_pipe[1], "A", 1);
waitpid(pid, NULL, 0);
 
/*
-- 
2.30.0.296.g2bfb1c46d8-goog



[PATCH 2/2] libperf tests: Fail when failing to get a tracepoint id

2021-01-14 Thread Ian Rogers
Permissions are necessary to get a tracepoint id. Fail the test when the
read fails.

Signed-off-by: Ian Rogers 
---
 tools/lib/perf/tests/test-evlist.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tools/lib/perf/tests/test-evlist.c 
b/tools/lib/perf/tests/test-evlist.c
index d913241d4135..bd19cabddaf6 100644
--- a/tools/lib/perf/tests/test-evlist.c
+++ b/tools/lib/perf/tests/test-evlist.c
@@ -215,6 +215,7 @@ static int test_mmap_thread(void)
 sysfs__mountpoint());
 
if (filename__read_int(path, )) {
+   tests_failed++;
fprintf(stderr, "error: failed to get tracepoint id: %s\n", 
path);
return -1;
}
-- 
2.30.0.284.gd98b1dd5eaa7-goog



[PATCH 1/2] libperf tests: If a test fails return non-zero

2021-01-14 Thread Ian Rogers
If a test fails return -1 rather than 0. This is consistent with the
return value in test-cpumap.c

Signed-off-by: Ian Rogers 
---
 tools/lib/perf/tests/test-cpumap.c| 2 +-
 tools/lib/perf/tests/test-evlist.c| 2 +-
 tools/lib/perf/tests/test-evsel.c | 2 +-
 tools/lib/perf/tests/test-threadmap.c | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/tools/lib/perf/tests/test-cpumap.c 
b/tools/lib/perf/tests/test-cpumap.c
index c8d45091e7c2..c70e9e03af3e 100644
--- a/tools/lib/perf/tests/test-cpumap.c
+++ b/tools/lib/perf/tests/test-cpumap.c
@@ -27,5 +27,5 @@ int main(int argc, char **argv)
perf_cpu_map__put(cpus);
 
__T_END;
-   return 0;
+   return tests_failed == 0 ? 0 : -1;
 }
diff --git a/tools/lib/perf/tests/test-evlist.c 
b/tools/lib/perf/tests/test-evlist.c
index 6d8ebe0c2504..d913241d4135 100644
--- a/tools/lib/perf/tests/test-evlist.c
+++ b/tools/lib/perf/tests/test-evlist.c
@@ -409,5 +409,5 @@ int main(int argc, char **argv)
test_mmap_cpus();
 
__T_END;
-   return 0;
+   return tests_failed == 0 ? 0 : -1;
 }
diff --git a/tools/lib/perf/tests/test-evsel.c 
b/tools/lib/perf/tests/test-evsel.c
index 135722ac965b..0ad82d7a2a51 100644
--- a/tools/lib/perf/tests/test-evsel.c
+++ b/tools/lib/perf/tests/test-evsel.c
@@ -131,5 +131,5 @@ int main(int argc, char **argv)
test_stat_thread_enable();
 
__T_END;
-   return 0;
+   return tests_failed == 0 ? 0 : -1;
 }
diff --git a/tools/lib/perf/tests/test-threadmap.c 
b/tools/lib/perf/tests/test-threadmap.c
index 7dc4d6fbedde..384471441b48 100644
--- a/tools/lib/perf/tests/test-threadmap.c
+++ b/tools/lib/perf/tests/test-threadmap.c
@@ -27,5 +27,5 @@ int main(int argc, char **argv)
perf_thread_map__put(threads);
 
__T_END;
-   return 0;
+   return tests_failed == 0 ? 0 : -1;
 }
-- 
2.30.0.284.gd98b1dd5eaa7-goog



[PATCH 2/2] tools/bpftool: Add -Wall when building BPF programs

2021-01-13 Thread Ian Rogers
No additional warnings are generated by enabling this, but having it
enabled will help avoid regressions.

Signed-off-by: Ian Rogers 
---
 tools/bpf/bpftool/Makefile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile
index f897cb5fb12d..45ac2f9e0aa9 100644
--- a/tools/bpf/bpftool/Makefile
+++ b/tools/bpf/bpftool/Makefile
@@ -166,7 +166,7 @@ $(OUTPUT)%.bpf.o: skeleton/%.bpf.c $(OUTPUT)vmlinux.h 
$(LIBBPF)
-I$(srctree)/tools/include/uapi/ \
-I$(LIBBPF_PATH) \
-I$(srctree)/tools/lib \
-   -g -O2 -target bpf -c $< -o $@ && $(LLVM_STRIP) -g $@
+   -g -O2 -Wall -target bpf -c $< -o $@ && $(LLVM_STRIP) -g $@
 
 $(OUTPUT)%.skel.h: $(OUTPUT)%.bpf.o $(BPFTOOL_BOOTSTRAP)
$(QUIET_GEN)$(BPFTOOL_BOOTSTRAP) gen skeleton $< > $@
-- 
2.30.0.284.gd98b1dd5eaa7-goog



[PATCH 1/2] bpf, libbpf: Avoid unused function warning on bpf_tail_call_static

2021-01-13 Thread Ian Rogers
Add inline to __always_inline making it match the linux/compiler.h.
Adding this avoids an unused function warning on bpf_tail_call_static
when compining with -Wall.

Signed-off-by: Ian Rogers 
---
 tools/lib/bpf/bpf_helpers.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/lib/bpf/bpf_helpers.h b/tools/lib/bpf/bpf_helpers.h
index 72b251110c4d..ae6c975e0b87 100644
--- a/tools/lib/bpf/bpf_helpers.h
+++ b/tools/lib/bpf/bpf_helpers.h
@@ -30,7 +30,7 @@
 #define SEC(NAME) __attribute__((section(NAME), used))
 
 #ifndef __always_inline
-#define __always_inline __attribute__((always_inline))
+#define __always_inline inline __attribute__((always_inline))
 #endif
 #ifndef __noinline
 #define __noinline __attribute__((noinline))
-- 
2.30.0.284.gd98b1dd5eaa7-goog



[PATCH v7 5/5] perf metric: Don't compute unused events.

2021-01-12 Thread Ian Rogers
For a metric like:
  EVENT1 if #smt_on else EVENT2

currently EVENT1 and EVENT2 will be measured and then when the metric is
reported EVENT1 or EVENT2 will be printed depending on the value from
smt_on() during the expr parsing. Computing both events is unnecessary and
can lead to multiplexing as discussed in this thread:
https://lore.kernel.org/lkml/20201110100346.2527031-1-irog...@google.com/

This change modifies the expression parsing code by:
 - getting rid of the "other" parsing and introducing a boolean argument
   to say whether ids should be computed or not.
 - expressions are changed so that a pair of value and ids are returned.
 - when computing the metric value the ids are unused.
 - when computing the ids, constant values and smt_on are assigned to
   the value.
 - If the value is from an event ID then the event is added to the ids
   hashmap and the value set to bottom (encoded as NAN).
 - Typically operators union IDs for their inputs and set the value to
   bottom, however, if the inputs are constant then these are computed and
   propagated as the value.
 - If the input is constant to certain operators like:
 IDS1 if CONST else IDS2
   then the result will be either IDS1 or IDS2 depending on CONST (which
   may be evaluated from an entire expression), and so IDS1 or IDS2 may
   be discarded avoiding events from being programmed.
 - The ids at the end of parsing are added to the context.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/expr.c |  17 ++
 tools/perf/util/expr.c  |   9 +-
 tools/perf/util/expr.h  |   1 -
 tools/perf/util/expr.l  |   9 --
 tools/perf/util/expr.y  | 340 ++--
 5 files changed, 282 insertions(+), 94 deletions(-)

diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
index 1c881bea7fca..5cab5960b257 100644
--- a/tools/perf/tests/expr.c
+++ b/tools/perf/tests/expr.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 #include "util/debug.h"
 #include "util/expr.h"
+#include "util/smt.h"
 #include "tests.h"
 #include 
 #include 
@@ -132,6 +133,22 @@ int test__expr(struct test *t __maybe_unused, int subtest 
__maybe_unused)
TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "EVENT2,param=3/",
(void **)_ptr));
 
+   /* Only EVENT1 or EVENT2 need be measured depending on the value of 
smt_on. */
+   expr__ctx_clear(ctx);
+   TEST_ASSERT_VAL("find ids",
+   expr__find_ids("EVENT1 if #smt_on else EVENT2",
+   NULL, ctx, 0) == 0);
+   TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 1);
+   TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids,
+ smt_on() ? "EVENT1" : 
"EVENT2",
+ (void **)_ptr));
+
+   /* The expression is a constant 1.0 without needing to evaluate EVENT1. 
*/
+   expr__ctx_clear(ctx);
+   TEST_ASSERT_VAL("find ids",
+   expr__find_ids("1.0 if EVENT1 > 100.0 else 1.0",
+   NULL, ctx, 0) == 0);
+   TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 0);
expr__ctx_free(ctx);
 
return 0;
diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
index 34b51ca5e87f..e9396a309fb7 100644
--- a/tools/perf/util/expr.c
+++ b/tools/perf/util/expr.c
@@ -330,10 +330,9 @@ void expr__ctx_free(struct expr_parse_ctx *ctx)
 
 static int
 __expr__parse(double *val, struct expr_parse_ctx *ctx, const char *expr,
- int start, int runtime)
+ bool compute_ids, int runtime)
 {
struct expr_scanner_ctx scanner_ctx = {
-   .start_token = start,
.runtime = runtime,
};
YY_BUFFER_STATE buffer;
@@ -353,7 +352,7 @@ __expr__parse(double *val, struct expr_parse_ctx *ctx, 
const char *expr,
expr_set_debug(1, scanner);
 #endif
 
-   ret = expr_parse(val, ctx, scanner);
+   ret = expr_parse(val, ctx, compute_ids, scanner);
 
expr__flush_buffer(buffer, scanner);
expr__delete_buffer(buffer, scanner);
@@ -364,13 +363,13 @@ __expr__parse(double *val, struct expr_parse_ctx *ctx, 
const char *expr,
 int expr__parse(double *final_val, struct expr_parse_ctx *ctx,
const char *expr, int runtime)
 {
-   return __expr__parse(final_val, ctx, expr, EXPR_PARSE, runtime) ? -1 : 
0;
+   return __expr__parse(final_val, ctx, expr, /*compute_ids=*/false, 
runtime) ? -1 : 0;
 }
 
 int expr__find_ids(const char *expr, const char *one,
   struct expr_parse_ctx *ctx, int runtime)
 {
-   int ret = __expr__parse(NULL, ctx, expr, EXPR_OTHER, runtime);
+   int ret = __expr__parse(NULL, ctx, expr, /*compute_ids=*/true, runtime);
 

[PATCH v7 3/5] perf metric: Rename expr__find_other.

2021-01-12 Thread Ian Rogers
A later change will remove the notion of other, rename the function to
expr__find_ids as this is what it populates.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/expr.c   | 26 +-
 tools/perf/tests/pmu-events.c |  9 -
 tools/perf/util/expr.c|  4 ++--
 tools/perf/util/expr.h|  2 +-
 tools/perf/util/metricgroup.c |  2 +-
 tools/perf/util/stat-shadow.c |  6 +++---
 6 files changed, 24 insertions(+), 25 deletions(-)

diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
index b0a3b5fd0c00..7ccb97c73347 100644
--- a/tools/perf/tests/expr.c
+++ b/tools/perf/tests/expr.c
@@ -64,25 +64,25 @@ int test__expr(struct test *t __maybe_unused, int subtest 
__maybe_unused)
TEST_ASSERT_VAL("missing operand", ret == -1);
 
expr__ctx_clear(ctx);
-   TEST_ASSERT_VAL("find other",
-   expr__find_other("FOO + BAR + BAZ + BOZO", "FOO",
-ctx, 1) == 0);
-   TEST_ASSERT_VAL("find other", hashmap__size(ctx->ids) == 3);
-   TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "BAR",
+   TEST_ASSERT_VAL("find ids",
+   expr__find_ids("FOO + BAR + BAZ + BOZO", "FOO",
+   ctx, 1) == 0);
+   TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 3);
+   TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "BAR",
(void **)_ptr));
-   TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "BAZ",
+   TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "BAZ",
(void **)_ptr));
-   TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "BOZO",
+   TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "BOZO",
(void **)_ptr));
 
expr__ctx_clear(ctx);
-   TEST_ASSERT_VAL("find other",
-   expr__find_other("EVENT1\\,param\\=?@ + 
EVENT2\\,param\\=?@",
-NULL, ctx, 3) == 0);
-   TEST_ASSERT_VAL("find other", hashmap__size(ctx->ids) == 2);
-   TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "EVENT1,param=3/",
+   TEST_ASSERT_VAL("find ids",
+   expr__find_ids("EVENT1\\,param\\=?@ + 
EVENT2\\,param\\=?@",
+   NULL, ctx, 3) == 0);
+   TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 2);
+   TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "EVENT1,param=3/",
(void **)_ptr));
-   TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "EVENT2,param=3/",
+   TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "EVENT2,param=3/",
(void **)_ptr));
 
expr__ctx_free(ctx);
diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c
index cf5afd126934..d14a42406549 100644
--- a/tools/perf/tests/pmu-events.c
+++ b/tools/perf/tests/pmu-events.c
@@ -502,9 +502,8 @@ static int test_parsing(void)
if (!pe->metric_expr)
continue;
expr__ctx_clear(ctx);
-   if (expr__find_other(pe->metric_expr, NULL, ctx, 0)
- < 0) {
-   expr_failure("Parse other failed", map, pe);
+   if (expr__find_ids(pe->metric_expr, NULL, ctx, 0) < 0) {
+   expr_failure("Parse find ids failed", map, pe);
ret++;
continue;
}
@@ -559,8 +558,8 @@ static int metric_parse_fake(const char *str)
pr_debug("parsing '%s'\n", str);
 
ctx = expr__ctx_new();
-   if (expr__find_other(str, NULL, ctx, 0) < 0) {
-   pr_err("expr__find_other failed\n");
+   if (expr__find_ids(str, NULL, ctx, 0) < 0) {
+   pr_err("expr__find_ids failed\n");
return -1;
}
 
diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
index 7b1c06772a49..adf16bb7571a 100644
--- a/tools/perf/util/expr.c
+++ b/tools/perf/util/expr.c
@@ -288,8 +288,8 @@ int expr__parse(double *final_val, struct expr_parse_ctx 
*ctx,
return __expr__parse(final_val, ctx, expr, EXPR_PARSE, runtime) ? -1 : 
0;
 }
 
-int expr__find_other(const char *expr, const char 

[PATCH v7 4/5] perf metric: Add utilities to work on ids map.

2021-01-12 Thread Ian Rogers
Add utilities to new/free an ids hashmap, as well as to union. Add
testing of the union. Unioning hashmaps will be used when parsing the
metric, if a value is known then the hashmap is unnecessary, otherwise
we need to union together all the event ids to compute their values for
reporting.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/expr.c | 47 ++
 tools/perf/util/expr.c  | 87 +++--
 tools/perf/util/expr.h  | 13 ++
 3 files changed, 143 insertions(+), 4 deletions(-)

diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
index 7ccb97c73347..1c881bea7fca 100644
--- a/tools/perf/tests/expr.c
+++ b/tools/perf/tests/expr.c
@@ -6,6 +6,51 @@
 #include 
 #include 
 
+static int test_ids_union(void)
+{
+   struct hashmap *ids1, *ids2;
+
+   /* Empty union. */
+   ids1 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids1);
+   ids2 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids2);
+
+   ids1 = ids__union(ids1, ids2);
+   TEST_ASSERT_EQUAL("union", (int)hashmap__size(ids1), 0);
+
+   /* Union {foo, bar} against {}. */
+   ids2 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids2);
+
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids1, strdup("foo"), 
NULL), 0);
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids1, strdup("bar"), 
NULL), 0);
+
+   ids1 = ids__union(ids1, ids2);
+   TEST_ASSERT_EQUAL("union", (int)hashmap__size(ids1), 2);
+
+   /* Union {foo, bar} against {foo}. */
+   ids2 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids2);
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids2, strdup("foo"), 
NULL), 0);
+
+   ids1 = ids__union(ids1, ids2);
+   TEST_ASSERT_EQUAL("union", (int)hashmap__size(ids1), 2);
+
+   /* Union {foo, bar} against {bar,baz}. */
+   ids2 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids2);
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids2, strdup("bar"), 
NULL), 0);
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids2, strdup("baz"), 
NULL), 0);
+
+   ids1 = ids__union(ids1, ids2);
+   TEST_ASSERT_EQUAL("union", (int)hashmap__size(ids1), 3);
+
+   ids__free(ids1);
+
+   return 0;
+}
+
 static int test(struct expr_parse_ctx *ctx, const char *e, double val2)
 {
double val;
@@ -24,6 +69,8 @@ int test__expr(struct test *t __maybe_unused, int subtest 
__maybe_unused)
int ret;
struct expr_parse_ctx *ctx;
 
+   TEST_ASSERT_EQUAL("ids_union", test_ids_union(), 0);
+
ctx = expr__ctx_new();
TEST_ASSERT_VAL("expr__ctx_new", ctx);
expr__add_id_val(ctx, strdup("FOO"), 1);
diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
index adf16bb7571a..34b51ca5e87f 100644
--- a/tools/perf/util/expr.c
+++ b/tools/perf/util/expr.c
@@ -59,8 +59,48 @@ static bool key_equal(const void *key1, const void *key2,
return !strcmp((const char *)key1, (const char *)key2);
 }
 
-/* Caller must make sure id is allocated */
-int expr__add_id(struct expr_parse_ctx *ctx, const char *id)
+struct hashmap *ids__new(void)
+{
+   return hashmap__new(key_hash, key_equal, NULL);
+}
+
+void ids__free(struct hashmap *ids)
+{
+   struct hashmap_entry *cur;
+   size_t bkt;
+
+   if (ids == NULL)
+   return;
+
+#ifdef PARSER_DEBUG
+   fprintf(stderr, "freeing ids: ");
+   ids__print(ids);
+   fprintf(stderr, "\n");
+#endif
+
+   hashmap__for_each_entry(ids, cur, bkt) {
+   free((char *)cur->key);
+   free(cur->value);
+   }
+
+   hashmap__free(ids);
+}
+
+void ids__print(struct hashmap *ids)
+{
+   size_t bkt;
+   struct hashmap_entry *cur;
+
+   if (!ids)
+   return;
+
+   hashmap__for_each_entry(ids, cur, bkt) {
+   fprintf(stderr, "key:%s, ", (const char *)cur->key);
+   }
+}
+
+int ids__insert(struct hashmap *ids, const char *id,
+   struct expr_id *parent)
 {
struct expr_id_data *data_ptr = NULL, *old_data = NULL;
char *old_key = NULL;
@@ -70,10 +110,10 @@ int expr__add_id(struct expr_parse_ctx *ctx, const char 
*id)
if (!data_ptr)
return -ENOMEM;
 
-   data_ptr->parent = ctx->parent;
+   data_ptr->parent = parent;
data_ptr->kind = EXPR_ID_DATA__PARENT;
 
-   ret = hashmap__set(ctx->ids, id, data_ptr,
+   ret = hashmap__set(ids, id, data_ptr,
   (const void **)_key, (void **)_data);
if (ret)
free(data_ptr);
@@ -82,6 +122,45 @@ int expr__add_id(struct expr_parse_ctx *ctx, const char *id)
return ret;
 }
 
+struct hashmap *ids__union(struct hashmap

[PATCH v7 2/5] perf metric: Use NAN for missing event IDs.

2021-01-12 Thread Ian Rogers
If during computing a metric an event (id) is missing the parsing
aborts. A later patch will make it so that events that aren't used in
the output are deliberately omitted, in which case we don't want the
abort. Modify the missing ID case to report NAN for these cases.

Signed-off-by: Ian Rogers 
---
 tools/perf/util/expr.y | 9 -
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/tools/perf/util/expr.y b/tools/perf/util/expr.y
index b2ada8f8309a..41c9cd4efadd 100644
--- a/tools/perf/util/expr.y
+++ b/tools/perf/util/expr.y
@@ -1,6 +1,7 @@
 /* Simple expression parser */
 %{
 #define YYDEBUG 1
+#include 
 #include 
 #include "util.h"
 #include "util/debug.h"
@@ -88,12 +89,10 @@ expr: NUMBER
| ID{
struct expr_id_data *data;
 
-   if (expr__resolve_id(ctx, $1, )) {
-   free($1);
-   YYABORT;
-   }
+   $$ = NAN;
+   if (expr__resolve_id(ctx, $1, ) == 
0)
+   $$ = expr_id_data__value(data);
 
-   $$ = expr_id_data__value(data);
free($1);
}
| expr '|' expr { $$ = (long)$1 | (long)$3; }
-- 
2.30.0.284.gd98b1dd5eaa7-goog



[PATCH v7 1/5] perf metric: Restructure struct expr_parse_ctx.

2021-01-12 Thread Ian Rogers
A later change to parsing the ids out (in expr__find_other) will
potentially drop hashmaps and so it is more convenient to move
expr_parse_ctx to have a hashmap pointer rather than a struct value. As
this pointer must be freed, rather than just going out of scope,
add expr__ctx_new and expr__ctx_free to manage expr_parse_ctx memory.
Adjust use of struct expr_parse_ctx accordingly.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/expr.c   | 81 ++-
 tools/perf/tests/pmu-events.c | 37 +---
 tools/perf/util/expr.c| 39 +
 tools/perf/util/expr.h|  5 ++-
 tools/perf/util/metricgroup.c | 44 ++-
 tools/perf/util/stat-shadow.c | 50 +
 6 files changed, 152 insertions(+), 104 deletions(-)

diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
index 4d01051951cd..b0a3b5fd0c00 100644
--- a/tools/perf/tests/expr.c
+++ b/tools/perf/tests/expr.c
@@ -22,67 +22,70 @@ int test__expr(struct test *t __maybe_unused, int subtest 
__maybe_unused)
const char *p;
double val;
int ret;
-   struct expr_parse_ctx ctx;
+   struct expr_parse_ctx *ctx;
 
-   expr__ctx_init();
-   expr__add_id_val(, strdup("FOO"), 1);
-   expr__add_id_val(, strdup("BAR"), 2);
+   ctx = expr__ctx_new();
+   TEST_ASSERT_VAL("expr__ctx_new", ctx);
+   expr__add_id_val(ctx, strdup("FOO"), 1);
+   expr__add_id_val(ctx, strdup("BAR"), 2);
 
-   ret = test(, "1+1", 2);
-   ret |= test(, "FOO+BAR", 3);
-   ret |= test(, "(BAR/2)%2", 1);
-   ret |= test(, "1 - -4",  5);
-   ret |= test(, "(FOO-1)*2 + (BAR/2)%2 - -4",  5);
-   ret |= test(, "1-1 | 1", 1);
-   ret |= test(, "1-1 & 1", 0);
-   ret |= test(, "min(1,2) + 1", 2);
-   ret |= test(, "max(1,2) + 1", 3);
-   ret |= test(, "1+1 if 3*4 else 0", 2);
-   ret |= test(, "1.1 + 2.1", 3.2);
-   ret |= test(, ".1 + 2.", 2.1);
-   ret |= test(, "d_ratio(1, 2)", 0.5);
-   ret |= test(, "d_ratio(2.5, 0)", 0);
-   ret |= test(, "1.1 < 2.2", 1);
-   ret |= test(, "2.2 > 1.1", 1);
-   ret |= test(, "1.1 < 1.1", 0);
-   ret |= test(, "2.2 > 2.2", 0);
-   ret |= test(, "2.2 < 1.1", 0);
-   ret |= test(, "1.1 > 2.2", 0);
+   ret = test(ctx, "1+1", 2);
+   ret |= test(ctx, "FOO+BAR", 3);
+   ret |= test(ctx, "(BAR/2)%2", 1);
+   ret |= test(ctx, "1 - -4",  5);
+   ret |= test(ctx, "(FOO-1)*2 + (BAR/2)%2 - -4",  5);
+   ret |= test(ctx, "1-1 | 1", 1);
+   ret |= test(ctx, "1-1 & 1", 0);
+   ret |= test(ctx, "min(1,2) + 1", 2);
+   ret |= test(ctx, "max(1,2) + 1", 3);
+   ret |= test(ctx, "1+1 if 3*4 else 0", 2);
+   ret |= test(ctx, "1.1 + 2.1", 3.2);
+   ret |= test(ctx, ".1 + 2.", 2.1);
+   ret |= test(ctx, "d_ratio(1, 2)", 0.5);
+   ret |= test(ctx, "d_ratio(2.5, 0)", 0);
+   ret |= test(ctx, "1.1 < 2.2", 1);
+   ret |= test(ctx, "2.2 > 1.1", 1);
+   ret |= test(ctx, "1.1 < 1.1", 0);
+   ret |= test(ctx, "2.2 > 2.2", 0);
+   ret |= test(ctx, "2.2 < 1.1", 0);
+   ret |= test(ctx, "1.1 > 2.2", 0);
 
-   if (ret)
+   if (ret) {
+   expr__ctx_free(ctx);
return ret;
+   }
 
p = "FOO/0";
-   ret = expr__parse(, , p, 1);
+   ret = expr__parse(, ctx, p, 1);
TEST_ASSERT_VAL("division by zero", ret == -1);
 
p = "BAR/";
-   ret = expr__parse(, , p, 1);
+   ret = expr__parse(, ctx, p, 1);
TEST_ASSERT_VAL("missing operand", ret == -1);
 
-   expr__ctx_clear();
+   expr__ctx_clear(ctx);
TEST_ASSERT_VAL("find other",
expr__find_other("FOO + BAR + BAZ + BOZO", "FOO",
-, 1) == 0);
-   TEST_ASSERT_VAL("find other", hashmap__size() == 3);
-   TEST_ASSERT_VAL("find other", hashmap__find(, "BAR",
+ctx, 1) == 0);
+   TEST_ASSERT_VAL("find other", hashmap__size(ctx->ids) == 3);
+   TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "BAR",
(void **)_ptr));
-   TEST_ASSERT_VAL("find other", hashmap__find(, "BAZ",
+   TEST_ASSERT_VAL("find other", hashmap__find(

[PATCH v7 0/5] Don't compute events that won't be used in a metric.

2021-01-12 Thread Ian Rogers

For a metric like:
  EVENT1 if #smt_on else EVENT2

currently EVENT1 and EVENT2 will be measured and then when the metric
is reported EVENT1 or EVENT2 will be printed depending on the value
from smt_on() during the expr parsing. Computing both events is
unnecessary and can lead to multiplexing as discussed in this thread:
https://lore.kernel.org/lkml/20201110100346.2527031-1-irog...@google.com/

This change modifies expression parsing so that constants are
considered when building the set of ids (events) and only events not
contributing to a constant value are measured.

v7. fixes the fix to be in the correct patch.

v6. rebases and fixes issues raised by Namhyung Kim ,
a memory leak and a function comment.

v5. uses macros to reduce boiler plate in patch 5/5 as suggested by
Andi Kleen .

v4. reduces references to BOTTOM/NAN in patch 5/5 by using utility
functions. It improves comments and fixes an unnecessary union in a
peephole optimization.

v3. fixes an assignment in patch 2/5. In patch 5/5 additional comments
are added and useless frees are replaced by asserts. A new peephole
optimization is added for the case CONST IF expr ELSE CONST, where the
the constants are identical, as we don't need to evaluate the IF
condition.

v2. is a rebase.

Ian Rogers (5):
  perf metric: Restructure struct expr_parse_ctx.
  perf metric: Use NAN for missing event IDs.
  perf metric: Rename expr__find_other.
  perf metric: Add utilities to work on ids map.
  perf metric: Don't compute unused events.

 tools/perf/tests/expr.c   | 159 +++-
 tools/perf/tests/pmu-events.c |  42 +++--
 tools/perf/util/expr.c| 137 --
 tools/perf/util/expr.h|  21 ++-
 tools/perf/util/expr.l|   9 -
 tools/perf/util/expr.y| 343 ++
 tools/perf/util/metricgroup.c |  44 +++--
 tools/perf/util/stat-shadow.c |  54 --
 8 files changed, 591 insertions(+), 218 deletions(-)

-- 
2.30.0.284.gd98b1dd5eaa7-goog



[PATCH v6 0/5] Don't compute events that won't be used in a metric.

2021-01-12 Thread Ian Rogers

For a metric like:
  EVENT1 if #smt_on else EVENT2

currently EVENT1 and EVENT2 will be measured and then when the metric
is reported EVENT1 or EVENT2 will be printed depending on the value
from smt_on() during the expr parsing. Computing both events is
unnecessary and can lead to multiplexing as discussed in this thread:
https://lore.kernel.org/lkml/20201110100346.2527031-1-irog...@google.com/

This change modifies expression parsing so that constants are
considered when building the set of ids (events) and only events not
contributing to a constant value are measured.

v6. rebases and fixes issues raised by Namhyung Kim ,
a memory leak and a function comment.

v5. uses macros to reduce boiler plate in patch 5/5 as suggested by
Andi Kleen .

v4. reduces references to BOTTOM/NAN in patch 5/5 by using utility
functions. It improves comments and fixes an unnecessary union in a
peephole optimization.

v3. fixes an assignment in patch 2/5. In patch 5/5 additional comments
are added and useless frees are replaced by asserts. A new peephole
optimization is added for the case CONST IF expr ELSE CONST, where the
the constants are identical, as we don't need to evaluate the IF
condition.

v2. is a rebase.

Ian Rogers (5):
  perf metric: Restructure struct expr_parse_ctx.
  perf metric: Use NAN for missing event IDs.
  perf metric: Rename expr__find_other.
  perf metric: Add utilities to work on ids map.
  perf metric: Don't compute unused events.

 tools/perf/tests/expr.c   | 159 +++-
 tools/perf/tests/pmu-events.c |  42 +++--
 tools/perf/util/expr.c| 137 --
 tools/perf/util/expr.h|  21 ++-
 tools/perf/util/expr.l|   9 -
 tools/perf/util/expr.y| 343 ++
 tools/perf/util/metricgroup.c |  44 +++--
 tools/perf/util/stat-shadow.c |  54 --
 8 files changed, 591 insertions(+), 218 deletions(-)

-- 
2.30.0.284.gd98b1dd5eaa7-goog



[PATCH v6 1/5] perf metric: Restructure struct expr_parse_ctx.

2021-01-12 Thread Ian Rogers
A later change to parsing the ids out (in expr__find_other) will
potentially drop hashmaps and so it is more convenient to move
expr_parse_ctx to have a hashmap pointer rather than a struct value. As
this pointer must be freed, rather than just going out of scope,
add expr__ctx_new and expr__ctx_free to manage expr_parse_ctx memory.
Adjust use of struct expr_parse_ctx accordingly.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/expr.c   | 81 ++-
 tools/perf/tests/pmu-events.c | 37 +---
 tools/perf/util/expr.c| 38 
 tools/perf/util/expr.h|  5 ++-
 tools/perf/util/metricgroup.c | 44 ++-
 tools/perf/util/stat-shadow.c | 50 +
 6 files changed, 151 insertions(+), 104 deletions(-)

diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
index 4d01051951cd..b0a3b5fd0c00 100644
--- a/tools/perf/tests/expr.c
+++ b/tools/perf/tests/expr.c
@@ -22,67 +22,70 @@ int test__expr(struct test *t __maybe_unused, int subtest 
__maybe_unused)
const char *p;
double val;
int ret;
-   struct expr_parse_ctx ctx;
+   struct expr_parse_ctx *ctx;
 
-   expr__ctx_init();
-   expr__add_id_val(, strdup("FOO"), 1);
-   expr__add_id_val(, strdup("BAR"), 2);
+   ctx = expr__ctx_new();
+   TEST_ASSERT_VAL("expr__ctx_new", ctx);
+   expr__add_id_val(ctx, strdup("FOO"), 1);
+   expr__add_id_val(ctx, strdup("BAR"), 2);
 
-   ret = test(, "1+1", 2);
-   ret |= test(, "FOO+BAR", 3);
-   ret |= test(, "(BAR/2)%2", 1);
-   ret |= test(, "1 - -4",  5);
-   ret |= test(, "(FOO-1)*2 + (BAR/2)%2 - -4",  5);
-   ret |= test(, "1-1 | 1", 1);
-   ret |= test(, "1-1 & 1", 0);
-   ret |= test(, "min(1,2) + 1", 2);
-   ret |= test(, "max(1,2) + 1", 3);
-   ret |= test(, "1+1 if 3*4 else 0", 2);
-   ret |= test(, "1.1 + 2.1", 3.2);
-   ret |= test(, ".1 + 2.", 2.1);
-   ret |= test(, "d_ratio(1, 2)", 0.5);
-   ret |= test(, "d_ratio(2.5, 0)", 0);
-   ret |= test(, "1.1 < 2.2", 1);
-   ret |= test(, "2.2 > 1.1", 1);
-   ret |= test(, "1.1 < 1.1", 0);
-   ret |= test(, "2.2 > 2.2", 0);
-   ret |= test(, "2.2 < 1.1", 0);
-   ret |= test(, "1.1 > 2.2", 0);
+   ret = test(ctx, "1+1", 2);
+   ret |= test(ctx, "FOO+BAR", 3);
+   ret |= test(ctx, "(BAR/2)%2", 1);
+   ret |= test(ctx, "1 - -4",  5);
+   ret |= test(ctx, "(FOO-1)*2 + (BAR/2)%2 - -4",  5);
+   ret |= test(ctx, "1-1 | 1", 1);
+   ret |= test(ctx, "1-1 & 1", 0);
+   ret |= test(ctx, "min(1,2) + 1", 2);
+   ret |= test(ctx, "max(1,2) + 1", 3);
+   ret |= test(ctx, "1+1 if 3*4 else 0", 2);
+   ret |= test(ctx, "1.1 + 2.1", 3.2);
+   ret |= test(ctx, ".1 + 2.", 2.1);
+   ret |= test(ctx, "d_ratio(1, 2)", 0.5);
+   ret |= test(ctx, "d_ratio(2.5, 0)", 0);
+   ret |= test(ctx, "1.1 < 2.2", 1);
+   ret |= test(ctx, "2.2 > 1.1", 1);
+   ret |= test(ctx, "1.1 < 1.1", 0);
+   ret |= test(ctx, "2.2 > 2.2", 0);
+   ret |= test(ctx, "2.2 < 1.1", 0);
+   ret |= test(ctx, "1.1 > 2.2", 0);
 
-   if (ret)
+   if (ret) {
+   expr__ctx_free(ctx);
return ret;
+   }
 
p = "FOO/0";
-   ret = expr__parse(, , p, 1);
+   ret = expr__parse(, ctx, p, 1);
TEST_ASSERT_VAL("division by zero", ret == -1);
 
p = "BAR/";
-   ret = expr__parse(, , p, 1);
+   ret = expr__parse(, ctx, p, 1);
TEST_ASSERT_VAL("missing operand", ret == -1);
 
-   expr__ctx_clear();
+   expr__ctx_clear(ctx);
TEST_ASSERT_VAL("find other",
expr__find_other("FOO + BAR + BAZ + BOZO", "FOO",
-, 1) == 0);
-   TEST_ASSERT_VAL("find other", hashmap__size() == 3);
-   TEST_ASSERT_VAL("find other", hashmap__find(, "BAR",
+ctx, 1) == 0);
+   TEST_ASSERT_VAL("find other", hashmap__size(ctx->ids) == 3);
+   TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "BAR",
(void **)_ptr));
-   TEST_ASSERT_VAL("find other", hashmap__find(, "BAZ",
+   TEST_ASSERT_VAL("find other", hashmap__find(

[PATCH v6 5/5] perf metric: Don't compute unused events.

2021-01-12 Thread Ian Rogers
For a metric like:
  EVENT1 if #smt_on else EVENT2

currently EVENT1 and EVENT2 will be measured and then when the metric is
reported EVENT1 or EVENT2 will be printed depending on the value from
smt_on() during the expr parsing. Computing both events is unnecessary and
can lead to multiplexing as discussed in this thread:
https://lore.kernel.org/lkml/20201110100346.2527031-1-irog...@google.com/

This change modifies the expression parsing code by:
 - getting rid of the "other" parsing and introducing a boolean argument
   to say whether ids should be computed or not.
 - expressions are changed so that a pair of value and ids are returned.
 - when computing the metric value the ids are unused.
 - when computing the ids, constant values and smt_on are assigned to
   the value.
 - If the value is from an event ID then the event is added to the ids
   hashmap and the value set to bottom (encoded as NAN).
 - Typically operators union IDs for their inputs and set the value to
   bottom, however, if the inputs are constant then these are computed and
   propagated as the value.
 - If the input is constant to certain operators like:
 IDS1 if CONST else IDS2
   then the result will be either IDS1 or IDS2 depending on CONST (which
   may be evaluated from an entire expression), and so IDS1 or IDS2 may
   be discarded avoiding events from being programmed.
 - The ids at the end of parsing are added to the context.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/expr.c |  17 ++
 tools/perf/util/expr.c  |  10 +-
 tools/perf/util/expr.h  |   1 -
 tools/perf/util/expr.l  |   9 --
 tools/perf/util/expr.y  | 340 ++--
 5 files changed, 283 insertions(+), 94 deletions(-)

diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
index 1c881bea7fca..5cab5960b257 100644
--- a/tools/perf/tests/expr.c
+++ b/tools/perf/tests/expr.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 #include "util/debug.h"
 #include "util/expr.h"
+#include "util/smt.h"
 #include "tests.h"
 #include 
 #include 
@@ -132,6 +133,22 @@ int test__expr(struct test *t __maybe_unused, int subtest 
__maybe_unused)
TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "EVENT2,param=3/",
(void **)_ptr));
 
+   /* Only EVENT1 or EVENT2 need be measured depending on the value of 
smt_on. */
+   expr__ctx_clear(ctx);
+   TEST_ASSERT_VAL("find ids",
+   expr__find_ids("EVENT1 if #smt_on else EVENT2",
+   NULL, ctx, 0) == 0);
+   TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 1);
+   TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids,
+ smt_on() ? "EVENT1" : 
"EVENT2",
+ (void **)_ptr));
+
+   /* The expression is a constant 1.0 without needing to evaluate EVENT1. 
*/
+   expr__ctx_clear(ctx);
+   TEST_ASSERT_VAL("find ids",
+   expr__find_ids("1.0 if EVENT1 > 100.0 else 1.0",
+   NULL, ctx, 0) == 0);
+   TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 0);
expr__ctx_free(ctx);
 
return 0;
diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
index 1adb6cd202e0..e9396a309fb7 100644
--- a/tools/perf/util/expr.c
+++ b/tools/perf/util/expr.c
@@ -325,14 +325,14 @@ void expr__ctx_free(struct expr_parse_ctx *ctx)
free(cur->value);
}
hashmap__free(ctx->ids);
+   free(ctx);
 }
 
 static int
 __expr__parse(double *val, struct expr_parse_ctx *ctx, const char *expr,
- int start, int runtime)
+ bool compute_ids, int runtime)
 {
struct expr_scanner_ctx scanner_ctx = {
-   .start_token = start,
.runtime = runtime,
};
YY_BUFFER_STATE buffer;
@@ -352,7 +352,7 @@ __expr__parse(double *val, struct expr_parse_ctx *ctx, 
const char *expr,
expr_set_debug(1, scanner);
 #endif
 
-   ret = expr_parse(val, ctx, scanner);
+   ret = expr_parse(val, ctx, compute_ids, scanner);
 
expr__flush_buffer(buffer, scanner);
expr__delete_buffer(buffer, scanner);
@@ -363,13 +363,13 @@ __expr__parse(double *val, struct expr_parse_ctx *ctx, 
const char *expr,
 int expr__parse(double *final_val, struct expr_parse_ctx *ctx,
const char *expr, int runtime)
 {
-   return __expr__parse(final_val, ctx, expr, EXPR_PARSE, runtime) ? -1 : 
0;
+   return __expr__parse(final_val, ctx, expr, /*compute_ids=*/false, 
runtime) ? -1 : 0;
 }
 
 int expr__find_ids(const char *expr, const char *one,
   struct expr_parse_ctx *ctx, int runtime)
 {
-   int ret = __expr__parse(NULL, ctx, expr, EXPR_OTHE

[PATCH v6 3/5] perf metric: Rename expr__find_other.

2021-01-12 Thread Ian Rogers
A later change will remove the notion of other, rename the function to
expr__find_ids as this is what it populates.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/expr.c   | 26 +-
 tools/perf/tests/pmu-events.c |  9 -
 tools/perf/util/expr.c|  4 ++--
 tools/perf/util/expr.h|  2 +-
 tools/perf/util/metricgroup.c |  2 +-
 tools/perf/util/stat-shadow.c |  6 +++---
 6 files changed, 24 insertions(+), 25 deletions(-)

diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
index b0a3b5fd0c00..7ccb97c73347 100644
--- a/tools/perf/tests/expr.c
+++ b/tools/perf/tests/expr.c
@@ -64,25 +64,25 @@ int test__expr(struct test *t __maybe_unused, int subtest 
__maybe_unused)
TEST_ASSERT_VAL("missing operand", ret == -1);
 
expr__ctx_clear(ctx);
-   TEST_ASSERT_VAL("find other",
-   expr__find_other("FOO + BAR + BAZ + BOZO", "FOO",
-ctx, 1) == 0);
-   TEST_ASSERT_VAL("find other", hashmap__size(ctx->ids) == 3);
-   TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "BAR",
+   TEST_ASSERT_VAL("find ids",
+   expr__find_ids("FOO + BAR + BAZ + BOZO", "FOO",
+   ctx, 1) == 0);
+   TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 3);
+   TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "BAR",
(void **)_ptr));
-   TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "BAZ",
+   TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "BAZ",
(void **)_ptr));
-   TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "BOZO",
+   TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "BOZO",
(void **)_ptr));
 
expr__ctx_clear(ctx);
-   TEST_ASSERT_VAL("find other",
-   expr__find_other("EVENT1\\,param\\=?@ + 
EVENT2\\,param\\=?@",
-NULL, ctx, 3) == 0);
-   TEST_ASSERT_VAL("find other", hashmap__size(ctx->ids) == 2);
-   TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "EVENT1,param=3/",
+   TEST_ASSERT_VAL("find ids",
+   expr__find_ids("EVENT1\\,param\\=?@ + 
EVENT2\\,param\\=?@",
+   NULL, ctx, 3) == 0);
+   TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 2);
+   TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "EVENT1,param=3/",
(void **)_ptr));
-   TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "EVENT2,param=3/",
+   TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "EVENT2,param=3/",
(void **)_ptr));
 
expr__ctx_free(ctx);
diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c
index cf5afd126934..d14a42406549 100644
--- a/tools/perf/tests/pmu-events.c
+++ b/tools/perf/tests/pmu-events.c
@@ -502,9 +502,8 @@ static int test_parsing(void)
if (!pe->metric_expr)
continue;
expr__ctx_clear(ctx);
-   if (expr__find_other(pe->metric_expr, NULL, ctx, 0)
- < 0) {
-   expr_failure("Parse other failed", map, pe);
+   if (expr__find_ids(pe->metric_expr, NULL, ctx, 0) < 0) {
+   expr_failure("Parse find ids failed", map, pe);
ret++;
continue;
}
@@ -559,8 +558,8 @@ static int metric_parse_fake(const char *str)
pr_debug("parsing '%s'\n", str);
 
ctx = expr__ctx_new();
-   if (expr__find_other(str, NULL, ctx, 0) < 0) {
-   pr_err("expr__find_other failed\n");
+   if (expr__find_ids(str, NULL, ctx, 0) < 0) {
+   pr_err("expr__find_ids failed\n");
return -1;
}
 
diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
index e0623d38e6ee..a248d14882cc 100644
--- a/tools/perf/util/expr.c
+++ b/tools/perf/util/expr.c
@@ -287,8 +287,8 @@ int expr__parse(double *final_val, struct expr_parse_ctx 
*ctx,
return __expr__parse(final_val, ctx, expr, EXPR_PARSE, runtime) ? -1 : 
0;
 }
 
-int expr__find_other(const char *expr, const char 

[PATCH v6 2/5] perf metric: Use NAN for missing event IDs.

2021-01-12 Thread Ian Rogers
If during computing a metric an event (id) is missing the parsing
aborts. A later patch will make it so that events that aren't used in
the output are deliberately omitted, in which case we don't want the
abort. Modify the missing ID case to report NAN for these cases.

Signed-off-by: Ian Rogers 
---
 tools/perf/util/expr.y | 9 -
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/tools/perf/util/expr.y b/tools/perf/util/expr.y
index b2ada8f8309a..41c9cd4efadd 100644
--- a/tools/perf/util/expr.y
+++ b/tools/perf/util/expr.y
@@ -1,6 +1,7 @@
 /* Simple expression parser */
 %{
 #define YYDEBUG 1
+#include 
 #include 
 #include "util.h"
 #include "util/debug.h"
@@ -88,12 +89,10 @@ expr: NUMBER
| ID{
struct expr_id_data *data;
 
-   if (expr__resolve_id(ctx, $1, )) {
-   free($1);
-   YYABORT;
-   }
+   $$ = NAN;
+   if (expr__resolve_id(ctx, $1, ) == 
0)
+   $$ = expr_id_data__value(data);
 
-   $$ = expr_id_data__value(data);
free($1);
}
| expr '|' expr { $$ = (long)$1 | (long)$3; }
-- 
2.30.0.284.gd98b1dd5eaa7-goog



[PATCH v6 4/5] perf metric: Add utilities to work on ids map.

2021-01-12 Thread Ian Rogers
Add utilities to new/free an ids hashmap, as well as to union. Add
testing of the union. Unioning hashmaps will be used when parsing the
metric, if a value is known then the hashmap is unnecessary, otherwise
we need to union together all the event ids to compute their values for
reporting.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/expr.c | 47 ++
 tools/perf/util/expr.c  | 87 +++--
 tools/perf/util/expr.h  | 13 ++
 3 files changed, 143 insertions(+), 4 deletions(-)

diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
index 7ccb97c73347..1c881bea7fca 100644
--- a/tools/perf/tests/expr.c
+++ b/tools/perf/tests/expr.c
@@ -6,6 +6,51 @@
 #include 
 #include 
 
+static int test_ids_union(void)
+{
+   struct hashmap *ids1, *ids2;
+
+   /* Empty union. */
+   ids1 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids1);
+   ids2 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids2);
+
+   ids1 = ids__union(ids1, ids2);
+   TEST_ASSERT_EQUAL("union", (int)hashmap__size(ids1), 0);
+
+   /* Union {foo, bar} against {}. */
+   ids2 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids2);
+
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids1, strdup("foo"), 
NULL), 0);
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids1, strdup("bar"), 
NULL), 0);
+
+   ids1 = ids__union(ids1, ids2);
+   TEST_ASSERT_EQUAL("union", (int)hashmap__size(ids1), 2);
+
+   /* Union {foo, bar} against {foo}. */
+   ids2 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids2);
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids2, strdup("foo"), 
NULL), 0);
+
+   ids1 = ids__union(ids1, ids2);
+   TEST_ASSERT_EQUAL("union", (int)hashmap__size(ids1), 2);
+
+   /* Union {foo, bar} against {bar,baz}. */
+   ids2 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids2);
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids2, strdup("bar"), 
NULL), 0);
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids2, strdup("baz"), 
NULL), 0);
+
+   ids1 = ids__union(ids1, ids2);
+   TEST_ASSERT_EQUAL("union", (int)hashmap__size(ids1), 3);
+
+   ids__free(ids1);
+
+   return 0;
+}
+
 static int test(struct expr_parse_ctx *ctx, const char *e, double val2)
 {
double val;
@@ -24,6 +69,8 @@ int test__expr(struct test *t __maybe_unused, int subtest 
__maybe_unused)
int ret;
struct expr_parse_ctx *ctx;
 
+   TEST_ASSERT_EQUAL("ids_union", test_ids_union(), 0);
+
ctx = expr__ctx_new();
TEST_ASSERT_VAL("expr__ctx_new", ctx);
expr__add_id_val(ctx, strdup("FOO"), 1);
diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
index a248d14882cc..1adb6cd202e0 100644
--- a/tools/perf/util/expr.c
+++ b/tools/perf/util/expr.c
@@ -59,8 +59,48 @@ static bool key_equal(const void *key1, const void *key2,
return !strcmp((const char *)key1, (const char *)key2);
 }
 
-/* Caller must make sure id is allocated */
-int expr__add_id(struct expr_parse_ctx *ctx, const char *id)
+struct hashmap *ids__new(void)
+{
+   return hashmap__new(key_hash, key_equal, NULL);
+}
+
+void ids__free(struct hashmap *ids)
+{
+   struct hashmap_entry *cur;
+   size_t bkt;
+
+   if (ids == NULL)
+   return;
+
+#ifdef PARSER_DEBUG
+   fprintf(stderr, "freeing ids: ");
+   ids__print(ids);
+   fprintf(stderr, "\n");
+#endif
+
+   hashmap__for_each_entry(ids, cur, bkt) {
+   free((char *)cur->key);
+   free(cur->value);
+   }
+
+   hashmap__free(ids);
+}
+
+void ids__print(struct hashmap *ids)
+{
+   size_t bkt;
+   struct hashmap_entry *cur;
+
+   if (!ids)
+   return;
+
+   hashmap__for_each_entry(ids, cur, bkt) {
+   fprintf(stderr, "key:%s, ", (const char *)cur->key);
+   }
+}
+
+int ids__insert(struct hashmap *ids, const char *id,
+   struct expr_id *parent)
 {
struct expr_id_data *data_ptr = NULL, *old_data = NULL;
char *old_key = NULL;
@@ -70,10 +110,10 @@ int expr__add_id(struct expr_parse_ctx *ctx, const char 
*id)
if (!data_ptr)
return -ENOMEM;
 
-   data_ptr->parent = ctx->parent;
+   data_ptr->parent = parent;
data_ptr->kind = EXPR_ID_DATA__PARENT;
 
-   ret = hashmap__set(ctx->ids, id, data_ptr,
+   ret = hashmap__set(ids, id, data_ptr,
   (const void **)_key, (void **)_data);
if (ret)
free(data_ptr);
@@ -82,6 +122,45 @@ int expr__add_id(struct expr_parse_ctx *ctx, const char *id)
return ret;
 }
 
+struct hashmap *ids__union(struct hashmap

[PATCH v5 4/5] perf metric: Add utilities to work on ids map.

2020-12-01 Thread Ian Rogers
Add utilities to new/free an ids hashmap, as well as to union. Add
testing of the union. Unioning hashmaps will be used when parsing the
metric, if a value is known then the hashmap is unnecessary, otherwise
we need to union together all the event ids to compute their values for
reporting.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/expr.c | 47 ++
 tools/perf/util/expr.c  | 87 +++--
 tools/perf/util/expr.h  |  9 +
 3 files changed, 139 insertions(+), 4 deletions(-)

diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
index 7ccb97c73347..1c881bea7fca 100644
--- a/tools/perf/tests/expr.c
+++ b/tools/perf/tests/expr.c
@@ -6,6 +6,51 @@
 #include 
 #include 
 
+static int test_ids_union(void)
+{
+   struct hashmap *ids1, *ids2;
+
+   /* Empty union. */
+   ids1 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids1);
+   ids2 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids2);
+
+   ids1 = ids__union(ids1, ids2);
+   TEST_ASSERT_EQUAL("union", (int)hashmap__size(ids1), 0);
+
+   /* Union {foo, bar} against {}. */
+   ids2 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids2);
+
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids1, strdup("foo"), 
NULL), 0);
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids1, strdup("bar"), 
NULL), 0);
+
+   ids1 = ids__union(ids1, ids2);
+   TEST_ASSERT_EQUAL("union", (int)hashmap__size(ids1), 2);
+
+   /* Union {foo, bar} against {foo}. */
+   ids2 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids2);
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids2, strdup("foo"), 
NULL), 0);
+
+   ids1 = ids__union(ids1, ids2);
+   TEST_ASSERT_EQUAL("union", (int)hashmap__size(ids1), 2);
+
+   /* Union {foo, bar} against {bar,baz}. */
+   ids2 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids2);
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids2, strdup("bar"), 
NULL), 0);
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids2, strdup("baz"), 
NULL), 0);
+
+   ids1 = ids__union(ids1, ids2);
+   TEST_ASSERT_EQUAL("union", (int)hashmap__size(ids1), 3);
+
+   ids__free(ids1);
+
+   return 0;
+}
+
 static int test(struct expr_parse_ctx *ctx, const char *e, double val2)
 {
double val;
@@ -24,6 +69,8 @@ int test__expr(struct test *t __maybe_unused, int subtest 
__maybe_unused)
int ret;
struct expr_parse_ctx *ctx;
 
+   TEST_ASSERT_EQUAL("ids_union", test_ids_union(), 0);
+
ctx = expr__ctx_new();
TEST_ASSERT_VAL("expr__ctx_new", ctx);
expr__add_id_val(ctx, strdup("FOO"), 1);
diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
index a248d14882cc..1adb6cd202e0 100644
--- a/tools/perf/util/expr.c
+++ b/tools/perf/util/expr.c
@@ -59,8 +59,48 @@ static bool key_equal(const void *key1, const void *key2,
return !strcmp((const char *)key1, (const char *)key2);
 }
 
-/* Caller must make sure id is allocated */
-int expr__add_id(struct expr_parse_ctx *ctx, const char *id)
+struct hashmap *ids__new(void)
+{
+   return hashmap__new(key_hash, key_equal, NULL);
+}
+
+void ids__free(struct hashmap *ids)
+{
+   struct hashmap_entry *cur;
+   size_t bkt;
+
+   if (ids == NULL)
+   return;
+
+#ifdef PARSER_DEBUG
+   fprintf(stderr, "freeing ids: ");
+   ids__print(ids);
+   fprintf(stderr, "\n");
+#endif
+
+   hashmap__for_each_entry(ids, cur, bkt) {
+   free((char *)cur->key);
+   free(cur->value);
+   }
+
+   hashmap__free(ids);
+}
+
+void ids__print(struct hashmap *ids)
+{
+   size_t bkt;
+   struct hashmap_entry *cur;
+
+   if (!ids)
+   return;
+
+   hashmap__for_each_entry(ids, cur, bkt) {
+   fprintf(stderr, "key:%s, ", (const char *)cur->key);
+   }
+}
+
+int ids__insert(struct hashmap *ids, const char *id,
+   struct expr_id *parent)
 {
struct expr_id_data *data_ptr = NULL, *old_data = NULL;
char *old_key = NULL;
@@ -70,10 +110,10 @@ int expr__add_id(struct expr_parse_ctx *ctx, const char 
*id)
if (!data_ptr)
return -ENOMEM;
 
-   data_ptr->parent = ctx->parent;
+   data_ptr->parent = parent;
data_ptr->kind = EXPR_ID_DATA__PARENT;
 
-   ret = hashmap__set(ctx->ids, id, data_ptr,
+   ret = hashmap__set(ids, id, data_ptr,
   (const void **)_key, (void **)_data);
if (ret)
free(data_ptr);
@@ -82,6 +122,45 @@ int expr__add_id(struct expr_parse_ctx *ctx, const char *id)
return ret;
 }
 
+struct hashmap *ids__union(struct hashmap

[PATCH v5 5/5] perf metric: Don't compute unused events.

2020-12-01 Thread Ian Rogers
For a metric like:
  EVENT1 if #smt_on else EVENT2

currently EVENT1 and EVENT2 will be measured and then when the metric is
reported EVENT1 or EVENT2 will be printed depending on the value from
smt_on() during the expr parsing. Computing both events is unnecessary and
can lead to multiplexing as discussed in this thread:
https://lore.kernel.org/lkml/20201110100346.2527031-1-irog...@google.com/

This change modifies the expression parsing code by:
 - getting rid of the "other" parsing and introducing a boolean argument
   to say whether ids should be computed or not.
 - expressions are changed so that a pair of value and ids are returned.
 - when computing the metric value the ids are unused.
 - when computing the ids, constant values and smt_on are assigned to
   the value.
 - If the value is from an event ID then the event is added to the ids
   hashmap and the value set to bottom (encoded as NAN).
 - Typically operators union IDs for their inputs and set the value to
   bottom, however, if the inputs are constant then these are computed and
   propagated as the value.
 - If the input is constant to certain operators like:
 IDS1 if CONST else IDS2
   then the result will be either IDS1 or IDS2 depending on CONST (which
   may be evaluated from an entire expression), and so IDS1 or IDS2 may
   be discarded avoiding events from being programmed.
 - The ids at the end of parsing are added to the context.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/expr.c |  17 ++
 tools/perf/util/expr.c  |   9 +-
 tools/perf/util/expr.h  |   1 -
 tools/perf/util/expr.l  |   9 --
 tools/perf/util/expr.y  | 340 ++--
 5 files changed, 282 insertions(+), 94 deletions(-)

diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
index 1c881bea7fca..5cab5960b257 100644
--- a/tools/perf/tests/expr.c
+++ b/tools/perf/tests/expr.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 #include "util/debug.h"
 #include "util/expr.h"
+#include "util/smt.h"
 #include "tests.h"
 #include 
 #include 
@@ -132,6 +133,22 @@ int test__expr(struct test *t __maybe_unused, int subtest 
__maybe_unused)
TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "EVENT2,param=3/",
(void **)_ptr));
 
+   /* Only EVENT1 or EVENT2 need be measured depending on the value of 
smt_on. */
+   expr__ctx_clear(ctx);
+   TEST_ASSERT_VAL("find ids",
+   expr__find_ids("EVENT1 if #smt_on else EVENT2",
+   NULL, ctx, 0) == 0);
+   TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 1);
+   TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids,
+ smt_on() ? "EVENT1" : 
"EVENT2",
+ (void **)_ptr));
+
+   /* The expression is a constant 1.0 without needing to evaluate EVENT1. 
*/
+   expr__ctx_clear(ctx);
+   TEST_ASSERT_VAL("find ids",
+   expr__find_ids("1.0 if EVENT1 > 100.0 else 1.0",
+   NULL, ctx, 0) == 0);
+   TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 0);
expr__ctx_free(ctx);
 
return 0;
diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
index 1adb6cd202e0..28aaa50c6c68 100644
--- a/tools/perf/util/expr.c
+++ b/tools/perf/util/expr.c
@@ -329,10 +329,9 @@ void expr__ctx_free(struct expr_parse_ctx *ctx)
 
 static int
 __expr__parse(double *val, struct expr_parse_ctx *ctx, const char *expr,
- int start, int runtime)
+ bool compute_ids, int runtime)
 {
struct expr_scanner_ctx scanner_ctx = {
-   .start_token = start,
.runtime = runtime,
};
YY_BUFFER_STATE buffer;
@@ -352,7 +351,7 @@ __expr__parse(double *val, struct expr_parse_ctx *ctx, 
const char *expr,
expr_set_debug(1, scanner);
 #endif
 
-   ret = expr_parse(val, ctx, scanner);
+   ret = expr_parse(val, ctx, compute_ids, scanner);
 
expr__flush_buffer(buffer, scanner);
expr__delete_buffer(buffer, scanner);
@@ -363,13 +362,13 @@ __expr__parse(double *val, struct expr_parse_ctx *ctx, 
const char *expr,
 int expr__parse(double *final_val, struct expr_parse_ctx *ctx,
const char *expr, int runtime)
 {
-   return __expr__parse(final_val, ctx, expr, EXPR_PARSE, runtime) ? -1 : 
0;
+   return __expr__parse(final_val, ctx, expr, /*compute_ids=*/false, 
runtime) ? -1 : 0;
 }
 
 int expr__find_ids(const char *expr, const char *one,
   struct expr_parse_ctx *ctx, int runtime)
 {
-   int ret = __expr__parse(NULL, ctx, expr, EXPR_OTHER, runtime);
+   int ret = __expr__parse(NULL, ctx, expr, /*compute_ids=*/true, runtime);
 

[PATCH v5 2/5] perf metric: Use NAN for missing event IDs.

2020-12-01 Thread Ian Rogers
If during computing a metric an event (id) is missing the parsing
aborts. A later patch will make it so that events that aren't used in
the output are deliberately omitted, in which case we don't want the
abort. Modify the missing ID case to report NAN for these cases.

Signed-off-by: Ian Rogers 
---
 tools/perf/util/expr.y | 9 -
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/tools/perf/util/expr.y b/tools/perf/util/expr.y
index b2ada8f8309a..41c9cd4efadd 100644
--- a/tools/perf/util/expr.y
+++ b/tools/perf/util/expr.y
@@ -1,6 +1,7 @@
 /* Simple expression parser */
 %{
 #define YYDEBUG 1
+#include 
 #include 
 #include "util.h"
 #include "util/debug.h"
@@ -88,12 +89,10 @@ expr: NUMBER
| ID{
struct expr_id_data *data;
 
-   if (expr__resolve_id(ctx, $1, )) {
-   free($1);
-   YYABORT;
-   }
+   $$ = NAN;
+   if (expr__resolve_id(ctx, $1, ) == 
0)
+   $$ = expr_id_data__value(data);
 
-   $$ = expr_id_data__value(data);
free($1);
}
| expr '|' expr { $$ = (long)$1 | (long)$3; }
-- 
2.29.2.454.gaff20da3a2-goog



[PATCH v5 1/5] perf metric: Restructure struct expr_parse_ctx.

2020-12-01 Thread Ian Rogers
A later change to parsing the ids out (in expr__find_other) will
potentially drop hashmaps and so it is more convenient to move
expr_parse_ctx to have a hashmap pointer rather than a struct value. As
this pointer must be freed, rather than just going out of scope,
add expr__ctx_new and expr__ctx_free to manage expr_parse_ctx memory.
Adjust use of struct expr_parse_ctx accordingly.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/expr.c   | 81 ++-
 tools/perf/tests/pmu-events.c | 37 +---
 tools/perf/util/expr.c| 38 
 tools/perf/util/expr.h|  5 ++-
 tools/perf/util/metricgroup.c | 44 ++-
 tools/perf/util/stat-shadow.c | 50 +
 6 files changed, 151 insertions(+), 104 deletions(-)

diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
index 4d01051951cd..b0a3b5fd0c00 100644
--- a/tools/perf/tests/expr.c
+++ b/tools/perf/tests/expr.c
@@ -22,67 +22,70 @@ int test__expr(struct test *t __maybe_unused, int subtest 
__maybe_unused)
const char *p;
double val;
int ret;
-   struct expr_parse_ctx ctx;
+   struct expr_parse_ctx *ctx;
 
-   expr__ctx_init();
-   expr__add_id_val(, strdup("FOO"), 1);
-   expr__add_id_val(, strdup("BAR"), 2);
+   ctx = expr__ctx_new();
+   TEST_ASSERT_VAL("expr__ctx_new", ctx);
+   expr__add_id_val(ctx, strdup("FOO"), 1);
+   expr__add_id_val(ctx, strdup("BAR"), 2);
 
-   ret = test(, "1+1", 2);
-   ret |= test(, "FOO+BAR", 3);
-   ret |= test(, "(BAR/2)%2", 1);
-   ret |= test(, "1 - -4",  5);
-   ret |= test(, "(FOO-1)*2 + (BAR/2)%2 - -4",  5);
-   ret |= test(, "1-1 | 1", 1);
-   ret |= test(, "1-1 & 1", 0);
-   ret |= test(, "min(1,2) + 1", 2);
-   ret |= test(, "max(1,2) + 1", 3);
-   ret |= test(, "1+1 if 3*4 else 0", 2);
-   ret |= test(, "1.1 + 2.1", 3.2);
-   ret |= test(, ".1 + 2.", 2.1);
-   ret |= test(, "d_ratio(1, 2)", 0.5);
-   ret |= test(, "d_ratio(2.5, 0)", 0);
-   ret |= test(, "1.1 < 2.2", 1);
-   ret |= test(, "2.2 > 1.1", 1);
-   ret |= test(, "1.1 < 1.1", 0);
-   ret |= test(, "2.2 > 2.2", 0);
-   ret |= test(, "2.2 < 1.1", 0);
-   ret |= test(, "1.1 > 2.2", 0);
+   ret = test(ctx, "1+1", 2);
+   ret |= test(ctx, "FOO+BAR", 3);
+   ret |= test(ctx, "(BAR/2)%2", 1);
+   ret |= test(ctx, "1 - -4",  5);
+   ret |= test(ctx, "(FOO-1)*2 + (BAR/2)%2 - -4",  5);
+   ret |= test(ctx, "1-1 | 1", 1);
+   ret |= test(ctx, "1-1 & 1", 0);
+   ret |= test(ctx, "min(1,2) + 1", 2);
+   ret |= test(ctx, "max(1,2) + 1", 3);
+   ret |= test(ctx, "1+1 if 3*4 else 0", 2);
+   ret |= test(ctx, "1.1 + 2.1", 3.2);
+   ret |= test(ctx, ".1 + 2.", 2.1);
+   ret |= test(ctx, "d_ratio(1, 2)", 0.5);
+   ret |= test(ctx, "d_ratio(2.5, 0)", 0);
+   ret |= test(ctx, "1.1 < 2.2", 1);
+   ret |= test(ctx, "2.2 > 1.1", 1);
+   ret |= test(ctx, "1.1 < 1.1", 0);
+   ret |= test(ctx, "2.2 > 2.2", 0);
+   ret |= test(ctx, "2.2 < 1.1", 0);
+   ret |= test(ctx, "1.1 > 2.2", 0);
 
-   if (ret)
+   if (ret) {
+   expr__ctx_free(ctx);
return ret;
+   }
 
p = "FOO/0";
-   ret = expr__parse(, , p, 1);
+   ret = expr__parse(, ctx, p, 1);
TEST_ASSERT_VAL("division by zero", ret == -1);
 
p = "BAR/";
-   ret = expr__parse(, , p, 1);
+   ret = expr__parse(, ctx, p, 1);
TEST_ASSERT_VAL("missing operand", ret == -1);
 
-   expr__ctx_clear();
+   expr__ctx_clear(ctx);
TEST_ASSERT_VAL("find other",
expr__find_other("FOO + BAR + BAZ + BOZO", "FOO",
-, 1) == 0);
-   TEST_ASSERT_VAL("find other", hashmap__size() == 3);
-   TEST_ASSERT_VAL("find other", hashmap__find(, "BAR",
+ctx, 1) == 0);
+   TEST_ASSERT_VAL("find other", hashmap__size(ctx->ids) == 3);
+   TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "BAR",
(void **)_ptr));
-   TEST_ASSERT_VAL("find other", hashmap__find(, "BAZ",
+   TEST_ASSERT_VAL("find other", hashmap__find(

[PATCH v5 3/5] perf metric: Rename expr__find_other.

2020-12-01 Thread Ian Rogers
A later change will remove the notion of other, rename the function to
expr__find_ids as this is what it populates.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/expr.c   | 26 +-
 tools/perf/tests/pmu-events.c |  9 -
 tools/perf/util/expr.c|  4 ++--
 tools/perf/util/expr.h|  2 +-
 tools/perf/util/metricgroup.c |  2 +-
 tools/perf/util/stat-shadow.c |  6 +++---
 6 files changed, 24 insertions(+), 25 deletions(-)

diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
index b0a3b5fd0c00..7ccb97c73347 100644
--- a/tools/perf/tests/expr.c
+++ b/tools/perf/tests/expr.c
@@ -64,25 +64,25 @@ int test__expr(struct test *t __maybe_unused, int subtest 
__maybe_unused)
TEST_ASSERT_VAL("missing operand", ret == -1);
 
expr__ctx_clear(ctx);
-   TEST_ASSERT_VAL("find other",
-   expr__find_other("FOO + BAR + BAZ + BOZO", "FOO",
-ctx, 1) == 0);
-   TEST_ASSERT_VAL("find other", hashmap__size(ctx->ids) == 3);
-   TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "BAR",
+   TEST_ASSERT_VAL("find ids",
+   expr__find_ids("FOO + BAR + BAZ + BOZO", "FOO",
+   ctx, 1) == 0);
+   TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 3);
+   TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "BAR",
(void **)_ptr));
-   TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "BAZ",
+   TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "BAZ",
(void **)_ptr));
-   TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "BOZO",
+   TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "BOZO",
(void **)_ptr));
 
expr__ctx_clear(ctx);
-   TEST_ASSERT_VAL("find other",
-   expr__find_other("EVENT1\\,param\\=?@ + 
EVENT2\\,param\\=?@",
-NULL, ctx, 3) == 0);
-   TEST_ASSERT_VAL("find other", hashmap__size(ctx->ids) == 2);
-   TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "EVENT1,param=3/",
+   TEST_ASSERT_VAL("find ids",
+   expr__find_ids("EVENT1\\,param\\=?@ + 
EVENT2\\,param\\=?@",
+   NULL, ctx, 3) == 0);
+   TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 2);
+   TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "EVENT1,param=3/",
(void **)_ptr));
-   TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "EVENT2,param=3/",
+   TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "EVENT2,param=3/",
(void **)_ptr));
 
expr__ctx_free(ctx);
diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c
index 294daf568bb6..3ac70fa31379 100644
--- a/tools/perf/tests/pmu-events.c
+++ b/tools/perf/tests/pmu-events.c
@@ -502,9 +502,8 @@ static int test_parsing(void)
if (!pe->metric_expr)
continue;
expr__ctx_clear(ctx);
-   if (expr__find_other(pe->metric_expr, NULL, ctx, 0)
- < 0) {
-   expr_failure("Parse other failed", map, pe);
+   if (expr__find_ids(pe->metric_expr, NULL, ctx, 0) < 0) {
+   expr_failure("Parse find ids failed", map, pe);
ret++;
continue;
}
@@ -559,8 +558,8 @@ static int metric_parse_fake(const char *str)
pr_debug("parsing '%s'\n", str);
 
ctx = expr__ctx_new();
-   if (expr__find_other(str, NULL, ctx, 0) < 0) {
-   pr_err("expr__find_other failed\n");
+   if (expr__find_ids(str, NULL, ctx, 0) < 0) {
+   pr_err("expr__find_ids failed\n");
return -1;
}
 
diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
index e0623d38e6ee..a248d14882cc 100644
--- a/tools/perf/util/expr.c
+++ b/tools/perf/util/expr.c
@@ -287,8 +287,8 @@ int expr__parse(double *final_val, struct expr_parse_ctx 
*ctx,
return __expr__parse(final_val, ctx, expr, EXPR_PARSE, runtime) ? -1 : 
0;
 }
 
-int expr__find_other(const char *expr, const char 

[PATCH v5 0/5] Don't compute events that won't be used in a metric.

2020-12-01 Thread Ian Rogers

For a metric like:
  EVENT1 if #smt_on else EVENT2

currently EVENT1 and EVENT2 will be measured and then when the metric
is reported EVENT1 or EVENT2 will be printed depending on the value
from smt_on() during the expr parsing. Computing both events is
unnecessary and can lead to multiplexing as discussed in this thread:
https://lore.kernel.org/lkml/20201110100346.2527031-1-irog...@google.com/

This change modifies expression parsing so that constants are
considered when building the set of ids (events) and only events not
contributing to a constant value are measured.

v5. uses macros to reduce boiler plate in patch 5/5 as suggested by
Andi Kleen .

v4. reduces references to BOTTOM/NAN in patch 5/5 by using utility
functions. It improves comments and fixes an unnecessary union in a
peephole optimization.

v3. fixes an assignment in patch 2/5. In patch 5/5 additional comments
are added and useless frees are replaced by asserts. A new peephole
optimization is added for the case CONST IF expr ELSE CONST, where the
the constants are identical, as we don't need to evaluate the IF
condition.

v2. is a rebase.

Ian Rogers (5):
  perf metric: Restructure struct expr_parse_ctx.
  perf metric: Use NAN for missing event IDs.
  perf metric: Rename expr__find_other.
  perf metric: Add utilities to work on ids map.
  perf metric: Don't compute unused events.

 tools/perf/tests/expr.c   | 159 +++-
 tools/perf/tests/pmu-events.c |  42 +++--
 tools/perf/util/expr.c| 136 --
 tools/perf/util/expr.h|  17 +-
 tools/perf/util/expr.l|   9 -
 tools/perf/util/expr.y| 343 ++
 tools/perf/util/metricgroup.c |  44 +++--
 tools/perf/util/stat-shadow.c |  54 --
 8 files changed, 586 insertions(+), 218 deletions(-)

-- 
2.29.2.454.gaff20da3a2-goog



[PATCH v4 1/5] perf metric: Restructure struct expr_parse_ctx.

2020-12-01 Thread Ian Rogers
A later change to parsing the ids out (in expr__find_other) will
potentially drop hashmaps and so it is more convenient to move
expr_parse_ctx to have a hashmap pointer rather than a struct value. As
this pointer must be freed, rather than just going out of scope,
add expr__ctx_new and expr__ctx_free to manage expr_parse_ctx memory.
Adjust use of struct expr_parse_ctx accordingly.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/expr.c   | 81 ++-
 tools/perf/tests/pmu-events.c | 37 +---
 tools/perf/util/expr.c| 38 
 tools/perf/util/expr.h|  5 ++-
 tools/perf/util/metricgroup.c | 44 ++-
 tools/perf/util/stat-shadow.c | 50 +
 6 files changed, 151 insertions(+), 104 deletions(-)

diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
index 4d01051951cd..b0a3b5fd0c00 100644
--- a/tools/perf/tests/expr.c
+++ b/tools/perf/tests/expr.c
@@ -22,67 +22,70 @@ int test__expr(struct test *t __maybe_unused, int subtest 
__maybe_unused)
const char *p;
double val;
int ret;
-   struct expr_parse_ctx ctx;
+   struct expr_parse_ctx *ctx;
 
-   expr__ctx_init();
-   expr__add_id_val(, strdup("FOO"), 1);
-   expr__add_id_val(, strdup("BAR"), 2);
+   ctx = expr__ctx_new();
+   TEST_ASSERT_VAL("expr__ctx_new", ctx);
+   expr__add_id_val(ctx, strdup("FOO"), 1);
+   expr__add_id_val(ctx, strdup("BAR"), 2);
 
-   ret = test(, "1+1", 2);
-   ret |= test(, "FOO+BAR", 3);
-   ret |= test(, "(BAR/2)%2", 1);
-   ret |= test(, "1 - -4",  5);
-   ret |= test(, "(FOO-1)*2 + (BAR/2)%2 - -4",  5);
-   ret |= test(, "1-1 | 1", 1);
-   ret |= test(, "1-1 & 1", 0);
-   ret |= test(, "min(1,2) + 1", 2);
-   ret |= test(, "max(1,2) + 1", 3);
-   ret |= test(, "1+1 if 3*4 else 0", 2);
-   ret |= test(, "1.1 + 2.1", 3.2);
-   ret |= test(, ".1 + 2.", 2.1);
-   ret |= test(, "d_ratio(1, 2)", 0.5);
-   ret |= test(, "d_ratio(2.5, 0)", 0);
-   ret |= test(, "1.1 < 2.2", 1);
-   ret |= test(, "2.2 > 1.1", 1);
-   ret |= test(, "1.1 < 1.1", 0);
-   ret |= test(, "2.2 > 2.2", 0);
-   ret |= test(, "2.2 < 1.1", 0);
-   ret |= test(, "1.1 > 2.2", 0);
+   ret = test(ctx, "1+1", 2);
+   ret |= test(ctx, "FOO+BAR", 3);
+   ret |= test(ctx, "(BAR/2)%2", 1);
+   ret |= test(ctx, "1 - -4",  5);
+   ret |= test(ctx, "(FOO-1)*2 + (BAR/2)%2 - -4",  5);
+   ret |= test(ctx, "1-1 | 1", 1);
+   ret |= test(ctx, "1-1 & 1", 0);
+   ret |= test(ctx, "min(1,2) + 1", 2);
+   ret |= test(ctx, "max(1,2) + 1", 3);
+   ret |= test(ctx, "1+1 if 3*4 else 0", 2);
+   ret |= test(ctx, "1.1 + 2.1", 3.2);
+   ret |= test(ctx, ".1 + 2.", 2.1);
+   ret |= test(ctx, "d_ratio(1, 2)", 0.5);
+   ret |= test(ctx, "d_ratio(2.5, 0)", 0);
+   ret |= test(ctx, "1.1 < 2.2", 1);
+   ret |= test(ctx, "2.2 > 1.1", 1);
+   ret |= test(ctx, "1.1 < 1.1", 0);
+   ret |= test(ctx, "2.2 > 2.2", 0);
+   ret |= test(ctx, "2.2 < 1.1", 0);
+   ret |= test(ctx, "1.1 > 2.2", 0);
 
-   if (ret)
+   if (ret) {
+   expr__ctx_free(ctx);
return ret;
+   }
 
p = "FOO/0";
-   ret = expr__parse(, , p, 1);
+   ret = expr__parse(, ctx, p, 1);
TEST_ASSERT_VAL("division by zero", ret == -1);
 
p = "BAR/";
-   ret = expr__parse(, , p, 1);
+   ret = expr__parse(, ctx, p, 1);
TEST_ASSERT_VAL("missing operand", ret == -1);
 
-   expr__ctx_clear();
+   expr__ctx_clear(ctx);
TEST_ASSERT_VAL("find other",
expr__find_other("FOO + BAR + BAZ + BOZO", "FOO",
-, 1) == 0);
-   TEST_ASSERT_VAL("find other", hashmap__size() == 3);
-   TEST_ASSERT_VAL("find other", hashmap__find(, "BAR",
+ctx, 1) == 0);
+   TEST_ASSERT_VAL("find other", hashmap__size(ctx->ids) == 3);
+   TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "BAR",
(void **)_ptr));
-   TEST_ASSERT_VAL("find other", hashmap__find(, "BAZ",
+   TEST_ASSERT_VAL("find other", hashmap__find(

[PATCH v4 5/5] perf metric: Don't compute unused events.

2020-12-01 Thread Ian Rogers
For a metric like:
  EVENT1 if #smt_on else EVENT2

currently EVENT1 and EVENT2 will be measured and then when the metric is
reported EVENT1 or EVENT2 will be printed depending on the value from
smt_on() during the expr parsing. Computing both events is unnecessary and
can lead to multiplexing as discussed in this thread:
https://lore.kernel.org/lkml/20201110100346.2527031-1-irog...@google.com/

This change modifies the expression parsing code by:
 - getting rid of the "other" parsing and introducing a boolean argument
   to say whether ids should be computed or not.
 - expressions are changed so that a pair of value and ids are returned.
 - when computing the metric value the ids are unused.
 - when computing the ids, constant values and smt_on are assigned to
   the value.
 - If the value is from an event ID then the event is added to the ids
   hashmap and the value set to bottom (encoded as NAN).
 - Typically operators union IDs for their inputs and set the value to
   bottom, however, if the inputs are constant then these are computed and
   propagated as the value.
 - If the input is constant to certain operators like:
 IDS1 if CONST else IDS2
   then the result will be either IDS1 or IDS2 depending on CONST (which
   may be evaluated from an entire expression), and so IDS1 or IDS2 may
   be discarded avoiding events from being programmed.
 - The ids at the end of parsing are added to the context.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/expr.c |  17 ++
 tools/perf/util/expr.c  |   9 +-
 tools/perf/util/expr.h  |   1 -
 tools/perf/util/expr.l  |   9 -
 tools/perf/util/expr.y  | 415 
 5 files changed, 354 insertions(+), 97 deletions(-)

diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
index 1c881bea7fca..5cab5960b257 100644
--- a/tools/perf/tests/expr.c
+++ b/tools/perf/tests/expr.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 #include "util/debug.h"
 #include "util/expr.h"
+#include "util/smt.h"
 #include "tests.h"
 #include 
 #include 
@@ -132,6 +133,22 @@ int test__expr(struct test *t __maybe_unused, int subtest 
__maybe_unused)
TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "EVENT2,param=3/",
(void **)_ptr));
 
+   /* Only EVENT1 or EVENT2 need be measured depending on the value of 
smt_on. */
+   expr__ctx_clear(ctx);
+   TEST_ASSERT_VAL("find ids",
+   expr__find_ids("EVENT1 if #smt_on else EVENT2",
+   NULL, ctx, 0) == 0);
+   TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 1);
+   TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids,
+ smt_on() ? "EVENT1" : 
"EVENT2",
+ (void **)_ptr));
+
+   /* The expression is a constant 1.0 without needing to evaluate EVENT1. 
*/
+   expr__ctx_clear(ctx);
+   TEST_ASSERT_VAL("find ids",
+   expr__find_ids("1.0 if EVENT1 > 100.0 else 1.0",
+   NULL, ctx, 0) == 0);
+   TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 0);
expr__ctx_free(ctx);
 
return 0;
diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
index 1adb6cd202e0..28aaa50c6c68 100644
--- a/tools/perf/util/expr.c
+++ b/tools/perf/util/expr.c
@@ -329,10 +329,9 @@ void expr__ctx_free(struct expr_parse_ctx *ctx)
 
 static int
 __expr__parse(double *val, struct expr_parse_ctx *ctx, const char *expr,
- int start, int runtime)
+ bool compute_ids, int runtime)
 {
struct expr_scanner_ctx scanner_ctx = {
-   .start_token = start,
.runtime = runtime,
};
YY_BUFFER_STATE buffer;
@@ -352,7 +351,7 @@ __expr__parse(double *val, struct expr_parse_ctx *ctx, 
const char *expr,
expr_set_debug(1, scanner);
 #endif
 
-   ret = expr_parse(val, ctx, scanner);
+   ret = expr_parse(val, ctx, compute_ids, scanner);
 
expr__flush_buffer(buffer, scanner);
expr__delete_buffer(buffer, scanner);
@@ -363,13 +362,13 @@ __expr__parse(double *val, struct expr_parse_ctx *ctx, 
const char *expr,
 int expr__parse(double *final_val, struct expr_parse_ctx *ctx,
const char *expr, int runtime)
 {
-   return __expr__parse(final_val, ctx, expr, EXPR_PARSE, runtime) ? -1 : 
0;
+   return __expr__parse(final_val, ctx, expr, /*compute_ids=*/false, 
runtime) ? -1 : 0;
 }
 
 int expr__find_ids(const char *expr, const char *one,
   struct expr_parse_ctx *ctx, int runtime)
 {
-   int ret = __expr__parse(NULL, ctx, expr, EXPR_OTHER, runtime);
+   int ret = __expr__parse(NULL, ctx, expr, /*compute_ids=*/true, runtime);
 
if 

[PATCH v4 3/5] perf metric: Rename expr__find_other.

2020-12-01 Thread Ian Rogers
A later change will remove the notion of other, rename the function to
expr__find_ids as this is what it populates.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/expr.c   | 26 +-
 tools/perf/tests/pmu-events.c |  9 -
 tools/perf/util/expr.c|  4 ++--
 tools/perf/util/expr.h|  2 +-
 tools/perf/util/metricgroup.c |  2 +-
 tools/perf/util/stat-shadow.c |  6 +++---
 6 files changed, 24 insertions(+), 25 deletions(-)

diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
index b0a3b5fd0c00..7ccb97c73347 100644
--- a/tools/perf/tests/expr.c
+++ b/tools/perf/tests/expr.c
@@ -64,25 +64,25 @@ int test__expr(struct test *t __maybe_unused, int subtest 
__maybe_unused)
TEST_ASSERT_VAL("missing operand", ret == -1);
 
expr__ctx_clear(ctx);
-   TEST_ASSERT_VAL("find other",
-   expr__find_other("FOO + BAR + BAZ + BOZO", "FOO",
-ctx, 1) == 0);
-   TEST_ASSERT_VAL("find other", hashmap__size(ctx->ids) == 3);
-   TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "BAR",
+   TEST_ASSERT_VAL("find ids",
+   expr__find_ids("FOO + BAR + BAZ + BOZO", "FOO",
+   ctx, 1) == 0);
+   TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 3);
+   TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "BAR",
(void **)_ptr));
-   TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "BAZ",
+   TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "BAZ",
(void **)_ptr));
-   TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "BOZO",
+   TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "BOZO",
(void **)_ptr));
 
expr__ctx_clear(ctx);
-   TEST_ASSERT_VAL("find other",
-   expr__find_other("EVENT1\\,param\\=?@ + 
EVENT2\\,param\\=?@",
-NULL, ctx, 3) == 0);
-   TEST_ASSERT_VAL("find other", hashmap__size(ctx->ids) == 2);
-   TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "EVENT1,param=3/",
+   TEST_ASSERT_VAL("find ids",
+   expr__find_ids("EVENT1\\,param\\=?@ + 
EVENT2\\,param\\=?@",
+   NULL, ctx, 3) == 0);
+   TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 2);
+   TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "EVENT1,param=3/",
(void **)_ptr));
-   TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "EVENT2,param=3/",
+   TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "EVENT2,param=3/",
(void **)_ptr));
 
expr__ctx_free(ctx);
diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c
index 294daf568bb6..3ac70fa31379 100644
--- a/tools/perf/tests/pmu-events.c
+++ b/tools/perf/tests/pmu-events.c
@@ -502,9 +502,8 @@ static int test_parsing(void)
if (!pe->metric_expr)
continue;
expr__ctx_clear(ctx);
-   if (expr__find_other(pe->metric_expr, NULL, ctx, 0)
- < 0) {
-   expr_failure("Parse other failed", map, pe);
+   if (expr__find_ids(pe->metric_expr, NULL, ctx, 0) < 0) {
+   expr_failure("Parse find ids failed", map, pe);
ret++;
continue;
}
@@ -559,8 +558,8 @@ static int metric_parse_fake(const char *str)
pr_debug("parsing '%s'\n", str);
 
ctx = expr__ctx_new();
-   if (expr__find_other(str, NULL, ctx, 0) < 0) {
-   pr_err("expr__find_other failed\n");
+   if (expr__find_ids(str, NULL, ctx, 0) < 0) {
+   pr_err("expr__find_ids failed\n");
return -1;
}
 
diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
index e0623d38e6ee..a248d14882cc 100644
--- a/tools/perf/util/expr.c
+++ b/tools/perf/util/expr.c
@@ -287,8 +287,8 @@ int expr__parse(double *final_val, struct expr_parse_ctx 
*ctx,
return __expr__parse(final_val, ctx, expr, EXPR_PARSE, runtime) ? -1 : 
0;
 }
 
-int expr__find_other(const char *expr, const char 

[PATCH v4 4/5] perf metric: Add utilities to work on ids map.

2020-12-01 Thread Ian Rogers
Add utilities to new/free an ids hashmap, as well as to union. Add
testing of the union. Unioning hashmaps will be used when parsing the
metric, if a value is known then the hashmap is unnecessary, otherwise
we need to union together all the event ids to compute their values for
reporting.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/expr.c | 47 ++
 tools/perf/util/expr.c  | 87 +++--
 tools/perf/util/expr.h  |  9 +
 3 files changed, 139 insertions(+), 4 deletions(-)

diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
index 7ccb97c73347..1c881bea7fca 100644
--- a/tools/perf/tests/expr.c
+++ b/tools/perf/tests/expr.c
@@ -6,6 +6,51 @@
 #include 
 #include 
 
+static int test_ids_union(void)
+{
+   struct hashmap *ids1, *ids2;
+
+   /* Empty union. */
+   ids1 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids1);
+   ids2 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids2);
+
+   ids1 = ids__union(ids1, ids2);
+   TEST_ASSERT_EQUAL("union", (int)hashmap__size(ids1), 0);
+
+   /* Union {foo, bar} against {}. */
+   ids2 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids2);
+
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids1, strdup("foo"), 
NULL), 0);
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids1, strdup("bar"), 
NULL), 0);
+
+   ids1 = ids__union(ids1, ids2);
+   TEST_ASSERT_EQUAL("union", (int)hashmap__size(ids1), 2);
+
+   /* Union {foo, bar} against {foo}. */
+   ids2 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids2);
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids2, strdup("foo"), 
NULL), 0);
+
+   ids1 = ids__union(ids1, ids2);
+   TEST_ASSERT_EQUAL("union", (int)hashmap__size(ids1), 2);
+
+   /* Union {foo, bar} against {bar,baz}. */
+   ids2 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids2);
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids2, strdup("bar"), 
NULL), 0);
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids2, strdup("baz"), 
NULL), 0);
+
+   ids1 = ids__union(ids1, ids2);
+   TEST_ASSERT_EQUAL("union", (int)hashmap__size(ids1), 3);
+
+   ids__free(ids1);
+
+   return 0;
+}
+
 static int test(struct expr_parse_ctx *ctx, const char *e, double val2)
 {
double val;
@@ -24,6 +69,8 @@ int test__expr(struct test *t __maybe_unused, int subtest 
__maybe_unused)
int ret;
struct expr_parse_ctx *ctx;
 
+   TEST_ASSERT_EQUAL("ids_union", test_ids_union(), 0);
+
ctx = expr__ctx_new();
TEST_ASSERT_VAL("expr__ctx_new", ctx);
expr__add_id_val(ctx, strdup("FOO"), 1);
diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
index a248d14882cc..1adb6cd202e0 100644
--- a/tools/perf/util/expr.c
+++ b/tools/perf/util/expr.c
@@ -59,8 +59,48 @@ static bool key_equal(const void *key1, const void *key2,
return !strcmp((const char *)key1, (const char *)key2);
 }
 
-/* Caller must make sure id is allocated */
-int expr__add_id(struct expr_parse_ctx *ctx, const char *id)
+struct hashmap *ids__new(void)
+{
+   return hashmap__new(key_hash, key_equal, NULL);
+}
+
+void ids__free(struct hashmap *ids)
+{
+   struct hashmap_entry *cur;
+   size_t bkt;
+
+   if (ids == NULL)
+   return;
+
+#ifdef PARSER_DEBUG
+   fprintf(stderr, "freeing ids: ");
+   ids__print(ids);
+   fprintf(stderr, "\n");
+#endif
+
+   hashmap__for_each_entry(ids, cur, bkt) {
+   free((char *)cur->key);
+   free(cur->value);
+   }
+
+   hashmap__free(ids);
+}
+
+void ids__print(struct hashmap *ids)
+{
+   size_t bkt;
+   struct hashmap_entry *cur;
+
+   if (!ids)
+   return;
+
+   hashmap__for_each_entry(ids, cur, bkt) {
+   fprintf(stderr, "key:%s, ", (const char *)cur->key);
+   }
+}
+
+int ids__insert(struct hashmap *ids, const char *id,
+   struct expr_id *parent)
 {
struct expr_id_data *data_ptr = NULL, *old_data = NULL;
char *old_key = NULL;
@@ -70,10 +110,10 @@ int expr__add_id(struct expr_parse_ctx *ctx, const char 
*id)
if (!data_ptr)
return -ENOMEM;
 
-   data_ptr->parent = ctx->parent;
+   data_ptr->parent = parent;
data_ptr->kind = EXPR_ID_DATA__PARENT;
 
-   ret = hashmap__set(ctx->ids, id, data_ptr,
+   ret = hashmap__set(ids, id, data_ptr,
   (const void **)_key, (void **)_data);
if (ret)
free(data_ptr);
@@ -82,6 +122,45 @@ int expr__add_id(struct expr_parse_ctx *ctx, const char *id)
return ret;
 }
 
+struct hashmap *ids__union(struct hashmap

[PATCH v4 2/5] perf metric: Use NAN for missing event IDs.

2020-12-01 Thread Ian Rogers
If during computing a metric an event (id) is missing the parsing
aborts. A later patch will make it so that events that aren't used in
the output are deliberately omitted, in which case we don't want the
abort. Modify the missing ID case to report NAN for these cases.

Signed-off-by: Ian Rogers 
---
 tools/perf/util/expr.y | 9 -
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/tools/perf/util/expr.y b/tools/perf/util/expr.y
index b2ada8f8309a..41c9cd4efadd 100644
--- a/tools/perf/util/expr.y
+++ b/tools/perf/util/expr.y
@@ -1,6 +1,7 @@
 /* Simple expression parser */
 %{
 #define YYDEBUG 1
+#include 
 #include 
 #include "util.h"
 #include "util/debug.h"
@@ -88,12 +89,10 @@ expr: NUMBER
| ID{
struct expr_id_data *data;
 
-   if (expr__resolve_id(ctx, $1, )) {
-   free($1);
-   YYABORT;
-   }
+   $$ = NAN;
+   if (expr__resolve_id(ctx, $1, ) == 
0)
+   $$ = expr_id_data__value(data);
 
-   $$ = expr_id_data__value(data);
free($1);
}
| expr '|' expr { $$ = (long)$1 | (long)$3; }
-- 
2.29.2.454.gaff20da3a2-goog



[PATCH v4 0/5] Don't compute events that won't be used in a metric.

2020-12-01 Thread Ian Rogers

For a metric like:
  EVENT1 if #smt_on else EVENT2

currently EVENT1 and EVENT2 will be measured and then when the metric
is reported EVENT1 or EVENT2 will be printed depending on the value
from smt_on() during the expr parsing. Computing both events is
unnecessary and can lead to multiplexing as discussed in this thread:
https://lore.kernel.org/lkml/20201110100346.2527031-1-irog...@google.com/

This change modifies expression parsing so that constants are
considered when building the set of ids (events) and only events not
contributing to a constant value are measured.

v4. reduces references to BOTTOM/NAN in patch 5/5 by using utility
functions. It improves comments and fixes an unnecessary union in a
peephole optimization.

v3. fixes an assignment in patch 2/5. In patch 5/5 additional comments
are added and useless frees are replaced by asserts. A new peephole
optimization is added for the case CONST IF expr ELSE CONST, where the
the constants are identical, as we don't need to evaluate the IF
condition.

v2. is a rebase.

Ian Rogers (5):
  perf metric: Restructure struct expr_parse_ctx.
  perf metric: Use NAN for missing event IDs.
  perf metric: Rename expr__find_other.
  perf metric: Add utilities to work on ids map.
  perf metric: Don't compute unused events.

 tools/perf/tests/expr.c   | 159 +
 tools/perf/tests/pmu-events.c |  42 ++--
 tools/perf/util/expr.c| 136 +--
 tools/perf/util/expr.h|  17 +-
 tools/perf/util/expr.l|   9 -
 tools/perf/util/expr.y| 418 +++---
 tools/perf/util/metricgroup.c |  44 ++--
 tools/perf/util/stat-shadow.c |  54 +++--
 8 files changed, 658 insertions(+), 221 deletions(-)

-- 
2.29.2.454.gaff20da3a2-goog



[PATCH v3 4/5] perf metric: Add utilities to work on ids map.

2020-11-20 Thread Ian Rogers
Add utilities to new/free an ids hashmap, as well as to union. Add
testing of the union. Unioning hashmaps will be used when parsing the
metric, if a value is known then the hashmap is unnecessary, otherwise
we need to union together all the event ids to compute their values for
reporting.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/expr.c | 47 ++
 tools/perf/util/expr.c  | 87 +++--
 tools/perf/util/expr.h  |  9 +
 3 files changed, 139 insertions(+), 4 deletions(-)

diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
index 7ccb97c73347..1c881bea7fca 100644
--- a/tools/perf/tests/expr.c
+++ b/tools/perf/tests/expr.c
@@ -6,6 +6,51 @@
 #include 
 #include 
 
+static int test_ids_union(void)
+{
+   struct hashmap *ids1, *ids2;
+
+   /* Empty union. */
+   ids1 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids1);
+   ids2 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids2);
+
+   ids1 = ids__union(ids1, ids2);
+   TEST_ASSERT_EQUAL("union", (int)hashmap__size(ids1), 0);
+
+   /* Union {foo, bar} against {}. */
+   ids2 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids2);
+
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids1, strdup("foo"), 
NULL), 0);
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids1, strdup("bar"), 
NULL), 0);
+
+   ids1 = ids__union(ids1, ids2);
+   TEST_ASSERT_EQUAL("union", (int)hashmap__size(ids1), 2);
+
+   /* Union {foo, bar} against {foo}. */
+   ids2 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids2);
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids2, strdup("foo"), 
NULL), 0);
+
+   ids1 = ids__union(ids1, ids2);
+   TEST_ASSERT_EQUAL("union", (int)hashmap__size(ids1), 2);
+
+   /* Union {foo, bar} against {bar,baz}. */
+   ids2 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids2);
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids2, strdup("bar"), 
NULL), 0);
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids2, strdup("baz"), 
NULL), 0);
+
+   ids1 = ids__union(ids1, ids2);
+   TEST_ASSERT_EQUAL("union", (int)hashmap__size(ids1), 3);
+
+   ids__free(ids1);
+
+   return 0;
+}
+
 static int test(struct expr_parse_ctx *ctx, const char *e, double val2)
 {
double val;
@@ -24,6 +69,8 @@ int test__expr(struct test *t __maybe_unused, int subtest 
__maybe_unused)
int ret;
struct expr_parse_ctx *ctx;
 
+   TEST_ASSERT_EQUAL("ids_union", test_ids_union(), 0);
+
ctx = expr__ctx_new();
TEST_ASSERT_VAL("expr__ctx_new", ctx);
expr__add_id_val(ctx, strdup("FOO"), 1);
diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
index a248d14882cc..1adb6cd202e0 100644
--- a/tools/perf/util/expr.c
+++ b/tools/perf/util/expr.c
@@ -59,8 +59,48 @@ static bool key_equal(const void *key1, const void *key2,
return !strcmp((const char *)key1, (const char *)key2);
 }
 
-/* Caller must make sure id is allocated */
-int expr__add_id(struct expr_parse_ctx *ctx, const char *id)
+struct hashmap *ids__new(void)
+{
+   return hashmap__new(key_hash, key_equal, NULL);
+}
+
+void ids__free(struct hashmap *ids)
+{
+   struct hashmap_entry *cur;
+   size_t bkt;
+
+   if (ids == NULL)
+   return;
+
+#ifdef PARSER_DEBUG
+   fprintf(stderr, "freeing ids: ");
+   ids__print(ids);
+   fprintf(stderr, "\n");
+#endif
+
+   hashmap__for_each_entry(ids, cur, bkt) {
+   free((char *)cur->key);
+   free(cur->value);
+   }
+
+   hashmap__free(ids);
+}
+
+void ids__print(struct hashmap *ids)
+{
+   size_t bkt;
+   struct hashmap_entry *cur;
+
+   if (!ids)
+   return;
+
+   hashmap__for_each_entry(ids, cur, bkt) {
+   fprintf(stderr, "key:%s, ", (const char *)cur->key);
+   }
+}
+
+int ids__insert(struct hashmap *ids, const char *id,
+   struct expr_id *parent)
 {
struct expr_id_data *data_ptr = NULL, *old_data = NULL;
char *old_key = NULL;
@@ -70,10 +110,10 @@ int expr__add_id(struct expr_parse_ctx *ctx, const char 
*id)
if (!data_ptr)
return -ENOMEM;
 
-   data_ptr->parent = ctx->parent;
+   data_ptr->parent = parent;
data_ptr->kind = EXPR_ID_DATA__PARENT;
 
-   ret = hashmap__set(ctx->ids, id, data_ptr,
+   ret = hashmap__set(ids, id, data_ptr,
   (const void **)_key, (void **)_data);
if (ret)
free(data_ptr);
@@ -82,6 +122,45 @@ int expr__add_id(struct expr_parse_ctx *ctx, const char *id)
return ret;
 }
 
+struct hashmap *ids__union(struct hashmap

[PATCH v3 5/5] perf metric: Don't compute unused events.

2020-11-20 Thread Ian Rogers
For a metric like:
  EVENT1 if #smt_on else EVENT2

currently EVENT1 and EVENT2 will be measured and then when the metric is
reported EVENT1 or EVENT2 will be printed depending on the value from
smt_on() during the expr parsing. Computing both events is unnecessary and
can lead to multiplexing as discussed in this thread:
https://lore.kernel.org/lkml/20201110100346.2527031-1-irog...@google.com/

This change modifies the expression parsing code by:
 - getting rid of the "other" parsing and introducing a boolean argument
   to say whether ids should be computed or not.
 - expressions are changed so that a pair of value and ids are returned.
 - when computing the metric value the ids are unused.
 - when computing the ids, constant values and smt_on are assigned to
   the value.
 - If the value is from an event ID then the event is added to the ids
   hashmap and the value set to NAN.
 - Typically operators union IDs for their inputs and set the value to
   NAN, however, if the inputs are constant then these are computed and
   propagated as the value.
 - If the input is constant to certain operators like:
 IDS1 if CONST else IDS2
   then the result will be either IDS1 or IDS2 depending on CONST (which
   may be evaluated from an entire expression), and so IDS1 or IDS2 may
   be discarded avoiding events from being programmed.
 - The ids at the end of parsing are added to the context.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/expr.c |  17 ++
 tools/perf/util/expr.c  |   9 +-
 tools/perf/util/expr.h  |   1 -
 tools/perf/util/expr.l  |   9 -
 tools/perf/util/expr.y  | 373 
 5 files changed, 319 insertions(+), 90 deletions(-)

diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
index 1c881bea7fca..5cab5960b257 100644
--- a/tools/perf/tests/expr.c
+++ b/tools/perf/tests/expr.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 #include "util/debug.h"
 #include "util/expr.h"
+#include "util/smt.h"
 #include "tests.h"
 #include 
 #include 
@@ -132,6 +133,22 @@ int test__expr(struct test *t __maybe_unused, int subtest 
__maybe_unused)
TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "EVENT2,param=3/",
(void **)_ptr));
 
+   /* Only EVENT1 or EVENT2 need be measured depending on the value of 
smt_on. */
+   expr__ctx_clear(ctx);
+   TEST_ASSERT_VAL("find ids",
+   expr__find_ids("EVENT1 if #smt_on else EVENT2",
+   NULL, ctx, 0) == 0);
+   TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 1);
+   TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids,
+ smt_on() ? "EVENT1" : 
"EVENT2",
+ (void **)_ptr));
+
+   /* The expression is a constant 1.0 without needing to evaluate EVENT1. 
*/
+   expr__ctx_clear(ctx);
+   TEST_ASSERT_VAL("find ids",
+   expr__find_ids("1.0 if EVENT1 > 100.0 else 1.0",
+   NULL, ctx, 0) == 0);
+   TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 0);
expr__ctx_free(ctx);
 
return 0;
diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
index 1adb6cd202e0..28aaa50c6c68 100644
--- a/tools/perf/util/expr.c
+++ b/tools/perf/util/expr.c
@@ -329,10 +329,9 @@ void expr__ctx_free(struct expr_parse_ctx *ctx)
 
 static int
 __expr__parse(double *val, struct expr_parse_ctx *ctx, const char *expr,
- int start, int runtime)
+ bool compute_ids, int runtime)
 {
struct expr_scanner_ctx scanner_ctx = {
-   .start_token = start,
.runtime = runtime,
};
YY_BUFFER_STATE buffer;
@@ -352,7 +351,7 @@ __expr__parse(double *val, struct expr_parse_ctx *ctx, 
const char *expr,
expr_set_debug(1, scanner);
 #endif
 
-   ret = expr_parse(val, ctx, scanner);
+   ret = expr_parse(val, ctx, compute_ids, scanner);
 
expr__flush_buffer(buffer, scanner);
expr__delete_buffer(buffer, scanner);
@@ -363,13 +362,13 @@ __expr__parse(double *val, struct expr_parse_ctx *ctx, 
const char *expr,
 int expr__parse(double *final_val, struct expr_parse_ctx *ctx,
const char *expr, int runtime)
 {
-   return __expr__parse(final_val, ctx, expr, EXPR_PARSE, runtime) ? -1 : 
0;
+   return __expr__parse(final_val, ctx, expr, /*compute_ids=*/false, 
runtime) ? -1 : 0;
 }
 
 int expr__find_ids(const char *expr, const char *one,
   struct expr_parse_ctx *ctx, int runtime)
 {
-   int ret = __expr__parse(NULL, ctx, expr, EXPR_OTHER, runtime);
+   int ret = __expr__parse(NULL, ctx, expr, /*compute_ids=*/true, runtime);
 
if (one)

[PATCH v3 3/5] perf metric: Rename expr__find_other.

2020-11-20 Thread Ian Rogers
A later change will remove the notion of other, rename the function to
expr__find_ids as this is what it populates.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/expr.c   | 26 +-
 tools/perf/tests/pmu-events.c |  9 -
 tools/perf/util/expr.c|  4 ++--
 tools/perf/util/expr.h|  2 +-
 tools/perf/util/metricgroup.c |  2 +-
 tools/perf/util/stat-shadow.c |  6 +++---
 6 files changed, 24 insertions(+), 25 deletions(-)

diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
index b0a3b5fd0c00..7ccb97c73347 100644
--- a/tools/perf/tests/expr.c
+++ b/tools/perf/tests/expr.c
@@ -64,25 +64,25 @@ int test__expr(struct test *t __maybe_unused, int subtest 
__maybe_unused)
TEST_ASSERT_VAL("missing operand", ret == -1);
 
expr__ctx_clear(ctx);
-   TEST_ASSERT_VAL("find other",
-   expr__find_other("FOO + BAR + BAZ + BOZO", "FOO",
-ctx, 1) == 0);
-   TEST_ASSERT_VAL("find other", hashmap__size(ctx->ids) == 3);
-   TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "BAR",
+   TEST_ASSERT_VAL("find ids",
+   expr__find_ids("FOO + BAR + BAZ + BOZO", "FOO",
+   ctx, 1) == 0);
+   TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 3);
+   TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "BAR",
(void **)_ptr));
-   TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "BAZ",
+   TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "BAZ",
(void **)_ptr));
-   TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "BOZO",
+   TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "BOZO",
(void **)_ptr));
 
expr__ctx_clear(ctx);
-   TEST_ASSERT_VAL("find other",
-   expr__find_other("EVENT1\\,param\\=?@ + 
EVENT2\\,param\\=?@",
-NULL, ctx, 3) == 0);
-   TEST_ASSERT_VAL("find other", hashmap__size(ctx->ids) == 2);
-   TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "EVENT1,param=3/",
+   TEST_ASSERT_VAL("find ids",
+   expr__find_ids("EVENT1\\,param\\=?@ + 
EVENT2\\,param\\=?@",
+   NULL, ctx, 3) == 0);
+   TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 2);
+   TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "EVENT1,param=3/",
(void **)_ptr));
-   TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "EVENT2,param=3/",
+   TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids, "EVENT2,param=3/",
(void **)_ptr));
 
expr__ctx_free(ctx);
diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c
index 294daf568bb6..3ac70fa31379 100644
--- a/tools/perf/tests/pmu-events.c
+++ b/tools/perf/tests/pmu-events.c
@@ -502,9 +502,8 @@ static int test_parsing(void)
if (!pe->metric_expr)
continue;
expr__ctx_clear(ctx);
-   if (expr__find_other(pe->metric_expr, NULL, ctx, 0)
- < 0) {
-   expr_failure("Parse other failed", map, pe);
+   if (expr__find_ids(pe->metric_expr, NULL, ctx, 0) < 0) {
+   expr_failure("Parse find ids failed", map, pe);
ret++;
continue;
}
@@ -559,8 +558,8 @@ static int metric_parse_fake(const char *str)
pr_debug("parsing '%s'\n", str);
 
ctx = expr__ctx_new();
-   if (expr__find_other(str, NULL, ctx, 0) < 0) {
-   pr_err("expr__find_other failed\n");
+   if (expr__find_ids(str, NULL, ctx, 0) < 0) {
+   pr_err("expr__find_ids failed\n");
return -1;
}
 
diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
index e0623d38e6ee..a248d14882cc 100644
--- a/tools/perf/util/expr.c
+++ b/tools/perf/util/expr.c
@@ -287,8 +287,8 @@ int expr__parse(double *final_val, struct expr_parse_ctx 
*ctx,
return __expr__parse(final_val, ctx, expr, EXPR_PARSE, runtime) ? -1 : 
0;
 }
 
-int expr__find_other(const char *expr, const char 

[PATCH v3 2/5] perf metric: Use NAN for missing event IDs.

2020-11-20 Thread Ian Rogers
If during computing a metric an event (id) is missing the parsing
aborts. A later patch will make it so that events that aren't used in
the output are deliberately omitted, in which case we don't want the
abort. Modify the missing ID case to report NAN for these cases.

Signed-off-by: Ian Rogers 
---
 tools/perf/util/expr.y | 9 -
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/tools/perf/util/expr.y b/tools/perf/util/expr.y
index b2ada8f8309a..41c9cd4efadd 100644
--- a/tools/perf/util/expr.y
+++ b/tools/perf/util/expr.y
@@ -1,6 +1,7 @@
 /* Simple expression parser */
 %{
 #define YYDEBUG 1
+#include 
 #include 
 #include "util.h"
 #include "util/debug.h"
@@ -88,12 +89,10 @@ expr: NUMBER
| ID{
struct expr_id_data *data;
 
-   if (expr__resolve_id(ctx, $1, )) {
-   free($1);
-   YYABORT;
-   }
+   $$ = NAN;
+   if (expr__resolve_id(ctx, $1, ) == 
0)
+   $$ = expr_id_data__value(data);
 
-   $$ = expr_id_data__value(data);
free($1);
}
| expr '|' expr { $$ = (long)$1 | (long)$3; }
-- 
2.29.2.454.gaff20da3a2-goog



[PATCH v3 1/5] perf metric: Restructure struct expr_parse_ctx.

2020-11-20 Thread Ian Rogers
A later change to parsing the ids out (in expr__find_other) will
potentially drop hashmaps and so it is more convenient to move
expr_parse_ctx to have a hashmap pointer rather than a struct value. As
this pointer must be freed, rather than just going out of scope,
add expr__ctx_new and expr__ctx_free to manage expr_parse_ctx memory.
Adjust use of struct expr_parse_ctx accordingly.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/expr.c   | 81 ++-
 tools/perf/tests/pmu-events.c | 37 +---
 tools/perf/util/expr.c| 38 
 tools/perf/util/expr.h|  5 ++-
 tools/perf/util/metricgroup.c | 44 ++-
 tools/perf/util/stat-shadow.c | 50 +
 6 files changed, 151 insertions(+), 104 deletions(-)

diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
index 4d01051951cd..b0a3b5fd0c00 100644
--- a/tools/perf/tests/expr.c
+++ b/tools/perf/tests/expr.c
@@ -22,67 +22,70 @@ int test__expr(struct test *t __maybe_unused, int subtest 
__maybe_unused)
const char *p;
double val;
int ret;
-   struct expr_parse_ctx ctx;
+   struct expr_parse_ctx *ctx;
 
-   expr__ctx_init();
-   expr__add_id_val(, strdup("FOO"), 1);
-   expr__add_id_val(, strdup("BAR"), 2);
+   ctx = expr__ctx_new();
+   TEST_ASSERT_VAL("expr__ctx_new", ctx);
+   expr__add_id_val(ctx, strdup("FOO"), 1);
+   expr__add_id_val(ctx, strdup("BAR"), 2);
 
-   ret = test(, "1+1", 2);
-   ret |= test(, "FOO+BAR", 3);
-   ret |= test(, "(BAR/2)%2", 1);
-   ret |= test(, "1 - -4",  5);
-   ret |= test(, "(FOO-1)*2 + (BAR/2)%2 - -4",  5);
-   ret |= test(, "1-1 | 1", 1);
-   ret |= test(, "1-1 & 1", 0);
-   ret |= test(, "min(1,2) + 1", 2);
-   ret |= test(, "max(1,2) + 1", 3);
-   ret |= test(, "1+1 if 3*4 else 0", 2);
-   ret |= test(, "1.1 + 2.1", 3.2);
-   ret |= test(, ".1 + 2.", 2.1);
-   ret |= test(, "d_ratio(1, 2)", 0.5);
-   ret |= test(, "d_ratio(2.5, 0)", 0);
-   ret |= test(, "1.1 < 2.2", 1);
-   ret |= test(, "2.2 > 1.1", 1);
-   ret |= test(, "1.1 < 1.1", 0);
-   ret |= test(, "2.2 > 2.2", 0);
-   ret |= test(, "2.2 < 1.1", 0);
-   ret |= test(, "1.1 > 2.2", 0);
+   ret = test(ctx, "1+1", 2);
+   ret |= test(ctx, "FOO+BAR", 3);
+   ret |= test(ctx, "(BAR/2)%2", 1);
+   ret |= test(ctx, "1 - -4",  5);
+   ret |= test(ctx, "(FOO-1)*2 + (BAR/2)%2 - -4",  5);
+   ret |= test(ctx, "1-1 | 1", 1);
+   ret |= test(ctx, "1-1 & 1", 0);
+   ret |= test(ctx, "min(1,2) + 1", 2);
+   ret |= test(ctx, "max(1,2) + 1", 3);
+   ret |= test(ctx, "1+1 if 3*4 else 0", 2);
+   ret |= test(ctx, "1.1 + 2.1", 3.2);
+   ret |= test(ctx, ".1 + 2.", 2.1);
+   ret |= test(ctx, "d_ratio(1, 2)", 0.5);
+   ret |= test(ctx, "d_ratio(2.5, 0)", 0);
+   ret |= test(ctx, "1.1 < 2.2", 1);
+   ret |= test(ctx, "2.2 > 1.1", 1);
+   ret |= test(ctx, "1.1 < 1.1", 0);
+   ret |= test(ctx, "2.2 > 2.2", 0);
+   ret |= test(ctx, "2.2 < 1.1", 0);
+   ret |= test(ctx, "1.1 > 2.2", 0);
 
-   if (ret)
+   if (ret) {
+   expr__ctx_free(ctx);
return ret;
+   }
 
p = "FOO/0";
-   ret = expr__parse(, , p, 1);
+   ret = expr__parse(, ctx, p, 1);
TEST_ASSERT_VAL("division by zero", ret == -1);
 
p = "BAR/";
-   ret = expr__parse(, , p, 1);
+   ret = expr__parse(, ctx, p, 1);
TEST_ASSERT_VAL("missing operand", ret == -1);
 
-   expr__ctx_clear();
+   expr__ctx_clear(ctx);
TEST_ASSERT_VAL("find other",
expr__find_other("FOO + BAR + BAZ + BOZO", "FOO",
-, 1) == 0);
-   TEST_ASSERT_VAL("find other", hashmap__size() == 3);
-   TEST_ASSERT_VAL("find other", hashmap__find(, "BAR",
+ctx, 1) == 0);
+   TEST_ASSERT_VAL("find other", hashmap__size(ctx->ids) == 3);
+   TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "BAR",
(void **)_ptr));
-   TEST_ASSERT_VAL("find other", hashmap__find(, "BAZ",
+   TEST_ASSERT_VAL("find other", hashmap__find(

[PATCH v3 0/5] Don't compute events that won't be used in a metric.

2020-11-20 Thread Ian Rogers

For a metric like:
  EVENT1 if #smt_on else EVENT2

currently EVENT1 and EVENT2 will be measured and then when the metric
is reported EVENT1 or EVENT2 will be printed depending on the value
from smt_on() during the expr parsing. Computing both events is
unnecessary and can lead to multiplexing as discussed in this thread:
https://lore.kernel.org/lkml/20201110100346.2527031-1-irog...@google.com/

This change modifies expression parsing so that constants are
considered when building the set of ids (events) and only events not
contributing to a constant value are measured.

v3. fixes an assignment in patch 2/5. In patch 5/5 additional comments
are added and useless frees are replaced by asserts. A new peephole
optimization is added for the case CONST IF expr ELSE CONST, where the
the constants are identical, as we don't need to evaluate the IF
condition.

v2. is a rebase.

Ian Rogers (5):
  perf metric: Restructure struct expr_parse_ctx.
  perf metric: Use NAN for missing event IDs.
  perf metric: Rename expr__find_other.
  perf metric: Add utilities to work on ids map.
  perf metric: Don't compute unused events.

 tools/perf/tests/expr.c   | 159 +-
 tools/perf/tests/pmu-events.c |  42 ++--
 tools/perf/util/expr.c| 136 ++--
 tools/perf/util/expr.h|  17 +-
 tools/perf/util/expr.l|   9 -
 tools/perf/util/expr.y| 376 +++---
 tools/perf/util/metricgroup.c |  44 ++--
 tools/perf/util/stat-shadow.c |  54 +++--
 8 files changed, 623 insertions(+), 214 deletions(-)

-- 
2.29.2.454.gaff20da3a2-goog



[PATCH v2] perf docs: Add man pages to see also

2020-11-19 Thread Ian Rogers
Add all other man pages to the "see also" list except for
perf-script-perl and perf-script-python that are linked to from
perf-script.

v2. Fix accidentally listing perf-top twice.

Signed-off-by: Ian Rogers 
---
 tools/perf/Documentation/perf.txt | 12 
 1 file changed, 12 insertions(+)

diff --git a/tools/perf/Documentation/perf.txt 
b/tools/perf/Documentation/perf.txt
index c130a3c46a90..9c330cdfa973 100644
--- a/tools/perf/Documentation/perf.txt
+++ b/tools/perf/Documentation/perf.txt
@@ -76,3 +76,15 @@ SEE ALSO
 linkperf:perf-stat[1], linkperf:perf-top[1],
 linkperf:perf-record[1], linkperf:perf-report[1],
 linkperf:perf-list[1]
+
+linkperf:perf-annotate[1],linkperf:perf-archive[1],
+linkperf:perf-bench[1], linkperf:perf-buildid-cache[1],
+linkperf:perf-buildid-list[1], linkperf:perf-c2c[1],
+linkperf:perf-config[1], linkperf:perf-data[1], linkperf:perf-diff[1],
+linkperf:perf-evlist[1], linkperf:perf-ftrace[1],
+linkperf:perf-help[1], linkperf:perf-inject[1],
+linkperf:perf-intel-pt[1], linkperf:perf-kallsyms[1],
+linkperf:perf-kmem[1], linkperf:perf-kvm[1], linkperf:perf-lock[1],
+linkperf:perf-mem[1], linkperf:perf-probe[1], linkperf:perf-sched[1],
+linkperf:perf-script[1], linkperf:perf-test[1],
+linkperf:perf-trace[1], linkperf:perf-version[1]
-- 
2.29.2.454.gaff20da3a2-goog



[PATCH] perf docs: Add man pages to see also

2020-11-19 Thread Ian Rogers
Add all other man pages to the "see also" list except for
perf-script-perl and perf-script-python that are linked to from
perf-script.

Signed-off-by: Ian Rogers 
---
 tools/perf/Documentation/perf.txt | 12 
 1 file changed, 12 insertions(+)

diff --git a/tools/perf/Documentation/perf.txt 
b/tools/perf/Documentation/perf.txt
index c130a3c46a90..c18b5872277e 100644
--- a/tools/perf/Documentation/perf.txt
+++ b/tools/perf/Documentation/perf.txt
@@ -76,3 +76,15 @@ SEE ALSO
 linkperf:perf-stat[1], linkperf:perf-top[1],
 linkperf:perf-record[1], linkperf:perf-report[1],
 linkperf:perf-list[1]
+
+linkperf:perf-annotate[1],linkperf:perf-archive[1],
+linkperf:perf-bench[1], linkperf:perf-buildid-cache[1],
+linkperf:perf-buildid-list[1], linkperf:perf-c2c[1],
+linkperf:perf-config[1], linkperf:perf-data[1], linkperf:perf-diff[1],
+linkperf:perf-evlist[1], linkperf:perf-ftrace[1],
+linkperf:perf-help[1], linkperf:perf-inject[1],
+linkperf:perf-intel-pt[1], linkperf:perf-kallsyms[1],
+linkperf:perf-kmem[1], linkperf:perf-kvm[1], linkperf:perf-lock[1],
+linkperf:perf-mem[1], linkperf:perf-probe[1], linkperf:perf-sched[1],
+linkperf:perf-script[1], linkperf:perf-test[1], linkperf:perf-top[1],
+linkperf:perf-trace[1], linkperf:perf-version[1]
-- 
2.29.2.454.gaff20da3a2-goog



[PATCH v2 1/5] perf metric: Restructure struct expr_parse_ctx.

2020-11-17 Thread Ian Rogers
A later change to parsing the ids out (in expr__find_other) will
potentially drop hashmaps and so it is more convenient to move
expr_parse_ctx to have a hashmap pointer rather than a struct value. As
this pointer must be freed, rather than just going out of scope,
add expr__ctx_new and expr__ctx_free to manage expr_parse_ctx memory.
Adjust use of struct expr_parse_ctx accordingly.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/expr.c   | 81 ++-
 tools/perf/tests/pmu-events.c | 37 +---
 tools/perf/util/expr.c| 38 
 tools/perf/util/expr.h|  5 ++-
 tools/perf/util/metricgroup.c | 44 ++-
 tools/perf/util/stat-shadow.c | 50 +
 6 files changed, 151 insertions(+), 104 deletions(-)

diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
index 4d01051951cd..b0a3b5fd0c00 100644
--- a/tools/perf/tests/expr.c
+++ b/tools/perf/tests/expr.c
@@ -22,67 +22,70 @@ int test__expr(struct test *t __maybe_unused, int subtest 
__maybe_unused)
const char *p;
double val;
int ret;
-   struct expr_parse_ctx ctx;
+   struct expr_parse_ctx *ctx;
 
-   expr__ctx_init();
-   expr__add_id_val(, strdup("FOO"), 1);
-   expr__add_id_val(, strdup("BAR"), 2);
+   ctx = expr__ctx_new();
+   TEST_ASSERT_VAL("expr__ctx_new", ctx);
+   expr__add_id_val(ctx, strdup("FOO"), 1);
+   expr__add_id_val(ctx, strdup("BAR"), 2);
 
-   ret = test(, "1+1", 2);
-   ret |= test(, "FOO+BAR", 3);
-   ret |= test(, "(BAR/2)%2", 1);
-   ret |= test(, "1 - -4",  5);
-   ret |= test(, "(FOO-1)*2 + (BAR/2)%2 - -4",  5);
-   ret |= test(, "1-1 | 1", 1);
-   ret |= test(, "1-1 & 1", 0);
-   ret |= test(, "min(1,2) + 1", 2);
-   ret |= test(, "max(1,2) + 1", 3);
-   ret |= test(, "1+1 if 3*4 else 0", 2);
-   ret |= test(, "1.1 + 2.1", 3.2);
-   ret |= test(, ".1 + 2.", 2.1);
-   ret |= test(, "d_ratio(1, 2)", 0.5);
-   ret |= test(, "d_ratio(2.5, 0)", 0);
-   ret |= test(, "1.1 < 2.2", 1);
-   ret |= test(, "2.2 > 1.1", 1);
-   ret |= test(, "1.1 < 1.1", 0);
-   ret |= test(, "2.2 > 2.2", 0);
-   ret |= test(, "2.2 < 1.1", 0);
-   ret |= test(, "1.1 > 2.2", 0);
+   ret = test(ctx, "1+1", 2);
+   ret |= test(ctx, "FOO+BAR", 3);
+   ret |= test(ctx, "(BAR/2)%2", 1);
+   ret |= test(ctx, "1 - -4",  5);
+   ret |= test(ctx, "(FOO-1)*2 + (BAR/2)%2 - -4",  5);
+   ret |= test(ctx, "1-1 | 1", 1);
+   ret |= test(ctx, "1-1 & 1", 0);
+   ret |= test(ctx, "min(1,2) + 1", 2);
+   ret |= test(ctx, "max(1,2) + 1", 3);
+   ret |= test(ctx, "1+1 if 3*4 else 0", 2);
+   ret |= test(ctx, "1.1 + 2.1", 3.2);
+   ret |= test(ctx, ".1 + 2.", 2.1);
+   ret |= test(ctx, "d_ratio(1, 2)", 0.5);
+   ret |= test(ctx, "d_ratio(2.5, 0)", 0);
+   ret |= test(ctx, "1.1 < 2.2", 1);
+   ret |= test(ctx, "2.2 > 1.1", 1);
+   ret |= test(ctx, "1.1 < 1.1", 0);
+   ret |= test(ctx, "2.2 > 2.2", 0);
+   ret |= test(ctx, "2.2 < 1.1", 0);
+   ret |= test(ctx, "1.1 > 2.2", 0);
 
-   if (ret)
+   if (ret) {
+   expr__ctx_free(ctx);
return ret;
+   }
 
p = "FOO/0";
-   ret = expr__parse(, , p, 1);
+   ret = expr__parse(, ctx, p, 1);
TEST_ASSERT_VAL("division by zero", ret == -1);
 
p = "BAR/";
-   ret = expr__parse(, , p, 1);
+   ret = expr__parse(, ctx, p, 1);
TEST_ASSERT_VAL("missing operand", ret == -1);
 
-   expr__ctx_clear();
+   expr__ctx_clear(ctx);
TEST_ASSERT_VAL("find other",
expr__find_other("FOO + BAR + BAZ + BOZO", "FOO",
-, 1) == 0);
-   TEST_ASSERT_VAL("find other", hashmap__size() == 3);
-   TEST_ASSERT_VAL("find other", hashmap__find(, "BAR",
+ctx, 1) == 0);
+   TEST_ASSERT_VAL("find other", hashmap__size(ctx->ids) == 3);
+   TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "BAR",
(void **)_ptr));
-   TEST_ASSERT_VAL("find other", hashmap__find(, "BAZ",
+   TEST_ASSERT_VAL("find other", hashmap__find(

[PATCH v2 5/5] perf metric: Don't compute unused events.

2020-11-17 Thread Ian Rogers
For a metric like:
  EVENT1 if #smt_on else EVENT2

currently EVENT1 and EVENT2 will be measured and then when the metric is
reported EVENT1 or EVENT2 will be printed depending on the value from
smt_on() during the expr parsing. Computing both events is unnecessary and
can lead to multiplexing as discussed in this thread:
https://lore.kernel.org/lkml/20201110100346.2527031-1-irog...@google.com/

This change modifies the expression parsing code by:
 - getting rid of the "other" parsing and introducing a boolean argument
   to say whether ids should be computed or not.
 - expressions are changed so that a pair of value and ids are returned.
 - when computing the metric value the ids are unused.
 - when computing the ids, constant values and smt_on are assigned to
   the value.
 - If the value is from an event ID then the event is added to the ids
   hashmap and the value set to NAN.
 - Typically operators union IDs for their inputs and set the value to
   NAN, however, if the inputs are constant then these are computed and
   propagated as the value.
 - If the input is constant to certain operators like:
 IDS1 if CONST else IDS2
   then the result will be either IDS1 or IDS2 depending on CONST which
   may be evaluated from an entire expression.
 - The ids at the end of parsing are added to the context.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/expr.c |  10 ++
 tools/perf/util/expr.c  |   9 +-
 tools/perf/util/expr.h  |   1 -
 tools/perf/util/expr.l  |   9 --
 tools/perf/util/expr.y  | 341 +++-
 5 files changed, 284 insertions(+), 86 deletions(-)

diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
index 7c2a01cf0650..94ddd01b29fc 100644
--- a/tools/perf/tests/expr.c
+++ b/tools/perf/tests/expr.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 #include "util/debug.h"
 #include "util/expr.h"
+#include "util/smt.h"
 #include "tests.h"
 #include 
 #include 
@@ -132,6 +133,15 @@ int test__expr(struct test *t __maybe_unused, int subtest 
__maybe_unused)
TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "EVENT2,param=3/",
(void **)_ptr));
 
+   expr__ctx_clear(ctx);
+   TEST_ASSERT_VAL("find ids",
+   expr__find_ids("EVENT1 if #smt_on else EVENT2",
+   NULL, ctx, 0) == 0);
+   TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 1);
+   TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids,
+ smt_on() ? "EVENT1" : 
"EVENT2",
+ (void **)_ptr));
+
expr__ctx_free(ctx);
 
return 0;
diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
index 1adb6cd202e0..28aaa50c6c68 100644
--- a/tools/perf/util/expr.c
+++ b/tools/perf/util/expr.c
@@ -329,10 +329,9 @@ void expr__ctx_free(struct expr_parse_ctx *ctx)
 
 static int
 __expr__parse(double *val, struct expr_parse_ctx *ctx, const char *expr,
- int start, int runtime)
+ bool compute_ids, int runtime)
 {
struct expr_scanner_ctx scanner_ctx = {
-   .start_token = start,
.runtime = runtime,
};
YY_BUFFER_STATE buffer;
@@ -352,7 +351,7 @@ __expr__parse(double *val, struct expr_parse_ctx *ctx, 
const char *expr,
expr_set_debug(1, scanner);
 #endif
 
-   ret = expr_parse(val, ctx, scanner);
+   ret = expr_parse(val, ctx, compute_ids, scanner);
 
expr__flush_buffer(buffer, scanner);
expr__delete_buffer(buffer, scanner);
@@ -363,13 +362,13 @@ __expr__parse(double *val, struct expr_parse_ctx *ctx, 
const char *expr,
 int expr__parse(double *final_val, struct expr_parse_ctx *ctx,
const char *expr, int runtime)
 {
-   return __expr__parse(final_val, ctx, expr, EXPR_PARSE, runtime) ? -1 : 
0;
+   return __expr__parse(final_val, ctx, expr, /*compute_ids=*/false, 
runtime) ? -1 : 0;
 }
 
 int expr__find_ids(const char *expr, const char *one,
   struct expr_parse_ctx *ctx, int runtime)
 {
-   int ret = __expr__parse(NULL, ctx, expr, EXPR_OTHER, runtime);
+   int ret = __expr__parse(NULL, ctx, expr, /*compute_ids=*/true, runtime);
 
if (one)
expr__del_id(ctx, one);
diff --git a/tools/perf/util/expr.h b/tools/perf/util/expr.h
index 62d3ae5ddfba..cefeb2c8d85e 100644
--- a/tools/perf/util/expr.h
+++ b/tools/perf/util/expr.h
@@ -26,7 +26,6 @@ struct expr_parse_ctx {
 struct expr_id_data;
 
 struct expr_scanner_ctx {
-   int start_token;
int runtime;
 };
 
diff --git a/tools/perf/util/expr.l b/tools/perf/util/expr.l
index 13e5e3c75f56..702fdf6456ca 100644
--- a/tools/perf/util/expr.l
+++ b/tools/perf/util/expr.l
@@ -91,15 +91,6 @@ symbol   ({spec}|{sy

[PATCH v2 3/5] perf metric: Rename expr__find_other.

2020-11-17 Thread Ian Rogers
A later change will remove the notion of other, rename the function to
expr__find_ids as this is what it populated.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/expr.c   | 8 
 tools/perf/tests/pmu-events.c | 9 -
 tools/perf/util/expr.c| 4 ++--
 tools/perf/util/expr.h| 2 +-
 tools/perf/util/metricgroup.c | 2 +-
 tools/perf/util/stat-shadow.c | 6 +++---
 6 files changed, 15 insertions(+), 16 deletions(-)

diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
index b0a3b5fd0c00..9d7032041318 100644
--- a/tools/perf/tests/expr.c
+++ b/tools/perf/tests/expr.c
@@ -65,8 +65,8 @@ int test__expr(struct test *t __maybe_unused, int subtest 
__maybe_unused)
 
expr__ctx_clear(ctx);
TEST_ASSERT_VAL("find other",
-   expr__find_other("FOO + BAR + BAZ + BOZO", "FOO",
-ctx, 1) == 0);
+   expr__find_ids("FOO + BAR + BAZ + BOZO", "FOO",
+   ctx, 1) == 0);
TEST_ASSERT_VAL("find other", hashmap__size(ctx->ids) == 3);
TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "BAR",
(void **)_ptr));
@@ -77,8 +77,8 @@ int test__expr(struct test *t __maybe_unused, int subtest 
__maybe_unused)
 
expr__ctx_clear(ctx);
TEST_ASSERT_VAL("find other",
-   expr__find_other("EVENT1\\,param\\=?@ + 
EVENT2\\,param\\=?@",
-NULL, ctx, 3) == 0);
+   expr__find_ids("EVENT1\\,param\\=?@ + 
EVENT2\\,param\\=?@",
+   NULL, ctx, 3) == 0);
TEST_ASSERT_VAL("find other", hashmap__size(ctx->ids) == 2);
TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "EVENT1,param=3/",
(void **)_ptr));
diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c
index 294daf568bb6..3ac70fa31379 100644
--- a/tools/perf/tests/pmu-events.c
+++ b/tools/perf/tests/pmu-events.c
@@ -502,9 +502,8 @@ static int test_parsing(void)
if (!pe->metric_expr)
continue;
expr__ctx_clear(ctx);
-   if (expr__find_other(pe->metric_expr, NULL, ctx, 0)
- < 0) {
-   expr_failure("Parse other failed", map, pe);
+   if (expr__find_ids(pe->metric_expr, NULL, ctx, 0) < 0) {
+   expr_failure("Parse find ids failed", map, pe);
ret++;
continue;
}
@@ -559,8 +558,8 @@ static int metric_parse_fake(const char *str)
pr_debug("parsing '%s'\n", str);
 
ctx = expr__ctx_new();
-   if (expr__find_other(str, NULL, ctx, 0) < 0) {
-   pr_err("expr__find_other failed\n");
+   if (expr__find_ids(str, NULL, ctx, 0) < 0) {
+   pr_err("expr__find_ids failed\n");
return -1;
}
 
diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
index e0623d38e6ee..a248d14882cc 100644
--- a/tools/perf/util/expr.c
+++ b/tools/perf/util/expr.c
@@ -287,8 +287,8 @@ int expr__parse(double *final_val, struct expr_parse_ctx 
*ctx,
return __expr__parse(final_val, ctx, expr, EXPR_PARSE, runtime) ? -1 : 
0;
 }
 
-int expr__find_other(const char *expr, const char *one,
-struct expr_parse_ctx *ctx, int runtime)
+int expr__find_ids(const char *expr, const char *one,
+  struct expr_parse_ctx *ctx, int runtime)
 {
int ret = __expr__parse(NULL, ctx, expr, EXPR_OTHER, runtime);
 
diff --git a/tools/perf/util/expr.h b/tools/perf/util/expr.h
index 00b941cfe6a6..955d5adb7ca4 100644
--- a/tools/perf/util/expr.h
+++ b/tools/perf/util/expr.h
@@ -43,7 +43,7 @@ int expr__resolve_id(struct expr_parse_ctx *ctx, const char 
*id,
 struct expr_id_data **datap);
 int expr__parse(double *final_val, struct expr_parse_ctx *ctx,
const char *expr, int runtime);
-int expr__find_other(const char *expr, const char *one,
+int expr__find_ids(const char *expr, const char *one,
struct expr_parse_ctx *ids, int runtime);
 
 double expr_id_data__value(const struct expr_id_data *data);
diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c
index 342dcccb860f..0be684bb020f 100644
--- a/tools/perf/util/metricgroup.c
+++ b/tools/perf/util/metricgroup.c
@@ -761,7 +761,7 @@ static int __add_metric(struct list_head *metric_list,
 * For both the parent and referenced metrics, we parse
 * all the metric's 

[PATCH v2 4/5] perf metric: Add utilities to work on ids map.

2020-11-17 Thread Ian Rogers
Add utilities to new/free an ids hashmap, as well as to union. Add
testing of the union. Unioning hashmaps will be used when parsing the
metric, if a value is known then the hashmap is unnecessary, otherwise
we need to union together all the event ids to compute their values for
reporting.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/expr.c | 47 ++
 tools/perf/util/expr.c  | 87 +++--
 tools/perf/util/expr.h  |  9 +
 3 files changed, 139 insertions(+), 4 deletions(-)

diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
index 9d7032041318..7c2a01cf0650 100644
--- a/tools/perf/tests/expr.c
+++ b/tools/perf/tests/expr.c
@@ -6,6 +6,51 @@
 #include 
 #include 
 
+static int test_ids_union(void)
+{
+   struct hashmap *ids1, *ids2;
+
+   /* Empty union. */
+   ids1 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids1);
+   ids2 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids2);
+
+   ids1 = ids__union(ids1, ids2);
+   TEST_ASSERT_EQUAL("union", (int)hashmap__size(ids1), 0);
+
+   /* Union {foo, bar} against {}. */
+   ids2 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids2);
+
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids1, strdup("foo"), 
NULL), 0);
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids1, strdup("bar"), 
NULL), 0);
+
+   ids1 = ids__union(ids1, ids2);
+   TEST_ASSERT_EQUAL("union", (int)hashmap__size(ids1), 2);
+
+   /* Union {foo, bar} against {foo}. */
+   ids2 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids2);
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids2, strdup("foo"), 
NULL), 0);
+
+   ids1 = ids__union(ids1, ids2);
+   TEST_ASSERT_EQUAL("union", (int)hashmap__size(ids1), 2);
+
+   /* Union {foo, bar} against {bar,baz}. */
+   ids2 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids2);
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids2, strdup("bar"), 
NULL), 0);
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids2, strdup("baz"), 
NULL), 0);
+
+   ids1 = ids__union(ids1, ids2);
+   TEST_ASSERT_EQUAL("union", (int)hashmap__size(ids1), 3);
+
+   ids__free(ids1);
+
+   return 0;
+}
+
 static int test(struct expr_parse_ctx *ctx, const char *e, double val2)
 {
double val;
@@ -24,6 +69,8 @@ int test__expr(struct test *t __maybe_unused, int subtest 
__maybe_unused)
int ret;
struct expr_parse_ctx *ctx;
 
+   TEST_ASSERT_EQUAL("ids_union", test_ids_union(), 0);
+
ctx = expr__ctx_new();
TEST_ASSERT_VAL("expr__ctx_new", ctx);
expr__add_id_val(ctx, strdup("FOO"), 1);
diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
index a248d14882cc..1adb6cd202e0 100644
--- a/tools/perf/util/expr.c
+++ b/tools/perf/util/expr.c
@@ -59,8 +59,48 @@ static bool key_equal(const void *key1, const void *key2,
return !strcmp((const char *)key1, (const char *)key2);
 }
 
-/* Caller must make sure id is allocated */
-int expr__add_id(struct expr_parse_ctx *ctx, const char *id)
+struct hashmap *ids__new(void)
+{
+   return hashmap__new(key_hash, key_equal, NULL);
+}
+
+void ids__free(struct hashmap *ids)
+{
+   struct hashmap_entry *cur;
+   size_t bkt;
+
+   if (ids == NULL)
+   return;
+
+#ifdef PARSER_DEBUG
+   fprintf(stderr, "freeing ids: ");
+   ids__print(ids);
+   fprintf(stderr, "\n");
+#endif
+
+   hashmap__for_each_entry(ids, cur, bkt) {
+   free((char *)cur->key);
+   free(cur->value);
+   }
+
+   hashmap__free(ids);
+}
+
+void ids__print(struct hashmap *ids)
+{
+   size_t bkt;
+   struct hashmap_entry *cur;
+
+   if (!ids)
+   return;
+
+   hashmap__for_each_entry(ids, cur, bkt) {
+   fprintf(stderr, "key:%s, ", (const char *)cur->key);
+   }
+}
+
+int ids__insert(struct hashmap *ids, const char *id,
+   struct expr_id *parent)
 {
struct expr_id_data *data_ptr = NULL, *old_data = NULL;
char *old_key = NULL;
@@ -70,10 +110,10 @@ int expr__add_id(struct expr_parse_ctx *ctx, const char 
*id)
if (!data_ptr)
return -ENOMEM;
 
-   data_ptr->parent = ctx->parent;
+   data_ptr->parent = parent;
data_ptr->kind = EXPR_ID_DATA__PARENT;
 
-   ret = hashmap__set(ctx->ids, id, data_ptr,
+   ret = hashmap__set(ids, id, data_ptr,
   (const void **)_key, (void **)_data);
if (ret)
free(data_ptr);
@@ -82,6 +122,45 @@ int expr__add_id(struct expr_parse_ctx *ctx, const char *id)
return ret;
 }
 
+struct hashmap *ids__union(struct hashmap

[PATCH v2 2/5] perf metric: Use NAN for missing event IDs.

2020-11-17 Thread Ian Rogers
If during computing a metric an event (id) is missing the parsing
aborts. A later patch will make it so that events that aren't used in
the output are deliberately omitted, in which case we don't want the
abort. Modify the missing ID case to report NAN for these cases.

Signed-off-by: Ian Rogers 
---
 tools/perf/util/expr.y | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/tools/perf/util/expr.y b/tools/perf/util/expr.y
index b2ada8f8309a..c22e3500a40f 100644
--- a/tools/perf/util/expr.y
+++ b/tools/perf/util/expr.y
@@ -1,6 +1,7 @@
 /* Simple expression parser */
 %{
 #define YYDEBUG 1
+#include 
 #include 
 #include "util.h"
 #include "util/debug.h"
@@ -89,8 +90,7 @@ expr:   NUMBER
struct expr_id_data *data;
 
if (expr__resolve_id(ctx, $1, )) {
-   free($1);
-   YYABORT;
+   $$ = NAN;
}
 
$$ = expr_id_data__value(data);
-- 
2.29.2.299.gdc1121823c-goog



[PATCH v2 0/5] Don't compute events that won't be used in a metric.

2020-11-17 Thread Ian Rogers

For a metric like:
  EVENT1 if #smt_on else EVENT2

currently EVENT1 and EVENT2 will be measured and then when the metric
is reported EVENT1 or EVENT2 will be printed depending on the value
from smt_on() during the expr parsing. Computing both events is
unnecessary and can lead to multiplexing as discussed in this thread:
https://lore.kernel.org/lkml/20201110100346.2527031-1-irog...@google.com/

This change modifies expression parsing so that constants are
considered when building the set of ids (events) and only events not
contributing to a constant value are measured.

v2. is a rebase.

Ian Rogers (5):
  perf metric: Restructure struct expr_parse_ctx.
  perf metric: Use NAN for missing event IDs.
  perf metric: Rename expr__find_other.
  perf metric: Add utilities to work on ids map.
  perf metric: Don't compute unused events.

 tools/perf/tests/expr.c   | 148 ++-
 tools/perf/tests/pmu-events.c |  42 +++--
 tools/perf/util/expr.c| 136 --
 tools/perf/util/expr.h|  17 +-
 tools/perf/util/expr.l|   9 -
 tools/perf/util/expr.y| 343 +++---
 tools/perf/util/metricgroup.c |  44 +++--
 tools/perf/util/stat-shadow.c |  54 --
 8 files changed, 586 insertions(+), 207 deletions(-)

-- 
2.29.2.299.gdc1121823c-goog



[PATCH] perf test: Fix dwarf unwind for optimized builds.

2020-11-13 Thread Ian Rogers
To ensure the stack frames are on the stack tail calls optimizations
need to be inhibited. If your compiler supports an attribute use it,
otherwise use an asm volatile barrier.

The barrier fix was suggested here:
https://lore.kernel.org/lkml/20201028081123.gt2...@hirez.programming.kicks-ass.net/

Fixes: 9ae1e990f1ab ("perf tools: Remove broken __no_tail_call
   attribute")
---
 tools/perf/tests/dwarf-unwind.c | 39 +++--
 1 file changed, 32 insertions(+), 7 deletions(-)

diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c
index 83638097c3bc..c8ce86bceea8 100644
--- a/tools/perf/tests/dwarf-unwind.c
+++ b/tools/perf/tests/dwarf-unwind.c
@@ -24,6 +24,23 @@
 /* For bsearch. We try to unwind functions in shared object. */
 #include 
 
+/*
+ * The test will assert frames are on the stack but tail call optimizations 
lose
+ * the frame of the caller. Clang can disable this optimization on a called
+ * function but GCC currently (11/2020) lacks this attribute. The barrier is
+ * used to inhibit tail calls in these cases.
+ */
+#ifdef __has_attribute
+#if __has_attribute(disable_tail_calls)
+#define NO_TAIL_CALL_ATTRIBUTE __attribute__((disable_tail_calls))
+#define NO_TAIL_CALL_BARRIER
+#endif
+#endif
+#ifndef NO_TAIL_CALL_ATTRIBUTE
+#define NO_TAIL_CALL_ATTRIBUTE
+#define NO_TAIL_CALL_BARRIER __asm__ __volatile__("" : : : "memory");
+#endif
+
 static int mmap_handler(struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct perf_sample *sample,
@@ -95,7 +112,7 @@ static int unwind_entry(struct unwind_entry *entry, void 
*arg)
return strcmp((const char *) symbol, funcs[idx]);
 }
 
-noinline int test_dwarf_unwind__thread(struct thread *thread)
+NO_TAIL_CALL_ATTRIBUTE noinline int test_dwarf_unwind__thread(struct thread 
*thread)
 {
struct perf_sample sample;
unsigned long cnt = 0;
@@ -126,7 +143,7 @@ noinline int test_dwarf_unwind__thread(struct thread 
*thread)
 
 static int global_unwind_retval = -INT_MAX;
 
-noinline int test_dwarf_unwind__compare(void *p1, void *p2)
+NO_TAIL_CALL_ATTRIBUTE noinline int test_dwarf_unwind__compare(void *p1, void 
*p2)
 {
/* Any possible value should be 'thread' */
struct thread *thread = *(struct thread **)p1;
@@ -145,7 +162,7 @@ noinline int test_dwarf_unwind__compare(void *p1, void *p2)
return p1 - p2;
 }
 
-noinline int test_dwarf_unwind__krava_3(struct thread *thread)
+NO_TAIL_CALL_ATTRIBUTE noinline int test_dwarf_unwind__krava_3(struct thread 
*thread)
 {
struct thread *array[2] = {thread, thread};
void *fp = 
@@ -164,14 +181,22 @@ noinline int test_dwarf_unwind__krava_3(struct thread 
*thread)
return global_unwind_retval;
 }
 
-noinline int test_dwarf_unwind__krava_2(struct thread *thread)
+NO_TAIL_CALL_ATTRIBUTE noinline int test_dwarf_unwind__krava_2(struct thread 
*thread)
 {
-   return test_dwarf_unwind__krava_3(thread);
+   int ret;
+
+   ret =  test_dwarf_unwind__krava_3(thread);
+   NO_TAIL_CALL_BARRIER;
+   return ret;
 }
 
-noinline int test_dwarf_unwind__krava_1(struct thread *thread)
+NO_TAIL_CALL_ATTRIBUTE noinline int test_dwarf_unwind__krava_1(struct thread 
*thread)
 {
-   return test_dwarf_unwind__krava_2(thread);
+   int ret;
+
+   ret =  test_dwarf_unwind__krava_2(thread);
+   NO_TAIL_CALL_BARRIER;
+   return ret;
 }
 
 int test__dwarf_unwind(struct test *test __maybe_unused, int subtest 
__maybe_unused)
-- 
2.29.2.299.gdc1121823c-goog



[PATCH] perf test: Avoid an msan warning in a copied stack.

2020-11-13 Thread Ian Rogers
 'perf_event__synthesize_mmap_events'
#0 0x559ceafc5f60 in perf_event__synthesize_mmap_events 
tools/perf/util/synthetic-events.c:445

SUMMARY: MemorySanitizer: use-of-uninitialized-value 
elfutils/libdwfl/frame_unwind.c:648:8 in handle_cfi
Signed-off-by: Ian Rogers 
---
 tools/perf/arch/x86/tests/dwarf-unwind.c | 7 +++
 1 file changed, 7 insertions(+)

diff --git a/tools/perf/arch/x86/tests/dwarf-unwind.c 
b/tools/perf/arch/x86/tests/dwarf-unwind.c
index 4e40402a4f81..478078fb0f22 100644
--- a/tools/perf/arch/x86/tests/dwarf-unwind.c
+++ b/tools/perf/arch/x86/tests/dwarf-unwind.c
@@ -38,6 +38,13 @@ static int sample_ustack(struct perf_sample *sample,
stack_size = stack_size > STACK_SIZE ? STACK_SIZE : stack_size;
 
memcpy(buf, (void *) sp, stack_size);
+#ifdef MEMORY_SANITIZER
+   /*
+* Copying the stack may copy msan poison, avoid false positives in the
+* unwinder by removing the poison here.
+*/
+   __msan_unpoison(buf, stack_size);
+#endif
stack->data = (char *) buf;
stack->size = stack_size;
return 0;
-- 
2.29.2.299.gdc1121823c-goog



[PATCH 3/5] perf metric: Rename expr__find_other.

2020-11-12 Thread Ian Rogers
A later change will remove the notion of other, rename the function to
expr__find_ids as this is what it populated.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/expr.c   | 8 
 tools/perf/tests/pmu-events.c | 9 -
 tools/perf/util/expr.c| 4 ++--
 tools/perf/util/expr.h| 2 +-
 tools/perf/util/metricgroup.c | 2 +-
 tools/perf/util/stat-shadow.c | 6 +++---
 6 files changed, 15 insertions(+), 16 deletions(-)

diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
index b0a3b5fd0c00..9d7032041318 100644
--- a/tools/perf/tests/expr.c
+++ b/tools/perf/tests/expr.c
@@ -65,8 +65,8 @@ int test__expr(struct test *t __maybe_unused, int subtest 
__maybe_unused)
 
expr__ctx_clear(ctx);
TEST_ASSERT_VAL("find other",
-   expr__find_other("FOO + BAR + BAZ + BOZO", "FOO",
-ctx, 1) == 0);
+   expr__find_ids("FOO + BAR + BAZ + BOZO", "FOO",
+   ctx, 1) == 0);
TEST_ASSERT_VAL("find other", hashmap__size(ctx->ids) == 3);
TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "BAR",
(void **)_ptr));
@@ -77,8 +77,8 @@ int test__expr(struct test *t __maybe_unused, int subtest 
__maybe_unused)
 
expr__ctx_clear(ctx);
TEST_ASSERT_VAL("find other",
-   expr__find_other("EVENT1\\,param\\=?@ + 
EVENT2\\,param\\=?@",
-NULL, ctx, 3) == 0);
+   expr__find_ids("EVENT1\\,param\\=?@ + 
EVENT2\\,param\\=?@",
+   NULL, ctx, 3) == 0);
TEST_ASSERT_VAL("find other", hashmap__size(ctx->ids) == 2);
TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "EVENT1,param=3/",
(void **)_ptr));
diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c
index 294daf568bb6..3ac70fa31379 100644
--- a/tools/perf/tests/pmu-events.c
+++ b/tools/perf/tests/pmu-events.c
@@ -502,9 +502,8 @@ static int test_parsing(void)
if (!pe->metric_expr)
continue;
expr__ctx_clear(ctx);
-   if (expr__find_other(pe->metric_expr, NULL, ctx, 0)
- < 0) {
-   expr_failure("Parse other failed", map, pe);
+   if (expr__find_ids(pe->metric_expr, NULL, ctx, 0) < 0) {
+   expr_failure("Parse find ids failed", map, pe);
ret++;
continue;
}
@@ -559,8 +558,8 @@ static int metric_parse_fake(const char *str)
pr_debug("parsing '%s'\n", str);
 
ctx = expr__ctx_new();
-   if (expr__find_other(str, NULL, ctx, 0) < 0) {
-   pr_err("expr__find_other failed\n");
+   if (expr__find_ids(str, NULL, ctx, 0) < 0) {
+   pr_err("expr__find_ids failed\n");
return -1;
}
 
diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
index a3bb802acace..0a3cc073c9e0 100644
--- a/tools/perf/util/expr.c
+++ b/tools/perf/util/expr.c
@@ -251,8 +251,8 @@ int expr__parse(double *final_val, struct expr_parse_ctx 
*ctx,
return __expr__parse(final_val, ctx, expr, EXPR_PARSE, runtime) ? -1 : 
0;
 }
 
-int expr__find_other(const char *expr, const char *one,
-struct expr_parse_ctx *ctx, int runtime)
+int expr__find_ids(const char *expr, const char *one,
+  struct expr_parse_ctx *ctx, int runtime)
 {
int ret = __expr__parse(NULL, ctx, expr, EXPR_OTHER, runtime);
 
diff --git a/tools/perf/util/expr.h b/tools/perf/util/expr.h
index 863b975baeb2..8a6ce628511d 100644
--- a/tools/perf/util/expr.h
+++ b/tools/perf/util/expr.h
@@ -55,7 +55,7 @@ int expr__resolve_id(struct expr_parse_ctx *ctx, const char 
*id,
 struct expr_id_data **datap);
 int expr__parse(double *final_val, struct expr_parse_ctx *ctx,
const char *expr, int runtime);
-int expr__find_other(const char *expr, const char *one,
+int expr__find_ids(const char *expr, const char *one,
struct expr_parse_ctx *ids, int runtime);
 
 #endif
diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c
index 7c3b267def9d..b4c70a4275d9 100644
--- a/tools/perf/util/metricgroup.c
+++ b/tools/perf/util/metricgroup.c
@@ -761,7 +761,7 @@ static int __add_metric(struct list_head *metric_list,
 * For both the parent and referenced metrics, we parse
 * all the metric's IDs and add it to the 

[PATCH 2/5] perf metric: Use NAN for missing event IDs.

2020-11-12 Thread Ian Rogers
If during computing a metric an event (id) is missing the parsing
aborts. A later patch will make it so that events that aren't used in
the output are deliberately omitted, in which case we don't want the
abort. Modify the missing ID case to report NAN for these cases.

Signed-off-by: Ian Rogers 
---
 tools/perf/util/expr.y | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/tools/perf/util/expr.y b/tools/perf/util/expr.y
index d34b370391c6..4ce76adeb337 100644
--- a/tools/perf/util/expr.y
+++ b/tools/perf/util/expr.y
@@ -1,6 +1,7 @@
 /* Simple expression parser */
 %{
 #define YYDEBUG 1
+#include 
 #include 
 #include "util.h"
 #include "util/debug.h"
@@ -89,8 +90,7 @@ expr:   NUMBER
struct expr_id_data *data;
 
if (expr__resolve_id(ctx, $1, )) {
-   free($1);
-   YYABORT;
+   $$ = NAN;
}
 
$$ = data->val;
-- 
2.29.2.299.gdc1121823c-goog



[PATCH 0/5] Don't compute events that won't be used in a metric.

2020-11-12 Thread Ian Rogers

For a metric like:
  EVENT1 if #smt_on else EVENT2

currently EVENT1 and EVENT2 will be measured and then when the metric
is reported EVENT1 or EVENT2 will be printed depending on the value
from smt_on() during the expr parsing. Computing both events is
unnecessary and can lead to multiplexing as discussed in this thread:
https://lore.kernel.org/lkml/20201110100346.2527031-1-irog...@google.com/

This change modifies expression parsing so that constants are
considered when building the set of ids (events) and only events not
contributing to a constant value are measured.

Ian Rogers (5):
  perf metric: Restructure struct expr_parse_ctx.
  perf metric: Use NAN for missing event IDs.
  perf metric: Rename expr__find_other.
  perf metric: Add utilities to work on ids map.
  perf metric: Don't compute unused events.

 tools/perf/tests/expr.c   | 148 ++-
 tools/perf/tests/pmu-events.c |  42 +++--
 tools/perf/util/expr.c| 136 --
 tools/perf/util/expr.h|  17 +-
 tools/perf/util/expr.l|   9 -
 tools/perf/util/expr.y| 343 +++---
 tools/perf/util/metricgroup.c |  44 +++--
 tools/perf/util/stat-shadow.c |  54 --
 8 files changed, 586 insertions(+), 207 deletions(-)

-- 
2.29.2.299.gdc1121823c-goog



[PATCH 5/5] perf metric: Don't compute unused events.

2020-11-12 Thread Ian Rogers
For a metric like:
  EVENT1 if #smt_on else EVENT2

currently EVENT1 and EVENT2 will be measured and then when the metric is
reported EVENT1 or EVENT2 will be printed depending on the value from
smt_on() during the expr parsing. Computing both events is unnecessary and
can lead to multiplexing as discussed in this thread:
https://lore.kernel.org/lkml/20201110100346.2527031-1-irog...@google.com/

This change modifies the expression parsing code by:
 - getting rid of the "other" parsing and introducing a boolean argument
   to say whether ids should be computed or not.
 - expressions are changed so that a pair of value and ids are returned.
 - when computing the metric value the ids are unused.
 - when computing the ids, constant values and smt_on are assigned to
   the value.
 - If the value is from an event ID then the event is added to the ids
   hashmap and the value set to NAN.
 - Typically operators union IDs for their inputs and set the value to
   NAN, however, if the inputs are constant then these are computed and
   propagated as the value.
 - If the input is constant to certain operators like:
 IDS1 if CONST else IDS2
   then the result will be either IDS1 or IDS2 depending on CONST which
   may be evaluated from an entire expression.
 - The ids at the end of parsing are added to the context.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/expr.c |  10 ++
 tools/perf/util/expr.c  |   9 +-
 tools/perf/util/expr.h  |   1 -
 tools/perf/util/expr.l  |   9 --
 tools/perf/util/expr.y  | 341 +++-
 5 files changed, 284 insertions(+), 86 deletions(-)

diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
index 7c2a01cf0650..94ddd01b29fc 100644
--- a/tools/perf/tests/expr.c
+++ b/tools/perf/tests/expr.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 #include "util/debug.h"
 #include "util/expr.h"
+#include "util/smt.h"
 #include "tests.h"
 #include 
 #include 
@@ -132,6 +133,15 @@ int test__expr(struct test *t __maybe_unused, int subtest 
__maybe_unused)
TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "EVENT2,param=3/",
(void **)_ptr));
 
+   expr__ctx_clear(ctx);
+   TEST_ASSERT_VAL("find ids",
+   expr__find_ids("EVENT1 if #smt_on else EVENT2",
+   NULL, ctx, 0) == 0);
+   TEST_ASSERT_VAL("find ids", hashmap__size(ctx->ids) == 1);
+   TEST_ASSERT_VAL("find ids", hashmap__find(ctx->ids,
+ smt_on() ? "EVENT1" : 
"EVENT2",
+ (void **)_ptr));
+
expr__ctx_free(ctx);
 
return 0;
diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
index d4e83439c965..8c62a6c83b00 100644
--- a/tools/perf/util/expr.c
+++ b/tools/perf/util/expr.c
@@ -293,10 +293,9 @@ void expr__ctx_free(struct expr_parse_ctx *ctx)
 
 static int
 __expr__parse(double *val, struct expr_parse_ctx *ctx, const char *expr,
- int start, int runtime)
+ bool compute_ids, int runtime)
 {
struct expr_scanner_ctx scanner_ctx = {
-   .start_token = start,
.runtime = runtime,
};
YY_BUFFER_STATE buffer;
@@ -316,7 +315,7 @@ __expr__parse(double *val, struct expr_parse_ctx *ctx, 
const char *expr,
expr_set_debug(1, scanner);
 #endif
 
-   ret = expr_parse(val, ctx, scanner);
+   ret = expr_parse(val, ctx, compute_ids, scanner);
 
expr__flush_buffer(buffer, scanner);
expr__delete_buffer(buffer, scanner);
@@ -327,13 +326,13 @@ __expr__parse(double *val, struct expr_parse_ctx *ctx, 
const char *expr,
 int expr__parse(double *final_val, struct expr_parse_ctx *ctx,
const char *expr, int runtime)
 {
-   return __expr__parse(final_val, ctx, expr, EXPR_PARSE, runtime) ? -1 : 
0;
+   return __expr__parse(final_val, ctx, expr, /*compute_ids=*/false, 
runtime) ? -1 : 0;
 }
 
 int expr__find_ids(const char *expr, const char *one,
   struct expr_parse_ctx *ctx, int runtime)
 {
-   int ret = __expr__parse(NULL, ctx, expr, EXPR_OTHER, runtime);
+   int ret = __expr__parse(NULL, ctx, expr, /*compute_ids=*/true, runtime);
 
if (one)
expr__del_id(ctx, one);
diff --git a/tools/perf/util/expr.h b/tools/perf/util/expr.h
index 398b2629beee..4e77b43ce94c 100644
--- a/tools/perf/util/expr.h
+++ b/tools/perf/util/expr.h
@@ -38,7 +38,6 @@ struct expr_id_data {
 };
 
 struct expr_scanner_ctx {
-   int start_token;
int runtime;
 };
 
diff --git a/tools/perf/util/expr.l b/tools/perf/util/expr.l
index 13e5e3c75f56..702fdf6456ca 100644
--- a/tools/perf/util/expr.l
+++ b/tools/perf/util/expr.l
@@ -91,15 +91,6 @@ symbol   ({spec}|{sym})+
 %%
struct expr

[PATCH 4/5] perf metric: Add utilities to work on ids map.

2020-11-12 Thread Ian Rogers
Add utilities to new/free an ids hashmap, as well as to union. Add
testing of the union. Unioning hashmaps will be used when parsing the
metric, if a value is known then the hashmap is unnecessary, otherwise
we need to union together all the event ids to compute their values for
reporting.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/expr.c | 47 ++
 tools/perf/util/expr.c  | 87 +++--
 tools/perf/util/expr.h  |  9 +
 3 files changed, 139 insertions(+), 4 deletions(-)

diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
index 9d7032041318..7c2a01cf0650 100644
--- a/tools/perf/tests/expr.c
+++ b/tools/perf/tests/expr.c
@@ -6,6 +6,51 @@
 #include 
 #include 
 
+static int test_ids_union(void)
+{
+   struct hashmap *ids1, *ids2;
+
+   /* Empty union. */
+   ids1 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids1);
+   ids2 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids2);
+
+   ids1 = ids__union(ids1, ids2);
+   TEST_ASSERT_EQUAL("union", (int)hashmap__size(ids1), 0);
+
+   /* Union {foo, bar} against {}. */
+   ids2 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids2);
+
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids1, strdup("foo"), 
NULL), 0);
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids1, strdup("bar"), 
NULL), 0);
+
+   ids1 = ids__union(ids1, ids2);
+   TEST_ASSERT_EQUAL("union", (int)hashmap__size(ids1), 2);
+
+   /* Union {foo, bar} against {foo}. */
+   ids2 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids2);
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids2, strdup("foo"), 
NULL), 0);
+
+   ids1 = ids__union(ids1, ids2);
+   TEST_ASSERT_EQUAL("union", (int)hashmap__size(ids1), 2);
+
+   /* Union {foo, bar} against {bar,baz}. */
+   ids2 = ids__new();
+   TEST_ASSERT_VAL("ids__new", ids2);
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids2, strdup("bar"), 
NULL), 0);
+   TEST_ASSERT_EQUAL("ids__insert", ids__insert(ids2, strdup("baz"), 
NULL), 0);
+
+   ids1 = ids__union(ids1, ids2);
+   TEST_ASSERT_EQUAL("union", (int)hashmap__size(ids1), 3);
+
+   ids__free(ids1);
+
+   return 0;
+}
+
 static int test(struct expr_parse_ctx *ctx, const char *e, double val2)
 {
double val;
@@ -24,6 +69,8 @@ int test__expr(struct test *t __maybe_unused, int subtest 
__maybe_unused)
int ret;
struct expr_parse_ctx *ctx;
 
+   TEST_ASSERT_EQUAL("ids_union", test_ids_union(), 0);
+
ctx = expr__ctx_new();
TEST_ASSERT_VAL("expr__ctx_new", ctx);
expr__add_id_val(ctx, strdup("FOO"), 1);
diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
index 0a3cc073c9e0..d4e83439c965 100644
--- a/tools/perf/util/expr.c
+++ b/tools/perf/util/expr.c
@@ -36,8 +36,48 @@ static bool key_equal(const void *key1, const void *key2,
return !strcmp((const char *)key1, (const char *)key2);
 }
 
-/* Caller must make sure id is allocated */
-int expr__add_id(struct expr_parse_ctx *ctx, const char *id)
+struct hashmap *ids__new(void)
+{
+   return hashmap__new(key_hash, key_equal, NULL);
+}
+
+void ids__free(struct hashmap *ids)
+{
+   struct hashmap_entry *cur;
+   size_t bkt;
+
+   if (ids == NULL)
+   return;
+
+#ifdef PARSER_DEBUG
+   fprintf(stderr, "freeing ids: ");
+   ids__print(ids);
+   fprintf(stderr, "\n");
+#endif
+
+   hashmap__for_each_entry(ids, cur, bkt) {
+   free((char *)cur->key);
+   free(cur->value);
+   }
+
+   hashmap__free(ids);
+}
+
+void ids__print(struct hashmap *ids)
+{
+   size_t bkt;
+   struct hashmap_entry *cur;
+
+   if (!ids)
+   return;
+
+   hashmap__for_each_entry(ids, cur, bkt) {
+   fprintf(stderr, "key:%s, ", (const char *)cur->key);
+   }
+}
+
+int ids__insert(struct hashmap *ids, const char *id,
+   struct expr_id *parent)
 {
struct expr_id_data *data_ptr = NULL, *old_data = NULL;
char *old_key = NULL;
@@ -47,9 +87,9 @@ int expr__add_id(struct expr_parse_ctx *ctx, const char *id)
if (!data_ptr)
return -ENOMEM;
 
-   data_ptr->parent = ctx->parent;
+   data_ptr->parent = parent;
 
-   ret = hashmap__set(ctx->ids, id, data_ptr,
+   ret = hashmap__set(ids, id, data_ptr,
   (const void **)_key, (void **)_data);
if (ret)
free(data_ptr);
@@ -58,6 +98,45 @@ int expr__add_id(struct expr_parse_ctx *ctx, const char *id)
return ret;
 }
 
+struct hashmap *ids__union(struct hashmap *ids1, struct hashmap *ids2)
+{
+   s

[PATCH 1/5] perf metric: Restructure struct expr_parse_ctx.

2020-11-12 Thread Ian Rogers
A later change to parsing the ids out (in expr__find_other) will
potentially drop hashmaps and so it is more convenient to move
expr_parse_ctx to have a hashmap pointer rather than a struct value. As
this pointer must be freed, rather than just going out of scope,
add expr__ctx_new and expr__ctx_free to manage expr_parse_ctx memory.
Adjust use of struct expr_parse_ctx accordingly.

Signed-off-by: Ian Rogers 
---
 tools/perf/tests/expr.c   | 81 ++-
 tools/perf/tests/pmu-events.c | 37 +---
 tools/perf/util/expr.c| 38 
 tools/perf/util/expr.h|  5 ++-
 tools/perf/util/metricgroup.c | 44 ++-
 tools/perf/util/stat-shadow.c | 50 +
 6 files changed, 151 insertions(+), 104 deletions(-)

diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
index 4d01051951cd..b0a3b5fd0c00 100644
--- a/tools/perf/tests/expr.c
+++ b/tools/perf/tests/expr.c
@@ -22,67 +22,70 @@ int test__expr(struct test *t __maybe_unused, int subtest 
__maybe_unused)
const char *p;
double val;
int ret;
-   struct expr_parse_ctx ctx;
+   struct expr_parse_ctx *ctx;
 
-   expr__ctx_init();
-   expr__add_id_val(, strdup("FOO"), 1);
-   expr__add_id_val(, strdup("BAR"), 2);
+   ctx = expr__ctx_new();
+   TEST_ASSERT_VAL("expr__ctx_new", ctx);
+   expr__add_id_val(ctx, strdup("FOO"), 1);
+   expr__add_id_val(ctx, strdup("BAR"), 2);
 
-   ret = test(, "1+1", 2);
-   ret |= test(, "FOO+BAR", 3);
-   ret |= test(, "(BAR/2)%2", 1);
-   ret |= test(, "1 - -4",  5);
-   ret |= test(, "(FOO-1)*2 + (BAR/2)%2 - -4",  5);
-   ret |= test(, "1-1 | 1", 1);
-   ret |= test(, "1-1 & 1", 0);
-   ret |= test(, "min(1,2) + 1", 2);
-   ret |= test(, "max(1,2) + 1", 3);
-   ret |= test(, "1+1 if 3*4 else 0", 2);
-   ret |= test(, "1.1 + 2.1", 3.2);
-   ret |= test(, ".1 + 2.", 2.1);
-   ret |= test(, "d_ratio(1, 2)", 0.5);
-   ret |= test(, "d_ratio(2.5, 0)", 0);
-   ret |= test(, "1.1 < 2.2", 1);
-   ret |= test(, "2.2 > 1.1", 1);
-   ret |= test(, "1.1 < 1.1", 0);
-   ret |= test(, "2.2 > 2.2", 0);
-   ret |= test(, "2.2 < 1.1", 0);
-   ret |= test(, "1.1 > 2.2", 0);
+   ret = test(ctx, "1+1", 2);
+   ret |= test(ctx, "FOO+BAR", 3);
+   ret |= test(ctx, "(BAR/2)%2", 1);
+   ret |= test(ctx, "1 - -4",  5);
+   ret |= test(ctx, "(FOO-1)*2 + (BAR/2)%2 - -4",  5);
+   ret |= test(ctx, "1-1 | 1", 1);
+   ret |= test(ctx, "1-1 & 1", 0);
+   ret |= test(ctx, "min(1,2) + 1", 2);
+   ret |= test(ctx, "max(1,2) + 1", 3);
+   ret |= test(ctx, "1+1 if 3*4 else 0", 2);
+   ret |= test(ctx, "1.1 + 2.1", 3.2);
+   ret |= test(ctx, ".1 + 2.", 2.1);
+   ret |= test(ctx, "d_ratio(1, 2)", 0.5);
+   ret |= test(ctx, "d_ratio(2.5, 0)", 0);
+   ret |= test(ctx, "1.1 < 2.2", 1);
+   ret |= test(ctx, "2.2 > 1.1", 1);
+   ret |= test(ctx, "1.1 < 1.1", 0);
+   ret |= test(ctx, "2.2 > 2.2", 0);
+   ret |= test(ctx, "2.2 < 1.1", 0);
+   ret |= test(ctx, "1.1 > 2.2", 0);
 
-   if (ret)
+   if (ret) {
+   expr__ctx_free(ctx);
return ret;
+   }
 
p = "FOO/0";
-   ret = expr__parse(, , p, 1);
+   ret = expr__parse(, ctx, p, 1);
TEST_ASSERT_VAL("division by zero", ret == -1);
 
p = "BAR/";
-   ret = expr__parse(, , p, 1);
+   ret = expr__parse(, ctx, p, 1);
TEST_ASSERT_VAL("missing operand", ret == -1);
 
-   expr__ctx_clear();
+   expr__ctx_clear(ctx);
TEST_ASSERT_VAL("find other",
expr__find_other("FOO + BAR + BAZ + BOZO", "FOO",
-, 1) == 0);
-   TEST_ASSERT_VAL("find other", hashmap__size() == 3);
-   TEST_ASSERT_VAL("find other", hashmap__find(, "BAR",
+ctx, 1) == 0);
+   TEST_ASSERT_VAL("find other", hashmap__size(ctx->ids) == 3);
+   TEST_ASSERT_VAL("find other", hashmap__find(ctx->ids, "BAR",
(void **)_ptr));
-   TEST_ASSERT_VAL("find other", hashmap__find(, "BAZ",
+   TEST_ASSERT_VAL("find other", hashmap__find(

[RFC PATCH 03/12] perf topdown-paser: Add a CSV file reader.

2020-11-10 Thread Ian Rogers
From: Sandeep Dasgupta 

Read a CSV file info a two dimensional vector of vectors. Open
parentheses are counted so that expressions like "min(a,b)" aren't
split. Escape characters and quotations aren't handled.

Co-authored-by: Ian Rogers 
Signed-off-by: Ian Rogers 
Signed-off-by: Sandeep Dasgupta 
---
 .../pmu-events/topdown-parser/csvreader.cpp   | 49 ++
 .../pmu-events/topdown-parser/csvreader.h | 51 +++
 2 files changed, 100 insertions(+)
 create mode 100644 tools/perf/pmu-events/topdown-parser/csvreader.cpp
 create mode 100644 tools/perf/pmu-events/topdown-parser/csvreader.h

diff --git a/tools/perf/pmu-events/topdown-parser/csvreader.cpp 
b/tools/perf/pmu-events/topdown-parser/csvreader.cpp
new file mode 100644
index ..142e0e7e5ce7
--- /dev/null
+++ b/tools/perf/pmu-events/topdown-parser/csvreader.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2020 Google LLC.
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include "csvreader.h"
+
+#include 
+#include 
+#include 
+
+#include "general_utils.h"
+#include "logging.h"
+
+namespace topdown_parser
+{
+std::vector > CsvReader::getData() const
+{
+   std::vector > dataList;
+   std::ifstream file(file_name_);
+   std::string line = "";
+   assert(file.is_open() && "unable to open csv file");
+
+   while (getline(file, line)) {
+   std::vector tokens;
+   int opens = 0;
+   int closes = 0;
+   for (const std::string  : Split(line, delimeter_)) {
+   std::string stripped_str = Strip(str, '"');
+   if (opens > closes) {
+   tokens.back() += ", " + stripped_str;
+   } else {
+   tokens.push_back(stripped_str);
+   }
+   opens += std::count(str.begin(), str.end(), '(');
+   closes += std::count(str.begin(), str.end(), ')');
+   }
+
+   dataList.push_back(tokens);
+   }
+
+   if (dataList.empty()) {
+   FATAL("Empty csv file" << file_name_);
+   }
+
+   return dataList;
+}
+
+} // namespace topdown_parser
diff --git a/tools/perf/pmu-events/topdown-parser/csvreader.h 
b/tools/perf/pmu-events/topdown-parser/csvreader.h
new file mode 100644
index ..a82470041145
--- /dev/null
+++ b/tools/perf/pmu-events/topdown-parser/csvreader.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+// -
+// File: csvheader.h
+// -
+//
+// The header file provides the interface for parsing csv file using
+// CsvReader::delimeter_ as the delimiter for parsing each line.
+//
+// The library provides the following utilities:
+//  `getData`: Reads the input csv file `file_name_` and parses its
+// contents, based on the delimeter `delimeter_`, as strings.
+// The parsed data is returned as a 2D vector, V, of strings such
+// that V[r][c] is same as the value of the input csv file at row r
+// and column c.
+//
+// For example, with the following content of a csv file,
+// a,b,c,
+// 1,2,3
+// and delimiter as ',', the return value is
+//
+// {
+//   {"a", "b", "c"},
+//   {"1", "2", "3"}
+// }
+
+#ifndef TOPDOWN_PARSER_CSV_READER_H_
+#define TOPDOWN_PARSER_CSV_READER_H_
+
+#include 
+#include 
+
+namespace topdown_parser
+{
+class CsvReader {
+public:
+   explicit CsvReader(std::string fname, char delm = ',')
+   : file_name_(fname), delimeter_(delm)
+   {
+   }
+
+   std::vector > getData() const;
+
+private:
+   const std::string file_name_;
+   const char delimeter_;
+};
+
+} // namespace topdown_parser
+
+#endif // TOPDOWN_PARSER_CSV_READER_H_
-- 
2.29.2.222.g5d2a92d10f8-goog



[RFC PATCH 09/12] perf topdown-paser: Add code generation API.

2020-11-10 Thread Ian Rogers
From: Sandeep Dasgupta 

Add API that is called to generate code using all registered targets.

Co-authored-by: Stephane Eranian 
Signed-off-by: Ian Rogers 
Signed-off-by: Sandeep Dasgupta 
---
 .../topdown-parser/code_gen_target.cpp| 51 
 .../topdown-parser/code_gen_target.h  | 77 +++
 2 files changed, 128 insertions(+)
 create mode 100644 tools/perf/pmu-events/topdown-parser/code_gen_target.cpp
 create mode 100644 tools/perf/pmu-events/topdown-parser/code_gen_target.h

diff --git a/tools/perf/pmu-events/topdown-parser/code_gen_target.cpp 
b/tools/perf/pmu-events/topdown-parser/code_gen_target.cpp
new file mode 100644
index ..c6d7ce8eb661
--- /dev/null
+++ b/tools/perf/pmu-events/topdown-parser/code_gen_target.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2020 Google LLC.
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include "code_gen_target.h"
+
+#include "configuration.h"
+
+namespace topdown_parser
+{
+/**
+ * Dump event list. Used for testing of auto-generation.
+ */
+bool g_DumpEvents = false;
+
+namespace
+{
+/**
+ * `kRegisteredTargets` enumerates all the target supported by the
+ * topdown generator tool. Each target is responsible for generating a
+ * "code", which essentially encodes the topdown metric expressions, in
+ * a particular language or format support.
+ */
+TargetInfo *kRegisteredTargets[] = {
+/* target to generate JSon code */,
+};
+
+} // namespace
+
+void CodeGenTarget(
+   const std::unordered_map _dag)
+{
+   for (size_t i = 0;
+i < sizeof(kRegisteredTargets) / sizeof(TargetInfo *); ++i) {
+   const std::string _name = kRegisteredTargets[i]->name;
+
+   if (target_name == kConfigParams->target_) {
+   kRegisteredTargets[i]->codegen_entry_point(
+   dependence_dag);
+   if (kRegisteredTargets[i]
+   ->codegen_test_harness_entry_point) {
+   kRegisteredTargets[i]
+   ->codegen_test_harness_entry_point();
+   }
+   break;
+   }
+   }
+}
+
+} // namespace topdown_parser
diff --git a/tools/perf/pmu-events/topdown-parser/code_gen_target.h 
b/tools/perf/pmu-events/topdown-parser/code_gen_target.h
new file mode 100644
index ..ab3e2b48bebc
--- /dev/null
+++ b/tools/perf/pmu-events/topdown-parser/code_gen_target.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+// --
+// File: code_gen_target.h
+// --
+//
+
+// The header provides the interface `CodeGenTarget` to generate code, encoding
+// the topdown metric expressions, using the data `dependence_dag` read from
+// the input csv file. The language or format of the generated code is the one
+// supported by a specific project (e.g. perf or projects using perf ) to 
encode
+// the topdown metric metric expressions. We define a `target` as a specific
+// project and use that to guide generation of topdown code in a language
+// supported by the project.
+
+#ifndef TOPDOWN_PARSER_CODE_GEN_TARGET_H_
+#define TOPDOWN_PARSER_CODE_GEN_TARGET_H_
+
+#include 
+#include 
+
+namespace topdown_parser
+{
+class MappedData;
+
+/**
+ * Dump event list. Used for testing of auto-generation.
+ */
+extern bool g_DumpEvents;
+
+/**
+ * The structure `TargetInfo` is used to specify a target.
+ */
+struct TargetInfo {
+   /**
+* Name of the target. This will be used to invoke code generation for a
+* particular target.
+*/
+   std::string name;
+
+   /**
+* Descriptive information of the target (Optional).
+*/
+   std::string description;
+
+   /**
+* The entry point function for generating code.
+*/
+   void (*codegen_entry_point)(
+   const std::unordered_map
+   _dag);
+
+   /**
+* Function to generate golden reference for testing the auto-generated
+* code.
+* (Optional)
+*/
+   void (*codegen_test_harness_entry_point)();
+};
+
+/**
+ * Target information for generating JSon code for perf encoding the topdown
+ * metric expressions.
+ */
+extern TargetInfo kTargetPerfJson;
+
+/**
+ * `CodeGenTarget` dispatches an appropriate callback, based on the
+ * configuration variable `kConfigParams->target_`, to generate  "code" for a
+ * particular target.
+ */
+void CodeGenTarget(
+   const std::unordered_map _dag);
+
+} // namespace topdown_parser
+
+#endif // TOPDOWN_PARSER_CODE_GEN_TARGET_H_
-- 
2.29.2.222.g5d2a92d10f8-goog



[RFC PATCH 07/12] perf topdown-parser: Metric expression parser.

2020-11-10 Thread Ian Rogers
From: Sandeep Dasgupta 

A parser capable of processing metrics found in TMA_Metrics.csv.

Co-authored-by: Ian Rogers 
Signed-off-by: Ian Rogers 
Signed-off-by: Sandeep Dasgupta 
---
 .../pmu-events/topdown-parser/expr_parser.y   | 224 ++
 1 file changed, 224 insertions(+)
 create mode 100644 tools/perf/pmu-events/topdown-parser/expr_parser.y

diff --git a/tools/perf/pmu-events/topdown-parser/expr_parser.y 
b/tools/perf/pmu-events/topdown-parser/expr_parser.y
new file mode 100644
index ..ddf635a6470c
--- /dev/null
+++ b/tools/perf/pmu-events/topdown-parser/expr_parser.y
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2020 Google LLC.
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+/* Topdown expression parser */
+%language "c++"
+%define api.value.type variant
+
+%code requires {
+#include 
+}
+
+/* Increase error verbosity. */
+%define parse.trace
+%define parse.error verbose
+/* Semantic value printer. */
+%printer { yyo << $$; } <*>;
+
+
+/* Inputs for the parser and yylex. */
+%param { const std::string  }
+%param { size_t *cursor }
+/* Inputs/output for the parser. */
+%parse-param { bool convert_if_stmt }
+%parse-param { bool remove_false_branch }
+%parse-param { bool wrap_div_in_function }
+%parse-param { std::string *final_val }
+
+/* Tokens returned by yylex. */
+%define api.token.prefix {TOK_}
+%token
+  MIN
+  MAX
+  IF
+  ELSE
+  MODEL
+  IN
+  NEG
+  EOF 0
+%token  ID_OR_NUM
+
+/* Type of non-terminal expressions. */
+%type  expr if_expr IDS
+
+/* Presidence and associativity. */
+%left MIN MAX MODEL IN
+%right ELSE
+%right IF
+%left '='
+%left '<' '>'
+%left '-' '+'
+%left '*' '/' '%'
+%left NEG
+
+%code {
+static int yylex(yy::parser::semantic_type *res,
+const std::string ,
+size_t *cursor);
+
+void yy::parser::error (const std::string& m)
+{
+// ERROR(m << '\n' << "Input:\n" << input << "\nCursor: " << *cursor);
+}
+
+}
+
+%%
+%start all_expr;
+all_expr: expr EOF { *final_val = $1; } ;
+
+IDS:
+'\'' ID_OR_NUM '\'' { $$ = std::string(" ") + $2 + " "; }
+|
+IDS '\'' ID_OR_NUM '\'' { $$ = $1 + " " + $3 + " "; }
+;
+
+if_expr:
+expr IF expr ELSE expr
+{
+   if (convert_if_stmt)
+   $$ = $3 + " ? " + $1 + " : " + $5;
+   else
+   $$ = $1 + " if " + $3 + " else " + $5;
+
+   if (remove_false_branch) {
+   if (std::string::npos !=  $3.find("0.000", 0))
+   $$ = $5;
+   else if (std::string::npos != $3.find("1.000", 0))
+   $$ = $1;
+   }
+}
+|
+expr IF MODEL IN '[' IDS ']' ELSE expr
+{
+   $$ = std::string("#Model in [ ") + $6 + " ] ? " + $1 + " : " + $9;
+}
+;
+
+expr:
+ID_OR_NUM { $$ = $1; }
+|
+expr '+' expr { $$ = $1 + " + " + $3; }
+|
+expr '-' expr { $$ = $1 + " - " + $3; }
+|
+expr '*' expr { $$ = $1 + " * " + $3; }
+|
+expr '>' expr { $$ = $1 + " > " + $3; }
+|
+expr '<' expr { $$ = $1 + " < " + $3; }
+|
+expr '%' expr { $$ = $1 + " % " + $3; }
+|
+'(' expr ')'  { $$ = std::string("( ") + $2 + " )"; }
+|
+expr '=' '=' expr { $$ = $1 + " == " + $4; }
+|
+'-' expr %prec NEG { $$ = std::string(" - ") + $2; }
+|
+expr '/' expr
+{
+   if (wrap_div_in_function)
+   $$ = std::string("d_ratio ( ") + $1 + " , " + $3 + " )";
+   else
+   $$ = $1 + " / " + $3;
+}
+|
+MIN '(' expr ',' expr ')'
+{
+   $$ = std::string("min ( ") + $3 + " , " + $5 + " )";
+}
+|
+MAX '(' expr ',' expr ')'
+{
+   $$ = std::string("max ( ") + $3 + " , " + $5 + " )";
+}
+|
+if_expr
+{
+   if (convert_if_stmt)
+   $$ = std::string("( ") + $1 + " )";
+   else
+   $$ = $1;
+}
+;
+
+%%
+static int expr__symbol(yy::parser::semantic_type *res,
+   size_t p,
+   const std::string ,
+   size_t *cursor)
+{
+   std::string dst;
+
+   if (input[p] == '#')
+   dst += input[p++];
+
+   while (p < input.size() &&
+  (isalnum(input[p]) ||
+   input[p] == '_' ||
+   input[p] == '.' ||
+   input[p] == ':' ||
+   input[p] == '@' ||
+   input[p] == '\\' ||
+   input[p] == '=')
+ ) {
+   if(input[p] == '\\') {
+   // Consume 2 consequitive '\\' and the escaped char.
+   dst += input[p++];
+   if (p >= input.size())
+   break;
+   dst += input[p++];
+

[RFC PATCH 12/12] perf pmu-events: Topdown parser tool

2020-11-10 Thread Ian Rogers
From: Sandeep Dasgupta 

A tool for processing Intel's TMA_Metrics.csv and from it generating
metrics encoded as json.

As an example, the build here is configured to wget TMA_Metrics.csv
from download.01.org/perfmon and then build the metric json using
events encoded in the pmu-events directory. As the TMA_Metrics.csv is
newer there are missing event encodings that will be warned about, in
particular icelake PERF_METRICS.*.

On a Skylakex this shows with 'perf list metricgroups' the new groups
of:
  Topdown_Group_Backend
  Topdown_Group_BadSpec
  Topdown_Group_BrMispredicts
  Topdown_Group_Cache_Misses
  Topdown_Group_DSB
  Topdown_Group_FLOPS
  Topdown_Group_Fetch_BW
  Topdown_Group_Fetch_Lat
  Topdown_Group_Frontend
  Topdown_Group_HPC
  Topdown_Group_IcMiss
  Topdown_Group_Machine_Clears
  Topdown_Group_Memory_BW
  Topdown_Group_Memory_Bound
  Topdown_Group_Memory_Lat
  Topdown_Group_MicroSeq
  Topdown_Group_Offcore
  Topdown_Group_Ports_Utilization
  Topdown_Group_Retire
  Topdown_Group_TLB
  Topdown_Group_TopDownL1
  Topdown_Group_TopDownL2

And the new metrics of:
  Topdown_Metric_Backend_Bound
  Topdown_Metric_Bad_Speculation
  Topdown_Metric_Branch_Mispredicts
  Topdown_Metric_Branch_Resteers
  Topdown_Metric_Core_Bound
  Topdown_Metric_DRAM_Bound
  Topdown_Metric_DSB
  Topdown_Metric_DSB_Switches
  Topdown_Metric_DTLB_Load
  Topdown_Metric_Divider
  Topdown_Metric_FB_Full
  Topdown_Metric_FP_Arith
  Topdown_Metric_FP_Scalar
  Topdown_Metric_FP_Vector
  Topdown_Metric_Fetch_Bandwidth
  Topdown_Metric_Fetch_Latency
  Topdown_Metric_Frontend_Bound
  Topdown_Metric_Heavy_Operations
  Topdown_Metric_ICache_Misses
  Topdown_Metric_ITLB_Misses
  Topdown_Metric_L1_Bound
  Topdown_Metric_L2_Bound
  Topdown_Metric_L3_Bound
  Topdown_Metric_Light_Operations
  Topdown_Metric_MEM_Bandwidth
  Topdown_Metric_MEM_Latency
  Topdown_Metric_MITE
  Topdown_Metric_MS_Switches
  Topdown_Metric_Machine_Clears
  Topdown_Metric_Memory_Bound
  Topdown_Metric_Microcode_Sequencer
  Topdown_Metric_Other
  Topdown_Metric_Ports_Utilization
  Topdown_Metric_Retiring
  Topdown_Metric_Serializing_Operation
  Topdown_Metric_Store_Bound

Using one of the metric groups shows:
$ perf stat -M Topdown_Group_TopDownL1 -a

 Performance counter stats for 'system wide':

18,224,977,565  cpu/idq_uops_not_delivered.core,edge,any,inv/ # 
0.38 Topdown_Metric_Frontend_Bound
  # 0.44 
Topdown_Metric_Backend_Bound  (57.11%)
   450,438,658  cpu/int_misc.recovery_cycles,edge,any,inv/ # 0.07 
Topdown_Metric_Bad_Speculation  (57.11%)
11,981,273,993  cpu/cpu_clk_unhalted.thread,edge,any,inv/ # 0.11 
Topdown_Metric_Retiring  (57.13%)
 5,288,258,009  cpu/uops_retired.retire_slots,edge,any,inv/ 
(57.17%)
 6,808,261,153  cpu/uops_issued.any,edge,any,inv/   
  (57.19%)
   456,255,269  cpu/int_misc.recovery_cycles_any,edge,any,inv/  
   (57.17%)
12,383,804,530  cpu/cpu_clk_unhalted.thread_any,edge,any,inv/   
  (57.12%)

  10.159307832 seconds time elapsed

Co-authored-by: Stephane Eranian 
Co-authored-by: Ian Rogers 
Signed-off-by: Ian Rogers 
Signed-off-by: Sandeep Dasgupta 
---
 tools/perf/Makefile.perf| 13 +-
 tools/perf/pmu-events/Build | 50 ++---
 2 files changed, 58 insertions(+), 5 deletions(-)

diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index 7ce3f2e8b9c7..b1f4145ca757 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -634,6 +634,11 @@ strip: $(PROGRAMS) $(OUTPUT)perf
 
 PERF_IN := $(OUTPUT)perf-in.o
 
+TOPDOWN_PARSER:= $(OUTPUT)pmu-events/topdown_parser
+TOPDOWN_PARSER_IN := $(OUTPUT)pmu-events/topdown_parser-in.o
+
+export TOPDOWN_PARSER
+
 JEVENTS   := $(OUTPUT)pmu-events/jevents
 JEVENTS_IN:= $(OUTPUT)pmu-events/jevents-in.o
 
@@ -646,13 +651,19 @@ build := -f $(srctree)/tools/build/Makefile.build dir=. 
obj
 $(PERF_IN): prepare FORCE
$(Q)$(MAKE) $(build)=perf
 
+$(TOPDOWN_PARSER_IN): FORCE
+   $(Q)$(MAKE) -f $(srctree)/tools/build/Makefile.build dir=pmu-events 
obj=topdown_parser
+
+$(TOPDOWN_PARSER): $(TOPDOWN_PARSER_IN)
+   $(QUIET_LINK)$(HOSTCC) $(TOPDOWN_PARSER_IN) -lstdc++ -o $@
+
 $(JEVENTS_IN): FORCE
$(Q)$(MAKE) -f $(srctree)/tools/build/Makefile.build dir=pmu-events 
obj=jevents
 
 $(JEVENTS): $(JEVENTS_IN)
$(QUIET_LINK)$(HOSTCC) $(JEVENTS_IN) -o $@
 
-$(PMU_EVENTS_IN): $(JEVENTS) FORCE
+$(PMU_EVENTS_IN): $(JEVENTS) $(TOPDOWN_PARSER) FORCE
$(Q)$(MAKE) -f $(srctree)/tools/build/Makefile.build dir=pmu-events 
obj=pmu-events
 
 $(OUTPUT)perf: $(PERFLIBS) $(PERF_IN) $(PMU_EVENTS_IN) 
$(LIBTRACEEVENT_DYNAMIC_LIST)
diff --git a/tools/perf/pmu-events/Build b/tools/perf/pmu-events/Build
index 215ba30b8534..d54bf9e8c224 100644
--- a/tools/perf/pmu-events

[RFC PATCH 08/12] perf topdown-parser: Add event interface.

2020-11-10 Thread Ian Rogers
From: Sandeep Dasgupta 

Add an ability to load then query events loaded from json files. Events
may be loaded from a single json file, such as on
download.01.org/perfmon, are from multiple json files within a
directory.

Co-authored-by: Ian Rogers 
Signed-off-by: Ian Rogers 
Signed-off-by: Sandeep Dasgupta 
---
 .../pmu-events/topdown-parser/event_info.cpp  | 443 ++
 .../pmu-events/topdown-parser/event_info.h| 114 +
 2 files changed, 557 insertions(+)
 create mode 100644 tools/perf/pmu-events/topdown-parser/event_info.cpp
 create mode 100644 tools/perf/pmu-events/topdown-parser/event_info.h

diff --git a/tools/perf/pmu-events/topdown-parser/event_info.cpp 
b/tools/perf/pmu-events/topdown-parser/event_info.cpp
new file mode 100644
index ..c5a6fa305fcb
--- /dev/null
+++ b/tools/perf/pmu-events/topdown-parser/event_info.cpp
@@ -0,0 +1,443 @@
+/*
+ * Copyright 2020 Google LLC.
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include "event_info.h"
+
+#include 
+
+#include 
+
+#include "configuration.h"
+#include "dependence_dag_utils.h"
+#include "expr_parser-bison.hpp"
+#include "general_utils.h"
+#include "jsmn_extras.h"
+#include "logging.h"
+
+namespace topdown_parser
+{
+namespace
+{
+/**
+ * g_EventInfoMap stores, the event information `EventInfo`
+ * corresponsing to an event name and a cpu, using the following map
+ * structure.
+ *
+ *CPU -> (Event Name -> "Meta Information of that event")
+ *
+ * The data-structure is useful for querying event name for a particular
+ * cpu.
+ */
+using EventNameToEventInfo = std::unordered_map;
+using CPUToEventInfo = std::unordered_map;
+CPUToEventInfo *g_EventInfoMap = nullptr;
+
+/**
+ * Initialize globals.
+ */
+void InitGlobals()
+{
+   if (g_EventInfoMap == nullptr) {
+   g_EventInfoMap = new std::unordered_map<
+   std::string,
+   std::unordered_map >;
+   }
+}
+
+/**
+ * SearchEvent implements the algorithm to search event E for CPU 'cpu'
+ */
+bool SearchEvent(const std::string , const std::string _token,
+const EventInfo **event_data)
+{
+   // If there is no event encoding map for 'cpu', return false;
+   if (g_EventInfoMap->count(cpu) == 0) {
+   return false;
+   }
+
+   // If there is event encoding map for 'cpu' and event is found
+   // in the map, return true;
+   if (g_EventInfoMap->at(cpu).count(event_token)) {
+   *event_data = _EventInfoMap->at(cpu).at(event_token);
+   return true;
+   }
+
+   // At this point, we have an event encoding map for 'cpu', but
+   // event is NOT found in the map. Check for the alias CPUs and
+   // search for the event in their encoding  maps.
+   for (auto _set : *g_CpuAliasesForEventInfo) {
+   // Go over all the alias sets and find the one where
+   // `cpu` belongs.
+   if (alias_set.count(cpu) == 0) {
+   continue;
+   }
+
+   for (auto  : alias_set) {
+   if (alias == cpu) {
+   continue;
+   }
+   if (g_EventInfoMap->count(alias) &&
+   g_EventInfoMap->at(alias).count(event_token)) {
+   *event_data = _EventInfoMap->at(alias).at(
+   event_token);
+   return true;
+   }
+   }
+   }
+
+   return false;
+}
+
+void PopulateEventInfoMap(const char *js, const jsmntok_t *t, int r,
+ void *metainfo)
+{
+   std::unordered_map *event_info =
+   (std::unordered_map *)metainfo;
+
+   // Events are organized as an array of objects of key value pairs.
+   for (int i = 1; i < r;) {
+   if (t[i].type != JSMN_OBJECT) {
+   continue;
+   }
+   int size = t[i].size;
+   i++;
+   std::unordered_map working_set;
+   for (int j = 0; j < size; j += 2) {
+   std::pair key_val;
+   i = get_key_val(js, t, i, _val);
+   i++;
+   working_set[key_val.first] = key_val.second;
+   }
+   auto name = working_set.find("EventName");
+   if (name != working_set.end()) {
+   (*event_info)[name->second] = EventInfo(
+   name->second, working_set["EventCode"],
+   working_set["UMask"], working_set["MSRValue"],
+   working_set["CounterMask"],
+   working_set["Invert"], 

[RFC PATCH 10/12] perf topdown-parser: Add json metric code generation.

2020-11-10 Thread Ian Rogers
From: Sandeep Dasgupta 

Code generation from read in TMA_Metrics.csv to json metric encoding.

Signed-off-by: Ian Rogers 
Signed-off-by: Sandeep Dasgupta 
---
 .../code_gen_target_perf_json.cpp | 546 ++
 .../code_gen_target_perf_json.h   |  25 +
 2 files changed, 571 insertions(+)
 create mode 100644 
tools/perf/pmu-events/topdown-parser/code_gen_target_perf_json.cpp
 create mode 100644 
tools/perf/pmu-events/topdown-parser/code_gen_target_perf_json.h

diff --git a/tools/perf/pmu-events/topdown-parser/code_gen_target_perf_json.cpp 
b/tools/perf/pmu-events/topdown-parser/code_gen_target_perf_json.cpp
new file mode 100644
index ..70bb45de6675
--- /dev/null
+++ b/tools/perf/pmu-events/topdown-parser/code_gen_target_perf_json.cpp
@@ -0,0 +1,546 @@
+/*
+ * Copyright 2020 Google LLC.
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include "code_gen_target_perf_json.h"
+
+#include 
+#include 
+#include 
+
+#include "configuration.h"
+#include "dependence_dag_utils.h"
+#include "event_info.h"
+#include "expr_parser-bison.hpp"
+#include "general_utils.h"
+#include "logging.h"
+
+namespace topdown_parser
+{
+namespace
+{
+/**
+ * The input csv file does not define the formula for some metrics which
+ * are meant to be defined by the host machine. For example, the
+ * expression entry for Boolean metric `SMT_on` is empty in the input
+ * csv file.  Perf tool evaluating the formula must extract information
+ * about the availability of hyper-threading from the host machine. We
+ * refer such metrics as external parameters.  While generating the
+ * metric json files (encoding the expression of each metric), we want
+ * to replace the expression for such metrics either with their
+ * definition or a symbol recognized by the perf tool so that it can
+ * parse the json file correctly.  For example,
+ * `#SMT_on` is the symbol used by perf tool identify the csv Boolean
+ * metric `SMT_on`
+ *
+ * 'CheckExternalParameter' checks if a name matches an external
+ * parameter name. If found, then `external_param_info` is used to
+ * return meta-information about the external parameter. The information
+ * includes: (1) The data-type of the metric, (2) The definition or
+ * the symbol used to replace the metric expression of the external
+ * parameter.
+ */
+bool CheckExternalParameter(
+   const std::string _name,
+   std::pair >
+   *external_param_info)
+{
+   using ParamInfo = std::pair;
+   using ExternalParamNameToParamInfo = std::map;
+
+   /**
+* g_ExternalParameters stores the external parameters in the
+* following format:
+* Parameter name --> {Parameter Data Type, Definition or
+*   symbol to be used instead of the parameter}
+*/
+   static ExternalParamNameToParamInfo g_ExternalParameters = {
+   // SMT_on: Hyper-threading is ON on host machine.
+   { "SMT_on",
+ std::pair("bool", "#SMT_on") },
+   // EBS_Mode: Event Sampling Based Mode
+   { "EBS_Mode",
+ std::pair("bool", "0") },
+   };
+
+   for (auto  : g_ExternalParameters) {
+   const std::string _name = exp.first;
+   if (sym_name.find(exp_name) != std::string::npos) {
+   *external_param_info =
+   std::pair >(
+   exp_name, exp.second);
+   return true;
+   }
+   }
+   external_param_info = nullptr;
+   return false;
+}
+
+/**
+ * Create the event string for event 'event_str'.
+ *
+ * For example:
+ *  For the event "OFFCORE_REQUESTS_OUTSTANDING.ALL_DATA_RD:c4",
+ *  Return:
+ *  "cpu@OFFCORE_REQUESTS_OUTSTANDING.ALL_DATA_RD\\,cmask\\=4@"
+ */
+std::string GetEventString(const std::string _str, const std::string 
)
+{
+   std::string retval("");
+   const EventInfo *event_data;
+   std::vector tokens;
+
+   GetEventInfo(event_str, cpu, _data, );
+
+   const std::string _name = event_data->eventname_;
+   const std::string msrvalue = Trim(event_data->msrvalue_);
+   std::string cmask = event_data->countermask_;
+
+   std::string edge = "";
+   if (event_data->edgedetect_ != "0") {
+   edge = "edge";
+   }
+
+   const std::string any = (event_data->anythread_ != "0") ? "any" : "";
+
+   std::string invert = "";
+   if (event_data->invert_ != "0") {
+   invert = "inv";
+   }
+
+   if (tokens.size() > 1) {
+   for (size_t i = 1; i < tokens.size(); ++i) {
+ 

[RFC PATCH 02/12] perf topdown-parser: Add utility functions.

2020-11-10 Thread Ian Rogers
From: Sandeep Dasgupta 

Basic string, ostream and file functions.

Co-authored-by: Ian Rogers 
Signed-off-by: Ian Rogers 
Signed-off-by: Sandeep Dasgupta 
---
 .../topdown-parser/general_utils.cpp  | 173 ++
 .../pmu-events/topdown-parser/general_utils.h | 131 +
 2 files changed, 304 insertions(+)
 create mode 100644 tools/perf/pmu-events/topdown-parser/general_utils.cpp
 create mode 100644 tools/perf/pmu-events/topdown-parser/general_utils.h

diff --git a/tools/perf/pmu-events/topdown-parser/general_utils.cpp 
b/tools/perf/pmu-events/topdown-parser/general_utils.cpp
new file mode 100644
index ..810c27cf3724
--- /dev/null
+++ b/tools/perf/pmu-events/topdown-parser/general_utils.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2020 Google LLC.
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include "general_utils.h"
+
+#include 
+#include 
+#include 
+
+#include 
+#include 
+
+#include "logging.h"
+
+namespace topdown_parser
+{
+std::string Trim(const std::string )
+{
+   const char *ws = " \t\n\r\f\v";
+   size_t endpos = str.find_last_not_of(ws);
+   if (endpos == std::string::npos)
+   return "";
+
+   size_t startpos = str.find_first_not_of(ws);
+   return str.substr(startpos, endpos - startpos + 1);
+}
+
+std::vector Split(const std::string , char delim)
+{
+   std::vector tokens;
+   std::string token;
+   std::istringstream tokenStream(str);
+   while (std::getline(tokenStream, token, delim)) {
+   tokens.push_back(Trim(token));
+   }
+   return tokens;
+}
+
+std::string Strip(const std::string , char delim)
+{
+   std::string retval("");
+   for (size_t i = 0; i < str.length(); ++i) {
+   if (str[i] != delim) {
+   retval += str[i];
+   }
+   }
+   return retval;
+}
+
+std::vector WhitespaceSplit(const std::string )
+{
+   std::vector split_tokens = Split(s, ' ');
+   std::vector retval;
+   for (auto _token : split_tokens) {
+   if (split_token.empty() || split_token == " ") {
+   continue;
+   }
+   retval.push_back(split_token);
+   }
+   return retval;
+}
+
+bool IsOperator(const std::string )
+{
+   std::regex r(
+   "\\/|\\-|\\+|\\*|\\(|\\)|\\<|\\>|min|max|\\?|\\:|,|==|>=|<=|="
+   "|if|else|d_ratio|#Model|in|\\[|\\]");
+   return regex_match(Trim(str), r);
+}
+
+bool IsConstant(const std::string )
+{
+   std::regex integer("[-+]?[0-9]+");
+   std::regex floating("[-+]?[0-9]*\\.?[0-9]+");
+
+   return regex_match(str, integer) || regex_match(str, floating);
+}
+
+time_t GetTimestamp(const std::string )
+{
+   struct stat st;
+   int ierr = stat(fname.c_str(), );
+   if (ierr != 0) {
+   ERROR("Error getting stat on file: " << fname);
+   return 0;
+   }
+   return st.st_mtime;
+}
+
+bool CheckDirPathExists(const std::string )
+{
+   return opendir(dirname.c_str()) != nullptr;
+}
+
+std::string ConvertToCIdentifier(const std::string )
+{
+   static const char *int_to_word[] = { "zero",  "one",  "two", "three",
+"four",  "five", "six", "seven",
+"eight", "nine" };
+   std::regex r("\\/|#|\\.|-|:|=");
+   std::string retval = regex_replace(str, r, "_");
+
+   std::smatch sm;
+   if (regex_match(retval, sm, std::regex("^([0-9])(.*)"))) {
+   auto digit = stoi(sm[1].str());
+   std::string word = int_to_word[digit];
+   std::string rest = sm[2].str();
+   return word + "_" + rest;
+   }
+   return retval;
+}
+
+std::string ToLower(const std::string )
+{
+   std::string retval("");
+
+   for (auto  : str) {
+   retval.append(1, std::tolower(c));
+   }
+   return retval;
+}
+
+std::vector NormalizeModel(const std::vector ,
+   const std::string )
+{
+   std::vector retval;
+   // Track the event if encountering a '['
+   bool match_start = false;
+   // The evaluated value of the sub-expression #Model in ['CPUX' 'CPUY']
+   int condition = 0;
+
+   for (size_t i = 0; i < tokens.size(); ++i) {
+   // Skip keywords like "#Model" and "in"
+   if (tokens[i] == "#Model" || tokens[i] == "in") {
+   continue;
+   }
+   if (tokens[i] == "[") {
+   match_start = true;
+   

[RFC PATCH 11/12] perf topdown-parser: Main driver.

2020-11-10 Thread Ian Rogers
From: Sandeep Dasgupta 

Invoke the necessary configuration reading and parsing, then code
generation. Handles command line arguments.
Add a minor README.

Co-authored-by: Stephane Eranian 
Co-authored-by: Ian Rogers 
Signed-off-by: Ian Rogers 
Signed-off-by: Sandeep Dasgupta 
---
 tools/perf/pmu-events/topdown-parser/README   |   5 +
 .../topdown-parser/topdown_parser_main.cpp| 155 ++
 2 files changed, 160 insertions(+)
 create mode 100644 tools/perf/pmu-events/topdown-parser/README
 create mode 100644 tools/perf/pmu-events/topdown-parser/topdown_parser_main.cpp

diff --git a/tools/perf/pmu-events/topdown-parser/README 
b/tools/perf/pmu-events/topdown-parser/README
new file mode 100644
index ..7f100792b00c
--- /dev/null
+++ b/tools/perf/pmu-events/topdown-parser/README
@@ -0,0 +1,5 @@
+Topdown parser and code generator
+=
+
+The topdown parser processes a TMA_Metrics.csv file and generates
+Intel specific metrics from the data in the spreadsheet cells.
\ No newline at end of file
diff --git a/tools/perf/pmu-events/topdown-parser/topdown_parser_main.cpp 
b/tools/perf/pmu-events/topdown-parser/topdown_parser_main.cpp
new file mode 100644
index ..ba9acd32726e
--- /dev/null
+++ b/tools/perf/pmu-events/topdown-parser/topdown_parser_main.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2020 Google LLC.
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+
+#include "code_gen_target.h"
+#include "configuration.h"
+#include "csvreader.h"
+#include "dependence_dag_utils.h"
+#include "event_info.h"
+#include "logging.h"
+
+namespace topdown_parser
+{
+namespace
+{
+/**
+ * Printing usage model
+ */
+[[noreturn]] void ShowUsage()
+{
+   std::cout
+   << "\n"
+  " Usage: topdown_parser  --csv-file \n"
+  " --events-data-dir \n"
+  " --config-file \n"
+  " --output-path \n"
+  " [Options]\n"
+  " Synopsis: Auto-generates topdown.c \n\n"
+  " Options\n"
+  "\t--dump-events :Dump the unique events for each 
metric.\n"
+  "generated topdown file. Used for testing.\n"
+  "\t--help:Show help\n";
+   exit(0);
+}
+
+/**
+ * The input csv file name specifying formula encoding for topdown
+ * metric
+ */
+char *g_CsvFile = nullptr;
+
+/**
+ * ProcessArgs parses command-line arguments
+ */
+bool ProcessArgs(int argc, char **argv)
+{
+   // The following command-line arguments to the program
+   // todown_parser are required: --csv-file ,
+   // --events-data-dir , --config-file  --output-path
+   // 
+   if (argc < 9) {
+   ShowUsage();
+   return false;
+   }
+
+   const char *const short_opts = "f:a:z:hdt";
+   const option long_opts[] = {
+   { "csv-file", required_argument, nullptr, 'f' },
+   { "events-data-dir", required_argument, nullptr, 'a' },
+   { "config-file", required_argument, nullptr, 'z' },
+   { "output-path", required_argument, nullptr, 'o' },
+   { "dump-events", no_argument, nullptr, 'd' },
+   { "help", no_argument, nullptr, 'h' },
+   { nullptr, no_argument, nullptr, 0 }
+   };
+
+   while (true) {
+   const auto opt =
+   getopt_long(argc, argv, short_opts, long_opts, nullptr);
+
+   if (opt == -1)
+   break;
+
+   switch (opt) {
+   case 'f':
+   g_CsvFile = optarg;
+   break;
+
+   case 'a':
+   kConfigParams->event_data_dir_ = optarg;
+   kConfigParams->event_data_dir_ += "/";
+   break;
+
+   case 'z':
+   kConfigParams->config_file_ = optarg;
+   break;
+
+   case 'o':
+   kConfigParams->output_path_ = optarg;
+   break;
+
+   case 'd':
+   g_DumpEvents = true;
+   break;
+
+   case 'h':
+   case '?':
+   default:
+   ShowUsage();
+   return false;
+   }
+   }
+
+   INFO("csv filename: |" << g_CsvFile << "|");
+   INFO("events data dir: |" << kConfigParams->event_data_dir_ &

[RFC PATCH 05/12] perf topdown-parser: Add a configuration.

2020-11-10 Thread Ian Rogers
From: Sandeep Dasgupta 

The configuration.json holds configuration data that will be read
into the ConfigurationParameters class in configuration.h.

Co-authored-by: Ian Rogers 
Signed-off-by: Ian Rogers 
Signed-off-by: Sandeep Dasgupta 
---
 .../topdown-parser/configuration.cpp  | 198 ++
 .../pmu-events/topdown-parser/configuration.h | 181 
 .../topdown-parser/configuration.json |  72 +++
 3 files changed, 451 insertions(+)
 create mode 100644 tools/perf/pmu-events/topdown-parser/configuration.cpp
 create mode 100644 tools/perf/pmu-events/topdown-parser/configuration.h
 create mode 100644 tools/perf/pmu-events/topdown-parser/configuration.json

diff --git a/tools/perf/pmu-events/topdown-parser/configuration.cpp 
b/tools/perf/pmu-events/topdown-parser/configuration.cpp
new file mode 100644
index ..6cb4dffe7755
--- /dev/null
+++ b/tools/perf/pmu-events/topdown-parser/configuration.cpp
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2020 Google LLC.
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include "configuration.h"
+
+#include 
+
+#include "jsmn_extras.h"
+#include "logging.h"
+
+namespace topdown_parser
+{
+/**
+ * kConfigParams is the set of all the parameters defined by the
+ * configuration.json file.
+ */
+ConfigurationParameters *const kConfigParams =
+   ConfigurationParameters::GetConfigurationParameters();
+
+ConfigurationParameters *ConfigurationParameters::config_param_instance_ =
+   nullptr;
+
+namespace
+{
+/**
+ * Parse the 'configuration.json' file to populate the configuration
+ * parameters `kConfigParams`. Each key in the Json file corresponds to
+ * a parameter in `kConfigParams`.
+ */
+void ParseConfigJson(const char *js, const jsmntok_t *t, int r,
+void *metainfo __attribute__((unused)))
+{
+   for (int i = 1; i < r; ++i) {
+   if (jsoneq(js, [i], "configuration") == 0) {
+   i++;
+   assert(t[i].type == JSMN_OBJECT);
+   continue;
+   }
+
+   if (jsoneq(js, [i], "_COMMENT_") == 0) {
+   i++;
+   continue;
+   }
+
+   if (jsoneq(js, [i], "target") == 0) {
+   i++;
+   std::string retval;
+   i = get_primitive(js, t, i, );
+   kConfigParams->target_ = retval;
+   continue;
+   }
+
+   if (jsoneq(js, [i], "metric_max_header") == 0) {
+   i++;
+   std::string retval;
+   i = get_primitive(js, t, i, );
+   if (!retval.empty()) {
+   kConfigParams->metric_max_header_ =
+   stoi(retval);
+   }
+   continue;
+   }
+
+   if (jsoneq(js, [i], "header_row") == 0) {
+   i++;
+   std::string retval;
+   i = get_primitive(js, t, i, );
+   if (!retval.empty()) {
+   kConfigParams->header_row = stoi(retval) - 1;
+   }
+   continue;
+   }
+
+   if (jsoneq(js, [i], "formula_start_colm") == 0) {
+   i++;
+   std::string retval;
+   i = get_primitive(js, t, i, );
+   if (!retval.empty()) {
+   kConfigParams->formula_start_colm_ =
+   retval[0] - 'A';
+   }
+   continue;
+   }
+
+   if (jsoneq(js, [i], "formula_end_colm") == 0) {
+   i++;
+   std::string retval;
+   i = get_primitive(js, t, i, );
+   if (!retval.empty()) {
+   kConfigParams->formula_end_colm_ =
+   retval[0] - 'A';
+   }
+   continue;
+   }
+
+   if (jsoneq(js, [i], "server_identifier_row") == 0) {
+   i++;
+   std::string retval;
+   i = get_primitive(js, t, i, );
+   if (!retval.empty()) {
+   kConfigParams->server_identifier_row_ =
+   stoi(retval) - 1;
+   }
+   continue;
+   }
+
+   if (jsoneq(js, [i], "first_level") == 0) {
+   i++;
+   std::string retval;
+   i = get_pr

[RFC PATCH 04/12] perf topdown-parser: Add a json file reader.

2020-11-10 Thread Ian Rogers
From: Sandeep Dasgupta 

Wrap jsmn as a "C" library. Add some utilities for working with tokens
and to read a vector of tokens.

Co-authored-by: Ian Rogers 
Signed-off-by: Ian Rogers 
Signed-off-by: Sandeep Dasgupta 
---
 .../pmu-events/topdown-parser/jsmn_extras.cpp | 199 ++
 .../pmu-events/topdown-parser/jsmn_extras.h   |  42 
 2 files changed, 241 insertions(+)
 create mode 100644 tools/perf/pmu-events/topdown-parser/jsmn_extras.cpp
 create mode 100644 tools/perf/pmu-events/topdown-parser/jsmn_extras.h

diff --git a/tools/perf/pmu-events/topdown-parser/jsmn_extras.cpp 
b/tools/perf/pmu-events/topdown-parser/jsmn_extras.cpp
new file mode 100644
index ..83a15b636378
--- /dev/null
+++ b/tools/perf/pmu-events/topdown-parser/jsmn_extras.cpp
@@ -0,0 +1,199 @@
+#include "jsmn_extras.h"
+
+#include 
+#include 
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+
+#include "logging.h"
+
+namespace topdown_parser
+{
+int jsoneq(const char *json, const jsmntok_t *tok, const char *s)
+{
+   if (tok->type == JSMN_STRING &&
+   static_cast(strlen(s)) == tok->end - tok->start &&
+   strncmp(json + tok->start, s, tok->end - tok->start) == 0) {
+   return 0;
+   }
+   return -1;
+}
+
+int get_primitive(const char *js, const jsmntok_t *t, int i,
+ std::string *retval)
+{
+   if (t[i].type != JSMN_STRING && t[i].type != JSMN_PRIMITIVE) {
+   assert(0);
+   }
+   const jsmntok_t *g = t + i;
+   (*retval) = std::string(js + g->start, g->end - g->start);
+   return i;
+}
+
+// Parse the following pattern of key-values
+//  A:B
+int get_key_val(const char *js, const jsmntok_t *t, int i,
+   std::pair *P)
+{
+   assert(t[i].type == JSMN_STRING);
+   i = get_primitive(js, t, i, &((*P).first));
+
+   i++;
+   i = get_primitive(js, t, i, &((*P).second));
+
+   return i;
+}
+
+int get_array_of_primitives(const char *js, const jsmntok_t *t, int i,
+   std::vector *V)
+{
+   int j;
+   if (t[i].type != JSMN_ARRAY) {
+   assert(0);
+   }
+   int size = t[i].size;
+   if (size == 0) {
+   return i;
+   }
+
+   i++;
+   std::string retval;
+
+   for (j = 0; j < size - 1; j++) {
+   i = get_primitive(js, t, i, );
+   (*V).push_back(retval);
+   i++;
+   }
+   i = get_primitive(js, t, i, );
+   (*V).push_back(retval);
+
+   return i;
+}
+
+int get_struct(const char *js, const jsmntok_t *t, int i,
+  std::map *data)
+{
+   int j;
+   if (t[i].type != JSMN_OBJECT) {
+   assert(0);
+   }
+
+   int size = t[i].size;
+   i++;
+
+   for (j = 0; j < size - 2; j += 2) {
+   std::pair P;
+   i = get_key_val(js, t, i, );
+   (*data).insert(P);
+   i++;
+   }
+   std::pair P;
+   i = get_key_val(js, t, i, );
+   (*data).insert(P);
+   return i;
+}
+
+int get_struct_of_array(
+   const char *js, const jsmntok_t *t, int i,
+   std::unordered_map > *data)
+{
+   if (t[i].type != JSMN_OBJECT) {
+   assert(0);
+   }
+
+   int size = t[i].size;
+   i++;
+
+   std::string key;
+   for (int j = 0; j < size - 2; j += 2) {
+   i = get_primitive(js, t, i, );
+   i++;
+
+   i = get_array_of_primitives(js, t, i, &((*data)[key]));
+   i++;
+   }
+   i = get_primitive(js, t, i, );
+   i++;
+   i = get_array_of_primitives(js, t, i, &((*data)[key]));
+   return i;
+}
+
+/**
+ * ParseJson parses a json file file 'fname' and delegate the processing of the
+ * parsed model to an external callback function 'callback' provided by the
+ * clients of the function.
+ *
+ * The clients using the following routine are:
+ * 1. ReadEventInfoFromJson: Parsing the event encoding json file for each CPU
+ *as downloaded from  https://download.01.org/perfmon/
+ * 2. ReadConfig: Parsing the configuration.json file, which specifies the
+ *parameters for the topdown_parser tool.
+ */
+int ParseJson(const char *fname,
+ void (*callback)(const char *, const jsmntok_t *, int, void *),
+ void *metainfo)
+{
+   // Read the file fully into js.
+   int fd = open(fname, O_RDONLY);
+   if (fd == -1) {
+   ERROR("Failed to open '" << fname << "': " << strerror(errno));
+   return 1;
+   }
+   struct stat statbuf;
+   if (fstat(fd, ) == -1) {
+   ERROR("Failed to stat '" << fname << "': " << strerror(errno));
+   close(fd);
+   return 2;
+   }
+
+   std::unique_ptr js

[RFC PATCH 06/12] perf topdown-parser: Interface for TMA_Metrics.csv.

2020-11-10 Thread Ian Rogers
From: Sandeep Dasgupta 

Reads the CSV file then creates an in memory model from the data.

Co-authored-by: Stephane Eranian 
Signed-off-by: Ian Rogers 
Signed-off-by: Sandeep Dasgupta 
---
 .../topdown-parser/dependence_dag_utils.cpp   | 984 ++
 .../topdown-parser/dependence_dag_utils.h | 178 
 2 files changed, 1162 insertions(+)
 create mode 100644 
tools/perf/pmu-events/topdown-parser/dependence_dag_utils.cpp
 create mode 100644 tools/perf/pmu-events/topdown-parser/dependence_dag_utils.h

diff --git a/tools/perf/pmu-events/topdown-parser/dependence_dag_utils.cpp 
b/tools/perf/pmu-events/topdown-parser/dependence_dag_utils.cpp
new file mode 100644
index ..7c9eff06e2a9
--- /dev/null
+++ b/tools/perf/pmu-events/topdown-parser/dependence_dag_utils.cpp
@@ -0,0 +1,984 @@
+/*
+ * Copyright 2020 Google LLC.
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include "dependence_dag_utils.h"
+
+#include 
+#include 
+#include 
+
+#include "configuration.h"
+#include "general_utils.h"
+#include "logging.h"
+
+namespace topdown_parser
+{
+char g_PerfmonVersion[VERSION_MAX_STRLEN];
+
+std::map *g_TopdownHierarchy = nullptr;
+
+std::vector *g_RelevantCpus = nullptr;
+
+std::vector > *g_CpuAliasesForEventInfo = nullptr;
+
+namespace
+{
+/**
+ * Column number in the input csv file specifying 'Count Domain'
+ */
+size_t g_CountDomainColm = UINT_MAX;
+
+/**
+ * Column number in the input csv file specifying 'Metric Group'
+ */
+size_t g_MetricGroupColm = UINT_MAX;
+
+/**
+ * Column number in the input csv file specifying 'Description'
+ */
+size_t g_DescColm = UINT_MAX;
+
+/**
+ * header_rowKey is used to derive the row number of the header. The
+ * header of the input csv file specifies the information like level
+ * numbers, CPU product names, Metric Description etc.  A typical header
+ * row looks like: Key | Level1 | Level2 | SKX SKL | Count | Domain
+ * Metric Description |  ...
+ */
+const char *header_rowKey = "level[0-9]+";
+
+/**
+ * formula_start_colm_Key is used to derive the first column number
+ * specifying a formula.
+ */
+const char *formula_start_colm_Key = "level[0-9]+";
+
+/**
+ * formula_end_colm_Key is used to derive the last column number
+ * specifying a formula.
+ */
+const char *formula_end_colm_Key = "locate-with";
+
+/**
+ * g_CountDomainColmKey is used to derive column number in the input csv
+ * file specifying 'Count Domain'.
+ */
+const char *g_CountDomainColmKey = "count";
+
+/**
+ * g_DescColmKey is used to derive column number in the input csv file
+ * specifying 'Description'.
+ */
+const char *g_DescColmKey = "description";
+
+/**
+ * g_MetricGroupColmKey is used to derive column number in the input csv
+ * file specifying 'Metric Group'.
+ */
+const char *g_MetricGroupColmKey = "group";
+
+/**
+ * Last row number in the input csv file specifying topdown levels
+ */
+size_t g_LevelEndRow = UINT_MAX;
+
+/**
+ * g_LevelEndRowKey is used to derive the last row number in the input
+ * csv file specifying topdown levels.
+ */
+const char *g_LevelEndRowKey = "\\.";
+
+/**
+ * First and last column numbers in the input csv file specifying
+ * topdown levels.
+ */
+size_t g_LevelStartColm = UINT_MAX;
+size_t g_LevelEndColm = UINT_MAX;
+
+/**
+ * Initialize globals.
+ */
+void InitGlobals()
+{
+   if (g_TopdownHierarchy == nullptr) {
+   g_TopdownHierarchy = new std::map;
+   }
+
+   if (g_RelevantCpus == nullptr) {
+   g_RelevantCpus = new std::vector;
+   }
+
+   if (g_CpuAliasesForEventInfo == nullptr) {
+   g_CpuAliasesForEventInfo =
+   new std::vector >;
+   }
+}
+
+/**
+ * Plot the topdown hierarchy in graphviz dot.
+ */
+void PlotTopdownHierarchy(
+   const std::map _TopdownHierarchy)
+{
+   std::string topdown_hierarchy_dot =
+   kConfigParams->output_path_ + "topdown_hierarchy.dot";
+   std::ofstream ofile_dot(topdown_hierarchy_dot);
+   if (!ofile_dot.is_open()) {
+   ERROR("Error opening file: " << topdown_hierarchy_dot);
+   exit(1);
+   }
+
+   INFO("Generating topdown hierarchy file: " << topdown_hierarchy_dot);
+
+   ofile_dot << "digraph graphname {\n";
+   int toggle = 0;
+   std::string color = "[color=blue]";
+   for (auto  : g_TopdownHierarchy) {
+   auto metric_name = std::string("\"") + p.first + "\"";
+
+   if (toggle == 0) {
+   color = "[color=blue]";
+   } else {
+   color = "[color=red]";
+   }
+
+   for (size_t i = 0; i < p.second.child_metrics.size() - 1; ++i) {
+   ofile_dot <

[RFC PATCH 00/12] Topdown parser

2020-11-10 Thread Ian Rogers
This RFC is for a new tool that reads TMA_Metrics.csv as found on
download.01.org/perfmon and generates metrics and metric groups from
it. To show the functionality the TMA_Metrics.csv is downloaded, but
an accepted change would most likely include a copy of this file from
Intel. With this tool rather than just level 1 topdown metrics, a full
set of topdown metrics to level 4 are generated.

This change was:
Suggested-by: Stephane Eranian 

Sandeep Dasgupta (12):
  perf topdown-parser: Add a simple logging API.
  perf topdown-parser: Add utility functions.
  perf topdown-paser: Add a CSV file reader.
  perf topdown-parser: Add a json file reader.
  perf topdown-parser: Add a configuration.
  perf topdown-parser: Interface for TMA_Metrics.csv.
  perf topdown-parser: Metric expression parser.
  perf topdown-parser: Add event interface.
  perf topdown-paser: Add code generation API.
  perf topdown-parser: Add json metric code generation.
  perf topdown-parser: Main driver.
  perf pmu-events: Topdown parser tool

 tools/perf/Makefile.perf  |  13 +-
 tools/perf/pmu-events/Build   |  50 +-
 tools/perf/pmu-events/topdown-parser/README   |   5 +
 .../topdown-parser/code_gen_target.cpp|  51 +
 .../topdown-parser/code_gen_target.h  |  77 ++
 .../code_gen_target_perf_json.cpp | 546 ++
 .../code_gen_target_perf_json.h   |  25 +
 .../topdown-parser/configuration.cpp  | 198 
 .../pmu-events/topdown-parser/configuration.h | 181 
 .../topdown-parser/configuration.json |  72 ++
 .../pmu-events/topdown-parser/csvreader.cpp   |  49 +
 .../pmu-events/topdown-parser/csvreader.h |  51 +
 .../topdown-parser/dependence_dag_utils.cpp   | 984 ++
 .../topdown-parser/dependence_dag_utils.h | 178 
 .../pmu-events/topdown-parser/event_info.cpp  | 443 
 .../pmu-events/topdown-parser/event_info.h| 114 ++
 .../pmu-events/topdown-parser/expr_parser.y   | 224 
 .../topdown-parser/general_utils.cpp  | 173 +++
 .../pmu-events/topdown-parser/general_utils.h | 131 +++
 .../pmu-events/topdown-parser/jsmn_extras.cpp | 199 
 .../pmu-events/topdown-parser/jsmn_extras.h   |  42 +
 .../perf/pmu-events/topdown-parser/logging.h  |  25 +
 .../topdown-parser/topdown_parser_main.cpp| 155 +++
 23 files changed, 3981 insertions(+), 5 deletions(-)
 create mode 100644 tools/perf/pmu-events/topdown-parser/README
 create mode 100644 tools/perf/pmu-events/topdown-parser/code_gen_target.cpp
 create mode 100644 tools/perf/pmu-events/topdown-parser/code_gen_target.h
 create mode 100644 
tools/perf/pmu-events/topdown-parser/code_gen_target_perf_json.cpp
 create mode 100644 
tools/perf/pmu-events/topdown-parser/code_gen_target_perf_json.h
 create mode 100644 tools/perf/pmu-events/topdown-parser/configuration.cpp
 create mode 100644 tools/perf/pmu-events/topdown-parser/configuration.h
 create mode 100644 tools/perf/pmu-events/topdown-parser/configuration.json
 create mode 100644 tools/perf/pmu-events/topdown-parser/csvreader.cpp
 create mode 100644 tools/perf/pmu-events/topdown-parser/csvreader.h
 create mode 100644 
tools/perf/pmu-events/topdown-parser/dependence_dag_utils.cpp
 create mode 100644 tools/perf/pmu-events/topdown-parser/dependence_dag_utils.h
 create mode 100644 tools/perf/pmu-events/topdown-parser/event_info.cpp
 create mode 100644 tools/perf/pmu-events/topdown-parser/event_info.h
 create mode 100644 tools/perf/pmu-events/topdown-parser/expr_parser.y
 create mode 100644 tools/perf/pmu-events/topdown-parser/general_utils.cpp
 create mode 100644 tools/perf/pmu-events/topdown-parser/general_utils.h
 create mode 100644 tools/perf/pmu-events/topdown-parser/jsmn_extras.cpp
 create mode 100644 tools/perf/pmu-events/topdown-parser/jsmn_extras.h
 create mode 100644 tools/perf/pmu-events/topdown-parser/logging.h
 create mode 100644 tools/perf/pmu-events/topdown-parser/topdown_parser_main.cpp

-- 
2.29.2.222.g5d2a92d10f8-goog



[RFC PATCH 01/12] perf topdown-parser: Add a simple logging API.

2020-11-10 Thread Ian Rogers
From: Sandeep Dasgupta 

A logging API that is simpler but inspired by that in abseil.

Signed-off-by: Ian Rogers 
Signed-off-by: Sandeep Dasgupta 
---
 .../perf/pmu-events/topdown-parser/logging.h  | 25 +++
 1 file changed, 25 insertions(+)
 create mode 100644 tools/perf/pmu-events/topdown-parser/logging.h

diff --git a/tools/perf/pmu-events/topdown-parser/logging.h 
b/tools/perf/pmu-events/topdown-parser/logging.h
new file mode 100644
index ..9942018c4c75
--- /dev/null
+++ b/tools/perf/pmu-events/topdown-parser/logging.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+// -
+// File: logging.h
+// -
+//
+// The header provides the macro defintion for logging errors/warnings
+
+#ifndef TOPDOWN_PARSER_LOGGING_H_
+#define TOPDOWN_PARSER_LOGGING_H_
+
+#include 
+
+#define INFO(msg) std::cout << "\033[1;35mInfo: " << msg << "\033[0m\n"
+#define ERROR(msg) 
\
+   std::cout << __FILE__ << ":" << __LINE__   \
+ << " \033[1;31mError: " << msg << "\033[0m\n"
+#define FATAL(msg) 
\
+   do {   \
+   std::cout << __FILE__ << ":" << __LINE__   \
+ << " \033[1;31mFatal: " << msg << "\033[0m\n";   \
+   exit(1);   \
+   } while (false)
+
+#endif // TOPDOWN_PARSER_LOGGING_H_
-- 
2.29.2.222.g5d2a92d10f8-goog



Re: [perf metricgroup] fcc9c5243c: perf-sanity-tests.Parse_and_process_metrics.fail

2020-11-03 Thread Ian Rogers
On Tue, Nov 3, 2020 at 6:43 AM John Garry  wrote:
>
> On 20/10/2020 17:53, Ian Rogers wrote:
> >>> Thanks for taking a look John. If you want help you can send the
> >>> output of "perf test 67 -vvv" to me. It is possible Broadwell has
> >>> similar glitches in the json to Skylake. I tested the original test on
> >>> server parts as I can access them as cloud machines.
> >>>
> >>>> I will have a look, but I was hoping that Ian would have a proper fix
> >>>> for this on top of ("perf metricgroup: Fix uncore metric expressions"),
> >>>> which now looks to be merged.
> >>> I still have these changes to look at in my inbox but I'm assuming
> >>> they're good:-)  Sorry for not getting to them, but it's good they are
> >>> merged.
> >> Hi Ian,
> >> Checked in upstream kernel with your fix patch, in powerpc also test 
> >> case 67 is passing.
> >> But I am getting issue in test 10 for powerpc
> >>
> >> [command]# ./perf test 10
> >> 10: PMU events  :
> >> 10.1: PMU event table sanity: Ok
> >> 10.2: PMU event map aliases : Ok
> >> 10.3: Parsing of PMU event table metrics: Skip 
> >> (some metrics failed)
> >> 10.4: Parsing of PMU event table metrics with fake PMUs : 
> >> FAILED!
> >>
> >> Was debugging it, issue is with commit e1c92a7fbbc5 perf tests: Add 
> >> another metric parsing test.
> >>
> >> So, there we are passing different runtime parameter value in 
> >> "expr__find_other and expr__parse"
> >> in function `metric_parse_fake`. I believe we need to send same value.
> >> I will send fix patch for the same.
>
> Just wondering, was a patch ever submitted for this? Something still
> broken? I can't see any recent relevant changes to tests/pmu-events.c

The test itself shouldn't have changed, but the json files parsed by
jevents and turned into C code that the test exercises should have
changed. Jin Yao has sent two patch sets fixing a metric issue on SKL
(Skylake non-server) that should hopefully fix the issue there - I'll
check the status on these. Are you testing on Skylake?

Thanks,
Ian

> Thanks,
> John


Re: [PATCH v3 9/9] perf mem: Document event type 'ldst'

2020-10-29 Thread Ian Rogers
On Tue, Oct 27, 2020 at 11:39 PM Leo Yan  wrote:
>
> The event type 'ldst' is added for recording both load and store memory
> operations, this patch documents for the new event type.
>
> Signed-off-by: Leo Yan 
> ---
>  tools/perf/Documentation/perf-mem.txt | 6 +-
>  1 file changed, 5 insertions(+), 1 deletion(-)
>
> diff --git a/tools/perf/Documentation/perf-mem.txt 
> b/tools/perf/Documentation/perf-mem.txt
> index 199ea0f0a6c0..c50ef37dba72 100644
> --- a/tools/perf/Documentation/perf-mem.txt
> +++ b/tools/perf/Documentation/perf-mem.txt
> @@ -38,7 +38,11 @@ OPTIONS
>
>  -t::
>  --type=::
> -   Select the memory operation type: load or store (default: load,store)
> +   Select the memory operation type: load, store, ldst (default: 
> load,store).
> +   The type 'ldst' means the single event can record both for load and 
> store
> +   operations; Intel and PowerPC support the types 'load' and 'store' but
> +   'ldst' cannot be used; on Arm64, it uses Arm SPE as memory events and
> +   user needs to specify one of these three types.

Naive question, could the type remain load,store for mem record and
the tool internally change it to ldst for ARM SPE?

Thanks,
Ian

>  -D::
>  --dump-raw-samples::

> --
> 2.17.1
>


Re: [PATCH v3 4/9] perf mem: Only initialize memory event for recording

2020-10-29 Thread Ian Rogers
On Tue, Oct 27, 2020 at 11:38 PM Leo Yan  wrote:
>
> It's needless to initialize memory events for reporting, this patch
> moves memory event initialization for only recording.  Furthermore,
> the change allows to parse perf data on cross platforms, e.g. perf
> tool can report result properly even the machine doesn't support
> the memory events.
>
> Signed-off-by: Leo Yan 

Acked-by: Ian Rogers 

Thanks,
Ian

> ---
>  tools/perf/builtin-mem.c | 10 +-
>  1 file changed, 5 insertions(+), 5 deletions(-)
>
> diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c
> index 31144f586e77..f3dc2d2b879c 100644
> --- a/tools/perf/builtin-mem.c
> +++ b/tools/perf/builtin-mem.c
> @@ -78,6 +78,11 @@ static int __cmd_record(int argc, const char **argv, 
> struct perf_mem *mem)
> OPT_END()
> };
>
> +   if (perf_mem_events__init()) {
> +   pr_err("failed: memory events not supported\n");
> +   return -1;
> +   }
> +
> argc = parse_options(argc, argv, options, record_mem_usage,
>  PARSE_OPT_KEEP_UNKNOWN);
>
> @@ -436,11 +441,6 @@ int cmd_mem(int argc, const char **argv)
> NULL
> };
>
> -   if (perf_mem_events__init()) {
> -   pr_err("failed: memory events not supported\n");
> -   return -1;
> -   }
> -
> argc = parse_options_subcommand(argc, argv, mem_options, 
> mem_subcommands,
> mem_usage, PARSE_OPT_KEEP_UNKNOWN);
>
> --
> 2.17.1
>


[PATCH v2] libbpf hashmap: Fix undefined behavior in hash_bits

2020-10-29 Thread Ian Rogers
If bits is 0, the case when the map is empty, then the >> is the size of
the register which is undefined behavior - on x86 it is the same as a
shift by 0. Fix by handling the 0 case explicitly and guarding calls to
hash_bits for empty maps in hashmap__for_each_key_entry and
hashmap__for_each_entry_safe.

Suggested-by: Andrii Nakryiko ,
Signed-off-by: Ian Rogers 
---
 tools/lib/bpf/hashmap.h | 15 +--
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/tools/lib/bpf/hashmap.h b/tools/lib/bpf/hashmap.h
index d9b385fe808c..10a4c4cd13cf 100644
--- a/tools/lib/bpf/hashmap.h
+++ b/tools/lib/bpf/hashmap.h
@@ -15,6 +15,9 @@
 static inline size_t hash_bits(size_t h, int bits)
 {
/* shuffle bits and return requested number of upper bits */
+   if (bits == 0)
+   return 0;
+
 #if (__SIZEOF_SIZE_T__ == __SIZEOF_LONG_LONG__)
/* LP64 case */
return (h * 11400714819323198485llu) >> (__SIZEOF_LONG_LONG__ * 8 - 
bits);
@@ -174,17 +177,17 @@ bool hashmap__find(const struct hashmap *map, const void 
*key, void **value);
  * @key: key to iterate entries for
  */
 #define hashmap__for_each_key_entry(map, cur, _key)\
-   for (cur = ({ size_t bkt = hash_bits(map->hash_fn((_key), map->ctx),\
-map->cap_bits);\
-map->buckets ? map->buckets[bkt] : NULL; });   \
+   for (cur = map->buckets \
+? map->buckets[hash_bits(map->hash_fn((_key), map->ctx), 
map->cap_bits)] \
+: NULL;\
 cur;   \
 cur = cur->next)   \
if (map->equal_fn(cur->key, (_key), map->ctx))
 
 #define hashmap__for_each_key_entry_safe(map, cur, tmp, _key)  \
-   for (cur = ({ size_t bkt = hash_bits(map->hash_fn((_key), map->ctx),\
-map->cap_bits);\
-cur = map->buckets ? map->buckets[bkt] : NULL; }); \
+   for (cur = map->buckets \
+? map->buckets[hash_bits(map->hash_fn((_key), map->ctx), 
map->cap_bits)] \
+: NULL;\
 cur && ({ tmp = cur->next; true; });   \
 cur = tmp) \
if (map->equal_fn(cur->key, (_key), map->ctx))
-- 
2.29.1.341.ge80a0c044ae-goog



  1   2   3   4   5   6   7   >