foundationdb/contrib/debug_determinism/debug_determinism.cpp

52 lines
1.6 KiB
C++

#include <stdint.h>
#include <stdio.h>
namespace {
FILE* out = nullptr;
FILE* in = nullptr;
void loop_forever() {
// Try to convince the optimizer not to optimize away this loop
static volatile uint64_t x = 0;
for (;;) {
++x;
}
}
} // namespace
// This callback is inserted by the compiler as a module constructor
// into every DSO. 'start' and 'stop' correspond to the
// beginning and end of the section with the guards for the entire
// binary (executable or DSO). The callback will be called at least
// once per DSO and may be called multiple times with the same parameters.
extern "C" void __sanitizer_cov_trace_pc_guard_init(uint32_t* start, uint32_t* stop) {
in = fopen("in.bin", "r");
out = fopen("out.bin", "w");
static uint64_t N; // Counter for the guards.
if (start == stop || *start)
return; // Initialize only once.
for (uint32_t* x = start; x < stop; x++) {
*x = ++N; // Guards should start from 1.
}
}
// This callback is inserted by the compiler on every edge in the
// control flow (some optimizations apply).
// Typically, the compiler will emit the code like this:
// if(*guard)
// __sanitizer_cov_trace_pc_guard(guard);
// But for large functions it will emit a simple call:
// __sanitizer_cov_trace_pc_guard(guard);
extern "C" void __sanitizer_cov_trace_pc_guard(uint32_t* guard) {
if (!guard) {
return;
}
fwrite(guard, sizeof(*guard), 1, out);
if (in) {
uint32_t theirs;
auto read = fread(&theirs, sizeof(theirs), 1, in);
if (read != 1 || *guard != theirs) {
printf("Non-determinism detected\n");
loop_forever();
}
}
}