[compiler-rt] [XRay][test] Pin CPU affinity in fdr-mode.cpp to deflake UNWRITE checks (PR #211595)
Nikita Taranov via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 23 08:52:00 PDT 2026
https://github.com/nickitat created https://github.com/llvm/llvm-project/pull/211595
The UNWRITE portion of fdr-mode.cpp asserts that, with a high func_duration_threshold_us, all short function records are "unwritten" (erased) except arg1-logging records. FDR mode only rewinds a function's enter record when its exit lands within the threshold AND no metadata record was written in between: XRayFDRController::functionExit rewinds only when recordPreamble() returns NoChange. A CPU migration between the enter and exit makes recordPreamble() emit a NewCPUId metadata record and return WroteMetadata, which blocks the rewind and leaves stray function-enter/exit records, failing the UNWRITE-NEXT/UNWRITE-NOT checks.
On busy multi-core machines this migration is likely enough to flake the test ([observed](https://github.com/llvm/llvm-project/pull/210739#issuecomment-5053841943) on the sanitizer-aarch64-linux buildbot). The test already documented the hazard ("A CPU switch could erroneously fail this test").
Pin the process to a single CPU at the start of main() so no migration can occur; child threads inherit the affinity mask. Guarded to Linux; a no-op on other platforms.
>From 96e6b8804b3c132c0302701977a459b950ed3133 Mon Sep 17 00:00:00 2001
From: Nikita Taranov <nikita.taranov at clickhouse.com>
Date: Thu, 23 Jul 2026 22:05:39 +0700
Subject: [PATCH] [XRay][test] Pin CPU affinity in fdr-mode.cpp to deflake
UNWRITE checks
The UNWRITE portion of fdr-mode.cpp asserts that, with a high
func_duration_threshold_us, all short function records are "unwritten"
(erased) except arg1-logging records. FDR mode only rewinds a function's
enter record when its exit lands within the threshold AND no metadata
record was written in between: XRayFDRController::functionExit rewinds
only when recordPreamble() returns NoChange. A CPU migration between the
enter and exit makes recordPreamble() emit a NewCPUId metadata record and
return WroteMetadata, which blocks the rewind and leaves stray
function-enter/exit records, failing the UNWRITE-NEXT/UNWRITE-NOT checks.
On busy multi-core machines this migration is likely enough to flake the
test (observed on the sanitizer-aarch64-linux buildbot). The test already
documented the hazard ("A CPU switch could erroneously fail this test").
Pin the process to a single CPU at the start of main() so no migration can
occur; child threads inherit the affinity mask. The CPU is chosen from the
process's allowed affinity set rather than assuming CPU 0 is usable, and
the pinning degrades gracefully (no-op) if the affinity calls fail. Guarded
to Linux; a no-op on other platforms.
---
.../test/xray/TestCases/Posix/fdr-mode.cpp | 39 ++++++++++++++++++-
1 file changed, 37 insertions(+), 2 deletions(-)
diff --git a/compiler-rt/test/xray/TestCases/Posix/fdr-mode.cpp b/compiler-rt/test/xray/TestCases/Posix/fdr-mode.cpp
index 08152d3179b17..071e5b0319521 100644
--- a/compiler-rt/test/xray/TestCases/Posix/fdr-mode.cpp
+++ b/compiler-rt/test/xray/TestCases/Posix/fdr-mode.cpp
@@ -28,6 +28,15 @@
// UNSUPPORTED: armhf-linux
// REQUIRES: built-in-llvm-tree
+#if defined(__linux__)
+// Pull in sched_setaffinity/cpu_set_t (GNU extensions). _GNU_SOURCE must be
+// defined before any libc header is included.
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <sched.h>
+#endif
+
#include "xray/xray_log_interface.h"
#include <cassert>
#include <chrono>
@@ -48,6 +57,29 @@ thread_local uint64_t var = 0;
void __attribute__((noinline)) fArg(int) { }
int main(int argc, char *argv[]) {
+#if defined(__linux__)
+ // Pin to a single CPU. The UNWRITE portion of this test relies on FDR mode
+ // "unwriting" (rewinding) short function records, which only happens when a
+ // function's exit record immediately follows its enter record in the
+ // per-thread buffer. A CPU migration in between forces a NewCPUId metadata
+ // record, which blocks the rewind and leaves stray function-enter/exit
+ // records, flaking the UNWRITE checks below. Pinning prevents the migration;
+ // child threads inherit this affinity mask. Pick the first CPU from the
+ // process's allowed set rather than assuming CPU 0 is usable.
+ cpu_set_t Allowed;
+ CPU_ZERO(&Allowed);
+ if (sched_getaffinity(0, sizeof(Allowed), &Allowed) == 0) {
+ for (int Cpu = 0; Cpu < CPU_SETSIZE; ++Cpu) {
+ if (CPU_ISSET(Cpu, &Allowed)) {
+ cpu_set_t Pinned;
+ CPU_ZERO(&Pinned);
+ CPU_SET(Cpu, &Pinned);
+ (void)sched_setaffinity(0, sizeof(Pinned), &Pinned);
+ break;
+ }
+ }
+ }
+#endif
std::cout << "Logging before init." << std::endl;
// CHECK: Logging before init.
assert(__xray_log_select_mode("xray-fdr") ==
@@ -106,8 +138,11 @@ int main(int argc, char *argv[]) {
// TRACE-DAG: - { type: 0, func-id: [[FIDARG:[0-9]+]], function: 'fArg(int)', args: [ 1 ], cpu: {{.*}}, thread: [[THREAD2]], process: [[PROCESS]], kind: function-enter-arg, tsc: {{[0-9]+}}, data: '' }
// TRACE-DAG: - { type: 0, func-id: [[FIDARG]], function: 'fArg(int)', cpu: {{.*}}, thread: [[THREAD2]], process: [[PROCESS]], kind: function-exit, tsc: {{[0-9]+}}, data: '' }
-// Assert that when unwriting is enabled with a high threshold time, all the function records are erased. A CPU switch could erroneously fail this test, but
-// is unlikely given the test program.
+// Assert that when unwriting is enabled with a high threshold time, all the
+// function records are erased. A CPU switch between a function's enter and exit
+// would block the unwrite and fail this test; on Linux main() pins the process
+// to a single CPU to prevent that (elsewhere it remains theoretically possible
+// but unlikely given the test program).
// Even with a high threshold, arg1 logging is never unwritten.
// UNWRITE: header:
// UNWRITE: records:
More information about the llvm-commits
mailing list