xref: /plan9/sys/src/libc/port/malloc.c (revision 59cc4ca53493a3c6d2349fe2b7f7c40f7dce7294)
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 	long *x;
46 
47 	n += 8;	/* two longs for us */
48 	x = sbrk(n);
49 	if((int)x == -1)
50 		x = 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 	long *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 	int n;
115 	char buf[128];
116 	char *msg;
117 	Private *pv;
118 
119 	pv = p->private;
120 	if(pv->printfd == 0)
121 		pv->printfd = checkenv();
122 
123 	if(pv->printfd <= 0)
124 		pv->printfd = 2;
125 
126 	msg = buf;
127 	va_start(v, fmt);
128 	n = doprint(buf, buf+sizeof buf, fmt, v)-msg;
129 	write(pv->printfd, buf, n);
130 	va_end(v);
131 }
132 
133 static char panicbuf[256];
134 static void
135 ppanic(Pool *p, char *fmt, ...)
136 {
137 	va_list v;
138 	int n;
139 	char *msg;
140 	Private *pv;
141 
142 	pv = p->private;
143 	assert(canlock(&pv->lk)==0);
144 
145 	if(pv->printfd == 0)
146 		pv->printfd = checkenv();
147 	if(pv->printfd <= 0)
148 		pv->printfd = 2;
149 
150 	msg = panicbuf;
151 	va_start(v, fmt);
152 	n = doprint(msg, msg+sizeof panicbuf, fmt, v) - msg;
153 	write(2, "panic: ", 7);
154 	write(2, msg, n);
155 	write(2, "\n", 1);
156 	if(pv->printfd != 2){
157 		write(pv->printfd, "panic: ", 7);
158 		write(pv->printfd, msg, n);
159 		write(pv->printfd, "\n", 1);
160 	}
161 	va_end(v);
162 	unlock(&pv->lk);
163 	abort();
164 }
165 
166 /* - everything from here down should be the same in libc, libdebugmalloc, and the kernel - */
167 /* - except the code for malloc(), which alternately doesn't clear or does. - */
168 
169 /*
170  * Npadlong is the number of 32-bit longs to leave at the beginning of
171  * each allocated buffer for our own bookkeeping.  We return to the callers
172  * a pointer that points immediately after our bookkeeping area.  Incoming pointers
173  * must be decremented by that much, and outgoing pointers incremented.
174  * The malloc tag is stored at MallocOffset from the beginning of the block,
175  * and the realloc tag at ReallocOffset.  The offsets are from the true beginning
176  * of the block, not the beginning the caller sees.
177  *
178  * The extra if(Npadlong != 0) in various places is a hint for the compiler to
179  * compile out function calls that would otherwise be no-ops.
180  */
181 
182 /*	non tracing
183  *
184 enum {
185 	Npadlong	= 0,
186 	MallocOffset = 0,
187 	ReallocOffset = 0,
188 };
189  *
190  */
191 
192 /* tracing */
193 enum {
194 	Npadlong	= 2,
195 	MallocOffset = 0,
196 	ReallocOffset = 1
197 };
198 
199 void*
200 malloc(ulong size)
201 {
202 	void *v;
203 
204 	v = poolalloc(mainmem, size+Npadlong*sizeof(ulong));
205 	if(Npadlong && v != nil) {
206 		v = (ulong*)v+Npadlong;
207 		setmalloctag(v, getcallerpc(&size));
208 		setrealloctag(v, 0);
209 	}
210 	return v;
211 }
212 
213 void*
214 mallocz(ulong size, int clr)
215 {
216 	void *v;
217 
218 	v = poolalloc(mainmem, size+Npadlong*sizeof(ulong));
219 	if(Npadlong && v != nil){
220 		v = (ulong*)v+Npadlong;
221 		setmalloctag(v, getcallerpc(&size));
222 		setrealloctag(v, 0);
223 	}
224 	if(clr && v != nil)
225 		memset(v, 0, size);
226 	return v;
227 }
228 
229 void
230 free(void *v)
231 {
232 	if(v != nil)
233 		poolfree(mainmem, (ulong*)v-Npadlong);
234 }
235 
236 void*
237 realloc(void *v, ulong size)
238 {
239 	void *nv;
240 
241 	if(v == nil)
242 		return malloc(size);
243 
244 	if(size == 0){
245 		free(v);
246 		return nil;
247 	}
248 
249 	v = (ulong*)v-Npadlong;
250 	size += Npadlong*sizeof(ulong);
251 
252 	if(nv = poolrealloc(mainmem, v, size)){
253 		nv = (ulong*)nv+Npadlong;
254 		setrealloctag(nv, getcallerpc(&v));
255 		if(v == nil)
256 			setmalloctag(nv, getcallerpc(&v));
257 	}
258 	return nv;
259 }
260 
261 ulong
262 msize(void *v)
263 {
264 	return poolmsize(mainmem, (ulong*)v-Npadlong)-Npadlong*sizeof(ulong);
265 }
266 
267 void*
268 calloc(ulong n, ulong szelem)
269 {
270 	void *v;
271 	if(v = mallocz(n*szelem, 1))
272 		setmalloctag(v, getcallerpc(&n));
273 	return v;
274 }
275 
276 void
277 setmalloctag(void *v, ulong pc)
278 {
279 	ulong *u;
280 	USED(v, pc);
281 	if(Npadlong <= MallocOffset || v == nil)
282 		return;
283 	u = v;
284 	u[-Npadlong+MallocOffset] = pc;
285 }
286 
287 void
288 setrealloctag(void *v, ulong pc)
289 {
290 	ulong *u;
291 	USED(v, pc);
292 	if(Npadlong <= ReallocOffset || v == nil)
293 		return;
294 	u = v;
295 	u[-Npadlong+ReallocOffset] = pc;
296 }
297 
298 ulong
299 getmalloctag(void *v)
300 {
301 	USED(v);
302 	if(Npadlong <= MallocOffset)
303 		return ~0;
304 	return ((ulong*)v)[-Npadlong+MallocOffset];
305 }
306 
307 ulong
308 getrealloctag(void *v)
309 {
310 	USED(v);
311 	if(Npadlong <= ReallocOffset)
312 		return ((ulong*)v)[-Npadlong+ReallocOffset];
313 	return ~0;
314 }
315 
316 void*
317 malloctopoolblock(void *v)
318 {
319 	if(v == nil)
320 		return nil;
321 
322 	return &((ulong*)v)[-Npadlong];
323 }
324