Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 | // 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" /* * Except x86_64/i386 and Arm64, other archs don't support TSC in perf. Just * enable the test for x86_64/i386 and Arm64 archs. */ #if defined(__x86_64__) || defined(__i386__) || defined(__aarch64__) #define TSC_IS_SUPPORTED 1 #else #define TSC_IS_SUPPORTED 0 #endif #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; \ } \ } static int test__tsc_is_supported(struct test_suite *test __maybe_unused, int subtest __maybe_unused) { if (!TSC_IS_SUPPORTED) { pr_debug("Test not supported on this architecture\n"); return TEST_SKIP; } return TEST_OK; } /** * 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 the test passes but " (not supported)" is printed. */ static int test__perf_time_to_tsc(struct test_suite *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 = TEST_FAIL, 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_event(evlist, "cycles:u")); evlist__config(evlist, &opts, NULL); /* For hybrid "cycles:u", it creates two events */ evlist__for_each_entry(evlist, evsel) { evsel->core.attr.comm = 1; evsel->core.attr.disabled = 1; evsel->core.attr.enable_on_exec = 0; } ret = evlist__open(evlist); if (ret < 0) { if (ret == -ENOENT) err = TEST_SKIP; else pr_debug("evlist__open() failed\n"); goto out_err; } CHECK__(evlist__mmap(evlist, UINT_MAX)); pc = evlist->mmap[0].core.base; ret = perf_read_tsc_conversion(pc, &tc); if (ret) { if (ret == -EOPNOTSUPP) { pr_debug("perf_read_tsc_conversion is not supported in current kernel\n"); err = TEST_SKIP; } 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_NOT_NULL__(evsel = evlist__event2evsel(evlist, event)); CHECK__(evsel__parse_sample(evsel, event, &sample)); comm1_time = sample.time; } if (strcmp(event->comm.comm, comm2) == 0) { CHECK_NOT_NULL__(evsel = evlist__event2evsel(evlist, event)); 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 = TEST_OK; out_err: evlist__delete(evlist); perf_cpu_map__put(cpus); perf_thread_map__put(threads); return err; } static struct test_case time_to_tsc_tests[] = { TEST_CASE_REASON("TSC support", tsc_is_supported, "This architecture does not support"), TEST_CASE_REASON("Perf time to TSC", perf_time_to_tsc, "perf_read_tsc_conversion is not supported"), { .name = NULL, } }; struct test_suite suite__perf_time_to_tsc = { .desc = "Convert perf time to TSC", .test_cases = time_to_tsc_tests, }; |