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