2 * Must come before including darwintest.h
6 #endif /* defined(T_NAMESPACE) */
8 #include <darwintest.h>
13 * Need new CPU families.
16 #include <mach/machine.h>
18 #else /* !defined(PRIVATE) */
19 #include <mach/machine.h>
20 #endif /* defined(PRIVATE) */
22 #include <System/sys/guarded.h>
23 #include <System/sys/monotonic.h>
24 #include <sys/ioctl.h>
25 #include <sys/sysctl.h>
29 T_META_NAMESPACE("xnu.monotonic"),
30 T_META_CHECK_LEAKS(false),
35 device_supports_uncore(void)
40 size_t size
= sizeof(type
);
43 * Only arm64 Monsoon devices support uncore counters.
46 r
= sysctlbyname("hw.cputype", &type
, &size
, NULL
, 0);
47 T_QUIET
; T_ASSERT_POSIX_SUCCESS(r
, "sysctlbyname(\"hw.cputype\")");
48 r
= sysctlbyname("hw.cpusubtype", &subtype
, &size
, NULL
, 0);
49 T_QUIET
; T_ASSERT_POSIX_SUCCESS(r
, "sysctlbyname(\"hw.cpusubtype\")");
50 r
= sysctlbyname("hw.cpufamily", &family
, &size
, NULL
, 0);
51 T_QUIET
; T_ASSERT_POSIX_SUCCESS(r
, "sysctlbyname(\"hw.cpufamily\")");
53 if (type
== CPU_TYPE_ARM64
&&
54 subtype
== CPU_SUBTYPE_ARM64_V8
&&
55 (family
== CPUFAMILY_ARM_MONSOON_MISTRAL
||
56 family
== CPUFAMILY_ARM_VORTEX_TEMPEST
)) {
63 #define UNCORE_DEV_PATH "/dev/monotonic/uncore"
66 open_uncore_error(int *error
)
75 fd
= guarded_open_np(UNCORE_DEV_PATH
, &guard
,
76 GUARD_CLOSE
| GUARD_DUP
| GUARD_WRITE
, O_CLOEXEC
| O_EXCL
);
77 if (fd
< 0 && errno
== ENOENT
) {
78 T_ASSERT_FALSE(device_supports_uncore(),
79 "lack of dev node implies no uncore support");
80 T_SKIP("uncore counters are unsupported");
81 __builtin_unreachable();
85 T_ASSERT_POSIX_SUCCESS(fd
, "open '%s'", UNCORE_DEV_PATH
);
96 uncore_counts(int fd
, uint64_t ctr_mask
, uint64_t *counts
)
99 union monotonic_ctl_counts
*cts_ctl
;
101 cts_ctl
= (union monotonic_ctl_counts
*)counts
;
102 cts_ctl
->in
.ctr_mask
= ctr_mask
;
104 r
= ioctl(fd
, MT_IOC_COUNTS
, cts_ctl
);
105 T_QUIET
; T_ASSERT_POSIX_SUCCESS(r
, "MT_IOC_COUNTS got counter values");
108 #define REF_TIMEBASE_EVENT 0x3
111 T_DECL(uncore_max_counters
,
112 "ensure that the maximum number of uncore countes is sane",
118 fd
= open_uncore_error(NULL
);
121 union monotonic_ctl_add add_ctl
;
124 add_ctl
.in
.config
.event
= REF_TIMEBASE_EVENT
;
125 add_ctl
.in
.config
.allowed_ctr_mask
= UINT64_MAX
;
127 r
= ioctl(fd
, MT_IOC_ADD
, &add_ctl
);
128 if (r
< 0 && errno
== E2BIG
) {
133 T_ASSERT_POSIX_SUCCESS(r
, "added reference timebase event to counters");
135 } while (nctrs
< CTRS_MAX
);
137 T_EXPECT_LT(nctrs
, CTRS_MAX
,
138 "only able to allocate a reasonable number of counters");
142 uncore_add(int fd
, uint64_t event
, uint64_t allowed_ctrs
, int error
)
147 union monotonic_ctl_add add_ctl
;
149 add_ctl
.in
.config
.event
= event
;
150 add_ctl
.in
.config
.allowed_ctr_mask
= allowed_ctrs
;
151 r
= ioctl(fd
, MT_IOC_ADD
, &add_ctl
);
154 T_EXPECT_LT(r
, 0, "adding event to counter should fail");
155 T_EXPECT_EQ(save_errno
, error
,
156 "adding event to counter should fail with %d: %s",
157 error
, strerror(error
));
161 T_ASSERT_POSIX_SUCCESS(r
,
162 "added event %#" PRIx64
" to counters", event
);
165 ctr
= add_ctl
.out
.ctr
;
166 T_QUIET
; T_ASSERT_LT(ctr
, (uint32_t)CTRS_MAX
, "counter returned should be sane");
170 T_DECL(uncore_collision
,
171 "ensure that trying to add an event on the same counter fails",
177 fd
= open_uncore_error(NULL
);
179 ctr
= uncore_add(fd
, REF_TIMEBASE_EVENT
, UINT64_MAX
, 0);
180 T_LOG("added event to uncore counter %d\n", ctr
);
182 (void)uncore_add(fd
, REF_TIMEBASE_EVENT
, UINT64_C(1) << ctr
, ENOSPC
);
186 uncore_enable(int fd
)
188 union monotonic_ctl_enable en_ctl
= {
189 .in
= { .enable
= true }
192 T_ASSERT_POSIX_SUCCESS(ioctl(fd
, MT_IOC_ENABLE
, &en_ctl
),
193 "enabling counters");
196 T_DECL(uncore_enabled_busy
,
197 "ensure that trying to add an event while enabled fails",
202 fd
= open_uncore_error(NULL
);
204 (void)uncore_add(fd
, REF_TIMEBASE_EVENT
, UINT64_MAX
, 0);
207 (void)uncore_add(fd
, REF_TIMEBASE_EVENT
, UINT64_MAX
, EBUSY
);
211 "ensure that resetting the counters works")
216 fd
= open_uncore_error(NULL
);
218 (void)uncore_add(fd
, REF_TIMEBASE_EVENT
, UINT64_C(1), 0);
219 (void)uncore_add(fd
, REF_TIMEBASE_EVENT
, UINT64_C(1), ENOSPC
);
221 r
= ioctl(fd
, MT_IOC_RESET
);
222 T_ASSERT_POSIX_SUCCESS(r
, "resetting succeeds");
224 T_LOG("adding event to same counter after reset");
225 (void)uncore_add(fd
, REF_TIMEBASE_EVENT
, UINT64_C(1), 0);
228 #define SLEEP_USECS (500 * 1000)
231 uncore_add_all(int fd
, uint64_t event
, int *nmonitors
)
237 union monotonic_ctl_add add_ctl
;
239 add_ctl
.in
.config
.event
= event
;
240 add_ctl
.in
.config
.allowed_ctr_mask
= UINT64_MAX
;
242 r
= ioctl(fd
, MT_IOC_ADD
, &add_ctl
);
243 if (r
< 0 && errno
== E2BIG
) {
248 T_ASSERT_POSIX_SUCCESS(r
, "added event %#" PRIx64
" to counters",
251 } while (nctrs
< CTRS_MAX
);
254 union monotonic_ctl_info info_ctl
;
255 r
= ioctl(fd
, MT_IOC_GET_INFO
, &info_ctl
);
256 T_QUIET
; T_ASSERT_POSIX_SUCCESS(r
, "got info about uncore counters");
258 *nmonitors
= (int)info_ctl
.out
.nmonitors
;
264 T_DECL(uncore_accuracy
,
265 "ensure that the uncore counters count accurately",
272 uint64_t counts
[2][CTRS_MAX
];
275 fd
= open_uncore_error(NULL
);
278 * The reference timebase event counts the same as mach_continuous_time
279 * (on hardware supporting uncore counters). Make sure that the counter
280 * is close to the values returned from the trap.
282 * Fill all the counters with this event.
284 nctrs
= uncore_add_all(fd
, REF_TIMEBASE_EVENT
, &nmonitors
);
285 ctr_mask
= (UINT64_C(1) << nctrs
) - 1;
287 T_LOG("added %d counters to check", nctrs
);
292 * First, make sure there's an upper bound on the counter -- take the
293 * time around getting the counter values.
296 times
[0] = mach_absolute_time();
297 uncore_counts(fd
, ctr_mask
, counts
[0]);
301 uncore_counts(fd
, ctr_mask
, counts
[1]);
302 times
[1] = mach_absolute_time();
304 T_QUIET
; T_EXPECT_GT(times
[1], times
[0],
305 "mach_continuous_time is monotonically increasing");
306 for (int i
= 0; i
< nctrs
; i
++) {
307 T_EXPECT_GT(counts
[1][i
], counts
[0][i
],
308 "uncore counter %d value is monotonically increasing", i
);
309 T_EXPECT_LT(counts
[1][i
] - counts
[0][i
], times
[1] - times
[0],
310 "reference timebase on uncore counter %d satisfies upper bound "
311 "from mach_absolute_time", i
);
315 * Next, the lower bound -- put mach_absolute_time inside getting the
319 uncore_counts(fd
, ctr_mask
, counts
[0]);
320 times
[0] = mach_absolute_time();
322 volatile int iterations
= 100000;
323 while (iterations
--) {
327 times
[1] = mach_absolute_time();
328 uncore_counts(fd
, ctr_mask
, counts
[1]);
330 for (int mon
= 0; mon
< nmonitors
; mon
++) {
331 for (int i
= 0; i
< nctrs
; i
++) {
333 T_EXPECT_GT(counts
[1][i
* mon
], counts
[0][i
* mon
],
334 "uncore %d counter %d value is monotonically increasing",
336 T_EXPECT_GT(counts
[1][i
* mon
] - counts
[0][i
* mon
],
338 "reference timebase on uncore %d counter %d satisfies "
339 "lower bound from mach_absolute_time", mon
, i
);
344 T_DECL(uncore_ownership
,
345 "ensure the dev node cannot be open in two places",
352 fd
= open_uncore_error(NULL
);
354 other_fd
= open_uncore_error(&error
);
355 T_ASSERT_LT(other_fd
, 0, "opening a second uncore fd should fail");
356 T_ASSERT_EQ(error
, EBUSY
, "failure should be EBUSY");
359 T_DECL(uncore_root_required
,
360 "ensure the dev node cannot be opened by non-root users",
361 T_META_ASROOT(false))
366 T_SKIP("libdarwintest doesn't drop privileges properly");
368 fd
= open_uncore_error(&error
);
369 T_ASSERT_LT(fd
, 0, "opening dev node should not return an fd");
370 T_ASSERT_EQ(error
, EPERM
,
371 "opening dev node as non-root user should fail with EPERM");
375 "measure the latency of accessing the counters",
383 dt_stat_thread_instructions_t counts_instrs
;
384 dt_stat_t counter_deltas
;
386 counts_instrs
= dt_stat_thread_instructions_create("ioctl_counts");
387 counter_deltas
= dt_stat_create("abs_time", "between_each_counter");
389 fd
= open_uncore_error(NULL
);
391 nctrs
= uncore_add_all(fd
, REF_TIMEBASE_EVENT
, &nmonitors
);
392 ctr_mask
= (UINT64_C(1) << nctrs
) - 1;
398 uint64_t counts
[nctrs
* nmonitors
];
399 union monotonic_ctl_counts
*cts_ctl
;
401 cts_ctl
= (union monotonic_ctl_counts
*)counts
;
402 cts_ctl
->in
.ctr_mask
= ctr_mask
;
404 token
= dt_stat_thread_instructions_begin(counts_instrs
);
405 r
= ioctl(fd
, MT_IOC_COUNTS
, cts_ctl
);
406 dt_stat_thread_instructions_end(counts_instrs
, token
);
408 T_ASSERT_POSIX_SUCCESS(r
,
409 "getting uncore counter values %#" PRIx64
, ctr_mask
);
411 for (int i
= 0; i
< (nctrs
- 1); i
++) {
412 dt_stat_add(counter_deltas
, (double)(counts
[i
+ 1] - counts
[i
]));
414 } while (!dt_stat_stable(counts_instrs
) || !dt_stat_stable(counter_deltas
));
416 dt_stat_finalize(counts_instrs
);
417 dt_stat_finalize(counter_deltas
);