xref: /plan9/sys/src/libc/port/malloc.c (revision 7dd7cddf99dd7472612f1413b4da293630e6b1bc)
13e12c5d1SDavid du Colombier #include <u.h>
23e12c5d1SDavid du Colombier #include <libc.h>
3*7dd7cddfSDavid du Colombier #include <pool.h>
43e12c5d1SDavid du Colombier 
5*7dd7cddfSDavid du Colombier static void*	sbrkalloc(ulong);
6*7dd7cddfSDavid du Colombier static int		sbrkmerge(void*, void*);
7*7dd7cddfSDavid du Colombier static void		plock(Pool*);
8*7dd7cddfSDavid du Colombier static void		punlock(Pool*);
9*7dd7cddfSDavid du Colombier static void		pprint(Pool*, char*, ...);
10*7dd7cddfSDavid du Colombier static void		ppanic(Pool*, char*, ...);
11*7dd7cddfSDavid du Colombier 
12*7dd7cddfSDavid du Colombier typedef struct Private Private;
13*7dd7cddfSDavid du Colombier struct Private {
14*7dd7cddfSDavid du Colombier 	Lock		lk;
15*7dd7cddfSDavid du Colombier 	int		printfd;	/* gets debugging output if set */
163e12c5d1SDavid du Colombier };
173e12c5d1SDavid du Colombier 
18*7dd7cddfSDavid du Colombier Private sbrkmempriv;
19*7dd7cddfSDavid du Colombier 
20*7dd7cddfSDavid du Colombier static Pool sbrkmem = {
21*7dd7cddfSDavid du Colombier 	.name=		"sbrkmem",
22*7dd7cddfSDavid du Colombier 	.maxsize=		2*1024*1024*1024,
23*7dd7cddfSDavid du Colombier 	.minarena=	4*1024,
24*7dd7cddfSDavid du Colombier 	.quantum=	32,
25*7dd7cddfSDavid du Colombier 	.alloc=		sbrkalloc,
26*7dd7cddfSDavid du Colombier 	.merge=		sbrkmerge,
27*7dd7cddfSDavid du Colombier 	.flags=		0,
28*7dd7cddfSDavid du Colombier 
29*7dd7cddfSDavid du Colombier 	.lock=		plock,
30*7dd7cddfSDavid du Colombier 	.unlock=		punlock,
31*7dd7cddfSDavid du Colombier 	.print=		pprint,
32*7dd7cddfSDavid du Colombier 	.panic=		ppanic,
33*7dd7cddfSDavid du Colombier 	.private=		&sbrkmempriv,
343e12c5d1SDavid du Colombier };
35*7dd7cddfSDavid du Colombier Pool *mainmem = &sbrkmem;
36*7dd7cddfSDavid du Colombier Pool *imagmem = &sbrkmem;
373e12c5d1SDavid du Colombier 
38*7dd7cddfSDavid du Colombier /*
39*7dd7cddfSDavid du Colombier  * we do minimal bookkeeping so we can tell pool
40*7dd7cddfSDavid du Colombier  * whether two blocks are adjacent and thus mergeable.
41*7dd7cddfSDavid du Colombier  */
42*7dd7cddfSDavid du Colombier static void*
43*7dd7cddfSDavid du Colombier sbrkalloc(ulong n)
443e12c5d1SDavid du Colombier {
45*7dd7cddfSDavid du Colombier 	long *x;
463e12c5d1SDavid du Colombier 
47*7dd7cddfSDavid du Colombier 	n += 8;	/* two longs for us */
48*7dd7cddfSDavid du Colombier 	x = sbrk(n);
49*7dd7cddfSDavid du Colombier 	if((int)x == -1)
50*7dd7cddfSDavid du Colombier 		x = nil;
51*7dd7cddfSDavid du Colombier 	x[0] = (n+7)&~7;	/* sbrk rounds size up to mult. of 8 */
52*7dd7cddfSDavid du Colombier 	x[1] = 0xDeadBeef;
53*7dd7cddfSDavid du Colombier 	return x+2;
543e12c5d1SDavid du Colombier }
553e12c5d1SDavid du Colombier 
56*7dd7cddfSDavid du Colombier static int
57*7dd7cddfSDavid du Colombier sbrkmerge(void *x, void *y)
58*7dd7cddfSDavid du Colombier {
59*7dd7cddfSDavid du Colombier 	long *lx, *ly;
603e12c5d1SDavid du Colombier 
61*7dd7cddfSDavid du Colombier 	lx = x;
62*7dd7cddfSDavid du Colombier 	if(lx[-1] != 0xDeadBeef)
633e12c5d1SDavid du Colombier 		abort();
643e12c5d1SDavid du Colombier 
65*7dd7cddfSDavid du Colombier 	if((uchar*)lx+lx[-2] == (uchar*)y) {
66*7dd7cddfSDavid du Colombier 		ly = y;
67*7dd7cddfSDavid du Colombier 		lx[-2] += ly[-2];
68*7dd7cddfSDavid du Colombier 		return 1;
693e12c5d1SDavid du Colombier 	}
70*7dd7cddfSDavid du Colombier 	return 0;
713e12c5d1SDavid du Colombier }
723e12c5d1SDavid du Colombier 
73*7dd7cddfSDavid du Colombier static void
74*7dd7cddfSDavid du Colombier plock(Pool *p)
75*7dd7cddfSDavid du Colombier {
76*7dd7cddfSDavid du Colombier 	Private *pv;
77*7dd7cddfSDavid du Colombier 	pv = p->private;
78*7dd7cddfSDavid du Colombier 	lock(&pv->lk);
79*7dd7cddfSDavid du Colombier }
803e12c5d1SDavid du Colombier 
81*7dd7cddfSDavid du Colombier static void
82*7dd7cddfSDavid du Colombier punlock(Pool *p)
83*7dd7cddfSDavid du Colombier {
84*7dd7cddfSDavid du Colombier 	Private *pv;
85*7dd7cddfSDavid du Colombier 	pv = p->private;
86*7dd7cddfSDavid du Colombier 	unlock(&pv->lk);
87*7dd7cddfSDavid du Colombier }
88*7dd7cddfSDavid du Colombier 
89*7dd7cddfSDavid du Colombier static int
90*7dd7cddfSDavid du Colombier checkenv(void)
91*7dd7cddfSDavid du Colombier {
92*7dd7cddfSDavid du Colombier 	int n, fd;
93*7dd7cddfSDavid du Colombier 	char buf[20];
94*7dd7cddfSDavid du Colombier 	fd = open("/env/MALLOCFD", OREAD);
95*7dd7cddfSDavid du Colombier 	if(fd < 0)
96*7dd7cddfSDavid du Colombier 		return -1;
97*7dd7cddfSDavid du Colombier 	if((n = read(fd, buf, sizeof buf)) < 0) {
98*7dd7cddfSDavid du Colombier 		close(fd);
99*7dd7cddfSDavid du Colombier 		return -1;
100*7dd7cddfSDavid du Colombier 	}
101*7dd7cddfSDavid du Colombier 	if(n >= sizeof buf)
102*7dd7cddfSDavid du Colombier 		n = sizeof(buf)-1;
103*7dd7cddfSDavid du Colombier 	buf[n] = 0;
104*7dd7cddfSDavid du Colombier 	n = atoi(buf);
105*7dd7cddfSDavid du Colombier 	if(n == 0)
106*7dd7cddfSDavid du Colombier 		n = -1;
107*7dd7cddfSDavid du Colombier 	return n;
108*7dd7cddfSDavid du Colombier }
109*7dd7cddfSDavid du Colombier 
110*7dd7cddfSDavid du Colombier static void
111*7dd7cddfSDavid du Colombier pprint(Pool *p, char *fmt, ...)
112*7dd7cddfSDavid du Colombier {
113*7dd7cddfSDavid du Colombier 	va_list v;
114*7dd7cddfSDavid du Colombier 	int n;
115*7dd7cddfSDavid du Colombier 	char buf[128];
116*7dd7cddfSDavid du Colombier 	char *msg;
117*7dd7cddfSDavid du Colombier 	Private *pv;
118*7dd7cddfSDavid du Colombier 
119*7dd7cddfSDavid du Colombier 	pv = p->private;
120*7dd7cddfSDavid du Colombier 	if(pv->printfd == 0)
121*7dd7cddfSDavid du Colombier 		pv->printfd = checkenv();
122*7dd7cddfSDavid du Colombier 
123*7dd7cddfSDavid du Colombier 	if(pv->printfd <= 0)
124*7dd7cddfSDavid du Colombier 		pv->printfd = 2;
125*7dd7cddfSDavid du Colombier 
126*7dd7cddfSDavid du Colombier 	msg = buf;
127*7dd7cddfSDavid du Colombier 	va_start(v, fmt);
128*7dd7cddfSDavid du Colombier 	n = doprint(buf, buf+sizeof buf, fmt, v)-msg;
129*7dd7cddfSDavid du Colombier 	write(pv->printfd, buf, n);
130*7dd7cddfSDavid du Colombier 	va_end(v);
131*7dd7cddfSDavid du Colombier }
132*7dd7cddfSDavid du Colombier 
133*7dd7cddfSDavid du Colombier static char panicbuf[256];
134*7dd7cddfSDavid du Colombier static void
135*7dd7cddfSDavid du Colombier ppanic(Pool *p, char *fmt, ...)
136*7dd7cddfSDavid du Colombier {
137*7dd7cddfSDavid du Colombier 	va_list v;
138*7dd7cddfSDavid du Colombier 	int n;
139*7dd7cddfSDavid du Colombier 	char *msg;
140*7dd7cddfSDavid du Colombier 	Private *pv;
141*7dd7cddfSDavid du Colombier 
142*7dd7cddfSDavid du Colombier 	pv = p->private;
143*7dd7cddfSDavid du Colombier 	assert(canlock(&pv->lk)==0);
144*7dd7cddfSDavid du Colombier 
145*7dd7cddfSDavid du Colombier 	if(pv->printfd == 0)
146*7dd7cddfSDavid du Colombier 		pv->printfd = checkenv();
147*7dd7cddfSDavid du Colombier 	if(pv->printfd <= 0)
148*7dd7cddfSDavid du Colombier 		pv->printfd = 2;
149*7dd7cddfSDavid du Colombier 
150*7dd7cddfSDavid du Colombier 	msg = panicbuf;
151*7dd7cddfSDavid du Colombier 	va_start(v, fmt);
152*7dd7cddfSDavid du Colombier 	n = doprint(msg, msg+sizeof panicbuf, fmt, v) - msg;
153*7dd7cddfSDavid du Colombier 	write(2, "panic: ", 7);
154*7dd7cddfSDavid du Colombier 	write(2, msg, n);
155*7dd7cddfSDavid du Colombier 	write(2, "\n", 1);
156*7dd7cddfSDavid du Colombier 	if(pv->printfd != 2){
157*7dd7cddfSDavid du Colombier 		write(pv->printfd, "panic: ", 7);
158*7dd7cddfSDavid du Colombier 		write(pv->printfd, msg, n);
159*7dd7cddfSDavid du Colombier 		write(pv->printfd, "\n", 1);
160*7dd7cddfSDavid du Colombier 	}
161*7dd7cddfSDavid du Colombier 	va_end(v);
162*7dd7cddfSDavid du Colombier 	unlock(&pv->lk);
163*7dd7cddfSDavid du Colombier 	abort();
164*7dd7cddfSDavid du Colombier }
165*7dd7cddfSDavid du Colombier 
166*7dd7cddfSDavid du Colombier /* - everything from here down should be the same in libc, libdebugmalloc, and the kernel - */
167*7dd7cddfSDavid du Colombier /* - except the code for malloc(), which alternately doesn't clear or does. - */
168*7dd7cddfSDavid du Colombier 
169*7dd7cddfSDavid du Colombier /*
170*7dd7cddfSDavid du Colombier  * Npadlong is the number of 32-bit longs to leave at the beginning of
171*7dd7cddfSDavid du Colombier  * each allocated buffer for our own bookkeeping.  We return to the callers
172*7dd7cddfSDavid du Colombier  * a pointer that points immediately after our bookkeeping area.  Incoming pointers
173*7dd7cddfSDavid du Colombier  * must be decremented by that much, and outgoing pointers incremented.
174*7dd7cddfSDavid du Colombier  * The malloc tag is stored at MallocOffset from the beginning of the block,
175*7dd7cddfSDavid du Colombier  * and the realloc tag at ReallocOffset.  The offsets are from the true beginning
176*7dd7cddfSDavid du Colombier  * of the block, not the beginning the caller sees.
177*7dd7cddfSDavid du Colombier  *
178*7dd7cddfSDavid du Colombier  * The extra if(Npadlong != 0) in various places is a hint for the compiler to
179*7dd7cddfSDavid du Colombier  * compile out function calls that would otherwise be no-ops.
180*7dd7cddfSDavid du Colombier  */
181*7dd7cddfSDavid du Colombier 
182*7dd7cddfSDavid du Colombier /*	non tracing
183*7dd7cddfSDavid du Colombier  *
184*7dd7cddfSDavid du Colombier enum {
185*7dd7cddfSDavid du Colombier 	Npadlong	= 0,
186*7dd7cddfSDavid du Colombier 	MallocOffset = 0,
187*7dd7cddfSDavid du Colombier 	ReallocOffset = 0,
188*7dd7cddfSDavid du Colombier };
189*7dd7cddfSDavid du Colombier  *
190*7dd7cddfSDavid du Colombier  */
191*7dd7cddfSDavid du Colombier 
192*7dd7cddfSDavid du Colombier /* tracing */
193*7dd7cddfSDavid du Colombier enum {
194*7dd7cddfSDavid du Colombier 	Npadlong	= 2,
195*7dd7cddfSDavid du Colombier 	MallocOffset = 0,
196*7dd7cddfSDavid du Colombier 	ReallocOffset = 1
197*7dd7cddfSDavid du Colombier };
198*7dd7cddfSDavid du Colombier 
199*7dd7cddfSDavid du Colombier void*
200*7dd7cddfSDavid du Colombier malloc(ulong size)
201*7dd7cddfSDavid du Colombier {
202*7dd7cddfSDavid du Colombier 	void *v;
203*7dd7cddfSDavid du Colombier 
204*7dd7cddfSDavid du Colombier 	v = poolalloc(mainmem, size+Npadlong*sizeof(ulong));
205*7dd7cddfSDavid du Colombier 	if(Npadlong && v != nil) {
206*7dd7cddfSDavid du Colombier 		v = (ulong*)v+Npadlong;
207*7dd7cddfSDavid du Colombier 		setmalloctag(v, getcallerpc(&size));
208*7dd7cddfSDavid du Colombier 		setrealloctag(v, 0);
209*7dd7cddfSDavid du Colombier 	}
210*7dd7cddfSDavid du Colombier 	return v;
2113e12c5d1SDavid du Colombier }
2123e12c5d1SDavid du Colombier 
2133e12c5d1SDavid du Colombier void*
214*7dd7cddfSDavid du Colombier mallocz(ulong size, int clr)
2153e12c5d1SDavid du Colombier {
216*7dd7cddfSDavid du Colombier 	void *v;
2173e12c5d1SDavid du Colombier 
218*7dd7cddfSDavid du Colombier 	v = poolalloc(mainmem, size+Npadlong*sizeof(ulong));
219*7dd7cddfSDavid du Colombier 	if(Npadlong && v != nil){
220*7dd7cddfSDavid du Colombier 		v = (ulong*)v+Npadlong;
221*7dd7cddfSDavid du Colombier 		setmalloctag(v, getcallerpc(&size));
222*7dd7cddfSDavid du Colombier 		setrealloctag(v, 0);
223*7dd7cddfSDavid du Colombier 	}
224*7dd7cddfSDavid du Colombier 	if(clr && v != nil)
225*7dd7cddfSDavid du Colombier 		memset(v, 0, size);
226*7dd7cddfSDavid du Colombier 	return v;
2273e12c5d1SDavid du Colombier }
2283e12c5d1SDavid du Colombier 
2293e12c5d1SDavid du Colombier void
230*7dd7cddfSDavid du Colombier free(void *v)
2313e12c5d1SDavid du Colombier {
232*7dd7cddfSDavid du Colombier 	if(v != nil)
233*7dd7cddfSDavid du Colombier 		poolfree(mainmem, (ulong*)v-Npadlong);
2343e12c5d1SDavid du Colombier }
2353e12c5d1SDavid du Colombier 
2363e12c5d1SDavid du Colombier void*
237*7dd7cddfSDavid du Colombier realloc(void *v, ulong size)
2383e12c5d1SDavid du Colombier {
239*7dd7cddfSDavid du Colombier 	void *nv;
2403e12c5d1SDavid du Colombier 
241*7dd7cddfSDavid du Colombier 	if(v == nil)
242*7dd7cddfSDavid du Colombier 		return malloc(size);
2433e12c5d1SDavid du Colombier 
244*7dd7cddfSDavid du Colombier 	if(size == 0){
245*7dd7cddfSDavid du Colombier 		free(v);
246*7dd7cddfSDavid du Colombier 		return nil;
247*7dd7cddfSDavid du Colombier 	}
2483e12c5d1SDavid du Colombier 
249*7dd7cddfSDavid du Colombier 	v = (ulong*)v-Npadlong;
250*7dd7cddfSDavid du Colombier 	size += Npadlong*sizeof(ulong);
2513e12c5d1SDavid du Colombier 
252*7dd7cddfSDavid du Colombier 	if(nv = poolrealloc(mainmem, v, size)){
253*7dd7cddfSDavid du Colombier 		nv = (ulong*)nv+Npadlong;
254*7dd7cddfSDavid du Colombier 		setrealloctag(nv, getcallerpc(&v));
255*7dd7cddfSDavid du Colombier 		if(v == nil)
256*7dd7cddfSDavid du Colombier 			setmalloctag(nv, getcallerpc(&v));
257*7dd7cddfSDavid du Colombier 	}
258*7dd7cddfSDavid du Colombier 	return nv;
259*7dd7cddfSDavid du Colombier }
2603e12c5d1SDavid du Colombier 
261*7dd7cddfSDavid du Colombier ulong
262*7dd7cddfSDavid du Colombier msize(void *v)
263*7dd7cddfSDavid du Colombier {
264*7dd7cddfSDavid du Colombier 	return poolmsize(mainmem, (ulong*)v-Npadlong)-Npadlong*sizeof(ulong);
265*7dd7cddfSDavid du Colombier }
266*7dd7cddfSDavid du Colombier 
267*7dd7cddfSDavid du Colombier void*
268*7dd7cddfSDavid du Colombier calloc(ulong n, ulong szelem)
269*7dd7cddfSDavid du Colombier {
270*7dd7cddfSDavid du Colombier 	void *v;
271*7dd7cddfSDavid du Colombier 	if(v = mallocz(n*szelem, 1))
272*7dd7cddfSDavid du Colombier 		setmalloctag(v, getcallerpc(&n));
273*7dd7cddfSDavid du Colombier 	return v;
274*7dd7cddfSDavid du Colombier }
275*7dd7cddfSDavid du Colombier 
276*7dd7cddfSDavid du Colombier void
277*7dd7cddfSDavid du Colombier setmalloctag(void *v, ulong pc)
278*7dd7cddfSDavid du Colombier {
279*7dd7cddfSDavid du Colombier 	ulong *u;
280*7dd7cddfSDavid du Colombier 	USED(v, pc);
281*7dd7cddfSDavid du Colombier 	if(Npadlong <= MallocOffset || v == nil)
282*7dd7cddfSDavid du Colombier 		return;
283*7dd7cddfSDavid du Colombier 	u = v;
284*7dd7cddfSDavid du Colombier 	u[-Npadlong+MallocOffset] = pc;
285*7dd7cddfSDavid du Colombier }
286*7dd7cddfSDavid du Colombier 
287*7dd7cddfSDavid du Colombier void
288*7dd7cddfSDavid du Colombier setrealloctag(void *v, ulong pc)
289*7dd7cddfSDavid du Colombier {
290*7dd7cddfSDavid du Colombier 	ulong *u;
291*7dd7cddfSDavid du Colombier 	USED(v, pc);
292*7dd7cddfSDavid du Colombier 	if(Npadlong <= ReallocOffset || v == nil)
293*7dd7cddfSDavid du Colombier 		return;
294*7dd7cddfSDavid du Colombier 	u = v;
295*7dd7cddfSDavid du Colombier 	u[-Npadlong+ReallocOffset] = pc;
296*7dd7cddfSDavid du Colombier }
297*7dd7cddfSDavid du Colombier 
298*7dd7cddfSDavid du Colombier ulong
299*7dd7cddfSDavid du Colombier getmalloctag(void *v)
300*7dd7cddfSDavid du Colombier {
301*7dd7cddfSDavid du Colombier 	USED(v);
302*7dd7cddfSDavid du Colombier 	if(Npadlong <= MallocOffset)
303*7dd7cddfSDavid du Colombier 		return ~0;
304*7dd7cddfSDavid du Colombier 	return ((ulong*)v)[-Npadlong+MallocOffset];
305*7dd7cddfSDavid du Colombier }
306*7dd7cddfSDavid du Colombier 
307*7dd7cddfSDavid du Colombier ulong
308*7dd7cddfSDavid du Colombier getrealloctag(void *v)
309*7dd7cddfSDavid du Colombier {
310*7dd7cddfSDavid du Colombier 	USED(v);
311*7dd7cddfSDavid du Colombier 	if(Npadlong <= ReallocOffset)
312*7dd7cddfSDavid du Colombier 		return ((ulong*)v)[-Npadlong+ReallocOffset];
313*7dd7cddfSDavid du Colombier 	return ~0;
314*7dd7cddfSDavid du Colombier }
315*7dd7cddfSDavid du Colombier 
316*7dd7cddfSDavid du Colombier void*
317*7dd7cddfSDavid du Colombier malloctopoolblock(void *v)
318*7dd7cddfSDavid du Colombier {
319*7dd7cddfSDavid du Colombier 	if(v == nil)
3203e12c5d1SDavid du Colombier 		return nil;
3213e12c5d1SDavid du Colombier 
322*7dd7cddfSDavid du Colombier 	return &((ulong*)v)[-Npadlong];
3233e12c5d1SDavid du Colombier }
324