1360d4974SIngo Weinhold /* 2360d4974SIngo Weinhold * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de 3360d4974SIngo Weinhold * Distributed under the terms of the MIT License. 4360d4974SIngo Weinhold */ 5360d4974SIngo Weinhold 6*7198f765SAxel Dörfler 7360d4974SIngo Weinhold #include <debug_heap.h> 8360d4974SIngo Weinhold 9360d4974SIngo Weinhold #include <new> 10360d4974SIngo Weinhold 11360d4974SIngo Weinhold #include <util/AutoLock.h> 12*7198f765SAxel Dörfler #include <vm/vm.h> 13360d4974SIngo Weinhold 14360d4974SIngo Weinhold 15360d4974SIngo Weinhold #define INITIAL_HEAP_SIZE B_PAGE_SIZE 16360d4974SIngo Weinhold 17360d4974SIngo Weinhold static char sInitialHeap[INITIAL_HEAP_SIZE]; 18360d4974SIngo Weinhold static void* sHeapBase = sInitialHeap; 19360d4974SIngo Weinhold static size_t sHeapSize = INITIAL_HEAP_SIZE; 20360d4974SIngo Weinhold 21360d4974SIngo Weinhold const kdebug_alloc_t kdebug_alloc = {}; 22360d4974SIngo Weinhold 23360d4974SIngo Weinhold 24360d4974SIngo Weinhold struct allocation_header { 25360d4974SIngo Weinhold uint32 size : 31; // size in allocation_header units 26360d4974SIngo Weinhold bool free : 1; 27360d4974SIngo Weinhold uint32 previous; 28360d4974SIngo Weinhold }; 29360d4974SIngo Weinhold 30360d4974SIngo Weinhold struct free_entry : allocation_header { 31360d4974SIngo Weinhold uint32 previous_free; 32360d4974SIngo Weinhold uint32 next_free; 33360d4974SIngo Weinhold }; 34360d4974SIngo Weinhold 35360d4974SIngo Weinhold 36360d4974SIngo Weinhold struct DebugAllocPool { 37360d4974SIngo Weinhold void Init(void* heap, size_t heapSize) 38360d4974SIngo Weinhold { 39360d4974SIngo Weinhold fParent = NULL; 40360d4974SIngo Weinhold fChild = NULL; 41360d4974SIngo Weinhold 42360d4974SIngo Weinhold uint32 size = heapSize / 8; 43360d4974SIngo Weinhold fBase = (allocation_header*)heap - 1; 44360d4974SIngo Weinhold fEnd = size + 1; 45360d4974SIngo Weinhold fFirstFree = 0; 46360d4974SIngo Weinhold fLastFree = 0; 47360d4974SIngo Weinhold 48360d4974SIngo Weinhold // add free entry spanning the whole area 49360d4974SIngo Weinhold fBase[1].size = size - 1; 50360d4974SIngo Weinhold fBase[1].previous = 0; 51360d4974SIngo Weinhold _InsertFreeEntry(1); 52360d4974SIngo Weinhold } 53360d4974SIngo Weinhold 54360d4974SIngo Weinhold DebugAllocPool* CreateChildPool() 55360d4974SIngo Weinhold { 56360d4974SIngo Weinhold // do we already have a child pool? 57360d4974SIngo Weinhold if (fChild != NULL) 58360d4974SIngo Weinhold return NULL; 59360d4974SIngo Weinhold 60360d4974SIngo Weinhold // create the pool object 61360d4974SIngo Weinhold DebugAllocPool* pool 62360d4974SIngo Weinhold = (DebugAllocPool*)Allocate(sizeof(DebugAllocPool)); 63360d4974SIngo Weinhold if (pool == NULL) 64360d4974SIngo Weinhold return NULL; 65360d4974SIngo Weinhold 66360d4974SIngo Weinhold // do we have enough free space? 67360d4974SIngo Weinhold if (fLastFree == 0 || fBase[fLastFree].size < 2) { 68360d4974SIngo Weinhold Free(pool); 69360d4974SIngo Weinhold return NULL; 70360d4974SIngo Weinhold } 71360d4974SIngo Weinhold 72360d4974SIngo Weinhold allocation_header* header = &fBase[fLastFree]; 73360d4974SIngo Weinhold _RemoveFreeEntry(fLastFree); 74360d4974SIngo Weinhold 75360d4974SIngo Weinhold pool->Init(header + 1, header->size * 8); 76360d4974SIngo Weinhold pool->fParent = this; 77360d4974SIngo Weinhold 78360d4974SIngo Weinhold return fChild = pool; 79360d4974SIngo Weinhold } 80360d4974SIngo Weinhold 81360d4974SIngo Weinhold void Destroy() 82360d4974SIngo Weinhold { 83360d4974SIngo Weinhold if (fParent != NULL) { 84360d4974SIngo Weinhold fParent->fChild = NULL; 85360d4974SIngo Weinhold fParent->Free(fBase + 1); 86360d4974SIngo Weinhold } 87360d4974SIngo Weinhold } 88360d4974SIngo Weinhold 89360d4974SIngo Weinhold DebugAllocPool* Parent() const 90360d4974SIngo Weinhold { 91360d4974SIngo Weinhold return fParent; 92360d4974SIngo Weinhold } 93360d4974SIngo Weinhold 94360d4974SIngo Weinhold void* Allocate(size_t size) 95360d4974SIngo Weinhold { 96360d4974SIngo Weinhold size = (size + 7) / 8; 97360d4974SIngo Weinhold uint32 index = fFirstFree; 98360d4974SIngo Weinhold while (index != 0 && fBase[index].size < size) 99360d4974SIngo Weinhold index = ((free_entry*)&fBase[index])->next_free; 100360d4974SIngo Weinhold 101360d4974SIngo Weinhold if (index == 0) 102360d4974SIngo Weinhold return NULL; 103360d4974SIngo Weinhold 104360d4974SIngo Weinhold _RemoveFreeEntry(index); 105360d4974SIngo Weinhold 106360d4974SIngo Weinhold // if the entry is big enough, we split it 107360d4974SIngo Weinhold if (fBase[index].size - size >= 2) { 108360d4974SIngo Weinhold uint32 next = index + 1 + size; 109360d4974SIngo Weinhold uint32 nextNext = index + 1 + fBase[index].size; 110360d4974SIngo Weinhold fBase[next].size = fBase[index].size - size - 1; 111360d4974SIngo Weinhold fBase[next].previous = index; 112360d4974SIngo Weinhold fBase[index].size = size; 113360d4974SIngo Weinhold _InsertFreeEntry(next); 114360d4974SIngo Weinhold 115360d4974SIngo Weinhold if (nextNext < fEnd) 116360d4974SIngo Weinhold fBase[nextNext].previous = next; 117360d4974SIngo Weinhold } 118360d4974SIngo Weinhold 119360d4974SIngo Weinhold return &fBase[index + 1]; 120360d4974SIngo Weinhold } 121360d4974SIngo Weinhold 122360d4974SIngo Weinhold void Free(void* address) 123360d4974SIngo Weinhold { 124360d4974SIngo Weinhold // check address 125360d4974SIngo Weinhold if (((addr_t)address & 7) != 0 || address <= fBase + 1 126360d4974SIngo Weinhold || address >= fBase + fEnd) { 127360d4974SIngo Weinhold kprintf("DebugAllocator::Free(%p): bad address\n", address); 128360d4974SIngo Weinhold return; 129360d4974SIngo Weinhold } 130360d4974SIngo Weinhold 131360d4974SIngo Weinhold // get header 132360d4974SIngo Weinhold allocation_header* header = (allocation_header*)address - 1; 133360d4974SIngo Weinhold uint32 index = header - fBase; 134360d4974SIngo Weinhold if (header->free) { 135360d4974SIngo Weinhold kprintf("DebugAllocator::Free(%p): double free\n", address); 136360d4974SIngo Weinhold return; 137360d4974SIngo Weinhold } 138360d4974SIngo Weinhold 139360d4974SIngo Weinhold uint32 next = index + 1 + header->size; 140360d4974SIngo Weinhold 141360d4974SIngo Weinhold // join with previous, if possible 142360d4974SIngo Weinhold if (index > 1 && fBase[header->previous].free) { 143360d4974SIngo Weinhold uint32 previous = header->previous; 144360d4974SIngo Weinhold _RemoveFreeEntry(previous); 145360d4974SIngo Weinhold 146360d4974SIngo Weinhold fBase[previous].size += 1 + header->size; 147360d4974SIngo Weinhold fBase[next].previous = previous; 148360d4974SIngo Weinhold 149360d4974SIngo Weinhold index = previous; 150360d4974SIngo Weinhold header = fBase + index; 151360d4974SIngo Weinhold } 152360d4974SIngo Weinhold 153360d4974SIngo Weinhold // join with next, if possible 154360d4974SIngo Weinhold if (next < fEnd && fBase[next].free) { 155360d4974SIngo Weinhold _RemoveFreeEntry(next); 156360d4974SIngo Weinhold 157360d4974SIngo Weinhold header->size += 1 + fBase[next].size; 158360d4974SIngo Weinhold 159360d4974SIngo Weinhold uint32 nextNext = index + 1 + header->size; 160360d4974SIngo Weinhold if (nextNext < fEnd) 161360d4974SIngo Weinhold fBase[nextNext].previous = index; 162360d4974SIngo Weinhold } 163360d4974SIngo Weinhold 164360d4974SIngo Weinhold _InsertFreeEntry(index); 165360d4974SIngo Weinhold } 166360d4974SIngo Weinhold 167360d4974SIngo Weinhold private: 168360d4974SIngo Weinhold void _InsertFreeEntry(uint32 index) 169360d4974SIngo Weinhold { 170360d4974SIngo Weinhold // find the insertion point -- list is sorted by ascending size 171360d4974SIngo Weinhold uint32 size = fBase[index].size; 172360d4974SIngo Weinhold uint32 next = fFirstFree; 173360d4974SIngo Weinhold while (next != 0 && size > fBase[next].size) 174360d4974SIngo Weinhold next = ((free_entry*)&fBase[next])->next_free; 175360d4974SIngo Weinhold 176360d4974SIngo Weinhold // insert 177360d4974SIngo Weinhold uint32 previous; 178360d4974SIngo Weinhold if (next != 0) { 179360d4974SIngo Weinhold previous = ((free_entry*)&fBase[next])->previous_free; 180360d4974SIngo Weinhold ((free_entry*)&fBase[next])->previous_free = index; 181360d4974SIngo Weinhold } else { 182360d4974SIngo Weinhold previous = fLastFree; 183360d4974SIngo Weinhold fLastFree = index; 184360d4974SIngo Weinhold } 185360d4974SIngo Weinhold 186360d4974SIngo Weinhold if (previous != 0) 187360d4974SIngo Weinhold ((free_entry*)&fBase[previous])->next_free = index; 188360d4974SIngo Weinhold else 189360d4974SIngo Weinhold fFirstFree = index; 190360d4974SIngo Weinhold 191360d4974SIngo Weinhold ((free_entry*)&fBase[index])->previous_free = previous; 192360d4974SIngo Weinhold ((free_entry*)&fBase[index])->next_free = next; 193360d4974SIngo Weinhold 194360d4974SIngo Weinhold fBase[index].free = true; 195360d4974SIngo Weinhold } 196360d4974SIngo Weinhold 197360d4974SIngo Weinhold void _RemoveFreeEntry(uint32 index) 198360d4974SIngo Weinhold { 199360d4974SIngo Weinhold uint32 previous = ((free_entry*)&fBase[index])->previous_free; 200360d4974SIngo Weinhold uint32 next = ((free_entry*)&fBase[index])->next_free; 201360d4974SIngo Weinhold 202360d4974SIngo Weinhold if (previous != 0) 203360d4974SIngo Weinhold ((free_entry*)&fBase[previous])->next_free = next; 204360d4974SIngo Weinhold else 205360d4974SIngo Weinhold fFirstFree = next; 206360d4974SIngo Weinhold 207360d4974SIngo Weinhold if (next != 0) 208360d4974SIngo Weinhold ((free_entry*)&fBase[next])->previous_free = previous; 209360d4974SIngo Weinhold else 210360d4974SIngo Weinhold fLastFree = previous; 211360d4974SIngo Weinhold 212360d4974SIngo Weinhold fBase[index].free = false; 213360d4974SIngo Weinhold } 214360d4974SIngo Weinhold 215360d4974SIngo Weinhold private: 216360d4974SIngo Weinhold DebugAllocPool* fParent; 217360d4974SIngo Weinhold DebugAllocPool* fChild; 218360d4974SIngo Weinhold allocation_header* fBase; // actually base - 1, so that index 0 is 219360d4974SIngo Weinhold // invalid 220360d4974SIngo Weinhold uint32 fEnd; 221360d4974SIngo Weinhold uint32 fFirstFree; 222360d4974SIngo Weinhold uint32 fLastFree; 223360d4974SIngo Weinhold }; 224360d4974SIngo Weinhold 225360d4974SIngo Weinhold 226360d4974SIngo Weinhold static DebugAllocPool* sCurrentPool; 227360d4974SIngo Weinhold static DebugAllocPool sInitialPool; 228360d4974SIngo Weinhold 229360d4974SIngo Weinhold 230360d4974SIngo Weinhold debug_alloc_pool* 231360d4974SIngo Weinhold create_debug_alloc_pool() 232360d4974SIngo Weinhold { 233360d4974SIngo Weinhold if (sCurrentPool == NULL) { 234360d4974SIngo Weinhold sInitialPool.Init(sHeapBase, sHeapSize); 235360d4974SIngo Weinhold sCurrentPool = &sInitialPool; 236360d4974SIngo Weinhold return sCurrentPool; 237360d4974SIngo Weinhold } 238360d4974SIngo Weinhold 239360d4974SIngo Weinhold DebugAllocPool* pool = sCurrentPool->CreateChildPool(); 240360d4974SIngo Weinhold if (pool == NULL) 241360d4974SIngo Weinhold return NULL; 242360d4974SIngo Weinhold 243360d4974SIngo Weinhold sCurrentPool = pool; 244360d4974SIngo Weinhold return sCurrentPool; 245360d4974SIngo Weinhold } 246360d4974SIngo Weinhold 247360d4974SIngo Weinhold 248360d4974SIngo Weinhold void 249360d4974SIngo Weinhold delete_debug_alloc_pool(debug_alloc_pool* pool) 250360d4974SIngo Weinhold { 251360d4974SIngo Weinhold if (pool == NULL || sCurrentPool == NULL) 252360d4974SIngo Weinhold return; 253360d4974SIngo Weinhold 254360d4974SIngo Weinhold // find the pool in the hierarchy 255360d4974SIngo Weinhold DebugAllocPool* otherPool = sCurrentPool; 256360d4974SIngo Weinhold while (otherPool != NULL && otherPool != pool) 257360d4974SIngo Weinhold otherPool = otherPool->Parent(); 258360d4974SIngo Weinhold 259360d4974SIngo Weinhold if (otherPool == NULL) 260360d4974SIngo Weinhold return; 261360d4974SIngo Weinhold 262360d4974SIngo Weinhold // destroy the pool 263360d4974SIngo Weinhold sCurrentPool = pool->Parent(); 264360d4974SIngo Weinhold pool->Destroy(); 265360d4974SIngo Weinhold 266360d4974SIngo Weinhold if (pool != &sInitialPool) 267360d4974SIngo Weinhold debug_free(pool); 268360d4974SIngo Weinhold } 269360d4974SIngo Weinhold 270360d4974SIngo Weinhold 271360d4974SIngo Weinhold void* 272360d4974SIngo Weinhold debug_malloc(size_t size) 273360d4974SIngo Weinhold { 274360d4974SIngo Weinhold if (sCurrentPool == NULL) 275360d4974SIngo Weinhold return NULL; 276360d4974SIngo Weinhold 277360d4974SIngo Weinhold return sCurrentPool->Allocate(size); 278360d4974SIngo Weinhold } 279360d4974SIngo Weinhold 280360d4974SIngo Weinhold 281360d4974SIngo Weinhold void 282360d4974SIngo Weinhold debug_free(void* address) 283360d4974SIngo Weinhold { 284360d4974SIngo Weinhold if (address != NULL && sCurrentPool != NULL) 285360d4974SIngo Weinhold sCurrentPool->Free(address); 286360d4974SIngo Weinhold } 287360d4974SIngo Weinhold 288360d4974SIngo Weinhold 289360d4974SIngo Weinhold void 290360d4974SIngo Weinhold debug_heap_init() 291360d4974SIngo Weinhold { 292360d4974SIngo Weinhold // create the heap area 293360d4974SIngo Weinhold void* base; 294*7198f765SAxel Dörfler area_id area = create_area_etc(B_SYSTEM_TEAM, "kdebug heap", (void**)&base, 295360d4974SIngo Weinhold B_ANY_KERNEL_ADDRESS, KDEBUG_HEAP, B_FULL_LOCK, 296*7198f765SAxel Dörfler B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, 0, CREATE_AREA_DONT_WAIT); 297360d4974SIngo Weinhold if (area < 0) 298360d4974SIngo Weinhold return; 299360d4974SIngo Weinhold 300360d4974SIngo Weinhold // switch from the small static buffer to the area 301360d4974SIngo Weinhold InterruptsLocker locker; 302360d4974SIngo Weinhold sHeapBase = base; 303360d4974SIngo Weinhold sHeapSize = KDEBUG_HEAP; 304360d4974SIngo Weinhold } 305