606 lines
11 KiB
C++
606 lines
11 KiB
C++
/*
|
|
* Copyright 2008, Ingo Weinhold, [email protected].
|
|
* Distributed under the terms of the MIT License.
|
|
*/
|
|
|
|
#include <debug_paranoia.h>
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <new>
|
|
|
|
#include <OS.h>
|
|
|
|
#include <tracing.h>
|
|
#include <util/AutoLock.h>
|
|
|
|
|
|
#if ENABLE_PARANOIA_CHECKS
|
|
|
|
|
|
// #pragma mark - CRC-32
|
|
|
|
|
|
static const uint32 kCRC32Polynom = 0x04c11db7;
|
|
static uint32 sCRC32Table[256];
|
|
|
|
|
|
static uint32
|
|
crc32_reflect(uint32 value, int32 bits)
|
|
{
|
|
uint32 result = 0;
|
|
for (int32 i = 1; i <= bits; i++) {
|
|
if (value & 1)
|
|
result |= 1 << (bits - i);
|
|
value >>= 1;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
static void
|
|
init_crc32_table()
|
|
{
|
|
for (int32 i = 0; i < 256; i++) {
|
|
sCRC32Table[i] = crc32_reflect(i, 8) << 24;
|
|
for (int32 k = 0; k < 8; k++) {
|
|
sCRC32Table[i] = (sCRC32Table[i] << 1)
|
|
^ (sCRC32Table[i] & (1 << 31) ? kCRC32Polynom : 0);
|
|
}
|
|
sCRC32Table[i] = crc32_reflect(sCRC32Table[i], 32);
|
|
}
|
|
}
|
|
|
|
|
|
static uint32
|
|
crc32(const void* _data, size_t size)
|
|
{
|
|
uint8* data = (uint8*)_data;
|
|
uint32 crc = 0xffffffff;
|
|
|
|
while (size-- > 0) {
|
|
crc = (crc >> 8) ^ sCRC32Table[(crc & 0xff) ^ *data];
|
|
data++;
|
|
}
|
|
|
|
return crc;
|
|
}
|
|
|
|
|
|
// #pragma mark - ParanoiaCheck[Set]
|
|
|
|
|
|
class ParanoiaCheckSet;
|
|
|
|
class ParanoiaCheck {
|
|
public:
|
|
ParanoiaCheck(const void* address, size_t size)
|
|
:
|
|
fAddress(address),
|
|
fSize(size)
|
|
{
|
|
Update();
|
|
}
|
|
|
|
const void* Address() const { return fAddress; }
|
|
size_t Size() const { return fSize; }
|
|
|
|
void Update()
|
|
{
|
|
fCheckSum = crc32(fAddress, fSize);
|
|
}
|
|
|
|
bool Check() const
|
|
{
|
|
return crc32(fAddress, fSize) == fCheckSum;
|
|
}
|
|
|
|
private:
|
|
const void* fAddress;
|
|
size_t fSize;
|
|
uint32 fCheckSum;
|
|
ParanoiaCheck* fNext;
|
|
|
|
friend class ParanoiaCheckSet;
|
|
};
|
|
|
|
|
|
class ParanoiaCheckSet {
|
|
public:
|
|
ParanoiaCheckSet(const void* object, const char* description)
|
|
:
|
|
fObject(object),
|
|
fDescription(description),
|
|
fChecks(NULL)
|
|
{
|
|
}
|
|
|
|
const void* Object() const { return fObject; }
|
|
const char* Description() const { return fDescription; }
|
|
|
|
ParanoiaCheck* FirstCheck() const
|
|
{
|
|
return fChecks;
|
|
}
|
|
|
|
ParanoiaCheck* NextCheck(ParanoiaCheck* check) const
|
|
{
|
|
return check->fNext;
|
|
}
|
|
|
|
ParanoiaCheck* FindCheck(const void* address) const
|
|
{
|
|
ParanoiaCheck* check = fChecks;
|
|
while (check != NULL && check->Address() != address)
|
|
check = check->fNext;
|
|
return check;
|
|
}
|
|
|
|
void AddCheck(ParanoiaCheck* check)
|
|
{
|
|
check->fNext = fChecks;
|
|
fChecks = check;
|
|
}
|
|
|
|
void RemoveCheck(ParanoiaCheck* check)
|
|
{
|
|
if (check == fChecks) {
|
|
fChecks = check->fNext;
|
|
return;
|
|
}
|
|
|
|
ParanoiaCheck* previous = fChecks;
|
|
while (previous != NULL && previous->fNext != check)
|
|
previous = previous->fNext;
|
|
|
|
// if previous is NULL (which it shouldn't be), just crash here
|
|
previous->fNext = check->fNext;
|
|
}
|
|
|
|
ParanoiaCheck* RemoveFirstCheck()
|
|
{
|
|
ParanoiaCheck* check = fChecks;
|
|
if (check == NULL)
|
|
return NULL;
|
|
|
|
fChecks = check->fNext;
|
|
return check;
|
|
}
|
|
|
|
void SetHashNext(ParanoiaCheckSet* next)
|
|
{
|
|
fHashNext = next;
|
|
}
|
|
|
|
ParanoiaCheckSet* HashNext() const
|
|
{
|
|
return fHashNext;
|
|
}
|
|
|
|
private:
|
|
const void* fObject;
|
|
const char* fDescription;
|
|
ParanoiaCheck* fChecks;
|
|
ParanoiaCheckSet* fHashNext;
|
|
};
|
|
|
|
|
|
union paranoia_slot {
|
|
uint8 check[sizeof(ParanoiaCheck)];
|
|
uint8 checkSet[sizeof(ParanoiaCheckSet)];
|
|
paranoia_slot* nextFree;
|
|
};
|
|
|
|
|
|
// #pragma mark - Tracing
|
|
|
|
|
|
#if PARANOIA_TRACING
|
|
|
|
|
|
namespace ParanoiaTracing {
|
|
|
|
class ParanoiaTraceEntry : public AbstractTraceEntry {
|
|
public:
|
|
ParanoiaTraceEntry(const void* object)
|
|
:
|
|
fObject(object)
|
|
{
|
|
#if PARANOIA_TRACING_STACK_TRACE
|
|
fStackTrace = capture_tracing_stack_trace(PARANOIA_TRACING_STACK_TRACE,
|
|
1, false);
|
|
#endif
|
|
}
|
|
|
|
#if PARANOIA_TRACING_STACK_TRACE
|
|
virtual void DumpStackTrace(TraceOutput& out)
|
|
{
|
|
out.PrintStackTrace(fStackTrace);
|
|
}
|
|
#endif
|
|
|
|
protected:
|
|
const void* fObject;
|
|
#if PARANOIA_TRACING_STACK_TRACE
|
|
tracing_stack_trace* fStackTrace;
|
|
#endif
|
|
};
|
|
|
|
|
|
class CreateCheckSet : public ParanoiaTraceEntry {
|
|
public:
|
|
CreateCheckSet(const void* object, const char* description)
|
|
:
|
|
ParanoiaTraceEntry(object)
|
|
{
|
|
fDescription = alloc_tracing_buffer_strcpy(description, 64, false);
|
|
Initialized();
|
|
}
|
|
|
|
virtual void AddDump(TraceOutput& out)
|
|
{
|
|
out.Print("paranoia create check set: object: %p, "
|
|
"description: \"%s\"", fObject, fDescription);
|
|
}
|
|
|
|
private:
|
|
const char* fDescription;
|
|
};
|
|
|
|
|
|
class DeleteCheckSet : public ParanoiaTraceEntry {
|
|
public:
|
|
DeleteCheckSet(const void* object)
|
|
:
|
|
ParanoiaTraceEntry(object)
|
|
{
|
|
Initialized();
|
|
}
|
|
|
|
virtual void AddDump(TraceOutput& out)
|
|
{
|
|
out.Print("paranoia delete check set: object: %p", fObject);
|
|
}
|
|
};
|
|
|
|
|
|
class SetCheck : public ParanoiaTraceEntry {
|
|
public:
|
|
SetCheck(const void* object, const void* address, size_t size,
|
|
paranoia_set_check_mode mode)
|
|
:
|
|
ParanoiaTraceEntry(object),
|
|
fAddress(address),
|
|
fSize(size),
|
|
fMode(mode)
|
|
{
|
|
Initialized();
|
|
}
|
|
|
|
virtual void AddDump(TraceOutput& out)
|
|
{
|
|
const char* mode = "??? op:";
|
|
switch (fMode) {
|
|
case PARANOIA_DONT_FAIL:
|
|
mode = "set: ";
|
|
break;
|
|
case PARANOIA_FAIL_IF_EXISTS:
|
|
mode = "add: ";
|
|
break;
|
|
case PARANOIA_FAIL_IF_MISSING:
|
|
mode = "update:";
|
|
break;
|
|
}
|
|
out.Print("paranoia check %s object: %p, address: %p, size: %lu",
|
|
mode, fObject, fAddress, fSize);
|
|
}
|
|
|
|
private:
|
|
const void* fAddress;
|
|
size_t fSize;
|
|
paranoia_set_check_mode fMode;
|
|
};
|
|
|
|
|
|
class RemoveCheck : public ParanoiaTraceEntry {
|
|
public:
|
|
RemoveCheck(const void* object, const void* address, size_t size)
|
|
:
|
|
ParanoiaTraceEntry(object),
|
|
fAddress(address),
|
|
fSize(size)
|
|
{
|
|
Initialized();
|
|
}
|
|
|
|
virtual void AddDump(TraceOutput& out)
|
|
{
|
|
out.Print("paranoia check remove: object: %p, address: %p, size: "
|
|
"%lu", fObject, fAddress, fSize);
|
|
}
|
|
|
|
private:
|
|
const void* fAddress;
|
|
size_t fSize;
|
|
paranoia_set_check_mode fMode;
|
|
};
|
|
|
|
|
|
} // namespace ParanoiaTracing
|
|
|
|
# define T(x) new(std::nothrow) ParanoiaTracing::x
|
|
|
|
#else
|
|
# define T(x)
|
|
#endif // PARANOIA_TRACING
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
#define PARANOIA_HASH_SIZE PARANOIA_SLOT_COUNT
|
|
|
|
static paranoia_slot sSlots[PARANOIA_SLOT_COUNT];
|
|
static paranoia_slot* sSlotFreeList;
|
|
static ParanoiaCheckSet* sCheckSetHash[PARANOIA_HASH_SIZE];
|
|
static spinlock sParanoiaLock;
|
|
|
|
|
|
static paranoia_slot*
|
|
allocate_slot()
|
|
{
|
|
if (sSlotFreeList == NULL)
|
|
return NULL;
|
|
|
|
paranoia_slot* slot = sSlotFreeList;
|
|
sSlotFreeList = slot->nextFree;
|
|
return slot;
|
|
}
|
|
|
|
|
|
static void
|
|
free_slot(paranoia_slot* slot)
|
|
{
|
|
slot->nextFree = sSlotFreeList;
|
|
sSlotFreeList = slot;
|
|
}
|
|
|
|
|
|
static void
|
|
add_check_set(ParanoiaCheckSet* set)
|
|
{
|
|
int slot = (addr_t)set->Object() % PARANOIA_HASH_SIZE;
|
|
set->SetHashNext(sCheckSetHash[slot]);
|
|
sCheckSetHash[slot] = set;
|
|
}
|
|
|
|
|
|
static void
|
|
remove_check_set(ParanoiaCheckSet* set)
|
|
{
|
|
int slot = (addr_t)set->Object() % PARANOIA_HASH_SIZE;
|
|
if (set == sCheckSetHash[slot]) {
|
|
sCheckSetHash[slot] = set->HashNext();
|
|
return;
|
|
}
|
|
|
|
ParanoiaCheckSet* previousSet = sCheckSetHash[slot];
|
|
while (previousSet != NULL && previousSet->HashNext() != set)
|
|
previousSet = previousSet->HashNext();
|
|
|
|
// if previousSet is NULL (which it shouldn't be), just crash here
|
|
previousSet->SetHashNext(set->HashNext());
|
|
}
|
|
|
|
|
|
static ParanoiaCheckSet*
|
|
lookup_check_set(const void* object)
|
|
{
|
|
int slot = (addr_t)object % PARANOIA_HASH_SIZE;
|
|
ParanoiaCheckSet* set = sCheckSetHash[slot];
|
|
while (set != NULL && set->Object() != object)
|
|
set = set->HashNext();
|
|
|
|
return set;
|
|
}
|
|
|
|
// #pragma mark - public interface
|
|
|
|
|
|
status_t
|
|
create_paranoia_check_set(const void* object, const char* description)
|
|
{
|
|
T(CreateCheckSet(object, description));
|
|
|
|
if (object == NULL) {
|
|
panic("create_paranoia_check_set(): NULL object");
|
|
return B_BAD_VALUE;
|
|
}
|
|
|
|
InterruptsSpinLocker _(sParanoiaLock);
|
|
|
|
// check, if object is already registered
|
|
ParanoiaCheckSet* set = lookup_check_set(object);
|
|
if (set != NULL) {
|
|
panic("create_paranoia_check_set(): object %p already has a check set",
|
|
object);
|
|
return B_BAD_VALUE;
|
|
}
|
|
|
|
// allocate slot
|
|
paranoia_slot* slot = allocate_slot();
|
|
if (slot == NULL) {
|
|
panic("create_paranoia_check_set(): out of free slots");
|
|
return B_NO_MEMORY;
|
|
}
|
|
|
|
set = new(slot) ParanoiaCheckSet(object, description);
|
|
add_check_set(set);
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
status_t
|
|
delete_paranoia_check_set(const void* object)
|
|
{
|
|
T(DeleteCheckSet(object));
|
|
|
|
InterruptsSpinLocker _(sParanoiaLock);
|
|
|
|
// get check set
|
|
ParanoiaCheckSet* set = lookup_check_set(object);
|
|
if (set == NULL) {
|
|
panic("delete_paranoia_check_set(): object %p doesn't have a check set",
|
|
object);
|
|
return B_BAD_VALUE;
|
|
}
|
|
|
|
// free all checks
|
|
while (ParanoiaCheck* check = set->RemoveFirstCheck())
|
|
free_slot((paranoia_slot*)check);
|
|
|
|
// free check set
|
|
remove_check_set(set);
|
|
free_slot((paranoia_slot*)set);
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
status_t
|
|
run_paranoia_checks(const void* object)
|
|
{
|
|
InterruptsSpinLocker _(sParanoiaLock);
|
|
|
|
// get check set
|
|
ParanoiaCheckSet* set = lookup_check_set(object);
|
|
if (set == NULL) {
|
|
panic("run_paranoia_checks(): object %p doesn't have a check set",
|
|
object);
|
|
return B_BAD_VALUE;
|
|
}
|
|
|
|
status_t error = B_OK;
|
|
|
|
ParanoiaCheck* check = set->FirstCheck();
|
|
while (check != NULL) {
|
|
if (!check->Check()) {
|
|
panic("paranoia check failed for object %p (%s), address: %p, "
|
|
"size: %lu", set->Object(), set->Description(),
|
|
check->Address(), check->Size());
|
|
error = B_BAD_DATA;
|
|
}
|
|
|
|
check = set->NextCheck(check);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
status_t
|
|
set_paranoia_check(const void* object, const void* address, size_t size,
|
|
paranoia_set_check_mode mode)
|
|
{
|
|
T(SetCheck(object, address, size, mode));
|
|
|
|
InterruptsSpinLocker _(sParanoiaLock);
|
|
|
|
// get check set
|
|
ParanoiaCheckSet* set = lookup_check_set(object);
|
|
if (set == NULL) {
|
|
panic("set_paranoia_check(): object %p doesn't have a check set",
|
|
object);
|
|
return B_BAD_VALUE;
|
|
}
|
|
|
|
// update check, if already existing
|
|
ParanoiaCheck* check = set->FindCheck(address);
|
|
if (check != NULL) {
|
|
if (mode == PARANOIA_FAIL_IF_EXISTS) {
|
|
panic("set_paranoia_check(): object %p already has a check for "
|
|
"address %p", object, address);
|
|
return B_BAD_VALUE;
|
|
}
|
|
|
|
if (check->Size() != size) {
|
|
panic("set_paranoia_check(): changing check sizes not supported");
|
|
return B_BAD_VALUE;
|
|
}
|
|
|
|
check->Update();
|
|
return B_OK;
|
|
}
|
|
|
|
if (mode == PARANOIA_FAIL_IF_MISSING) {
|
|
panic("set_paranoia_check(): object %p doesn't have a check for "
|
|
"address %p yet", object, address);
|
|
return B_BAD_VALUE;
|
|
}
|
|
|
|
// allocate slot
|
|
paranoia_slot* slot = allocate_slot();
|
|
if (slot == NULL) {
|
|
panic("set_paranoia_check(): out of free slots");
|
|
return B_NO_MEMORY;
|
|
}
|
|
|
|
check = new(slot) ParanoiaCheck(address, size);
|
|
set->AddCheck(check);
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
status_t
|
|
remove_paranoia_check(const void* object, const void* address, size_t size)
|
|
{
|
|
T(RemoveCheck(object, address, size));
|
|
|
|
InterruptsSpinLocker _(sParanoiaLock);
|
|
|
|
// get check set
|
|
ParanoiaCheckSet* set = lookup_check_set(object);
|
|
if (set == NULL) {
|
|
panic("remove_paranoia_check(): object %p doesn't have a check set",
|
|
object);
|
|
return B_BAD_VALUE;
|
|
}
|
|
|
|
// get check
|
|
ParanoiaCheck* check = set->FindCheck(address);
|
|
if (check == NULL) {
|
|
panic("remove_paranoia_check(): no check for address %p "
|
|
"(object %p (%s))", address, object, set->Description());
|
|
return B_BAD_VALUE;
|
|
}
|
|
|
|
if (check->Size() != size) {
|
|
panic("remove_paranoia_check(): changing check sizes not "
|
|
"supported");
|
|
return B_BAD_VALUE;
|
|
}
|
|
|
|
set->RemoveCheck(check);
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
#endif // ENABLE_PARANOIA_CHECKS
|
|
|
|
|
|
void
|
|
debug_paranoia_init()
|
|
{
|
|
#if ENABLE_PARANOIA_CHECKS
|
|
// init CRC-32 table
|
|
init_crc32_table();
|
|
|
|
// init paranoia slot free list
|
|
for (int32 i = 0; i < PARANOIA_SLOT_COUNT; i++)
|
|
free_slot(&sSlots[i]);
|
|
#endif
|
|
}
|