diff --git a/build/config_headers/kernel_debug_config.h b/build/config_headers/kernel_debug_config.h index dd4d09d12c..ae329d4f35 100644 --- a/build/config_headers/kernel_debug_config.h +++ b/build/config_headers/kernel_debug_config.h @@ -18,6 +18,9 @@ // benaphore-style. #define KDEBUG KDEBUG_LEVEL_2 +// Size of the heap used by the kernel debugger. +#define KDEBUG_HEAP (64 * 1024) + // Set to 0 to disable support for kernel breakpoints. #define KERNEL_BREAKPOINTS 1 diff --git a/headers/private/kernel/debug_heap.h b/headers/private/kernel/debug_heap.h new file mode 100644 index 0000000000..ae110b9840 --- /dev/null +++ b/headers/private/kernel/debug_heap.h @@ -0,0 +1,71 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de + * Distributed under the terms of the MIT License. + */ +#ifndef _KERNEL_DEBUG_HEAP_H +#define _KERNEL_DEBUG_HEAP_H + +#include + + +struct DebugAllocPool; +typedef struct DebugAllocPool debug_alloc_pool; + + +#ifdef __cplusplus +extern "C" { +#endif + +debug_alloc_pool* create_debug_alloc_pool(); +void delete_debug_alloc_pool(debug_alloc_pool* pool); +void* debug_malloc(size_t size); +void debug_free(void* address); +void debug_heap_init(); + +#ifdef __cplusplus +} +#endif + + +#ifdef __cplusplus + +struct kdebug_alloc_t {}; +extern const kdebug_alloc_t kdebug_alloc; + +inline void* +operator new(size_t size, const kdebug_alloc_t&) throw() +{ + return debug_malloc(size); +} + +namespace DebugAlloc { + template + inline void + destroy(Type* object) + { + if (object != NULL) { + object->~Type(); + debug_free(object); + // NOTE: Doesn't work for multiple inheritence! + } + } +} + +struct DebugAllocPoolScope { + DebugAllocPoolScope() + { + fPool = create_debug_alloc_pool(); + } + + ~DebugAllocPoolScope() + { + delete_debug_alloc_pool(fPool); + } + +private: + DebugAllocPool* fPool; +}; + +#endif // __cplusplus + +#endif /* _KERNEL_DEBUG_HEAP_H */ diff --git a/src/system/kernel/debug/Jamfile b/src/system/kernel/debug/Jamfile index 84f944d494..a94a3dc84b 100644 --- a/src/system/kernel/debug/Jamfile +++ b/src/system/kernel/debug/Jamfile @@ -10,6 +10,7 @@ KernelMergeObject kernel_debug.o : debug.cpp debug_builtin_commands.cpp debug_commands.cpp + debug_heap.cpp debug_paranoia.cpp debug_parser.cpp debug_variables.cpp diff --git a/src/system/kernel/debug/debug.cpp b/src/system/kernel/debug/debug.cpp index 83bcafc0d5..1b54086448 100644 --- a/src/system/kernel/debug/debug.cpp +++ b/src/system/kernel/debug/debug.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2008-2009, Ingo Weinhold, ingo_weinhold@gmx.de. * Copyright 2002-2008, Axel Dörfler, axeld@pinc-software.de. * Distributed under the terms of the MIT License. * @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -665,6 +666,8 @@ kernel_debugger_loop(void) int32 previousCPU = sDebuggerOnCPU; sDebuggerOnCPU = smp_get_current_cpu(); + DebugAllocPool* allocPool = create_debug_alloc_pool(); + kprintf("Welcome to Kernel Debugging Land...\n"); if (struct thread* thread = thread_get_current_thread()) { @@ -721,6 +724,8 @@ kernel_debugger_loop(void) sCurrentLine = 0; } + delete_debug_alloc_pool(allocPool); + sDebuggerOnCPU = previousCPU; } @@ -1210,6 +1215,7 @@ debug_init_post_vm(kernel_args* args) debug_builtin_commands_init(); + debug_heap_init(); debug_variables_init(); frame_buffer_console_init(args); arch_debug_console_init_settings(args); diff --git a/src/system/kernel/debug/debug_commands.cpp b/src/system/kernel/debug/debug_commands.cpp index 419d1e15da..b0fd9be35d 100644 --- a/src/system/kernel/debug/debug_commands.cpp +++ b/src/system/kernel/debug/debug_commands.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de + * Copyright 2008-2009, Ingo Weinhold, ingo_weinhold@gmx.de * Copyright 2002-2008, Axel Dörfler, axeld@pinc-software.de * Distributed under the terms of the MIT License. * @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -273,6 +274,9 @@ invoke_debugger_command(struct debugger_command *command, int argc, char** argv) // replace argv[0] with the actual command name argv[0] = (char *)command->name; + DebugAllocPoolScope allocPoolScope; + // Will automatically clean up all allocations the command leaves over. + // Invoking the command directly might be useful when debugging debugger // commands. if (gInvokeCommandDirectly) diff --git a/src/system/kernel/debug/debug_heap.cpp b/src/system/kernel/debug/debug_heap.cpp new file mode 100644 index 0000000000..c5cec0cf68 --- /dev/null +++ b/src/system/kernel/debug/debug_heap.cpp @@ -0,0 +1,302 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de + * Distributed under the terms of the MIT License. + */ + +#include + +#include + +#include + + +#define INITIAL_HEAP_SIZE B_PAGE_SIZE + +static char sInitialHeap[INITIAL_HEAP_SIZE]; +static void* sHeapBase = sInitialHeap; +static size_t sHeapSize = INITIAL_HEAP_SIZE; + +const kdebug_alloc_t kdebug_alloc = {}; + + +struct allocation_header { + uint32 size : 31; // size in allocation_header units + bool free : 1; + uint32 previous; +}; + +struct free_entry : allocation_header { + uint32 previous_free; + uint32 next_free; +}; + + +struct DebugAllocPool { + void Init(void* heap, size_t heapSize) + { + fParent = NULL; + fChild = NULL; + + uint32 size = heapSize / 8; + fBase = (allocation_header*)heap - 1; + fEnd = size + 1; + fFirstFree = 0; + fLastFree = 0; + + // add free entry spanning the whole area + fBase[1].size = size - 1; + fBase[1].previous = 0; + _InsertFreeEntry(1); + } + + DebugAllocPool* CreateChildPool() + { + // do we already have a child pool? + if (fChild != NULL) + return NULL; + + // create the pool object + DebugAllocPool* pool + = (DebugAllocPool*)Allocate(sizeof(DebugAllocPool)); + if (pool == NULL) + return NULL; + + // do we have enough free space? + if (fLastFree == 0 || fBase[fLastFree].size < 2) { + Free(pool); + return NULL; + } + + allocation_header* header = &fBase[fLastFree]; + _RemoveFreeEntry(fLastFree); + + pool->Init(header + 1, header->size * 8); + pool->fParent = this; + + return fChild = pool; + } + + void Destroy() + { + if (fParent != NULL) { + fParent->fChild = NULL; + fParent->Free(fBase + 1); + } + } + + DebugAllocPool* Parent() const + { + return fParent; + } + + void* Allocate(size_t size) + { + size = (size + 7) / 8; + uint32 index = fFirstFree; + while (index != 0 && fBase[index].size < size) + index = ((free_entry*)&fBase[index])->next_free; + + if (index == 0) + return NULL; + + _RemoveFreeEntry(index); + + // if the entry is big enough, we split it + if (fBase[index].size - size >= 2) { + uint32 next = index + 1 + size; + uint32 nextNext = index + 1 + fBase[index].size; + fBase[next].size = fBase[index].size - size - 1; + fBase[next].previous = index; + fBase[index].size = size; + _InsertFreeEntry(next); + + if (nextNext < fEnd) + fBase[nextNext].previous = next; + } + + return &fBase[index + 1]; + } + + void Free(void* address) + { + // check address + if (((addr_t)address & 7) != 0 || address <= fBase + 1 + || address >= fBase + fEnd) { + kprintf("DebugAllocator::Free(%p): bad address\n", address); + return; + } + + // get header + allocation_header* header = (allocation_header*)address - 1; + uint32 index = header - fBase; + if (header->free) { + kprintf("DebugAllocator::Free(%p): double free\n", address); + return; + } + + uint32 next = index + 1 + header->size; + + // join with previous, if possible + if (index > 1 && fBase[header->previous].free) { + uint32 previous = header->previous; + _RemoveFreeEntry(previous); + + fBase[previous].size += 1 + header->size; + fBase[next].previous = previous; + + index = previous; + header = fBase + index; + } + + // join with next, if possible + if (next < fEnd && fBase[next].free) { + _RemoveFreeEntry(next); + + header->size += 1 + fBase[next].size; + + uint32 nextNext = index + 1 + header->size; + if (nextNext < fEnd) + fBase[nextNext].previous = index; + } + + _InsertFreeEntry(index); + } + +private: + void _InsertFreeEntry(uint32 index) + { + // find the insertion point -- list is sorted by ascending size + uint32 size = fBase[index].size; + uint32 next = fFirstFree; + while (next != 0 && size > fBase[next].size) + next = ((free_entry*)&fBase[next])->next_free; + + // insert + uint32 previous; + if (next != 0) { + previous = ((free_entry*)&fBase[next])->previous_free; + ((free_entry*)&fBase[next])->previous_free = index; + } else { + previous = fLastFree; + fLastFree = index; + } + + if (previous != 0) + ((free_entry*)&fBase[previous])->next_free = index; + else + fFirstFree = index; + + ((free_entry*)&fBase[index])->previous_free = previous; + ((free_entry*)&fBase[index])->next_free = next; + + fBase[index].free = true; + } + + void _RemoveFreeEntry(uint32 index) + { + uint32 previous = ((free_entry*)&fBase[index])->previous_free; + uint32 next = ((free_entry*)&fBase[index])->next_free; + + if (previous != 0) + ((free_entry*)&fBase[previous])->next_free = next; + else + fFirstFree = next; + + if (next != 0) + ((free_entry*)&fBase[next])->previous_free = previous; + else + fLastFree = previous; + + fBase[index].free = false; + } + +private: + DebugAllocPool* fParent; + DebugAllocPool* fChild; + allocation_header* fBase; // actually base - 1, so that index 0 is + // invalid + uint32 fEnd; + uint32 fFirstFree; + uint32 fLastFree; +}; + + +static DebugAllocPool* sCurrentPool; +static DebugAllocPool sInitialPool; + + +debug_alloc_pool* +create_debug_alloc_pool() +{ + if (sCurrentPool == NULL) { + sInitialPool.Init(sHeapBase, sHeapSize); + sCurrentPool = &sInitialPool; + return sCurrentPool; + } + + DebugAllocPool* pool = sCurrentPool->CreateChildPool(); + if (pool == NULL) + return NULL; + + sCurrentPool = pool; + return sCurrentPool; +} + + +void +delete_debug_alloc_pool(debug_alloc_pool* pool) +{ + if (pool == NULL || sCurrentPool == NULL) + return; + + // find the pool in the hierarchy + DebugAllocPool* otherPool = sCurrentPool; + while (otherPool != NULL && otherPool != pool) + otherPool = otherPool->Parent(); + + if (otherPool == NULL) + return; + + // destroy the pool + sCurrentPool = pool->Parent(); + pool->Destroy(); + + if (pool != &sInitialPool) + debug_free(pool); +} + + +void* +debug_malloc(size_t size) +{ + if (sCurrentPool == NULL) + return NULL; + + return sCurrentPool->Allocate(size); +} + + +void +debug_free(void* address) +{ + if (address != NULL && sCurrentPool != NULL) + sCurrentPool->Free(address); +} + + +void +debug_heap_init() +{ + // create the heap area + void* base; + area_id area = create_area("kdebug heap", (void**)&base, + B_ANY_KERNEL_ADDRESS, KDEBUG_HEAP, B_FULL_LOCK, + B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); + if (area < 0) + return; + + // switch from the small static buffer to the area + InterruptsLocker locker; + sHeapBase = base; + sHeapSize = KDEBUG_HEAP; +} diff --git a/src/system/kernel/debug/debug_parser.cpp b/src/system/kernel/debug/debug_parser.cpp index 1cb0a24c5a..f31e7504ef 100644 --- a/src/system/kernel/debug/debug_parser.cpp +++ b/src/system/kernel/debug/debug_parser.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de + * Copyright 2008-2009, Ingo Weinhold, ingo_weinhold@gmx.de * Copyright 2006, Stephan Aßmus, superstippi@gmx.de * Distributed under the terms of the MIT License. */ @@ -14,6 +14,8 @@ #include +#include + #include "debug_commands.h" #include "debug_variables.h" @@ -46,7 +48,6 @@ static const int kMaxTokenLength = 128; static const int kJumpBufferCount = 10; static const int kMaxArgumentCount = 64; -static const size_t kTemporaryStorageSize = 10240; static jmp_buf sJumpBuffers[kJumpBufferCount]; static int sNextJumpBufferIndex = 0; @@ -57,10 +58,6 @@ static int sExceptionPosition; static char sTempBuffer[128]; // for composing debug output etc. -// temporary storage for command argument vectors and the arguments itself -static uint8 sTemporaryStorage[kTemporaryStorageSize]; -static size_t sTemporaryStorageUsed = 0; - enum { TOKEN_ASSIGN_FLAG = 0x100, TOKEN_FLAGS = TOKEN_ASSIGN_FLAG, @@ -143,44 +140,16 @@ parse_exception(const char* message, int32 position) } -// #pragma mark - temporary storage - - static void* -allocate_temp_storage(size_t size) +checked_malloc(size_t size) { - // 8 byte align - size = (size + 7) & ~7; - - if (sTemporaryStorageUsed + size > kTemporaryStorageSize) { - parse_exception("out of temporary storage for command execution", -1); + void* address = debug_malloc(size); + if (address == NULL) { + parse_exception("out of memory for command execution", -1); return NULL; } - void* buffer = sTemporaryStorage + sTemporaryStorageUsed; - sTemporaryStorageUsed += size; - - return buffer; -} - - -static void -free_temp_storage(void* _buffer) -{ - uint8* buffer = (uint8*)_buffer; - - if (buffer == NULL) - return; - - // must be freed in the reverse allocation order - if (buffer < sTemporaryStorage - || buffer > sTemporaryStorage + sTemporaryStorageUsed) { - panic("Invalid pointer passed to free_temp_storage(): %p, temp " - "storage base: %p", buffer, sTemporaryStorage); - return; - } - - sTemporaryStorageUsed = buffer - sTemporaryStorage; + return address; } @@ -750,7 +719,7 @@ ExpressionParser::_ParseExpression(bool expectAssignment) uint64 ExpressionParser::_ParseCommandPipe(int& returnCode) { - debugger_command_pipe* pipe = (debugger_command_pipe*)allocate_temp_storage( + debugger_command_pipe* pipe = (debugger_command_pipe*)checked_malloc( sizeof(debugger_command_pipe)); pipe->segment_count = 0; @@ -773,7 +742,7 @@ ExpressionParser::_ParseCommandPipe(int& returnCode) // invoke the pipe returnCode = invoke_debugger_command_pipe(pipe); - free_temp_storage(pipe); + debug_free(pipe); return get_debug_variable("_", 0); } @@ -806,8 +775,7 @@ ExpressionParser::_ParseCommand(debugger_command_pipe_segment& segment) } // allocate temporary buffer for the argument vector - char** argv = (char**)allocate_temp_storage( - kMaxArgumentCount * sizeof(char*)); + char** argv = (char**)checked_malloc(kMaxArgumentCount * sizeof(char*)); int argc = 0; argv[argc++] = (char*)command->name; @@ -955,7 +923,7 @@ ExpressionParser::_AddArgument(int& argc, char** argv, const char* argument, if (length < 0) length = strlen(argument); length++; - char* buffer = (char*)allocate_temp_storage(length); + char* buffer = (char*)checked_malloc(length); strlcpy(buffer, argument, length); argv[argc++] = buffer; @@ -1169,9 +1137,8 @@ evaluate_debug_expression(const char* expression, uint64* _result, bool silent) bool success; uint64 result; - void* temporaryStorageMark = allocate_temp_storage(0); - // get a temporary storage mark, so we can cleanup everything that - // is allocated during the evaluation + DebugAllocPoolScope allocPoolScope; + // Will clean up all allocations when we return. if (setjmp(sJumpBuffers[sNextJumpBufferIndex++]) == 0) { result = ExpressionParser().EvaluateExpression(expression); @@ -1190,9 +1157,6 @@ evaluate_debug_expression(const char* expression, uint64* _result, bool silent) sNextJumpBufferIndex--; - // cleanup temp allocations - free_temp_storage(temporaryStorageMark); - if (success && _result != NULL) *_result = result; @@ -1210,9 +1174,8 @@ evaluate_debug_command(const char* commandLine) } int returnCode = 0; - void* temporaryStorageMark = allocate_temp_storage(0); - // get a temporary storage mark, so we can cleanup everything that - // is allocated during the evaluation + DebugAllocPoolScope allocPoolScope; + // Will clean up all allocations when we return. if (setjmp(sJumpBuffers[sNextJumpBufferIndex++]) == 0) { ExpressionParser().EvaluateCommand(commandLine, returnCode); @@ -1226,9 +1189,6 @@ evaluate_debug_command(const char* commandLine) sNextJumpBufferIndex--; - // cleanup temp allocations - free_temp_storage(temporaryStorageMark); - return returnCode; } @@ -1244,9 +1204,8 @@ parse_next_debug_command_argument(const char** expressionString, char* buffer, } status_t error; - void* temporaryStorageMark = allocate_temp_storage(0); - // get a temporary storage mark, so we can cleanup everything that - // is allocated during the evaluation + DebugAllocPoolScope allocPoolScope; + // Will clean up all allocations when we return. if (setjmp(sJumpBuffers[sNextJumpBufferIndex++]) == 0) { error = ExpressionParser().ParseNextCommandArgument(expressionString, @@ -1262,8 +1221,5 @@ parse_next_debug_command_argument(const char** expressionString, char* buffer, sNextJumpBufferIndex--; - // cleanup temp allocations - free_temp_storage(temporaryStorageMark); - return error; }