[PATCH v2 3/4] perf arm_arch_timer: Test conversion between counter and timestamp

From: Leo Yan
Date: Fri Aug 07 2020 - 03:16:55 EST


x86 arch has provides the testing for conversion between tsc and perf
time, this patch studies it and writes a similar testing for conversion
between arch timer's counter and sample's time.

The testing approach firstly creates three time points:

Time point 1: 1st process's sample with perf time 'comm1_time';
Time point 2: read out arch timer counter 'test_arch_timer_cnt';
Time point 3: 2nd process's sample with perf time 'comm2_time';

The testing converts perf time to and from arch timer counter for these
three values and the testing can pass only if the counter values and
time values are in the order.

The test steps are as below:

# perf test list
[...]
67: Convert perf time to arch timer counter

# perf test 67 -v
67: Convert perf time to arch timer counter
--- start ---
test child forked, pid 5463
mmap size 528384B
1st event perf time 2231755083020 arch timer cnt 113097053477
test time 2231755087460 arch timer cnt 113097053699
2nd event perf time 2231755090680 arch timer cnt 113097053860
test child finished with 0
---- end ----

Signed-off-by: Leo Yan <leo.yan@xxxxxxxxxx>
---
tools/perf/arch/arm64/include/arch-tests.h | 6 +
tools/perf/arch/arm64/tests/Build | 1 +
tools/perf/arch/arm64/tests/arch-tests.c | 4 +
.../tests/perf-time-to-arch-timer-counter.c | 189 ++++++++++++++++++
4 files changed, 200 insertions(+)
create mode 100644 tools/perf/arch/arm64/tests/perf-time-to-arch-timer-counter.c

diff --git a/tools/perf/arch/arm64/include/arch-tests.h b/tools/perf/arch/arm64/include/arch-tests.h
index 90ec4c8cb880..12ad7592c9aa 100644
--- a/tools/perf/arch/arm64/include/arch-tests.h
+++ b/tools/perf/arch/arm64/include/arch-tests.h
@@ -2,6 +2,12 @@
#ifndef ARCH_TESTS_H
#define ARCH_TESTS_H

+#include <linux/compiler.h>
+
+/* Tests */
+int test__perf_time_to_arch_timer_cnt(struct test *test __maybe_unused,
+ int subtest __maybe_unused);
+
#ifdef HAVE_DWARF_UNWIND_SUPPORT
struct thread;
struct perf_sample;
diff --git a/tools/perf/arch/arm64/tests/Build b/tools/perf/arch/arm64/tests/Build
index a61c06bdb757..1fd819e4f80f 100644
--- a/tools/perf/arch/arm64/tests/Build
+++ b/tools/perf/arch/arm64/tests/Build
@@ -2,3 +2,4 @@ perf-y += regs_load.o
perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o

perf-y += arch-tests.o
+perf-y += perf-time-to-arch-timer-counter.o
diff --git a/tools/perf/arch/arm64/tests/arch-tests.c b/tools/perf/arch/arm64/tests/arch-tests.c
index 5b1543c98022..99d8ab865668 100644
--- a/tools/perf/arch/arm64/tests/arch-tests.c
+++ b/tools/perf/arch/arm64/tests/arch-tests.c
@@ -4,6 +4,10 @@
#include "arch-tests.h"

