xref: /plan9/sys/src/libc/port/malloc.c (revision 144145943e440fe30c4e4512a0856f5e3d5ca4be)
13e12c5d1SDavid du Colombier #include <u.h>
23e12c5d1SDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <pool.h>
43e12c5d1SDavid du Colombier 
57dd7cddfSDavid du Colombier static void*	sbrkalloc(ulong);
67dd7cddfSDavid du Colombier static int		sbrkmerge(void*, void*);
77dd7cddfSDavid du Colombier static void		plock(Pool*);
87dd7cddfSDavid du Colombier static void		punlock(Pool*);
97dd7cddfSDavid du Colombier static void		pprint(Pool*, char*, ...);
107dd7cddfSDavid du Colombier static void		ppanic(Pool*, char*, ...);
117dd7cddfSDavid du Colombier 
127dd7cddfSDavid du Colombier typedef struct Private Private;
137dd7cddfSDavid du Colombier struct Private {
147dd7cddfSDavid du Colombier 	Lock		lk;
157dd7cddfSDavid du Colombier 	int		printfd;	/* gets debugging output if set */
163e12c5d1SDavid du Colombier };
173e12c5d1SDavid du Colombier 
187dd7cddfSDavid du Colombier Private sbrkmempriv;
197dd7cddfSDavid du Colombier 
207dd7cddfSDavid du Colombier static Pool sbrkmem = {
217dd7cddfSDavid du Colombier 	.name=		"sbrkmem",
22*14414594SDavid du Colombier 	.maxsize=		2UL*1024*1024*1024,
237dd7cddfSDavid du Colombier 	.minarena=	4*1024,
247dd7cddfSDavid du Colombier 	.quantum=	32,
257dd7cddfSDavid du Colombier 	.alloc=		sbrkalloc,
267dd7cddfSDavid du Colombier 	.merge=		sbrkmerge,
277dd7cddfSDavid du Colombier 	.flags=		0,
287dd7cddfSDavid du Colombier 
297dd7cddfSDavid du Colombier 	.lock=		plock,
307dd7cddfSDavid du Colombier 	.unlock=		punlock,
317dd7cddfSDavid du Colombier 	.print=		pprint,
327dd7cddfSDavid du Colombier 	.panic=		ppanic,
337dd7cddfSDavid du Colombier 	.private=		&sbrkmempriv,
343e12c5d1SDavid du Colombier };
357dd7cddfSDavid du Colombier Pool *mainmem = &sbrkmem;
367dd7cddfSDavid du Colombier Pool *imagmem = &sbrkmem;
373e12c5d1SDavid du Colombier 
387dd7cddfSDavid du Colombier /*
397dd7cddfSDavid du Colombier  * we do minimal bookkeeping so we can tell pool
407dd7cddfSDavid du Colombier  * whether two blocks are adjacent and thus mergeable.
417dd7cddfSDavid du Colombier  */
427dd7cddfSDavid du Colombier static void*
437dd7cddfSDavid du Colombier sbrkalloc(ulong n)
443e12c5d1SDavid du Colombier {
457dd7cddfSDavid du Colombier 	long *x;
463e12c5d1SDavid du Colombier 
477dd7cddfSDavid du Colombier 	n += 8;	/* two longs for us */
487dd7cddfSDavid du Colombier 	x = sbrk(n);
497dd7cddfSDavid du Colombier 	if((int)x == -1)
507dd7cddfSDavid du Colombier 		x = nil;
517dd7cddfSDavid du Colombier 	x[0] = (n+7)&~7;	/* sbrk rounds size up to mult. of 8 */
527dd7cddfSDavid du Colombier 	x[1] = 0xDeadBeef;
537dd7cddfSDavid du Colombier 	return x+2;
543e12c5d1SDavid du Colombier }
553e12c5d1SDavid du Colombier 
567dd7cddfSDavid du Colombier static int
577dd7cddfSDavid du Colombier sbrkmerge(void *x, void *y)
587dd7cddfSDavid du Colombier {
597dd7cddfSDavid du Colombier 	long *lx, *ly;
603e12c5d1SDavid du Colombier 
617dd7cddfSDavid du Colombier 	lx = x;
627dd7cddfSDavid du Colombier 	if(lx[-1] != 0xDeadBeef)
633e12c5d1SDavid du Colombier 		abort();
643e12c5d1SDavid du Colombier 
657dd7cddfSDavid du Colombier 	if((uchar*)lx+lx[-2] == (uchar*)y) {
667dd7cddfSDavid du Colombier 		ly = y;
677dd7cddfSDavid du Colombier 		lx[-2] += ly[-2];
687dd7cddfSDavid du Colombier 		return 1;
693e12c5d1SDavid du Colombier 	}
707dd7cddfSDavid du Colombier 	return 0;
713e12c5d1SDavid du Colombier }
723e12c5d1SDavid du Colombier 
737dd7cddfSDavid du Colombier static void
747dd7cddfSDavid du Colombier plock(Pool *p)
757dd7cddfSDavid du Colombier {
767dd7cddfSDavid du Colombier 	Private *pv;
777dd7cddfSDavid du Colombier 	pv = p->private;
787dd7cddfSDavid du Colombier 	lock(&pv->lk);
797dd7cddfSDavid du Colombier }
803e12c5d1SDavid du Colombier 
817dd7cddfSDavid du Colombier static void
827dd7cddfSDavid du Colombier punlock(Pool *p)
837dd7cddfSDavid du Colombier {
847dd7cddfSDavid du Colombier 	Private *pv;
857dd7cddfSDavid du Colombier 	pv = p->private;
867dd7cddfSDavid du Colombier 	unlock(&pv->lk);
877dd7cddfSDavid du Colombier }
887dd7cddfSDavid du Colombier 
897dd7cddfSDavid du Colombier static int
907dd7cddfSDavid du Colombier checkenv(void)
917dd7cddfSDavid du Colombier {
927dd7cddfSDavid du Colombier 	int n, fd;
937dd7cddfSDavid du Colombier 	char buf[20];
947dd7cddfSDavid du Colombier 	fd = open("/env/MALLOCFD", OREAD);
957dd7cddfSDavid du Colombier 	if(fd < 0)
967dd7cddfSDavid du Colombier 		return -1;
977dd7cddfSDavid du Colombier 	if((n = read(fd, buf, sizeof buf)) < 0) {
987dd7cddfSDavid du Colombier 		close(fd);
997dd7cddfSDavid du Colombier 		return -1;
1007dd7cddfSDavid du Colombier 	}
1017dd7cddfSDavid du Colombier 	if(n >= sizeof buf)
1027dd7cddfSDavid du Colombier 		n = sizeof(buf)-1;
1037dd7cddfSDavid du Colombier 	buf[n] = 0;
1047dd7cddfSDavid du Colombier 	n = atoi(buf);
1057dd7cddfSDavid du Colombier 	if(n == 0)
1067dd7cddfSDavid du Colombier 		n = -1;
1077dd7cddfSDavid du Colombier 	return n;
1087dd7cddfSDavid du Colombier }
1097dd7cddfSDavid du Colombier 
1107dd7cddfSDavid du Colombier static void
1117dd7cddfSDavid du Colombier pprint(Pool *p, char *fmt, ...)
1127dd7cddfSDavid du Colombier {
1137dd7cddfSDavid du Colombier 	va_list v;
1147dd7cddfSDavid du Colombier 	int n;
1157dd7cddfSDavid du Colombier 	char buf[128];
1167dd7cddfSDavid du Colombier 	char *msg;
1177dd7cddfSDavid du Colombier 	Private *pv;
1187dd7cddfSDavid du Colombier 
1197dd7cddfSDavid du Colombier 	pv = p->private;
1207dd7cddfSDavid du Colombier 	if(pv->printfd == 0)
1217dd7cddfSDavid du Colombier 		pv->printfd = checkenv();
1227dd7cddfSDavid du Colombier 
1237dd7cddfSDavid du Colombier 	if(pv->printfd <= 0)
1247dd7cddfSDavid du Colombier 		pv->printfd = 2;
1257dd7cddfSDavid du Colombier 
1267dd7cddfSDavid du Colombier 	msg = buf;
1277dd7cddfSDavid du Colombier 	va_start(v, fmt);
1287dd7cddfSDavid du Colombier 	n = doprint(buf, buf+sizeof buf, fmt, v)-msg;
1297dd7cddfSDavid du Colombier 	write(pv->printfd, buf, n);
1307dd7cddfSDavid du Colombier 	va_end(v);
1317dd7cddfSDavid du Colombier }
1327dd7cddfSDavid du Colombier 
1337dd7cddfSDavid du Colombier static char panicbuf[256];
1347dd7cddfSDavid du Colombier static void
1357dd7cddfSDavid du Colombier ppanic(Pool *p, char *fmt, ...)
1367dd7cddfSDavid du Colombier {
1377dd7cddfSDavid du Colombier 	va_list v;
1387dd7cddfSDavid du Colombier 	int n;
1397dd7cddfSDavid du Colombier 	char *msg;
1407dd7cddfSDavid du Colombier 	Private *pv;
1417dd7cddfSDavid du Colombier 
1427dd7cddfSDavid du Colombier 	pv = p->private;
1437dd7cddfSDavid du Colombier 	assert(canlock(&pv->lk)==0);
1447dd7cddfSDavid du Colombier 
1457dd7cddfSDavid du Colombier 	if(pv->printfd == 0)
1467dd7cddfSDavid du Colombier 		pv->printfd = checkenv();
1477dd7cddfSDavid du Colombier 	if(pv->printfd <= 0)
1487dd7cddfSDavid du Colombier 		pv->printfd = 2;
1497dd7cddfSDavid du Colombier 
1507dd7cddfSDavid du Colombier 	msg = panicbuf;
1517dd7cddfSDavid du Colombier 	va_start(v, fmt);
1527dd7cddfSDavid du Colombier 	n = doprint(msg, msg+sizeof panicbuf, fmt, v) - msg;
1537dd7cddfSDavid du Colombier 	write(2, "panic: ", 7);
1547dd7cddfSDavid du Colombier 	write(2, msg, n);
1557dd7cddfSDavid du Colombier 	write(2, "\n", 1);
1567dd7cddfSDavid du Colombier 	if(pv->printfd != 2){
1577dd7cddfSDavid du Colombier 		write(pv->printfd, "panic: ", 7);
1587dd7cddfSDavid du Colombier 		write(pv->printfd, msg, n);
1597dd7cddfSDavid du Colombier 		write(pv->printfd, "\n", 1);
1607dd7cddfSDavid du Colombier 	}
1617dd7cddfSDavid du Colombier 	va_end(v);
1627dd7cddfSDavid du Colombier 	unlock(&pv->lk);
1637dd7cddfSDavid du Colombier 	abort();
1647dd7cddfSDavid du Colombier }
1657dd7cddfSDavid du Colombier 
1667dd7cddfSDavid du Colombier /* - everything from here down should be the same in libc, libdebugmalloc, and the kernel - */
1677dd7cddfSDavid du Colombier /* - except the code for malloc(), which alternately doesn't clear or does. - */
1687dd7cddfSDavid du Colombier 
1697dd7cddfSDavid du Colombier /*
1707dd7cddfSDavid du Colombier  * Npadlong is the number of 32-bit longs to leave at the beginning of
1717dd7cddfSDavid du Colombier  * each allocated buffer for our own bookkeeping.  We return to the callers
1727dd7cddfSDavid du Colombier  * a pointer that points immediately after our bookkeeping area.  Incoming pointers
1737dd7cddfSDavid du Colombier  * must be decremented by that much, and outgoing pointers incremented.
1747dd7cddfSDavid du Colombier  * The malloc tag is stored at MallocOffset from the beginning of the block,
1757dd7cddfSDavid du Colombier  * and the realloc tag at ReallocOffset.  The offsets are from the true beginning
1767dd7cddfSDavid du Colombier  * of the block, not the beginning the caller sees.
1777dd7cddfSDavid du Colombier  *
1787dd7cddfSDavid du Colombier  * The extra if(Npadlong != 0) in various places is a hint for the compiler to
1797dd7cddfSDavid du Colombier  * compile out function calls that would otherwise be no-ops.
1807dd7cddfSDavid du Colombier  */
1817dd7cddfSDavid du Colombier 
1827dd7cddfSDavid du Colombier /*	non tracing
1837dd7cddfSDavid du Colombier  *
1847dd7cddfSDavid du Colombier enum {
1857dd7cddfSDavid du Colombier 	Npadlong	= 0,
1867dd7cddfSDavid du Colombier 	MallocOffset = 0,
1877dd7cddfSDavid du Colombier 	ReallocOffset = 0,
1887dd7cddfSDavid du Colombier };
1897dd7cddfSDavid du Colombier  *
1907dd7cddfSDavid du Colombier  */
1917dd7cddfSDavid du Colombier 
1927dd7cddfSDavid du Colombier /* tracing */
1937dd7cddfSDavid du Colombier enum {
1947dd7cddfSDavid du Colombier 	Npadlong	= 2,
1957dd7cddfSDavid du Colombier 	MallocOffset = 0,
1967dd7cddfSDavid du Colombier 	ReallocOffset = 1
1977dd7cddfSDavid du Colombier };
1987dd7cddfSDavid du Colombier 
1997dd7cddfSDavid du Colombier void*
2007dd7cddfSDavid du Colombier malloc(ulong size)
2017dd7cddfSDavid du Colombier {
2027dd7cddfSDavid du Colombier 	void *v;
2037dd7cddfSDavid du Colombier 
2047dd7cddfSDavid du Colombier 	v = poolalloc(mainmem, size+Npadlong*sizeof(ulong));
2057dd7cddfSDavid du Colombier 	if(Npadlong && v != nil) {
2067dd7cddfSDavid du Colombier 		v = (ulong*)v+Npadlong;
2077dd7cddfSDavid du Colombier 		setmalloctag(v, getcallerpc(&size));
2087dd7cddfSDavid du Colombier 		setrealloctag(v, 0);
2097dd7cddfSDavid du Colombier 	}
2107dd7cddfSDavid du Colombier 	return v;
2113e12c5d1SDavid du Colombier }
2123e12c5d1SDavid du Colombier 
2133e12c5d1SDavid du Colombier void*
2147dd7cddfSDavid du Colombier mallocz(ulong size, int clr)
2153e12c5d1SDavid du Colombier {
2167dd7cddfSDavid du Colombier 	void *v;
2173e12c5d1SDavid du Colombier 
2187dd7cddfSDavid du Colombier 	v = poolalloc(mainmem, size+Npadlong*sizeof(ulong));
2197dd7cddfSDavid du Colombier 	if(Npadlong && v != nil){
2207dd7cddfSDavid du Colombier 		v = (ulong*)v+Npadlong;
2217dd7cddfSDavid du Colombier 		setmalloctag(v, getcallerpc(&size));
2227dd7cddfSDavid du Colombier 		setrealloctag(v, 0);
2237dd7cddfSDavid du Colombier 	}
2247dd7cddfSDavid du Colombier 	if(clr && v != nil)
2257dd7cddfSDavid du Colombier 		memset(v, 0, size);
2267dd7cddfSDavid du Colombier 	return v;
2273e12c5d1SDavid du Colombier }
2283e12c5d1SDavid du Colombier 
2293e12c5d1SDavid du Colombier void
2307dd7cddfSDavid du Colombier free(void *v)
2313e12c5d1SDavid du Colombier {
2327dd7cddfSDavid du Colombier 	if(v != nil)
2337dd7cddfSDavid du Colombier 		poolfree(mainmem, (ulong*)v-Npadlong);
2343e12c5d1SDavid du Colombier }
2353e12c5d1SDavid du Colombier 
2363e12c5d1SDavid du Colombier void*
2377dd7cddfSDavid du Colombier realloc(void *v, ulong size)
2383e12c5d1SDavid du Colombier {
2397dd7cddfSDavid du Colombier 	void *nv;
2403e12c5d1SDavid du Colombier 
2417dd7cddfSDavid du Colombier 	if(v == nil)
2427dd7cddfSDavid du Colombier 		return malloc(size);
2433e12c5d1SDavid du Colombier 
2447dd7cddfSDavid du Colombier 	if(size == 0){
2457dd7cddfSDavid du Colombier 		free(v);
2467dd7cddfSDavid du Colombier 		return nil;
2477dd7cddfSDavid du Colombier 	}
2483e12c5d1SDavid du Colombier 
2497dd7cddfSDavid du Colombier 	v = (ulong*)v-Npadlong;
2507dd7cddfSDavid du Colombier 	size += Npadlong*sizeof(ulong);
2513e12c5d1SDavid du Colombier 
2527dd7cddfSDavid du Colombier 	if(nv = poolrealloc(mainmem, v, size)){
2537dd7cddfSDavid du Colombier 		nv = (ulong*)nv+Npadlong;
2547dd7cddfSDavid du Colombier 		setrealloctag(nv, getcallerpc(&v));
2557dd7cddfSDavid du Colombier 		if(v == nil)
2567dd7cddfSDavid du Colombier 			setmalloctag(nv, getcallerpc(&v));
2577dd7cddfSDavid du Colombier 	}
2587dd7cddfSDavid du Colombier 	return nv;
2597dd7cddfSDavid du Colombier }
2603e12c5d1SDavid du Colombier 
2617dd7cddfSDavid du Colombier ulong
2627dd7cddfSDavid du Colombier msize(void *v)
2637dd7cddfSDavid du Colombier {
2647dd7cddfSDavid du Colombier 	return poolmsize(mainmem, (ulong*)v-Npadlong)-Npadlong*sizeof(ulong);
2657dd7cddfSDavid du Colombier }
2667dd7cddfSDavid du Colombier 
2677dd7cddfSDavid du Colombier void*
2687dd7cddfSDavid du Colombier calloc(ulong n, ulong szelem)
2697dd7cddfSDavid du Colombier {
2707dd7cddfSDavid du Colombier 	void *v;
2717dd7cddfSDavid du Colombier 	if(v = mallocz(n*szelem, 1))
2727dd7cddfSDavid du Colombier 		setmalloctag(v, getcallerpc(&n));
2737dd7cddfSDavid du Colombier 	return v;
2747dd7cddfSDavid du Colombier }
2757dd7cddfSDavid du Colombier 
2767dd7cddfSDavid du Colombier void
2777dd7cddfSDavid du Colombier setmalloctag(void *v, ulong pc)
2787dd7cddfSDavid du Colombier {
2797dd7cddfSDavid du Colombier 	ulong *u;
2807dd7cddfSDavid du Colombier 	USED(v, pc);
2817dd7cddfSDavid du Colombier 	if(Npadlong <= MallocOffset || v == nil)
2827dd7cddfSDavid du Colombier 		return;
2837dd7cddfSDavid du Colombier 	u = v;
2847dd7cddfSDavid du Colombier 	u[-Npadlong+MallocOffset] = pc;
2857dd7cddfSDavid du Colombier }
2867dd7cddfSDavid du Colombier 
2877dd7cddfSDavid du Colombier void
2887dd7cddfSDavid du Colombier setrealloctag(void *v, ulong pc)
2897dd7cddfSDavid du Colombier {
2907dd7cddfSDavid du Colombier 	ulong *u;
2917dd7cddfSDavid du Colombier 	USED(v, pc);
2927dd7cddfSDavid du Colombier 	if(Npadlong <= ReallocOffset || v == nil)
2937dd7cddfSDavid du Colombier 		return;
2947dd7cddfSDavid du Colombier 	u = v;
2957dd7cddfSDavid du Colombier 	u[-Npadlong+ReallocOffset] = pc;
2967dd7cddfSDavid du Colombier }
2977dd7cddfSDavid du Colombier 
2987dd7cddfSDavid du Colombier ulong
2997dd7cddfSDavid du Colombier getmalloctag(void *v)
3007dd7cddfSDavid du Colombier {
3017dd7cddfSDavid du Colombier 	USED(v);
3027dd7cddfSDavid du Colombier 	if(Npadlong <= MallocOffset)
3037dd7cddfSDavid du Colombier 		return ~0;
3047dd7cddfSDavid du Colombier 	return ((ulong*)v)[-Npadlong+MallocOffset];
3057dd7cddfSDavid du Colombier }
3067dd7cddfSDavid du Colombier 
3077dd7cddfSDavid du Colombier ulong
3087dd7cddfSDavid du Colombier getrealloctag(void *v)
3097dd7cddfSDavid du Colombier {
3107dd7cddfSDavid du Colombier 	USED(v);
3117dd7cddfSDavid du Colombier 	if(Npadlong <= ReallocOffset)
3127dd7cddfSDavid du Colombier 		return ((ulong*)v)[-Npadlong+ReallocOffset];
3137dd7cddfSDavid du Colombier 	return ~0;
3147dd7cddfSDavid du Colombier }
3157dd7cddfSDavid du Colombier 
3167dd7cddfSDavid du Colombier void*
3177dd7cddfSDavid du Colombier malloctopoolblock(void *v)
3187dd7cddfSDavid du Colombier {
3197dd7cddfSDavid du Colombier 	if(v == nil)
3203e12c5d1SDavid du Colombier 		return nil;
3213e12c5d1SDavid du Colombier 
3227dd7cddfSDavid du Colombier 	return &((ulong*)v)[-Npadlong];
3233e12c5d1SDavid du Colombier }
324