/* * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ #include #include #include #include #include #include #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 }