struct test arch_tests[] = {
+ {
+ .desc = "Convert perf time to arch timer counter",
+ .func = test__perf_time_to_arch_timer_cnt,
+ },
#ifdef HAVE_DWARF_UNWIND_SUPPORT
{
.desc = "DWARF unwind",
diff --git a/tools/perf/arch/arm64/tests/perf-time-to-arch-timer-counter.c b/tools/perf/arch/arm64/tests/perf-time-to-arch-timer-counter.c
new file mode 100644
index 000000000000..09b9f43b48a4
--- /dev/null
+++ b/tools/perf/arch/arm64/tests/perf-time-to-arch-timer-counter.c
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <linux/types.h>
+#include <sys/prctl.h>
+#include <perf/cpumap.h>
+#include <perf/evlist.h>
+#include <perf/mmap.h>
+
+#include "arm_arch_timer.h"
+#include "debug.h"
+#include "parse-events.h"
+#include "evlist.h"
+#include "evsel.h"
+#include "thread_map.h"
+#include "record.h"
+#include "util/mmap.h"
+#include "tests/tests.h"
+
+#include "arch-tests.h"
+
+#define CHECK_NOT_LESS_ZERO(x) { \
+ while ((x) < 0) { \
+ pr_debug(#x " failed!\n"); \
+ goto out_err; \
+ } \
+}
+
+#define CHECK_NOT_NULL(x) { \
+ while ((x) == NULL) { \
+ pr_debug(#x " failed!\n"); \
+ goto out_err; \
+ } \
+}
+
+/*
+ * The arch timer's offset is set to zero, the virtual counter and physical
+ * counter should have the same value; so the user space program can read out
+ * counter with system register cntvct_el0 and it can be used to compare with
+ * kernel's counter.
+ */
+#define read_cntvct_el0(r) ({ \
+ u64 __val; \
+ asm volatile("mrs %0, cntvct_el0" : "=r" (__val)); \
+ __val; \
+})
+
+/**
+ * test__perf_time_to_arch_timer_cnt - test converting perf time to Arm arch
+ * timer counter.
+ *
+ * This function implements a test that checks that the conversion of perf time
+ * to and from Arm arch timer counter is consistent with the order of events.
+ * Returns 0 if the test passes, otherwise returns -1. If TSC conversion is not
+ * supported then then the test passes but " (not supported)" is printed.
+ */
+int test__perf_time_to_arch_timer_cnt(struct test *test __maybe_unused,
+ int subtest __maybe_unused)
+{
+ struct record_opts opts = {
+ .mmap_pages = UINT_MAX,
+ .user_freq = UINT_MAX,
+ .user_interval = ULLONG_MAX,
+ .target = {
+ .uses_mmap = true,
+ },
+ .sample_time = true,
+ };
+ struct perf_thread_map *threads = NULL;
+ struct perf_cpu_map *cpus = NULL;
+ struct evlist *evlist = NULL;
+ struct evsel *evsel = NULL;
+ int err = -1, ret, i;
+ const char *comm1, *comm2;
+ struct perf_arch_timer_conversion tc;
+ struct perf_event_mmap_page *pc;
+ union perf_event *event;
+ u64 test_arch_timer_cnt;
+ u64 comm1_arch_timer_cnt;
+ u64 comm2_arch_timer_cnt;
+ u64 test_time, comm1_time = 0, comm2_time = 0;
+ struct mmap *md;
+
+ threads = thread_map__new(-1, getpid(), UINT_MAX);
+ CHECK_NOT_NULL(threads);
+
+ cpus = perf_cpu_map__new(NULL);
+ CHECK_NOT_NULL(cpus);
+
+ evlist = evlist__new();
+ CHECK_NOT_NULL(evlist);
+
+ perf_evlist__set_maps(&evlist->core, cpus, threads);
+
+ CHECK_NOT_LESS_ZERO(parse_events(evlist, "cycles:u", NULL));
+
+ perf_evlist__config(evlist, &opts, NULL);
+
+ evsel = evlist__first(evlist);
+
+ evsel->core.attr.comm = 1;
+ evsel->core.attr.disabled = 1;
+ evsel->core.attr.enable_on_exec = 0;
+
+ CHECK_NOT_LESS_ZERO(evlist__open(evlist));
+
+ CHECK_NOT_LESS_ZERO(evlist__mmap(evlist, UINT_MAX));
+
+ pc = evlist->mmap[0].core.base;
+ ret = perf_read_arch_timer_conversion(pc, &tc);
+ if (ret) {
+ if (ret == -EOPNOTSUPP) {
+ fprintf(stderr, " (not supported)");
+ return 0;
+ }
+ goto out_err;
+ }
+
+ evlist__enable(evlist);
+
+ comm1 = "Test COMM 1";
+ CHECK_NOT_LESS_ZERO(prctl(PR_SET_NAME, (unsigned long)comm1, 0, 0, 0));
+
+ test_arch_timer_cnt = read_cntvct_el0();
+
+ comm2 = "Test COMM 2";
+ CHECK_NOT_LESS_ZERO(prctl(PR_SET_NAME, (unsigned long)comm2, 0, 0, 0));
+
+ evlist__disable(evlist);
+
+ for (i = 0; i < evlist->core.nr_mmaps; i++) {
+ md = &evlist->mmap[i];
+ if (perf_mmap__read_init(&md->core) < 0)
+ continue;
+
+ while ((event = perf_mmap__read_event(&md->core)) != NULL) {
+ struct perf_sample sample;
+
+ if (event->header.type != PERF_RECORD_COMM ||
+ (pid_t)event->comm.pid != getpid() ||
+ (pid_t)event->comm.tid != getpid())
+ goto next_event;
+
+ if (strcmp(event->comm.comm, comm1) == 0) {
+ CHECK_NOT_LESS_ZERO(evsel__parse_sample(evsel, event, &sample));
+ comm1_time = sample.time;
+ }
+ if (strcmp(event->comm.comm, comm2) == 0) {
+ CHECK_NOT_LESS_ZERO(evsel__parse_sample(evsel, event, &sample));
+ comm2_time = sample.time;
+ }
+next_event:
+ perf_mmap__consume(&md->core);
+ }
+ perf_mmap__read_done(&md->core);
+ }
+
+ if (!comm1_time || !comm2_time)
+ goto out_err;
+
+ test_time = arch_timer_cyc_to_perf_time(test_arch_timer_cnt, &tc);
+ comm1_arch_timer_cnt = perf_time_to_arch_timer_cyc(comm1_time, &tc);
+ comm2_arch_timer_cnt = perf_time_to_arch_timer_cyc(comm2_time, &tc);
+
+ pr_debug("1st event perf time %"PRIu64" arch timer cnt %"PRIu64"\n",
+ comm1_time, comm1_arch_timer_cnt);
+ pr_debug("test time %"PRIu64" arch timer cnt %"PRIu64"\n",
+ test_time, test_arch_timer_cnt);
+ pr_debug("2nd event perf time %"PRIu64" arch timer cnt %"PRIu64"\n",
+ comm2_time, comm2_arch_timer_cnt);
+
+ if (test_time <= comm1_time ||
+ test_time >= comm2_time)
+ goto out_err;
+
+ if (test_arch_timer_cnt <= comm1_arch_timer_cnt ||
+ test_arch_timer_cnt >= comm2_arch_timer_cnt)
+ goto out_err;
+
+ err = 0;
+
+out_err:
+ evlist__delete(evlist);
+ return err;
+}
--
2.17.1