39 RingRand(uint64 seed) :
s(seed ? seed : 0x9E3779B97F4A7C15ULL) {}
41 uint32
below(uint32 n) {
return n ? (uint32)(
next() % n) : 0; }
55 uint32 h = 2166136261u;
56 for (uint32 i = 0; i < len; i++) { h ^= (uint8)p[i]; h *= 16777619u; }
63 for (uint32 i = 0; i < len; i++)
64 buf[i] = (
char)(
'A' + ((serial * 31 + i * 7) % 26));
70 const char* v = getenv(name);
71 if (!v || !*v)
return def;
73 return (n > 0) ? (uint32)n : def;
79 uint32& gotLen, uint32& gotSum) {
80 if (!got)
return "LOST";
82 if (sz != want.
wireSize)
return "SHORT";
84 int64 s = got->
getInt(
"s", ok);
if (!ok)
return "CORRUPT";
85 gotSerial = (uint32)s;
87 const char* body = got->
getData(
"p", plen);
90 if (gotSerial != want.
serial)
return "REORDERED";
92 if (gotSum != want.
sum)
return "CORRUPT";
101 char* buf =
new char[len + 1];
110 char* tmp =
new char[len + 1];
132 char* detail, uint32 detailSize) {
133 if (!q) {
return expectCount == 0 ? NULL :
"HEADER_NULL"; }
139 if (q->
count != expectCount) {
140 snprintf(detail, detailSize,
141 "count=%u expect=%u startPos=%llu endPos=%llu padding=%llu cap=%llu",
143 return "COUNT_MISMATCH";
148 snprintf(detail, detailSize,
149 "startPos=%llu endPos=%llu padding=%llu cap=%llu",
151 return "POS_OUT_OF_RANGE";
157 snprintf(detail, detailSize,
"count=0 but startPos=%llu endPos=%llu padding=%llu",
159 return "EMPTY_BUT_SPANNED";
171 for (uint32 i = 0; i < q->
count; i++) {
173 if (pos >= cap - q->
padding) pos = 0;
175 snprintf(detail, detailSize,
176 "msg %u/%u at pos=%llu not a DataMessage (startPos=%llu endPos=%llu padding=%llu)",
178 return "WALK_BAD_OBJID";
181 if (msz == 0 || pos + msz > cap) {
182 snprintf(detail, detailSize,
183 "msg %u/%u pos=%llu size=%u overruns cap=%llu (padding=%llu)",
185 return "WALK_SIZE_OVERRUN";
194 snprintf(detail, detailSize,
195 "walk ended at pos=%llu but endPos=%llu (count=%u startPos=%llu padding=%llu cap=%llu)",
197 return "WALK_ENDPOS_MISMATCH";
205 bool checkInvariants,
bool invariantSelftest) {
207 std::deque<RingRef> model;
208 uint32 nextSerial = 1;
209 uint32 injectAt = selftest ? (iters / 2) : 0xFFFFFFFFu;
210 bool injected =
false;
215 uint32 invInjectAt = invariantSelftest ? (iters / 2) : 0xFFFFFFFFu;
216 bool invInjected =
false;
222 bool resizeHeavy = getenv(
"PSY_RING_RESIZEHEAVY") && *getenv(
"PSY_RING_RESIZEHEAVY") ==
'1';
224 for (uint32 op = 0; op < iters; op++) {
227 uint32 depth = (uint32)model.size();
231 bool forceRead = (op >= injectAt) && !injected && depth > 0;
232 if (forceRead) doWrite =
false;
233 else if (depth == 0) doWrite =
true;
234 else if (resizeHeavy) doWrite = (depth > 400) ? rng.
chance(45) : rng.
chance(80);
235 else if (depth > 200) doWrite = rng.
chance(20);
236 else doWrite = rng.
chance(60);
241 uint32 pick = rng.
below(100);
245 if (pick < 20) len = 1 + rng.
below(16);
246 else len = 700 + rng.
below(1400);
248 else if (pick < 15) len = 1 + rng.
below(8);
249 else if (pick < 25) len = 900 + rng.
below(700);
250 else len = 1 + rng.
below(300);
254 unittest::fail(
"seed %llu op %u: addToMsgQ failed (serial %u len %u)",
255 seed, op, ref.
serial, len);
260 model.push_back(ref);
262 if (checkInvariants) {
264 if ((op >= invInjectAt) && !invInjected && q) { q->
count += 7; invInjected =
true; }
267 unittest::fail(
"seed %llu op %u (post-write serial %u): INVARIANT %s :: %s",
268 seed, op, ref.
serial, iv, idet);
280 if ((op >= injectAt) && !injected) { want.
serial ^= 0xABCD; injected =
true; }
282 uint32 gs = 0, gl = 0, gsum = 0;
283 const char* cls =
RingClass(want, got, gs, gl, gsum);
285 unittest::fail(
"seed %llu op %u: %s want{serial=%u len=%u sum=%08x size=%u} "
286 "got{serial=%u len=%u sum=%08x size=%u}",
288 gs, gl, gsum, got ? got->
getSize() : 0);
294 if (checkInvariants) {
306 while (!model.empty()) {
310 uint32 gs = 0, gl = 0, gsum = 0;
311 const char* cls =
RingClass(want, got, gs, gl, gsum);
323 uint32 iters =
RingEnvU32(
"PSY_RING_ITERS", 20000);
324 uint32 seeds =
RingEnvU32(
"PSY_RING_SEEDS", 8);
325 bool selftest = getenv(
"PSY_RING_SELFTEST") && *getenv(
"PSY_RING_SELFTEST") ==
'1';
326 bool checkInvariants = getenv(
"PSY_RING_INVARIANTS") && *getenv(
"PSY_RING_INVARIANTS") ==
'1';
327 bool invariantSelftest = getenv(
"PSY_RING_INVSELFTEST") && *getenv(
"PSY_RING_INVSELFTEST") ==
'1';
328 if (invariantSelftest) checkInvariants =
true;
329 const char* sEnv = getenv(
"PSY_RING_SEED");
337 uint64 seed = (uint64)strtoull(sEnv, NULL, 0);
340 if (!m->
create(0)) {
unittest::fail(
"MemoryManager create(0) failed (replay seed %llu)", seed);
delete m;
return false; }
341 ok =
RingRunSeed(m, seed, iters, selftest, checkInvariants, invariantSelftest);
345 for (uint32 i = 0; i < seeds && ok; i++) {
346 uint64 seed = 0x1000ULL + i * 0x9E3779B1ULL;
348 "seed %llu (%u/%u, %u iters)", seed, i + 1, seeds, iters);
350 if (!m->
create(0)) {
unittest::fail(
"MemoryManager create(0) failed (seed %llu)", seed);
delete m; ok =
false;
break; }
351 ok =
RingRunSeed(m, seed, iters, selftest, checkInvariants, invariantSelftest);
Central shared-memory manager for a Psyclone node: master segment, per-subsystem shared maps and the ...
#define GetObjID(data)
Extract the cid field from a binary object block: the uint32 at byte offset 4 (after the leading size...
Shared-memory process ("space") table plus per-process message queues.
#define MSGQ_TYPE
Data-message queue.
Small, dependency-free unit test harness used by all CMSDK object tests.
The central Psyclone data container: a self-contained binary message with typed, named user entries.
bool getInt(const char *key, int64 &value)
getInt(const char* key, int64& value)
bool setInt(const char *key, int64 value)
setInt(const char* key, int64 value)
const char * getData(const char *key, uint32 &size)
getData(const char* key, uint32& size)
uint32 getSize()
getSize() Get message size Many types of data of any size can be put into a message as user entries; ...
bool setData(const char *key, const char *value, uint32 size)
setData(const char* key, const char* value, uint32 size)
Top-level facade of the shared-memory subsystem for one process.
ProcessMemory * processMemory
Accessor for the process table and per-process queues.
bool create(uint16 sysID, uint32 slotCount=100000, uint16 binCount=2, uint32 minBlockSize=1024, uint32 maxBlockSize=64 *1024, uint64 initSize=50000000L, uint64 maxSize=1000000000L, bool force=false)
Create all shared segments for a new node instance (master process only).
MessageQueueHeader * fuzzPeekQHeader(uint16 procID, uint8 qType)
Diagnostics-only: read-only access to a live queue header for the Phase-2 fuzz invariant checker (Pro...
bool addToMsgQ(uint16 procID, DataMessage *msg)
Enqueue on the data-message queue.
static bool FuzzTest()
Seeded, oracle-checked fuzz of the circular queue arithmetic (single process).
DataMessage * waitForMsgQ(uint16 procID, uint32 timeout)
Wait on the data-message queue.
void fail(const char *fmt,...)
Set an explanatory reason shown on the FAIL line.
void progressf(int percent, const char *fmt,...)
printf-style variant of progress().
void progress(int percent, const char *action)
Report progress with a short description of the current action.
static uint32 RingChecksum(const char *p, uint32 len)
FNV-1a over the payload body.
static uint32 RingEnvU32(const char *name, uint32 def)
Env integer with default.
static DataMessage * RingMakeMsg(uint32 serial, uint32 len, RingRef &ref)
Build one message carrying serial serial and a deterministic payload of len bytes,...
static const char * RingClass(const RingRef &want, DataMessage *got, uint32 &gotSerial, uint32 &gotLen, uint32 &gotSum)
Classifier for a single divergence between the queue and the reference model.
static const char * RingCheckInvariants(MessageQueueHeader *q, uint32 expectCount, char *detail, uint32 detailSize)
Walk the live region of a queue and assert its bookkeeping is self-consistent.
static void RingFillPayload(char *buf, uint32 len, uint32 serial)
Deterministic payload: content is a pure function of (serial, len), so a reader can re-derive what it...
static struct PsyType CTRL_TEST
static bool RingRunSeed(MemoryManager *manager, uint64 seed, uint32 iters, bool selftest, bool checkInvariants, bool invariantSelftest)
Run one seed to completion.
Deterministic xorshift64* — same sequence on every platform, unlike rand().
One entry of the reference model: what a correct queue MUST hand back, in order.