xref: /haiku/src/system/kernel/debug/debug_heap.cpp (revision fb2cbc781efc73da07a9b98acb318d2b77bc9a3c)
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 
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) {
127*fb2cbc78SIthamar 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) {
135*fb2cbc78SIthamar 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:
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;
294a8ad734fSIngo Weinhold 	virtual_address_restrictions virtualRestrictions = {};
295a8ad734fSIngo Weinhold 	virtualRestrictions.address_specification = B_ANY_KERNEL_ADDRESS;
296a8ad734fSIngo Weinhold 	physical_address_restrictions physicalRestrictions = {};
297a8ad734fSIngo Weinhold 	area_id area = create_area_etc(B_SYSTEM_TEAM, "kdebug heap", KDEBUG_HEAP,
298a8ad734fSIngo Weinhold 		B_FULL_LOCK, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
299a8ad734fSIngo Weinhold 		CREATE_AREA_DONT_WAIT, &virtualRestrictions, &physicalRestrictions,
300a8ad734fSIngo Weinhold 		(void**)&base);
301360d4974SIngo Weinhold 	if (area < 0)
302360d4974SIngo Weinhold 		return;
303360d4974SIngo Weinhold 
304360d4974SIngo Weinhold 	// switch from the small static buffer to the area
305360d4974SIngo Weinhold 	InterruptsLocker locker;
306360d4974SIngo Weinhold 	sHeapBase = base;
307360d4974SIngo Weinhold 	sHeapSize = KDEBUG_HEAP;
308360d4974SIngo Weinhold }
309