/*
* Copyright 2009-2010, Ingo Weinhold, ingo_weinhold@gmx.de
* Distributed under the terms of the MIT License.
*/
#include <debug_heap.h>
#include <new>
#include <util/AutoLock.h>
#include <vm/vm.h>
#define INITIAL_DEBUG_HEAP_SIZE B_PAGE_SIZE
static char sInitialHeap[INITIAL_DEBUG_HEAP_SIZE] __attribute__ ((aligned (8)));
static void* sHeapBase = sInitialHeap;
static size_t sHeapSize = INITIAL_DEBUG_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("DebugAllocPool::Free(%p): bad address\n", address);
return;
}
// get header
allocation_header* header = (allocation_header*)address - 1;
uint32 index = header - fBase;
if (header->free) {
kprintf("DebugAllocPool::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_calloc(size_t num, size_t size)
{
size_t allocationSize = num * size;
void* allocation = debug_malloc(allocationSize);
if (allocation == NULL)
return NULL;
memset(allocation, 0, allocationSize);
return allocation;
}
void
debug_free(void* address)
{
if (address != NULL && sCurrentPool != NULL)
sCurrentPool->Free(address);
}
void
debug_heap_init()
{
// create the heap area
void* base;
virtual_address_restrictions virtualRestrictions = {};
virtualRestrictions.address_specification = B_ANY_KERNEL_ADDRESS;
physical_address_restrictions physicalRestrictions = {};
area_id area = create_area_etc(B_SYSTEM_TEAM, "kdebug heap", KDEBUG_HEAP,
B_FULL_LOCK, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
CREATE_AREA_DONT_WAIT, 0, &virtualRestrictions, &physicalRestrictions,
(void**)&base);
if (area < 0)
return;
// switch from the small static buffer to the area
InterruptsLocker locker;
sHeapBase = base;
sHeapSize = KDEBUG_HEAP;
}
↑ V717 It is suspicious to cast object of base class 'allocation_header' to derived class 'free_entry'.
↑ V717 It is suspicious to cast object of base class 'allocation_header' to derived class 'free_entry'.
↑ V717 It is suspicious to cast object of base class 'allocation_header' to derived class 'free_entry'.
↑ V717 It is suspicious to cast object of base class 'allocation_header' to derived class 'free_entry'.
↑ V717 It is suspicious to cast object of base class 'allocation_header' to derived class 'free_entry'.
↑ V717 It is suspicious to cast object of base class 'allocation_header' to derived class 'free_entry'.
↑ V717 It is suspicious to cast object of base class 'allocation_header' to derived class 'free_entry'.
↑ V717 It is suspicious to cast object of base class 'allocation_header' to derived class 'free_entry'.
↑ V717 It is suspicious to cast object of base class 'allocation_header' to derived class 'free_entry'.
↑ V717 It is suspicious to cast object of base class 'allocation_header' to derived class 'free_entry'.
↑ V717 It is suspicious to cast object of base class 'allocation_header' to derived class 'free_entry'.