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