Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@
/callgrind/tests/runtime_obj_skip_c
/callgrind/tests/runtime_obj_skip_underflow
/callgrind/tests/prctl_get_auxv
/callgrind/tests/deterministic_rng
/callgrind/tests/*.so
/callgrind/tests/out_dbg
/callgrind/tests/test-suite-overall.log
Expand Down Expand Up @@ -1744,6 +1745,7 @@
/none/tests/amd64/clc
/none/tests/amd64/cmpxchg
/none/tests/amd64/crc32
/none/tests/amd64/deterministic_rng
/none/tests/amd64/f16c
/none/tests/amd64/faultstatus
/none/tests/amd64/fb_test_amd64
Expand Down
27 changes: 27 additions & 0 deletions CODSPEED-CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,33 @@ results are unchanged.
Reduce simulated cache-miss variation from argument and environment sizes
by keeping the Linux initial stack pointer at a fixed offset from the stack top.

### Deterministic randomness: `--deterministic-rng`

**Feature**: Added `--deterministic-rng=no|yes` (default `no`). When enabled, randomness the client reads from the kernel or the CPU comes from a fixed-seed PRNG, so it is identical across runs.

**Motivation**: Programs seed hash tables and similar structures from startup randomness. A different seed changes bucket collisions and probe lengths in HashMap-heavy benchmarks, which shows up as instruction-count noise under Callgrind.

**How it works**:
- `AT_RANDOM` auxv bytes (glibc stack canary and pointer guard, Go runtime hash seed) come from a dedicated stream.
- `getrandom(2)` (Rust `RandomState`, CPython hash seed, OpenSSL, glibc `arc4random`) is served from the PRNG.
- `read`, `pread` and `readv` on `/dev/random` and `/dev/urandom` are served from the PRNG. The device is identified by resolving the fd through `/proc/self/fd`, so symlinks to the devices are covered.
- Reads of `/proc/sys/kernel/random/uuid` return lowercase UUIDv4 text from the thread's stream; `/proc/sys/kernel/random/boot_id` uses a fixed seed-derived UUID independent of thread or read order. This covers `read`, `pread`, `readv`, `preadv` and `preadv2`. The kernel preserves offsets, short reads, EOF and errors; only returned bytes are replaced. UUIDs are not cached across short reads. Errors and empty reads consume no PRNG state.
- On amd64, `RDRAND` and `RDSEED` draw from the calling thread's PRNG stream and always report success. CPUID still reports them only when the host CPU supports them.
- Each client thread has its own splitmix64 stream, keyed by a fixed built-in seed and the thread's creation index. The N-th created thread always draws the same bytes regardless of scheduling. Output depends only on the order of draws within each thread.
- A forked child continues its parent's streams, so parent and child draw identical bytes. Under `--trace-children=yes` each exec'd program restarts from the seed.

**Usage**:
```bash
valgrind --tool=callgrind --deterministic-rng=yes ./your_program
```

**Limitations**:
- Time (`clock_gettime`, `rdtsc`), pids and tids are not made deterministic.
- Only `/proc/sys/kernel/random/uuid` and `/proc/sys/kernel/random/boot_id` are covered among the files under `/proc/sys/kernel/random/`.
- Fds to `/dev/urandom` or the two procfs files inherited across `exec` are not covered.
- The seed is fixed; there is no option to change it.
- Each thread draws from its own stream, so the bytes a call returns depend on which thread makes it. If a runtime moves work between threads based on timing (thread pools, work stealing, Go goroutines), the results can still differ between runs. `boot_id` is the exception: it is derived from the seed alone, not from a thread's stream.

### Skip `.plt.sec` in `--skip-plt`

**Feature**: Added support for skipping `.plt.sec` sections when using the `--skip-plt` option.
Expand Down
4 changes: 4 additions & 0 deletions VEX/priv/guest_amd64_helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -3979,6 +3979,8 @@ void amd64g_dirtyhelper_SxDT ( void *address, ULong op ) {
32 bit random number using RDRAND, and return it and the associated rflags.C
value. */
ULong amd64g_dirtyhelper_RDRAND ( void ) {
if (vex_random32)
return (1ULL << 32) | (ULong)vex_random32();
Comment thread
not-matthias marked this conversation as resolved.
# if defined(__x86_64__)
ULong res = 0;
ULong cflag = 0;
Expand All @@ -4002,6 +4004,8 @@ ULong amd64g_dirtyhelper_RDRAND ( void ) {
}

ULong amd64g_dirtyhelper_RDSEED ( void ) {
if (vex_random32)
return (1ULL << 32) | (ULong)vex_random32();
# if defined(__x86_64__)
ULong res = 0;
ULong cflag = 0;
Expand Down
8 changes: 6 additions & 2 deletions VEX/priv/guest_amd64_toIR.c
Original file line number Diff line number Diff line change
Expand Up @@ -22079,8 +22079,12 @@ Long dis_ESC_0F (
if (fAddr == &amd64g_dirtyhelper_CPUID_avx2
|| fAddr == &amd64g_dirtyhelper_CPUID_avx_and_cx16) {
Bool hasF16C = (archinfo->hwcaps & VEX_HWCAPS_AMD64_F16C) != 0;
Bool hasRDRAND = (archinfo->hwcaps & VEX_HWCAPS_AMD64_RDRAND) != 0;
Bool hasRDSEED = (archinfo->hwcaps & VEX_HWCAPS_AMD64_RDSEED) != 0;
/* A deterministic RNG still serves RDRAND/RDSEED but hides them. */
Bool advertiseRng = vex_random32 == NULL;
Bool hasRDRAND = advertiseRng
&& (archinfo->hwcaps & VEX_HWCAPS_AMD64_RDRAND) != 0;
Bool hasRDSEED = advertiseRng
&& (archinfo->hwcaps & VEX_HWCAPS_AMD64_RDSEED) != 0;
args = mkIRExprVec_4(IRExpr_GSPTR(),
mkIRExpr_HWord(hasF16C ? 1 : 0),
mkIRExpr_HWord(hasRDRAND ? 1 : 0),
Expand Down
8 changes: 8 additions & 0 deletions VEX/priv/main_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,14 @@ void LibVEX_set_VexControl ( VexControl vcon )
__builtin_memcpy(&vex_control, &vcon, sizeof vex_control);
}

UInt (*vex_random32)(void) = NULL;

void LibVEX_SetRandom32 ( UInt (*random32)(void) )
{
vassert(vex_initdone);
vex_random32 = random32;
}


/* Exported to library client. */

Expand Down
2 changes: 2 additions & 0 deletions VEX/priv/main_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@

#define NULL ((void*)0)

extern UInt (*vex_random32)(void);

#if defined(_MSC_VER) // building with MSVC
# define LIKELY(x) (x)
# define UNLIKELY(x) (x)
Expand Down
5 changes: 5 additions & 0 deletions VEX/pub/libvex.h
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,11 @@ extern void LibVEX_Init (
const VexControl* vcon
);

/* Install before guest execution. NULL preserves host RDRAND/RDSEED.
Otherwise CPUID hides them, and they return bits from this callback,
which runs on the executing guest thread. */
extern void LibVEX_SetRandom32 ( UInt (*random32)(void) );


/*-------------------------------------------------------*/
/*--- Make a translation ---*/
Expand Down
5 changes: 4 additions & 1 deletion callgrind/tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ EXTRA_DIST = \
allow_file_caps_refused.stdout.exp \
prctl_get_auxv.vgtest prctl_get_auxv.stderr.exp \
prctl_get_auxv.stdout.exp \
deterministic_rng.vgtest deterministic_rng.stderr.exp \
deterministic_rng.stdout.exp \
find_debuginfo.vgtest find_debuginfo.stderr.exp find_debuginfo.post.exp \
runtime_obj_skip_py.vgtest runtime_obj_skip_py.stderr.exp runtime_obj_skip_py.post.exp \
runtime_obj_skip_py.py runtime_obj_skip_py_shim.c \
Expand All @@ -44,7 +46,7 @@ EXTRA_DIST = \
inline-crossfile.vgtest inline-crossfile.stderr.exp inline-crossfile.stdout.exp inline-crossfile.post.exp \
inline-crossfile-helper1.h inline-crossfile-helper2.h filter_inline

check_PROGRAMS = clreq register_desc register_desc_threads find_debuginfo simwork threads inline-samefile inline-crossfile runtime_obj_skip_c runtime_obj_skip_underflow
check_PROGRAMS = clreq register_desc register_desc_threads find_debuginfo simwork threads inline-samefile inline-crossfile runtime_obj_skip_c runtime_obj_skip_underflow deterministic_rng
Comment thread
not-matthias marked this conversation as resolved.
if HAVE_PR_GET_AUXV
check_PROGRAMS += prctl_get_auxv
endif
Expand All @@ -57,6 +59,7 @@ inline_samefile_CFLAGS = $(AM_CFLAGS) -O2 -g
inline_crossfile_CFLAGS = $(AM_CFLAGS) -O2 -g

threads_LDADD = -lpthread
deterministic_rng_LDADD = -lpthread
register_desc_threads_LDADD = -lpthread

# Shim loaded by runtime_obj_skip_py.py via ctypes. Built unconditionally;
Expand Down
Loading
Loading