1360d4974SIngo Weinhold /*
2a8ad734fSIngo Weinhold * Copyright 2009-2010, Ingo Weinhold, ingo_weinhold@gmx.de
3360d4974SIngo Weinhold * Distributed under the terms of the MIT License.
4360d4974SIngo Weinhold */
5360d4974SIngo Weinhold
67198f765SAxel Dörfler
7360d4974SIngo Weinhold #include <debug_heap.h>
8360d4974SIngo Weinhold
9360d4974SIngo Weinhold #include <new>
10360d4974SIngo Weinhold
11360d4974SIngo Weinhold #include <util/AutoLock.h>
127198f765SAxel Dörfler #include <vm/vm.h>
13360d4974SIngo Weinhold
14360d4974SIngo Weinhold
1524df6592SIngo Weinhold #define INITIAL_DEBUG_HEAP_SIZE B_PAGE_SIZE
16360d4974SIngo Weinhold
1724df6592SIngo Weinhold static char sInitialHeap[INITIAL_DEBUG_HEAP_SIZE] __attribute__ ((aligned (8)));
18360d4974SIngo Weinhold static void* sHeapBase = sInitialHeap;
1924df6592SIngo Weinhold static size_t sHeapSize = INITIAL_DEBUG_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 {
InitDebugAllocPool37360d4974SIngo 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
CreateChildPoolDebugAllocPool54360d4974SIngo 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
DestroyDebugAllocPool81360d4974SIngo 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
ParentDebugAllocPool89360d4974SIngo Weinhold DebugAllocPool* Parent() const
90360d4974SIngo Weinhold {
91360d4974SIngo Weinhold return fParent;
92360d4974SIngo Weinhold }
93360d4974SIngo Weinhold
AllocateDebugAllocPool94360d4974SIngo 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
FreeDebugAllocPool122360d4974SIngo 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) {
127fb2cbc78SIthamar R. Adema kprintf("DebugAllocPool::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) {
135fb2cbc78SIthamar R. Adema kprintf("DebugAllocPool::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:
_InsertFreeEntryDebugAllocPool168360d4974SIngo 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
_RemoveFreeEntryDebugAllocPool197360d4974SIngo 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*
create_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
delete_debug_alloc_pool(debug_alloc_pool * pool)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*
debug_malloc(size_t size)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
281fcc4ecb0SMichael Lotz void*
debug_calloc(size_t num,size_t size)282fcc4ecb0SMichael Lotz debug_calloc(size_t num, size_t size)
283fcc4ecb0SMichael Lotz {
284fcc4ecb0SMichael Lotz size_t allocationSize = num * size;
285fcc4ecb0SMichael Lotz void* allocation = debug_malloc(allocationSize);
286fcc4ecb0SMichael Lotz if (allocation == NULL)
287fcc4ecb0SMichael Lotz return NULL;
288fcc4ecb0SMichael Lotz
289fcc4ecb0SMichael Lotz memset(allocation, 0, allocationSize);
290fcc4ecb0SMichael Lotz return allocation;
291fcc4ecb0SMichael Lotz }
292fcc4ecb0SMichael Lotz
293fcc4ecb0SMichael Lotz
294360d4974SIngo Weinhold void
debug_free(void * address)295360d4974SIngo Weinhold debug_free(void* address)
296360d4974SIngo Weinhold {
297360d4974SIngo Weinhold if (address != NULL && sCurrentPool != NULL)
298360d4974SIngo Weinhold sCurrentPool->Free(address);
299360d4974SIngo Weinhold }
300360d4974SIngo Weinhold
301360d4974SIngo Weinhold
302360d4974SIngo Weinhold void
debug_heap_init()303360d4974SIngo Weinhold debug_heap_init()
304360d4974SIngo Weinhold {
305360d4974SIngo Weinhold // create the heap area
306360d4974SIngo Weinhold void* base;
307a8ad734fSIngo Weinhold virtual_address_restrictions virtualRestrictions = {};
308a8ad734fSIngo Weinhold virtualRestrictions.address_specification = B_ANY_KERNEL_ADDRESS;
309a8ad734fSIngo Weinhold physical_address_restrictions physicalRestrictions = {};
310a8ad734fSIngo Weinhold area_id area = create_area_etc(B_SYSTEM_TEAM, "kdebug heap", KDEBUG_HEAP,
311a8ad734fSIngo Weinhold B_FULL_LOCK, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
312*d1f280c8SHamish Morrison CREATE_AREA_DONT_WAIT, 0, &virtualRestrictions, &physicalRestrictions,
313a8ad734fSIngo Weinhold (void**)&base);
314360d4974SIngo Weinhold if (area < 0)
315360d4974SIngo Weinhold return;
316360d4974SIngo Weinhold
317360d4974SIngo Weinhold // switch from the small static buffer to the area
318360d4974SIngo Weinhold InterruptsLocker locker;
319360d4974SIngo Weinhold sHeapBase = base;
320360d4974SIngo Weinhold sHeapSize = KDEBUG_HEAP;
321360d4974SIngo Weinhold }
322