Replaced the temporary storage used in the kernel debug command parser. We now

have a simple dedicated heap for the kernel debugger with stacked allocation
pools (deleting a pool frees all memory allocated in it). The heap should
eventually be used for all commands that need temporary storage too large for
the stack instead of each using its own static buffer.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@30949 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold 2009-06-03 12:28:49 +00:00
parent 5734ece852
commit 360d4974b9
7 changed files with 407 additions and 64 deletions

View File

@ -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

View File

@ -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 <debug.h>
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<typename Type>
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 */

View File

@ -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

View File

@ -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 <cpu.h>
#include <debug.h>
#include <debug_heap.h>
#include <debug_paranoia.h>
#include <driver_settings.h>
#include <frame_buffer_console.h>
@ -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);

View File

@ -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 <KernelExport.h>
#include <debug.h>
#include <debug_heap.h>
#include <lock.h>
#include <thread.h>
#include <util/AutoLock.h>
@ -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)

View File

@ -0,0 +1,302 @@
/*
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de
* Distributed under the terms of the MIT License.
*/
#include <debug_heap.h>
#include <new>
#include <util/AutoLock.h>
#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;
}

View File

@ -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 <KernelExport.h>
#include <debug_heap.h>
#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;
}