From dfa611bb04b180563729b0a7fbdc2f05ef04a03f Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Sat, 26 Apr 2008 20:25:02 +0000 Subject: [PATCH] Added paranoia checks debug facility. It helps checking that data in memory haven't changed behind one's back. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25193 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- build/config_headers/paranoia_config.h | 20 + headers/private/kernel/debug_paranoia.h | 91 +++++ src/system/kernel/debug/Jamfile | 1 + src/system/kernel/debug/debug.cpp | 2 + src/system/kernel/debug/debug_paranoia.cpp | 440 +++++++++++++++++++++ 5 files changed, 554 insertions(+) create mode 100644 build/config_headers/paranoia_config.h create mode 100644 headers/private/kernel/debug_paranoia.h create mode 100644 src/system/kernel/debug/debug_paranoia.cpp diff --git a/build/config_headers/paranoia_config.h b/build/config_headers/paranoia_config.h new file mode 100644 index 0000000000..c95c5da6d7 --- /dev/null +++ b/build/config_headers/paranoia_config.h @@ -0,0 +1,20 @@ +#ifndef DEBUG_PARANOIA_CONFIG_H +#define DEBUG_PARANOIA_CONFIG_H + + +// enable paranoia checks (0/1) +#ifndef ENABLE_PARANOIA_CHECKS +# define ENABLE_PARANOIA_CHECKS 1 +#endif + +// number of check slots (should depend on the components for which checks +// are enabled) +#define PARANOIA_SLOT_COUNT 1024 + + +// macros that enable paranoia checks for individual components + +//#define NET_BUFFER_PARANOIA 1 + + +#endif // DEBUG_PARANOIA_CONFIG_H diff --git a/headers/private/kernel/debug_paranoia.h b/headers/private/kernel/debug_paranoia.h new file mode 100644 index 0000000000..1dfe3d9a0e --- /dev/null +++ b/headers/private/kernel/debug_paranoia.h @@ -0,0 +1,91 @@ +/* + * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef _KERNEL_DEBUG_PARANOIA_H +#define _KERNEL_DEBUG_PARANOIA_H + +#include + +#include + +#include "paranoia_config.h" + + +// How to use: Include the header only from source files. Define +// ENABLE_PARANOIA_CHECK_COMPONENT (to 1 to enable) before. Use the macros +// defined below and the ParanoiaChecker class. + + +__BEGIN_DECLS + +#if ENABLE_PARANOIA_CHECKS + +status_t create_paranoia_check_set(const void* object, + const char* description); +status_t delete_paranoia_check_set(const void* object); +status_t run_paranoia_checks(const void* object); + +status_t set_paranoia_check(const void* object, const void* address, + size_t size); +status_t remove_paranoia_check(const void* object, const void* address, + size_t size); + +#endif // ENABLE_PARANOIA_CHECKS + +void debug_paranoia_init(); + +__END_DECLS + + +#if ENABLE_PARANOIA_CHECK_COMPONENT +# define PARANOIA_ONLY(x) x +#else +# define PARANOIA_ONLY(x) +#endif + +#define CREATE_PARANOIA_CHECK_SET(object, description) \ + PARANOIA_ONLY(create_paranoia_check_set((object), (description))) +#define DELETE_PARANOIA_CHECK_SET(object) \ + PARANOIA_ONLY(delete_paranoia_check_set((object))) +#define RUN_PARANOIA_CHECKS(object) \ + PARANOIA_ONLY(run_paranoia_checks((object))) +#define SET_PARANOIA_CHECK(object, address, size) \ + PARANOIA_ONLY(set_paranoia_check((object), (address), (size))) +#define REMOVE_PARANOIA_CHECK(object, address, size) \ + PARANOIA_ONLY(remove_paranoia_check((object), (address), (size))) + + +#ifdef __cplusplus + +class ParanoiaChecker { +public: + inline ParanoiaChecker(const void* object) + { + PARANOIA_ONLY(fObject = object); + RUN_PARANOIA_CHECKS(fObject); + } + + inline ~ParanoiaChecker() + { + PARANOIA_ONLY( + if (fObject != NULL) + RUN_PARANOIA_CHECKS(fObject); + ) + } + + inline void Disable() + { + PARANOIA_ONLY(fObject = NULL); + } + +private: + PARANOIA_ONLY( + const void* fObject; + ) +}; + +#endif // __cplusplus + + +#endif // _KERNEL_DEBUG_PARANOIA_H diff --git a/src/system/kernel/debug/Jamfile b/src/system/kernel/debug/Jamfile index b6bcad14bf..963b53fbbf 100644 --- a/src/system/kernel/debug/Jamfile +++ b/src/system/kernel/debug/Jamfile @@ -7,6 +7,7 @@ KernelMergeObject kernel_debug.o : blue_screen.cpp debug.cpp debug_commands.cpp + debug_paranoia.cpp debug_parser.cpp debug_variables.cpp frame_buffer_console.cpp diff --git a/src/system/kernel/debug/debug.cpp b/src/system/kernel/debug/debug.cpp index e87d17212f..a0919990fd 100644 --- a/src/system/kernel/debug/debug.cpp +++ b/src/system/kernel/debug/debug.cpp @@ -12,6 +12,7 @@ #include "gdb.h" #include +#include #include #include #include @@ -977,6 +978,7 @@ debug_early_boot_message(const char *string) status_t debug_init(kernel_args *args) { + debug_paranoia_init(); return arch_debug_console_init(args); } diff --git a/src/system/kernel/debug/debug_paranoia.cpp b/src/system/kernel/debug/debug_paranoia.cpp new file mode 100644 index 0000000000..80c9d6bef0 --- /dev/null +++ b/src/system/kernel/debug/debug_paranoia.cpp @@ -0,0 +1,440 @@ +/* + * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + +#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 - + + +#define PARANOIA_SLOT_COUNT 1024 +#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) +{ + 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) +{ + 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) +{ + 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 (check->Size() != size) { + panic("set_paranoia_check(): changing check sizes not supported"); + return B_BAD_VALUE; + } + + check->Update(); + return B_OK; + } + + // 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) +{ + 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) { + if (check->Size() != size) { + panic("remove_paranoia_check(): changing check sizes not " + "supported"); + return B_BAD_VALUE; + } + + check->Update(); + return B_OK; + } + + 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 +}