xref: /plan9/sys/src/libc/port/malloc.c (revision 3b86f2f88bade1f00206c7aa750b7add255f5724)
13e12c5d1SDavid du Colombier #include <u.h>
23e12c5d1SDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <pool.h>
4b85a8364SDavid du Colombier #include <tos.h>
53e12c5d1SDavid du Colombier 
67dd7cddfSDavid du Colombier static void*	sbrkalloc(ulong);
77dd7cddfSDavid du Colombier static int		sbrkmerge(void*, void*);
87dd7cddfSDavid du Colombier static void		plock(Pool*);
97dd7cddfSDavid du Colombier static void		punlock(Pool*);
107dd7cddfSDavid du Colombier static void		pprint(Pool*, char*, ...);
117dd7cddfSDavid du Colombier static void		ppanic(Pool*, char*, ...);
127dd7cddfSDavid du Colombier 
137dd7cddfSDavid du Colombier typedef struct Private Private;
147dd7cddfSDavid du Colombier struct Private {
157dd7cddfSDavid du Colombier 	Lock		lk;
16b85a8364SDavid du Colombier 	int		pid;
177dd7cddfSDavid du Colombier 	int		printfd;	/* gets debugging output if set */
183e12c5d1SDavid du Colombier };
193e12c5d1SDavid du Colombier 
207dd7cddfSDavid du Colombier Private sbrkmempriv;
217dd7cddfSDavid du Colombier 
227dd7cddfSDavid du Colombier static Pool sbrkmem = {
237dd7cddfSDavid du Colombier 	.name=		"sbrkmem",
24*3b86f2f8SDavid du Colombier 	.maxsize=	(3840UL-1)*1024*1024,	/* up to ~0xf0000000 */
257dd7cddfSDavid du Colombier 	.minarena=	4*1024,
267dd7cddfSDavid du Colombier 	.quantum=	32,
277dd7cddfSDavid du Colombier 	.alloc=		sbrkalloc,
287dd7cddfSDavid du Colombier 	.merge=		sbrkmerge,
297dd7cddfSDavid du Colombier 	.flags=		0,
307dd7cddfSDavid du Colombier 
317dd7cddfSDavid du Colombier 	.lock=		plock,
327dd7cddfSDavid du Colombier 	.unlock=		punlock,
337dd7cddfSDavid du Colombier 	.print=		pprint,
347dd7cddfSDavid du Colombier 	.panic=		ppanic,
357dd7cddfSDavid du Colombier 	.private=		&sbrkmempriv,
363e12c5d1SDavid du Colombier };
377dd7cddfSDavid du Colombier Pool *mainmem = &sbrkmem;
387dd7cddfSDavid du Colombier Pool *imagmem = &sbrkmem;
393e12c5d1SDavid du Colombier 
407dd7cddfSDavid du Colombier /*
417dd7cddfSDavid du Colombier  * we do minimal bookkeeping so we can tell pool
427dd7cddfSDavid du Colombier  * whether two blocks are adjacent and thus mergeable.
437dd7cddfSDavid du Colombier  */
447dd7cddfSDavid du Colombier static void*
sbrkalloc(ulong n)457dd7cddfSDavid du Colombier sbrkalloc(ulong n)
463e12c5d1SDavid du Colombier {
47810832d3SDavid du Colombier 	ulong *x;
483e12c5d1SDavid du Colombier 
49810832d3SDavid du Colombier 	n += 2*sizeof(ulong);	/* two longs for us */
507dd7cddfSDavid du Colombier 	x = sbrk(n);
5174f16c81SDavid du Colombier 	if(x == (void*)-1)
529a747e4fSDavid du Colombier 		return nil;
537dd7cddfSDavid du Colombier 	x[0] = (n+7)&~7;	/* sbrk rounds size up to mult. of 8 */
547dd7cddfSDavid du Colombier 	x[1] = 0xDeadBeef;
557dd7cddfSDavid du Colombier 	return x+2;
563e12c5d1SDavid du Colombier }
573e12c5d1SDavid du Colombier 
587dd7cddfSDavid du Colombier static int
sbrkmerge(void * x,void * y)597dd7cddfSDavid du Colombier sbrkmerge(void *x, void *y)
607dd7cddfSDavid du Colombier {
61810832d3SDavid du Colombier 	ulong *lx, *ly;
623e12c5d1SDavid du Colombier 
637dd7cddfSDavid du Colombier 	lx = x;
647dd7cddfSDavid du Colombier 	if(lx[-1] != 0xDeadBeef)
653e12c5d1SDavid du Colombier 		abort();
663e12c5d1SDavid du Colombier 
677dd7cddfSDavid du Colombier 	if((uchar*)lx+lx[-2] == (uchar*)y) {
687dd7cddfSDavid du Colombier 		ly = y;
697dd7cddfSDavid du Colombier 		lx[-2] += ly[-2];
707dd7cddfSDavid du Colombier 		return 1;
713e12c5d1SDavid du Colombier 	}
727dd7cddfSDavid du Colombier 	return 0;
733e12c5d1SDavid du Colombier }
743e12c5d1SDavid du Colombier 
757dd7cddfSDavid du Colombier static void
plock(Pool * p)767dd7cddfSDavid du Colombier plock(Pool *p)
777dd7cddfSDavid du Colombier {
787dd7cddfSDavid du Colombier 	Private *pv;
797dd7cddfSDavid du Colombier 	pv = p->private;
807dd7cddfSDavid du Colombier 	lock(&pv->lk);
81b85a8364SDavid du Colombier 	if(pv->pid != 0)
82b85a8364SDavid du Colombier 		abort();
83b85a8364SDavid du Colombier 	pv->pid = _tos->pid;
847dd7cddfSDavid du Colombier }
853e12c5d1SDavid du Colombier 
867dd7cddfSDavid du Colombier static void
punlock(Pool * p)877dd7cddfSDavid du Colombier punlock(Pool *p)
887dd7cddfSDavid du Colombier {
897dd7cddfSDavid du Colombier 	Private *pv;
907dd7cddfSDavid du Colombier 	pv = p->private;
91b85a8364SDavid du Colombier 	if(pv->pid != _tos->pid)
92b85a8364SDavid du Colombier 		abort();
93b85a8364SDavid du Colombier 	pv->pid = 0;
947dd7cddfSDavid du Colombier 	unlock(&pv->lk);
957dd7cddfSDavid du Colombier }
967dd7cddfSDavid du Colombier 
977dd7cddfSDavid du Colombier static int
checkenv(void)987dd7cddfSDavid du Colombier checkenv(void)
997dd7cddfSDavid du Colombier {
1007dd7cddfSDavid du Colombier 	int n, fd;
1017dd7cddfSDavid du Colombier 	char buf[20];
1027dd7cddfSDavid du Colombier 	fd = open("/env/MALLOCFD", OREAD);
1037dd7cddfSDavid du Colombier 	if(fd < 0)
1047dd7cddfSDavid du Colombier 		return -1;
1057dd7cddfSDavid du Colombier 	if((n = read(fd, buf, sizeof buf)) < 0) {
1067dd7cddfSDavid du Colombier 		close(fd);
1077dd7cddfSDavid du Colombier 		return -1;
1087dd7cddfSDavid du Colombier 	}
1097dd7cddfSDavid du Colombier 	if(n >= sizeof buf)
1107dd7cddfSDavid du Colombier 		n = sizeof(buf)-1;
1117dd7cddfSDavid du Colombier 	buf[n] = 0;
1127dd7cddfSDavid du Colombier 	n = atoi(buf);
1137dd7cddfSDavid du Colombier 	if(n == 0)
1147dd7cddfSDavid du Colombier 		n = -1;
1157dd7cddfSDavid du Colombier 	return n;
1167dd7cddfSDavid du Colombier }
1177dd7cddfSDavid du Colombier 
1187dd7cddfSDavid du Colombier static void
pprint(Pool * p,char * fmt,...)1197dd7cddfSDavid du Colombier pprint(Pool *p, char *fmt, ...)
1207dd7cddfSDavid du Colombier {
1217dd7cddfSDavid du Colombier 	va_list v;
1227dd7cddfSDavid du Colombier 	Private *pv;
1237dd7cddfSDavid du Colombier 
1247dd7cddfSDavid du Colombier 	pv = p->private;
1257dd7cddfSDavid du Colombier 	if(pv->printfd == 0)
1267dd7cddfSDavid du Colombier 		pv->printfd = checkenv();
1277dd7cddfSDavid du Colombier 
1287dd7cddfSDavid du Colombier 	if(pv->printfd <= 0)
1297dd7cddfSDavid du Colombier 		pv->printfd = 2;
1307dd7cddfSDavid du Colombier 
1317dd7cddfSDavid du Colombier 	va_start(v, fmt);
1329a747e4fSDavid du Colombier 	vfprint(pv->printfd, fmt, v);
1337dd7cddfSDavid du Colombier 	va_end(v);
1347dd7cddfSDavid du Colombier }
1357dd7cddfSDavid du Colombier 
1367dd7cddfSDavid du Colombier static char panicbuf[256];
1377dd7cddfSDavid du Colombier static void
ppanic(Pool * p,char * fmt,...)1387dd7cddfSDavid du Colombier ppanic(Pool *p, char *fmt, ...)
1397dd7cddfSDavid du Colombier {
1407dd7cddfSDavid du Colombier 	va_list v;
1417dd7cddfSDavid du Colombier 	int n;
1427dd7cddfSDavid du Colombier 	char *msg;
1437dd7cddfSDavid du Colombier 	Private *pv;
1447dd7cddfSDavid du Colombier 
1457dd7cddfSDavid du Colombier 	pv = p->private;
1467dd7cddfSDavid du Colombier 	assert(canlock(&pv->lk)==0);
1477dd7cddfSDavid du Colombier 
1487dd7cddfSDavid du Colombier 	if(pv->printfd == 0)
1497dd7cddfSDavid du Colombier 		pv->printfd = checkenv();
1507dd7cddfSDavid du Colombier 	if(pv->printfd <= 0)
1517dd7cddfSDavid du Colombier 		pv->printfd = 2;
1527dd7cddfSDavid du Colombier 
1537dd7cddfSDavid du Colombier 	msg = panicbuf;
1547dd7cddfSDavid du Colombier 	va_start(v, fmt);
1559a747e4fSDavid du Colombier 	n = vseprint(msg, msg+sizeof panicbuf, fmt, v) - msg;
1567dd7cddfSDavid du Colombier 	write(2, "panic: ", 7);
1577dd7cddfSDavid du Colombier 	write(2, msg, n);
1587dd7cddfSDavid du Colombier 	write(2, "\n", 1);
1597dd7cddfSDavid du Colombier 	if(pv->printfd != 2){
1607dd7cddfSDavid du Colombier 		write(pv->printfd, "panic: ", 7);
1617dd7cddfSDavid du Colombier 		write(pv->printfd, msg, n);
1627dd7cddfSDavid du Colombier 		write(pv->printfd, "\n", 1);
1637dd7cddfSDavid du Colombier 	}
1647dd7cddfSDavid du Colombier 	va_end(v);
165b85a8364SDavid du Colombier //	unlock(&pv->lk);
1667dd7cddfSDavid du Colombier 	abort();
1677dd7cddfSDavid du Colombier }
1687dd7cddfSDavid du Colombier 
1697dd7cddfSDavid du Colombier /* - everything from here down should be the same in libc, libdebugmalloc, and the kernel - */
1707dd7cddfSDavid du Colombier /* - except the code for malloc(), which alternately doesn't clear or does. - */
1717dd7cddfSDavid du Colombier 
1727dd7cddfSDavid du Colombier /*
1737dd7cddfSDavid du Colombier  * Npadlong is the number of 32-bit longs to leave at the beginning of
1747dd7cddfSDavid du Colombier  * each allocated buffer for our own bookkeeping.  We return to the callers
1757dd7cddfSDavid du Colombier  * a pointer that points immediately after our bookkeeping area.  Incoming pointers
1767dd7cddfSDavid du Colombier  * must be decremented by that much, and outgoing pointers incremented.
1777dd7cddfSDavid du Colombier  * The malloc tag is stored at MallocOffset from the beginning of the block,
1787dd7cddfSDavid du Colombier  * and the realloc tag at ReallocOffset.  The offsets are from the true beginning
1797dd7cddfSDavid du Colombier  * of the block, not the beginning the caller sees.
1807dd7cddfSDavid du Colombier  *
1817dd7cddfSDavid du Colombier  * The extra if(Npadlong != 0) in various places is a hint for the compiler to
1827dd7cddfSDavid du Colombier  * compile out function calls that would otherwise be no-ops.
1837dd7cddfSDavid du Colombier  */
1847dd7cddfSDavid du Colombier 
1857dd7cddfSDavid du Colombier /*	non tracing
1867dd7cddfSDavid du Colombier  *
1877dd7cddfSDavid du Colombier enum {
1887dd7cddfSDavid du Colombier 	Npadlong	= 0,
1897dd7cddfSDavid du Colombier 	MallocOffset = 0,
1907dd7cddfSDavid du Colombier 	ReallocOffset = 0,
1917dd7cddfSDavid du Colombier };
1927dd7cddfSDavid du Colombier  *
1937dd7cddfSDavid du Colombier  */
1947dd7cddfSDavid du Colombier 
1957dd7cddfSDavid du Colombier /* tracing */
1967dd7cddfSDavid du Colombier enum {
1977dd7cddfSDavid du Colombier 	Npadlong	= 2,
1987dd7cddfSDavid du Colombier 	MallocOffset = 0,
1997dd7cddfSDavid du Colombier 	ReallocOffset = 1
2007dd7cddfSDavid du Colombier };
2017dd7cddfSDavid du Colombier 
2027dd7cddfSDavid du Colombier void*
malloc(ulong size)2037dd7cddfSDavid du Colombier malloc(ulong size)
2047dd7cddfSDavid du Colombier {
2057dd7cddfSDavid du Colombier 	void *v;
2067dd7cddfSDavid du Colombier 
2077dd7cddfSDavid du Colombier 	v = poolalloc(mainmem, size+Npadlong*sizeof(ulong));
2087dd7cddfSDavid du Colombier 	if(Npadlong && v != nil) {
2097dd7cddfSDavid du Colombier 		v = (ulong*)v+Npadlong;
2107dd7cddfSDavid du Colombier 		setmalloctag(v, getcallerpc(&size));
2117dd7cddfSDavid du Colombier 		setrealloctag(v, 0);
2127dd7cddfSDavid du Colombier 	}
2137dd7cddfSDavid du Colombier 	return v;
2143e12c5d1SDavid du Colombier }
2153e12c5d1SDavid du Colombier 
2163e12c5d1SDavid du Colombier void*
mallocz(ulong size,int clr)2177dd7cddfSDavid du Colombier mallocz(ulong size, int clr)
2183e12c5d1SDavid du Colombier {
2197dd7cddfSDavid du Colombier 	void *v;
2203e12c5d1SDavid du Colombier 
2217dd7cddfSDavid du Colombier 	v = poolalloc(mainmem, size+Npadlong*sizeof(ulong));
2227dd7cddfSDavid du Colombier 	if(Npadlong && v != nil){
2237dd7cddfSDavid du Colombier 		v = (ulong*)v+Npadlong;
2247dd7cddfSDavid du Colombier 		setmalloctag(v, getcallerpc(&size));
2257dd7cddfSDavid du Colombier 		setrealloctag(v, 0);
2267dd7cddfSDavid du Colombier 	}
2277dd7cddfSDavid du Colombier 	if(clr && v != nil)
2287dd7cddfSDavid du Colombier 		memset(v, 0, size);
2297dd7cddfSDavid du Colombier 	return v;
2303e12c5d1SDavid du Colombier }
2313e12c5d1SDavid du Colombier 
2325243b8d1SDavid du Colombier void*
mallocalign(ulong size,ulong align,long offset,ulong span)2335243b8d1SDavid du Colombier mallocalign(ulong size, ulong align, long offset, ulong span)
2345243b8d1SDavid du Colombier {
2355243b8d1SDavid du Colombier 	void *v;
2365243b8d1SDavid du Colombier 
2375243b8d1SDavid du Colombier 	v = poolallocalign(mainmem, size+Npadlong*sizeof(ulong), align, offset-Npadlong*sizeof(ulong), span);
2385243b8d1SDavid du Colombier 	if(Npadlong && v != nil){
2395243b8d1SDavid du Colombier 		v = (ulong*)v+Npadlong;
2405243b8d1SDavid du Colombier 		setmalloctag(v, getcallerpc(&size));
2415243b8d1SDavid du Colombier 		setrealloctag(v, 0);
2425243b8d1SDavid du Colombier 	}
2435243b8d1SDavid du Colombier 	return v;
2445243b8d1SDavid du Colombier }
2455243b8d1SDavid du Colombier 
2463e12c5d1SDavid du Colombier void
free(void * v)2477dd7cddfSDavid du Colombier free(void *v)
2483e12c5d1SDavid du Colombier {
2497dd7cddfSDavid du Colombier 	if(v != nil)
2507dd7cddfSDavid du Colombier 		poolfree(mainmem, (ulong*)v-Npadlong);
2513e12c5d1SDavid du Colombier }
2523e12c5d1SDavid du Colombier 
2533e12c5d1SDavid du Colombier void*
realloc(void * v,ulong size)2547dd7cddfSDavid du Colombier realloc(void *v, ulong size)
2553e12c5d1SDavid du Colombier {
2567dd7cddfSDavid du Colombier 	void *nv;
2573e12c5d1SDavid du Colombier 
2587dd7cddfSDavid du Colombier 	if(size == 0){
2597dd7cddfSDavid du Colombier 		free(v);
2607dd7cddfSDavid du Colombier 		return nil;
2617dd7cddfSDavid du Colombier 	}
2623e12c5d1SDavid du Colombier 
2639a747e4fSDavid du Colombier 	if(v)
2647dd7cddfSDavid du Colombier 		v = (ulong*)v-Npadlong;
2657dd7cddfSDavid du Colombier 	size += Npadlong*sizeof(ulong);
2663e12c5d1SDavid du Colombier 
2677dd7cddfSDavid du Colombier 	if(nv = poolrealloc(mainmem, v, size)){
2687dd7cddfSDavid du Colombier 		nv = (ulong*)nv+Npadlong;
2697dd7cddfSDavid du Colombier 		setrealloctag(nv, getcallerpc(&v));
2707dd7cddfSDavid du Colombier 		if(v == nil)
2717dd7cddfSDavid du Colombier 			setmalloctag(nv, getcallerpc(&v));
2727dd7cddfSDavid du Colombier 	}
2737dd7cddfSDavid du Colombier 	return nv;
2747dd7cddfSDavid du Colombier }
2753e12c5d1SDavid du Colombier 
2767dd7cddfSDavid du Colombier ulong
msize(void * v)2777dd7cddfSDavid du Colombier msize(void *v)
2787dd7cddfSDavid du Colombier {
2797dd7cddfSDavid du Colombier 	return poolmsize(mainmem, (ulong*)v-Npadlong)-Npadlong*sizeof(ulong);
2807dd7cddfSDavid du Colombier }
2817dd7cddfSDavid du Colombier 
2827dd7cddfSDavid du Colombier void*
calloc(ulong n,ulong szelem)2837dd7cddfSDavid du Colombier calloc(ulong n, ulong szelem)
2847dd7cddfSDavid du Colombier {
2857dd7cddfSDavid du Colombier 	void *v;
2867dd7cddfSDavid du Colombier 	if(v = mallocz(n*szelem, 1))
2877dd7cddfSDavid du Colombier 		setmalloctag(v, getcallerpc(&n));
2887dd7cddfSDavid du Colombier 	return v;
2897dd7cddfSDavid du Colombier }
2907dd7cddfSDavid du Colombier 
2917dd7cddfSDavid du Colombier void
setmalloctag(void * v,ulong pc)2927dd7cddfSDavid du Colombier setmalloctag(void *v, ulong pc)
2937dd7cddfSDavid du Colombier {
2947dd7cddfSDavid du Colombier 	ulong *u;
2957dd7cddfSDavid du Colombier 	USED(v, pc);
2967dd7cddfSDavid du Colombier 	if(Npadlong <= MallocOffset || v == nil)
2977dd7cddfSDavid du Colombier 		return;
2987dd7cddfSDavid du Colombier 	u = v;
2997dd7cddfSDavid du Colombier 	u[-Npadlong+MallocOffset] = pc;
3007dd7cddfSDavid du Colombier }
3017dd7cddfSDavid du Colombier 
3027dd7cddfSDavid du Colombier void
setrealloctag(void * v,ulong pc)3037dd7cddfSDavid du Colombier setrealloctag(void *v, ulong pc)
3047dd7cddfSDavid du Colombier {
3057dd7cddfSDavid du Colombier 	ulong *u;
3067dd7cddfSDavid du Colombier 	USED(v, pc);
3077dd7cddfSDavid du Colombier 	if(Npadlong <= ReallocOffset || v == nil)
3087dd7cddfSDavid du Colombier 		return;
3097dd7cddfSDavid du Colombier 	u = v;
3107dd7cddfSDavid du Colombier 	u[-Npadlong+ReallocOffset] = pc;
3117dd7cddfSDavid du Colombier }
3127dd7cddfSDavid du Colombier 
3137dd7cddfSDavid du Colombier ulong
getmalloctag(void * v)3147dd7cddfSDavid du Colombier getmalloctag(void *v)
3157dd7cddfSDavid du Colombier {
3167dd7cddfSDavid du Colombier 	USED(v);
3177dd7cddfSDavid du Colombier 	if(Npadlong <= MallocOffset)
3187dd7cddfSDavid du Colombier 		return ~0;
3197dd7cddfSDavid du Colombier 	return ((ulong*)v)[-Npadlong+MallocOffset];
3207dd7cddfSDavid du Colombier }
3217dd7cddfSDavid du Colombier 
3227dd7cddfSDavid du Colombier ulong
getrealloctag(void * v)3237dd7cddfSDavid du Colombier getrealloctag(void *v)
3247dd7cddfSDavid du Colombier {
3257dd7cddfSDavid du Colombier 	USED(v);
3267dd7cddfSDavid du Colombier 	if(Npadlong <= ReallocOffset)
3277dd7cddfSDavid du Colombier 		return ((ulong*)v)[-Npadlong+ReallocOffset];
3287dd7cddfSDavid du Colombier 	return ~0;
3297dd7cddfSDavid du Colombier }
3307dd7cddfSDavid du Colombier 
3317dd7cddfSDavid du Colombier void*
malloctopoolblock(void * v)3327dd7cddfSDavid du Colombier malloctopoolblock(void *v)
3337dd7cddfSDavid du Colombier {
3347dd7cddfSDavid du Colombier 	if(v == nil)
3353e12c5d1SDavid du Colombier 		return nil;
3363e12c5d1SDavid du Colombier 
3377dd7cddfSDavid du Colombier 	return &((ulong*)v)[-Npadlong];
3383e12c5d1SDavid du Colombier }
339