blob: 8be7b44fe418c72905764363327a988eead47330 [file] [log] [blame]
#ifndef AOS_IPC_LIB_LOCKLESS_QUEUE_STEPPING_H_
#define AOS_IPC_LIB_LOCKLESS_QUEUE_STEPPING_H_
#include <stdlib.h>
#include <unistd.h>
#include <cinttypes>
#include <functional>
#include <tuple>
#include "aos/ipc_lib/lockless_queue.h"
namespace aos::ipc_lib::testing {
#if defined(__ARM_EABI__)
// There are various reasons why we might not actually be able to do this
// testing, but we still want to run the functions to test anything they test
// other than shm robustness.
//
// ARM doesn't have a nice way to do single-stepping, and I don't feel like
// dealing with editing the next instruction to be a breakpoint and then
// changing it back.
#else
#define SUPPORTS_SHM_ROBUSTNESS_TEST
// This code currently supports amd64 only, but it
// shouldn't be hard to port to i386 (it should just be using the name for
// only the smaller part of the flags register), but that's not tested, and
// porting it to any other architecture is more work.
// Currently, we skip doing anything exciting on arm (just run the code without
// any robustness testing) and refuse to compile anywhere else.
#define SIMPLE_ASSERT(condition, message) \
do { \
if (!(condition)) { \
static const char kMessage[] = message "\n"; \
if (write(STDERR_FILENO, kMessage, sizeof(kMessage) - 1) != \
(sizeof(kMessage) - 1)) { \
static const char kFailureMessage[] = "writing failed\n"; \
__attribute__((unused)) int ignore = write( \
STDERR_FILENO, kFailureMessage, sizeof(kFailureMessage) - 1); \
} \
abort(); \
} \
} while (false)
// Array to track writes to memory, and make sure they happen in the right
// order.
class WritesArray {
public:
uintptr_t At(size_t location) const {
SIMPLE_ASSERT(location < size_, "too far into writes array");
return writes_[location];
}
void Add(uintptr_t pointer) {
SIMPLE_ASSERT(size_ < kSize, "too many writes");
writes_[size_++] = pointer;
}
size_t size() const { return size_; }
private:
static const size_t kSize = 20000;
uintptr_t writes_[kSize];
size_t size_ = 0;
};
enum class DieAtState {
// No SEGVs should be happening.
kDisabled,
// SEGVs are fine. Normal operation.
kRunning,
// We are manipulating a mutex. No SEGVs should be happening.
kWriting,
};
// What we exit with when we're exiting in the middle.
const int kExitEarlyValue = 123;
// We have to keep track of everything in a global variable because there's no
// other way for the signal handlers to find it.
struct GlobalState {
// Constructs the singleton GlobalState.
static std::tuple<GlobalState *, WritesArray *> MakeGlobalState();
// Returns the global state. Atomic and safe from signal handlers.
static GlobalState *Get();
// Pointer to the queue memory, and its size.
void *lockless_queue_memory;
size_t lockless_queue_memory_size;
// Pointer to a second block of memory the same size. This (on purpose) has
// the same size as lockless_queue_memory so we can point the robust mutexes
// here.
void *lockless_queue_memory_lock_backup;
// Expected writes.
const WritesArray *writes_in;
// Actual writes.
WritesArray *writes_out;
// Location to die at, and how far we have gotten.
size_t die_at, current_location;
// State.
DieAtState state;
// Returns true if the address is in the queue memory chunk.
bool IsInLocklessQueueMemory(void *address);
// Calls mprotect(2) for the entire shared memory region with the given prot.
void ShmProtectOrDie(int prot);
// Checks a write into the queue and conditionally dies. Tracks the write.
void HandleWrite(void *address);
// Registers the handlers required to trap writes.
void RegisterSegvAndTrapHandlers();
};
void TestShmRobustness(const LocklessQueueConfiguration &config,
::std::function<void(void *)> prepare,
::std::function<void(void *)> function,
::std::function<void(void *)> check);
// Capture the tid in the child so we can tell if it died. Uses mmap so it
// works across the process boundary.
class SharedTid {
public:
SharedTid();
~SharedTid();
// Captures the tid.
void Set();
// Returns the captured tid.
pid_t Get();
private:
pid_t *tid_;
};
#endif
} // namespace aos::ipc_lib::testing
#endif // AOS_IPC_LIB_LOCKLESS_QUEUE_STEPPING_H_