mirror_ubuntu-kernels/tools/perf/tests/perf-time-to-tsc.c
Leo Yan 3989bbf960 perf tests tsc: Make tsc testing as a common testing
x86 arch provides the testing for conversion between tsc and perf time,
the testing is located in x86 arch folder.  Move this testing out from
x86 arch folder and place it into the common testing folder, so allows
to execute tsc testing on other architectures (e.g. Arm64).

This patch removes the inclusion of "arch-tests.h" from the testing
code, this can avoid building failure if any arch has no this header
file.

Committer testing:

  $ perf test -v tsc
  Couldn't bump rlimit(MEMLOCK), failures may take place when creating BPF maps, etc
  70: Convert perf time to TSC                                        :
  --- start ---
  test child forked, pid 4032834
  mmap size 528384B
  1st event perf time 165409788843605 tsc 336578703793868
  rdtsc          time 165409788854986 tsc 336578703837038
  2nd event perf time 165409788855487 tsc 336578703838935
  test child finished with 0
  ---- end ----
  Convert perf time to TSC: Ok
  $

Signed-off-by: Leo Yan <leo.yan@linaro.org>
Acked-by: Jiri Olsa <jolsa@redhat.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Link: https://lore.kernel.org/r/20201019100236.23675-2-leo.yan@linaro.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-11-04 09:42:40 -03:00

172 lines
4.1 KiB
C

// 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 "debug.h"
#include "parse-events.h"
#include "evlist.h"
#include "evsel.h"
#include "thread_map.h"
#include "record.h"
#include "tsc.h"
#include "mmap.h"
#include "tests.h"
#define CHECK__(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; \
} \
}
/**
* test__perf_time_to_tsc - test converting perf time to TSC.
*
* This function implements a test that checks that the conversion of perf time
* to and from TSC is consistent with the order of events. If the test passes
* %0 is returned, otherwise %-1 is returned. If TSC conversion is not
* supported then then the test passes but " (not supported)" is printed.
*/
int test__perf_time_to_tsc(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_tsc_conversion tc;
struct perf_event_mmap_page *pc;
union perf_event *event;
u64 test_tsc, comm1_tsc, comm2_tsc;
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__(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__(evlist__open(evlist));
CHECK__(evlist__mmap(evlist, UINT_MAX));
pc = evlist->mmap[0].core.base;
ret = perf_read_tsc_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__(prctl(PR_SET_NAME, (unsigned long)comm1, 0, 0, 0));
test_tsc = rdtsc();
comm2 = "Test COMM 2";
CHECK__(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__(evsel__parse_sample(evsel, event, &sample));
comm1_time = sample.time;
}
if (strcmp(event->comm.comm, comm2) == 0) {
CHECK__(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 = tsc_to_perf_time(test_tsc, &tc);
comm1_tsc = perf_time_to_tsc(comm1_time, &tc);
comm2_tsc = perf_time_to_tsc(comm2_time, &tc);
pr_debug("1st event perf time %"PRIu64" tsc %"PRIu64"\n",
comm1_time, comm1_tsc);
pr_debug("rdtsc time %"PRIu64" tsc %"PRIu64"\n",
test_time, test_tsc);
pr_debug("2nd event perf time %"PRIu64" tsc %"PRIu64"\n",
comm2_time, comm2_tsc);
if (test_time <= comm1_time ||
test_time >= comm2_time)
goto out_err;
if (test_tsc <= comm1_tsc ||
test_tsc >= comm2_tsc)
goto out_err;
err = 0;
out_err:
evlist__delete(evlist);
return err;
}