xref: /plan9/sys/src/libc/port/malloc.c (revision ff8c3af2f44d95267f67219afa20ba82ff6cf7e4)
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 		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 	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 	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 free(void *v)
226 {
227 	if(v != nil)
228 		poolfree(mainmem, (ulong*)v-Npadlong);
229 }
230 
231 void*
232 realloc(void *v, ulong size)
233 {
234 	void *nv;
235 
236 	if(size == 0){
237 		free(v);
238 		return nil;
239 	}
240 
241 	if(v)
242 		v = (ulong*)v-Npadlong;
243 	size += Npadlong*sizeof(ulong);
244 
245 	if(nv = poolrealloc(mainmem, v, size)){
246 		nv = (ulong*)nv+Npadlong;
247 		setrealloctag(nv, getcallerpc(&v));
248 		if(v == nil)
249 			setmalloctag(nv, getcallerpc(&v));
250 	}
251 	return nv;
252 }
253 
254 ulong
255 msize(void *v)
256 {
257 	return poolmsize(mainmem, (ulong*)v-Npadlong)-Npadlong*sizeof(ulong);
258 }
259 
260 void*
261 calloc(ulong n, ulong szelem)
262 {
263 	void *v;
264 	if(v = mallocz(n*szelem, 1))
265 		setmalloctag(v, getcallerpc(&n));
266 	return v;
267 }
268 
269 void
270 setmalloctag(void *v, ulong pc)
271 {
272 	ulong *u;
273 	USED(v, pc);
274 	if(Npadlong <= MallocOffset || v == nil)
275 		return;
276 	u = v;
277 	u[-Npadlong+MallocOffset] = pc;
278 }
279 
280 void
281 setrealloctag(void *v, ulong pc)
282 {
283 	ulong *u;
284 	USED(v, pc);
285 	if(Npadlong <= ReallocOffset || v == nil)
286 		return;
287 	u = v;
288 	u[-Npadlong+ReallocOffset] = pc;
289 }
290 
291 ulong
292 getmalloctag(void *v)
293 {
294 	USED(v);
295 	if(Npadlong <= MallocOffset)
296 		return ~0;
297 	return ((ulong*)v)[-Npadlong+MallocOffset];
298 }
299 
300 ulong
301 getrealloctag(void *v)
302 {
303 	USED(v);
304 	if(Npadlong <= ReallocOffset)
305 		return ((ulong*)v)[-Npadlong+ReallocOffset];
306 	return ~0;
307 }
308 
309 void*
310 malloctopoolblock(void *v)
311 {
312 	if(v == nil)
313 		return nil;
314 
315 	return &((ulong*)v)[-Npadlong];
316 }
317