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