#include <9pm/u.h> #include <9pm/libc.h> #include <9pm/pool.h> #include "dat.h" #include "fns.h" static void* sbrkalloc(ulong); static int sbrkmerge(void*, void*); static void plock(Pool*); static void punlock(Pool*); static void mpprint(Pool*, char*, ...); static void ppanic(Pool*, char*, ...); typedef struct Private Private; struct Private { Lock lk; }; Private sbrkmempriv; static Pool sbrkmem = { "sbrkmem", 2UL*1024*1024*1024, 0, 0, 0, 4*1024, 32, 0, 0, 0, sbrkalloc, sbrkmerge, 0, 0, 0, 0, plock, punlock, mpprint, ppanic, 0, &sbrkmempriv }; Pool *mainmem = &sbrkmem; Pool *imagmem = &sbrkmem; /* * we do minimal bookkeeping so we can tell pool * whether two blocks are adjacent and thus mergeable. */ static void* sbrkalloc(ulong n) { long *x; n += 8; /* two longs for us */ x = sbrk(n); if((int)x == -1) return nil; x[0] = (n+7)&~7; /* sbrk rounds size up to mult. of 8 */ x[1] = 0xDeadBeef; return x+2; } static int sbrkmerge(void *x, void *y) { long *lx, *ly; lx = x; if(lx[-1] != 0xDeadBeef) abort(); if((uchar*)lx+lx[-2] == (uchar*)y) { ly = y; lx[-2] += ly[-2]; return 1; } return 0; } static void plock(Pool *p) { Private *pv; pv = p->private; lock(&pv->lk); } static void punlock(Pool *p) { Private *pv; pv = p->private; unlock(&pv->lk); } static void mpprint(Pool *p, char *fmt, ...) { va_list v; char buf[128]; char *msg; Private *pv; pv = p->private; msg = buf; va_start(v, fmt); doprint(buf, buf+sizeof buf, fmt, v); dwrite(buf); va_end(v); } static char panicbuf[256]; static void ppanic(Pool *p, char *fmt, ...) { va_list v; char *msg; Private *pv; pv = p->private; canlock(&pv->lk); msg = panicbuf; va_start(v, fmt); doprint(msg, msg+sizeof panicbuf, fmt, v); va_end(v); panic(msg); abort(); } /* - everything from here down should be the same in libc, libdebugmalloc, and the kernel - */ /* - except the code for malloc(), which alternately doesn't clear or does. - */ /* * Npadlong is the number of 32-bit longs to leave at the beginning of * each allocated buffer for our own bookkeeping. We return to the callers * a pointer that points immediately after our bookkeeping area. Incoming pointers * must be decremented by that much, and outgoing pointers incremented. * The malloc tag is stored at MallocOffset from the beginning of the block, * and the realloc tag at ReallocOffset. The offsets are from the true beginning * of the block, not the beginning the caller sees. * * The extra if(Npadlong != 0) in various places is a hint for the compiler to * compile out function calls that would otherwise be no-ops. */ /* non tracing * enum { Npadlong = 0, MallocOffset = 0, ReallocOffset = 0, }; * */ /* tracing */ enum { Npadlong = 2, MallocOffset = 0, ReallocOffset = 1 }; void* smalloc(ulong size) { void *v; for(;;) { v = poolalloc(mainmem, size+Npadlong*sizeof(ulong)); if(v != nil) break; rendtsleep(&up->sleep, return0, 0, 100); } if(Npadlong){ v = (ulong*)v+Npadlong; setmalloctag(v, getcallerpc(&size)); } memset(v, 0, size); return v; } void* malloc(ulong size) { void *v; v = poolalloc(mainmem, size+Npadlong*sizeof(ulong)); if(Npadlong && v != nil) { v = (ulong*)v+Npadlong; setmalloctag(v, getcallerpc(&size)); setrealloctag(v, 0); } if(v != nil) memset(v, 0, size); return v; } void* mallocz(ulong size, int clr) { void *v; v = poolalloc(mainmem, size+Npadlong*sizeof(ulong)); if(Npadlong && v != nil){ v = (ulong*)v+Npadlong; setmalloctag(v, getcallerpc(&size)); setrealloctag(v, 0); } if(clr && v != nil) memset(v, 0, size); return v; } void free(void *v) { if(v != nil) poolfree(mainmem, (ulong*)v-Npadlong); } void* realloc(void *v, ulong size) { void *nv; if(size == 0){ free(v); return nil; } if(v) v = (ulong*)v-Npadlong; size += Npadlong*sizeof(ulong); if(nv = poolrealloc(mainmem, v, size)){ nv = (ulong*)nv+Npadlong; setrealloctag(nv, getcallerpc(&v)); if(v == nil) setmalloctag(nv, getcallerpc(&v)); } return nv; } ulong msize(void *v) { return poolmsize(mainmem, (ulong*)v-Npadlong)-Npadlong*sizeof(ulong); } void* calloc(ulong n, ulong szelem) { void *v; if(v = mallocz(n*szelem, 1)) setmalloctag(v, getcallerpc(&n)); return v; } void setmalloctag(void *v, ulong pc) { ulong *u; USED(v); USED(pc); if(Npadlong <= MallocOffset || v == nil) return; u = v; u[-Npadlong+MallocOffset] = pc; } void setrealloctag(void *v, ulong pc) { ulong *u; USED(v); USED(pc); if(Npadlong <= ReallocOffset || v == nil) return; u = v; u[-Npadlong+ReallocOffset] = pc; } ulong getmalloctag(void *v) { USED(v); if(Npadlong <= MallocOffset) return ~0; return ((ulong*)v)[-Npadlong+MallocOffset]; } ulong getrealloctag(void *v) { USED(v); if(Npadlong <= ReallocOffset) return ((ulong*)v)[-Npadlong+ReallocOffset]; return ~0; } void* malloctopoolblock(void *v) { if(v == nil) return nil; return &((ulong*)v)[-Npadlong]; }