xref: /haiku/src/system/kernel/debug/debug_heap.cpp (revision 360d4974b96a6821dbf4b31e0662bfcd31f713bc)
1*360d4974SIngo Weinhold /*
2*360d4974SIngo Weinhold  * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de
3*360d4974SIngo Weinhold  * Distributed under the terms of the MIT License.
4*360d4974SIngo Weinhold  */
5*360d4974SIngo Weinhold 
6*360d4974SIngo Weinhold #include <debug_heap.h>
7*360d4974SIngo Weinhold 
8*360d4974SIngo Weinhold #include <new>
9*360d4974SIngo Weinhold 
10*360d4974SIngo Weinhold #include <util/AutoLock.h>
11*360d4974SIngo Weinhold 
12*360d4974SIngo Weinhold 
13*360d4974SIngo Weinhold #define INITIAL_HEAP_SIZE	B_PAGE_SIZE
14*360d4974SIngo Weinhold 
15*360d4974SIngo Weinhold static char sInitialHeap[INITIAL_HEAP_SIZE];
16*360d4974SIngo Weinhold static void* sHeapBase = sInitialHeap;
17*360d4974SIngo Weinhold static size_t sHeapSize = INITIAL_HEAP_SIZE;
18*360d4974SIngo Weinhold 
19*360d4974SIngo Weinhold const kdebug_alloc_t kdebug_alloc = {};
20*360d4974SIngo Weinhold 
21*360d4974SIngo Weinhold 
22*360d4974SIngo Weinhold struct allocation_header {
23*360d4974SIngo Weinhold 	uint32	size : 31;	// size in allocation_header units
24*360d4974SIngo Weinhold 	bool	free : 1;
25*360d4974SIngo Weinhold 	uint32	previous;
26*360d4974SIngo Weinhold };
27*360d4974SIngo Weinhold 
28*360d4974SIngo Weinhold struct free_entry : allocation_header {
29*360d4974SIngo Weinhold 	uint32	previous_free;
30*360d4974SIngo Weinhold 	uint32	next_free;
31*360d4974SIngo Weinhold };
32*360d4974SIngo Weinhold 
33*360d4974SIngo Weinhold 
34*360d4974SIngo Weinhold struct DebugAllocPool {
35*360d4974SIngo Weinhold 	void Init(void* heap, size_t heapSize)
36*360d4974SIngo Weinhold 	{
37*360d4974SIngo Weinhold 		fParent = NULL;
38*360d4974SIngo Weinhold 		fChild = NULL;
39*360d4974SIngo Weinhold 
40*360d4974SIngo Weinhold 		uint32 size = heapSize / 8;
41*360d4974SIngo Weinhold 		fBase = (allocation_header*)heap - 1;
42*360d4974SIngo Weinhold 		fEnd = size + 1;
43*360d4974SIngo Weinhold 		fFirstFree = 0;
44*360d4974SIngo Weinhold 		fLastFree = 0;
45*360d4974SIngo Weinhold 
46*360d4974SIngo Weinhold 		// add free entry spanning the whole area
47*360d4974SIngo Weinhold 		fBase[1].size = size - 1;
48*360d4974SIngo Weinhold 		fBase[1].previous = 0;
49*360d4974SIngo Weinhold 		_InsertFreeEntry(1);
50*360d4974SIngo Weinhold 	}
51*360d4974SIngo Weinhold 
52*360d4974SIngo Weinhold 	DebugAllocPool* CreateChildPool()
53*360d4974SIngo Weinhold 	{
54*360d4974SIngo Weinhold 		// do we already have a child pool?
55*360d4974SIngo Weinhold 		if (fChild != NULL)
56*360d4974SIngo Weinhold 			return NULL;
57*360d4974SIngo Weinhold 
58*360d4974SIngo Weinhold 		// create the pool object
59*360d4974SIngo Weinhold 		DebugAllocPool* pool
60*360d4974SIngo Weinhold 			= (DebugAllocPool*)Allocate(sizeof(DebugAllocPool));
61*360d4974SIngo Weinhold 		if (pool == NULL)
62*360d4974SIngo Weinhold 			return NULL;
63*360d4974SIngo Weinhold 
64*360d4974SIngo Weinhold 		// do we have enough free space?
65*360d4974SIngo Weinhold 		if (fLastFree == 0 || fBase[fLastFree].size < 2) {
66*360d4974SIngo Weinhold 			Free(pool);
67*360d4974SIngo Weinhold 			return NULL;
68*360d4974SIngo Weinhold 		}
69*360d4974SIngo Weinhold 
70*360d4974SIngo Weinhold 		allocation_header* header = &fBase[fLastFree];
71*360d4974SIngo Weinhold 		_RemoveFreeEntry(fLastFree);
72*360d4974SIngo Weinhold 
73*360d4974SIngo Weinhold 		pool->Init(header + 1, header->size * 8);
74*360d4974SIngo Weinhold 		pool->fParent = this;
75*360d4974SIngo Weinhold 
76*360d4974SIngo Weinhold 		return fChild = pool;
77*360d4974SIngo Weinhold 	}
78*360d4974SIngo Weinhold 
79*360d4974SIngo Weinhold 	void Destroy()
80*360d4974SIngo Weinhold 	{
81*360d4974SIngo Weinhold 		if (fParent != NULL) {
82*360d4974SIngo Weinhold 			fParent->fChild = NULL;
83*360d4974SIngo Weinhold 			fParent->Free(fBase + 1);
84*360d4974SIngo Weinhold 		}
85*360d4974SIngo Weinhold 	}
86*360d4974SIngo Weinhold 
87*360d4974SIngo Weinhold 	DebugAllocPool* Parent() const
88*360d4974SIngo Weinhold 	{
89*360d4974SIngo Weinhold 		return fParent;
90*360d4974SIngo Weinhold 	}
91*360d4974SIngo Weinhold 
92*360d4974SIngo Weinhold 	void* Allocate(size_t size)
93*360d4974SIngo Weinhold 	{
94*360d4974SIngo Weinhold 		size = (size + 7) / 8;
95*360d4974SIngo Weinhold 		uint32 index = fFirstFree;
96*360d4974SIngo Weinhold 		while (index != 0 && fBase[index].size < size)
97*360d4974SIngo Weinhold 			index = ((free_entry*)&fBase[index])->next_free;
98*360d4974SIngo Weinhold 
99*360d4974SIngo Weinhold 		if (index == 0)
100*360d4974SIngo Weinhold 			return NULL;
101*360d4974SIngo Weinhold 
102*360d4974SIngo Weinhold 		_RemoveFreeEntry(index);
103*360d4974SIngo Weinhold 
104*360d4974SIngo Weinhold 		// if the entry is big enough, we split it
105*360d4974SIngo Weinhold 		if (fBase[index].size - size >= 2) {
106*360d4974SIngo Weinhold 			uint32 next = index + 1 + size;
107*360d4974SIngo Weinhold 			uint32 nextNext = index + 1 + fBase[index].size;
108*360d4974SIngo Weinhold 			fBase[next].size = fBase[index].size - size - 1;
109*360d4974SIngo Weinhold 			fBase[next].previous = index;
110*360d4974SIngo Weinhold 			fBase[index].size = size;
111*360d4974SIngo Weinhold 			_InsertFreeEntry(next);
112*360d4974SIngo Weinhold 
113*360d4974SIngo Weinhold 			if (nextNext < fEnd)
114*360d4974SIngo Weinhold 				fBase[nextNext].previous = next;
115*360d4974SIngo Weinhold 		}
116*360d4974SIngo Weinhold 
117*360d4974SIngo Weinhold 		return &fBase[index + 1];
118*360d4974SIngo Weinhold 	}
119*360d4974SIngo Weinhold 
120*360d4974SIngo Weinhold 	void Free(void* address)
121*360d4974SIngo Weinhold 	{
122*360d4974SIngo Weinhold 		// check address
123*360d4974SIngo Weinhold 		if (((addr_t)address & 7) != 0 || address <= fBase + 1
124*360d4974SIngo Weinhold 			|| address >= fBase + fEnd) {
125*360d4974SIngo Weinhold 			kprintf("DebugAllocator::Free(%p): bad address\n", address);
126*360d4974SIngo Weinhold 			return;
127*360d4974SIngo Weinhold 		}
128*360d4974SIngo Weinhold 
129*360d4974SIngo Weinhold 		// get header
130*360d4974SIngo Weinhold 		allocation_header* header = (allocation_header*)address - 1;
131*360d4974SIngo Weinhold 		uint32 index = header - fBase;
132*360d4974SIngo Weinhold 		if (header->free) {
133*360d4974SIngo Weinhold 			kprintf("DebugAllocator::Free(%p): double free\n", address);
134*360d4974SIngo Weinhold 			return;
135*360d4974SIngo Weinhold 		}
136*360d4974SIngo Weinhold 
137*360d4974SIngo Weinhold 		uint32 next = index + 1 + header->size;
138*360d4974SIngo Weinhold 
139*360d4974SIngo Weinhold 		// join with previous, if possible
140*360d4974SIngo Weinhold 		if (index > 1 && fBase[header->previous].free) {
141*360d4974SIngo Weinhold 			uint32 previous = header->previous;
142*360d4974SIngo Weinhold 			_RemoveFreeEntry(previous);
143*360d4974SIngo Weinhold 
144*360d4974SIngo Weinhold 			fBase[previous].size += 1 + header->size;
145*360d4974SIngo Weinhold 			fBase[next].previous = previous;
146*360d4974SIngo Weinhold 
147*360d4974SIngo Weinhold 			index = previous;
148*360d4974SIngo Weinhold 			header = fBase + index;
149*360d4974SIngo Weinhold 		}
150*360d4974SIngo Weinhold 
151*360d4974SIngo Weinhold 		// join with next, if possible
152*360d4974SIngo Weinhold 		if (next < fEnd && fBase[next].free) {
153*360d4974SIngo Weinhold 			_RemoveFreeEntry(next);
154*360d4974SIngo Weinhold 
155*360d4974SIngo Weinhold 			header->size += 1 + fBase[next].size;
156*360d4974SIngo Weinhold 
157*360d4974SIngo Weinhold 			uint32 nextNext = index + 1 + header->size;
158*360d4974SIngo Weinhold 			if (nextNext < fEnd)
159*360d4974SIngo Weinhold 				fBase[nextNext].previous = index;
160*360d4974SIngo Weinhold 		}
161*360d4974SIngo Weinhold 
162*360d4974SIngo Weinhold 		_InsertFreeEntry(index);
163*360d4974SIngo Weinhold 	}
164*360d4974SIngo Weinhold 
165*360d4974SIngo Weinhold private:
166*360d4974SIngo Weinhold 	void _InsertFreeEntry(uint32 index)
167*360d4974SIngo Weinhold 	{
168*360d4974SIngo Weinhold 		// find the insertion point -- list is sorted by ascending size
169*360d4974SIngo Weinhold 		uint32 size = fBase[index].size;
170*360d4974SIngo Weinhold 		uint32 next = fFirstFree;
171*360d4974SIngo Weinhold 		while (next != 0 && size > fBase[next].size)
172*360d4974SIngo Weinhold 			next = ((free_entry*)&fBase[next])->next_free;
173*360d4974SIngo Weinhold 
174*360d4974SIngo Weinhold 		// insert
175*360d4974SIngo Weinhold 		uint32 previous;
176*360d4974SIngo Weinhold 		if (next != 0) {
177*360d4974SIngo Weinhold 			previous = ((free_entry*)&fBase[next])->previous_free;
178*360d4974SIngo Weinhold 			((free_entry*)&fBase[next])->previous_free = index;
179*360d4974SIngo Weinhold 		} else {
180*360d4974SIngo Weinhold 			previous = fLastFree;
181*360d4974SIngo Weinhold 			fLastFree = index;
182*360d4974SIngo Weinhold 		}
183*360d4974SIngo Weinhold 
184*360d4974SIngo Weinhold 		if (previous != 0)
185*360d4974SIngo Weinhold 			((free_entry*)&fBase[previous])->next_free = index;
186*360d4974SIngo Weinhold 		else
187*360d4974SIngo Weinhold 			fFirstFree = index;
188*360d4974SIngo Weinhold 
189*360d4974SIngo Weinhold 		((free_entry*)&fBase[index])->previous_free = previous;
190*360d4974SIngo Weinhold 		((free_entry*)&fBase[index])->next_free = next;
191*360d4974SIngo Weinhold 
192*360d4974SIngo Weinhold 		fBase[index].free = true;
193*360d4974SIngo Weinhold 	}
194*360d4974SIngo Weinhold 
195*360d4974SIngo Weinhold 	void _RemoveFreeEntry(uint32 index)
196*360d4974SIngo Weinhold 	{
197*360d4974SIngo Weinhold 		uint32 previous = ((free_entry*)&fBase[index])->previous_free;
198*360d4974SIngo Weinhold 		uint32 next = ((free_entry*)&fBase[index])->next_free;
199*360d4974SIngo Weinhold 
200*360d4974SIngo Weinhold 		if (previous != 0)
201*360d4974SIngo Weinhold 			((free_entry*)&fBase[previous])->next_free = next;
202*360d4974SIngo Weinhold 		else
203*360d4974SIngo Weinhold 			fFirstFree = next;
204*360d4974SIngo Weinhold 
205*360d4974SIngo Weinhold 		if (next != 0)
206*360d4974SIngo Weinhold 			((free_entry*)&fBase[next])->previous_free = previous;
207*360d4974SIngo Weinhold 		else
208*360d4974SIngo Weinhold 			fLastFree = previous;
209*360d4974SIngo Weinhold 
210*360d4974SIngo Weinhold 		fBase[index].free = false;
211*360d4974SIngo Weinhold 	}
212*360d4974SIngo Weinhold 
213*360d4974SIngo Weinhold private:
214*360d4974SIngo Weinhold 	DebugAllocPool*		fParent;
215*360d4974SIngo Weinhold 	DebugAllocPool*		fChild;
216*360d4974SIngo Weinhold 	allocation_header*	fBase;		// actually base - 1, so that index 0 is
217*360d4974SIngo Weinhold 									// invalid
218*360d4974SIngo Weinhold 	uint32				fEnd;
219*360d4974SIngo Weinhold 	uint32				fFirstFree;
220*360d4974SIngo Weinhold 	uint32				fLastFree;
221*360d4974SIngo Weinhold };
222*360d4974SIngo Weinhold 
223*360d4974SIngo Weinhold 
224*360d4974SIngo Weinhold static DebugAllocPool* sCurrentPool;
225*360d4974SIngo Weinhold static DebugAllocPool sInitialPool;
226*360d4974SIngo Weinhold 
227*360d4974SIngo Weinhold 
228*360d4974SIngo Weinhold debug_alloc_pool*
229*360d4974SIngo Weinhold create_debug_alloc_pool()
230*360d4974SIngo Weinhold {
231*360d4974SIngo Weinhold 	if (sCurrentPool == NULL) {
232*360d4974SIngo Weinhold 		sInitialPool.Init(sHeapBase, sHeapSize);
233*360d4974SIngo Weinhold 		sCurrentPool = &sInitialPool;
234*360d4974SIngo Weinhold 		return sCurrentPool;
235*360d4974SIngo Weinhold 	}
236*360d4974SIngo Weinhold 
237*360d4974SIngo Weinhold 	DebugAllocPool* pool = sCurrentPool->CreateChildPool();
238*360d4974SIngo Weinhold 	if (pool == NULL)
239*360d4974SIngo Weinhold 		return NULL;
240*360d4974SIngo Weinhold 
241*360d4974SIngo Weinhold 	sCurrentPool = pool;
242*360d4974SIngo Weinhold 	return sCurrentPool;
243*360d4974SIngo Weinhold }
244*360d4974SIngo Weinhold 
245*360d4974SIngo Weinhold 
246*360d4974SIngo Weinhold void
247*360d4974SIngo Weinhold delete_debug_alloc_pool(debug_alloc_pool* pool)
248*360d4974SIngo Weinhold {
249*360d4974SIngo Weinhold 	if (pool == NULL || sCurrentPool == NULL)
250*360d4974SIngo Weinhold 		return;
251*360d4974SIngo Weinhold 
252*360d4974SIngo Weinhold 	// find the pool in the hierarchy
253*360d4974SIngo Weinhold 	DebugAllocPool* otherPool = sCurrentPool;
254*360d4974SIngo Weinhold 	while (otherPool != NULL && otherPool != pool)
255*360d4974SIngo Weinhold 		otherPool = otherPool->Parent();
256*360d4974SIngo Weinhold 
257*360d4974SIngo Weinhold 	if (otherPool == NULL)
258*360d4974SIngo Weinhold 		return;
259*360d4974SIngo Weinhold 
260*360d4974SIngo Weinhold 	// destroy the pool
261*360d4974SIngo Weinhold 	sCurrentPool = pool->Parent();
262*360d4974SIngo Weinhold 	pool->Destroy();
263*360d4974SIngo Weinhold 
264*360d4974SIngo Weinhold 	if (pool != &sInitialPool)
265*360d4974SIngo Weinhold 		debug_free(pool);
266*360d4974SIngo Weinhold }
267*360d4974SIngo Weinhold 
268*360d4974SIngo Weinhold 
269*360d4974SIngo Weinhold void*
270*360d4974SIngo Weinhold debug_malloc(size_t size)
271*360d4974SIngo Weinhold {
272*360d4974SIngo Weinhold 	if (sCurrentPool == NULL)
273*360d4974SIngo Weinhold 		return NULL;
274*360d4974SIngo Weinhold 
275*360d4974SIngo Weinhold 	return sCurrentPool->Allocate(size);
276*360d4974SIngo Weinhold }
277*360d4974SIngo Weinhold 
278*360d4974SIngo Weinhold 
279*360d4974SIngo Weinhold void
280*360d4974SIngo Weinhold debug_free(void* address)
281*360d4974SIngo Weinhold {
282*360d4974SIngo Weinhold 	if (address != NULL && sCurrentPool != NULL)
283*360d4974SIngo Weinhold 		sCurrentPool->Free(address);
284*360d4974SIngo Weinhold }
285*360d4974SIngo Weinhold 
286*360d4974SIngo Weinhold 
287*360d4974SIngo Weinhold void
288*360d4974SIngo Weinhold debug_heap_init()
289*360d4974SIngo Weinhold {
290*360d4974SIngo Weinhold 	// create the heap area
291*360d4974SIngo Weinhold 	void* base;
292*360d4974SIngo Weinhold 	area_id area = create_area("kdebug heap", (void**)&base,
293*360d4974SIngo Weinhold 		B_ANY_KERNEL_ADDRESS, KDEBUG_HEAP, B_FULL_LOCK,
294*360d4974SIngo Weinhold 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
295*360d4974SIngo Weinhold 	if (area < 0)
296*360d4974SIngo Weinhold 		return;
297*360d4974SIngo Weinhold 
298*360d4974SIngo Weinhold 	// switch from the small static buffer to the area
299*360d4974SIngo Weinhold 	InterruptsLocker locker;
300*360d4974SIngo Weinhold 	sHeapBase = base;
301*360d4974SIngo Weinhold 	sHeapSize = KDEBUG_HEAP;
302*360d4974SIngo Weinhold }
303