[PATCH v2 2/3] perf header: Write hybrid CPU_PMU_CAPS

From: Jin Yao
Date: Fri May 07 2021 - 00:00:14 EST


On hybrid platform, it may have several cpu pmus, such as,
"cpu_core" and "cpu_atom". The CPU_PMU_CAPS feature in perf
header needs to be improved to support multiple cpu pmus.

The existing layout:

<caps nr>
<caps string 1>
<caps string 2>
<caps string N>

Now it's extended to:

<pmu1 caps nr>
<caps string 1>
<caps string 2>
<caps string N>
<pmu1 name>
<nr of rest pmus to process>
<pmu2 caps nr>
<caps string 1>
<caps string 2>
<caps string N>
<pmu2 name>
<nr of rest pmus to process>

The 'nr of rest pmus to process' indicates the remaining pmus
which are waiting for process. When we see it's 0 during data
processing, we can know all of the pmus have been processed.

The new layout is considered to be compatible with old perf.data
(the perf.data generated by old perf tool).

This patch doesn't change the processing part, so the result is

root@otcpl-adl-s-2:~# perf report --header-only -I
...
# cpu pmu capabilities: branches=32, max_precise=3, pmu_name=alderlake_hybrid

Signed-off-by: Jin Yao <yao.jin@xxxxxxxxxxxxxxx>
---
.../Documentation/perf.data-file-format.txt | 6 ++
tools/perf/util/header.c | 68 ++++++++++++++++---
2 files changed, 66 insertions(+), 8 deletions(-)

diff --git a/tools/perf/Documentation/perf.data-file-format.txt b/tools/perf/Documentation/perf.data-file-format.txt
index d9d82ca8aeb7..ee0af7bf655b 100644
--- a/tools/perf/Documentation/perf.data-file-format.txt
+++ b/tools/perf/Documentation/perf.data-file-format.txt
@@ -383,6 +383,8 @@ struct {
char name[];
char value[];
} [nr_cpu_pmu_caps]
+ char pmu_name[];
+ int nr_rest_pmus;
};


@@ -391,6 +393,10 @@ Example:

HEADER_CLOCK_DATA = 29,

+ On hybrid system:
+ cpu_core pmu capabilities: branches=32, max_precise=3, pmu_name=alderlake_hybrid
+ cpu_atom pmu capabilities: branches=32, max_precise=3, pmu_name=alderlake_hybrid
+
Contains clock id and its reference time together with wall clock
time taken at the 'same time', both values are in nanoseconds.
The format of data is as below.
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 72568ec3acf6..578f37655cc9 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -49,6 +49,7 @@
#include "cputopo.h"
#include "bpf-event.h"
#include "clockid.h"
+#include "pmu-hybrid.h"

#include <linux/ctype.h>
#include <internal/lib.h>
@@ -1459,18 +1460,29 @@ static int write_compressed(struct feat_fd *ff __maybe_unused,
return do_write(ff, &(ff->ph->env.comp_mmap_len), sizeof(ff->ph->env.comp_mmap_len));
}

-static int write_cpu_pmu_caps(struct feat_fd *ff,
- struct evlist *evlist __maybe_unused)
+static int write_per_cpu_pmu_caps(struct feat_fd *ff, struct perf_pmu *pmu,
+ int nr)
{
- struct perf_pmu *cpu_pmu = perf_pmu__find("cpu");
struct perf_pmu_caps *caps = NULL;
int nr_caps;
int ret;

- if (!cpu_pmu)
- return -ENOENT;
-
- nr_caps = perf_pmu__caps_parse(cpu_pmu);
+ /*
+ * The layout is:
+ * <pmu1 caps nr>
+ * <caps string 1>
+ * <caps string 2>
+ * <caps string N>
+ * <pmu1 name>
+ * <nr of rest pmus to process>
+ * <pmu2 caps nr>
+ * <caps string 1>
+ * <caps string 2>
+ * <caps string N>
+ * <pmu2 name>
+ * <nr of rest pmus to process>
+ */
+ nr_caps = perf_pmu__caps_parse(pmu);
if (nr_caps < 0)
return nr_caps;

@@ -1478,7 +1490,7 @@ static int write_cpu_pmu_caps(struct feat_fd *ff,
if (ret < 0)
return ret;

- list_for_each_entry(caps, &cpu_pmu->caps, list) {
+ list_for_each_entry(caps, &pmu->caps, list) {
ret = do_write_string(ff, caps->name);
if (ret < 0)
return ret;
@@ -1488,9 +1500,49 @@ static int write_cpu_pmu_caps(struct feat_fd *ff,
return ret;
}

+ ret = do_write_string(ff, pmu->name);
+ if (ret < 0)
+ return ret;
+
+ ret = do_write(ff, &nr, sizeof(nr));
+ if (ret < 0)
+ return ret;
+
return ret;
}

+static int write_cpu_pmu_caps(struct feat_fd *ff,
+ struct evlist *evlist __maybe_unused)
+{
+ struct perf_pmu *pmu = perf_pmu__find("cpu");
+ u32 nr;
+ int ret;
+
+ if (pmu)
+ nr = 1;
+ else {
+ nr = perf_pmu__hybrid_pmu_num();
+ pmu = NULL;
+ }
+
+ if (nr == 0)
+ return -1;
+
+ if (pmu) {
+ ret = write_per_cpu_pmu_caps(ff, pmu, 0);
+ if (ret < 0)
+ return ret;
+ } else {
+ perf_pmu__for_each_hybrid_pmu(pmu) {
+ ret = write_per_cpu_pmu_caps(ff, pmu, --nr);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
static void print_hostname(struct feat_fd *ff, FILE *fp)
{
fprintf(fp, "# hostname : %s\n", ff->ph->env.hostname);
--
2.17.1