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
This commit is contained in:
@@ -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
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2008, Ingo Weinhold, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef _KERNEL_DEBUG_PARANOIA_H
|
||||
#define _KERNEL_DEBUG_PARANOIA_H
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
#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
|
||||
@@ -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
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "gdb.h"
|
||||
|
||||
#include <debug.h>
|
||||
#include <debug_paranoia.h>
|
||||
#include <driver_settings.h>
|
||||
#include <frame_buffer_console.h>
|
||||
#include <int.h>
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,440 @@
|
||||
/*
|
||||
* Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include <debug_paranoia.h>
|
||||
|
||||
#include <sys/param.h>
|
||||
|
||||
#include <new>
|
||||
|
||||
#include <OS.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 -
|
||||
|
||||
|
||||
#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
|
||||
}
|
||||
Reference in New Issue
Block a user