xref: /inferno-os/emu/port/alloc.c (revision 46439007cf417cbd9ac8049bb4122c890097a0fa)
1 #include "dat.h"
2 #include "fns.h"
3 #include "interp.h"
4 #include "error.h"
5 
6 enum
7 {
8 	MAXPOOL		= 4
9 };
10 
11 #define left	u.s.bhl
12 #define right	u.s.bhr
13 #define fwd	u.s.bhf
14 #define prev	u.s.bhv
15 #define parent	u.s.bhp
16 
17 #define RESERVED	512*1024
18 
19 struct Pool
20 {
21 	char*	name;
22 	int	pnum;
23 	ulong	maxsize;
24 	int	quanta;
25 	int	chunk;
26 	int	monitor;
27 	ulong	ressize;	/* restricted size */
28 	ulong	cursize;
29 	ulong	arenasize;
30 	ulong	hw;
31 	Lock	l;
32 	Bhdr*	root;
33 	Bhdr*	chain;
34 	ulong	nalloc;
35 	ulong	nfree;
36 	int	nbrk;
37 	int	lastfree;
38 	void	(*move)(void*, void*);
39 };
40 
41 void*	initbrk(ulong);
42 
43 struct
44 {
45 	int	n;
46 	Pool	pool[MAXPOOL];
47 	/* Lock l; */
48 } table = {
49 	3,
50 	{
51 		{ "main",  0, 	32*1024*1024, 31,  512*1024, 0, 31*1024*1024 },
52 		{ "heap",  1, 	32*1024*1024, 31,  512*1024, 0, 31*1024*1024 },
53 		{ "image", 2,   32*1024*1024+256, 31, 4*1024*1024, 1, 31*1024*1024 },
54 	}
55 };
56 
57 Pool*	mainmem = &table.pool[0];
58 Pool*	heapmem = &table.pool[1];
59 Pool*	imagmem = &table.pool[2];
60 
61 static void _auditmemloc(char *, void *);
62 void (*auditmemloc)(char *, void *) = _auditmemloc;
63 static void _poolfault(void *, char *, ulong);
64 void (*poolfault)(void *, char *, ulong) = _poolfault;
65 
66 /*	non tracing
67  *
68 enum {
69 	Npadlong	= 0,
70 	MallocOffset = 0,
71 	ReallocOffset = 0
72 };
73  *
74  */
75 
76 /* tracing */
77 enum {
78 	Npadlong	= 2,
79 	MallocOffset = 0,
80 	ReallocOffset = 1
81 };
82 
83 enum {
84 	Monitor = 1
85 };
86 
87 void	(*memmonitor)(int, ulong, ulong, ulong) = nil;
88 #define	MM(v,pc,base,size)	if(!Monitor || memmonitor==nil){} else memmonitor((v),(pc),(base),(size))
89 
90 #define CKLEAK	0
91 int	ckleak;
92 #define	ML(v, sz, pc)	if(CKLEAK && ckleak && v){ if(sz) fprint(2, "%lux %lux %lux\n", (ulong)v, (ulong)sz, (ulong)pc); else fprint(2, "%lux\n", (ulong)v); }
93 
94 int
95 memusehigh(void)
96 {
97 	return 	mainmem->cursize > mainmem->ressize ||
98 			heapmem->cursize > heapmem->ressize ||
99 			0 && imagmem->cursize > imagmem->ressize;
100 }
101 
102 int
103 memlow(void)
104 {
105 	return heapmem->cursize > (heapmem->maxsize)/2;
106 }
107 
108 int
109 poolsetsize(char *s, int size)
110 {
111 	int i;
112 
113 	for(i = 0; i < table.n; i++) {
114 		if(strcmp(table.pool[i].name, s) == 0) {
115 			table.pool[i].maxsize = size;
116 			table.pool[i].ressize = size-RESERVED;
117 			if(size < RESERVED)
118 				panic("not enough memory");
119 			return 1;
120 		}
121 	}
122 	return 0;
123 }
124 
125 void
126 poolimmutable(void *v)
127 {
128 	Bhdr *b;
129 
130 	D2B(b, v);
131 	b->magic = MAGIC_I;
132 }
133 
134 void
135 poolmutable(void *v)
136 {
137 	Bhdr *b;
138 
139 	D2B(b, v);
140 	b->magic = MAGIC_A;
141 	((Heap*)v)->color = mutator;
142 }
143 
144 char*
145 poolname(Pool *p)
146 {
147 	return p->name;
148 }
149 
150 Bhdr*
151 poolchain(Pool *p)
152 {
153 	return p->chain;
154 }
155 
156 void
157 pooldel(Pool *p, Bhdr *t)
158 {
159 	Bhdr *s, *f, *rp, *q;
160 
161 	if(t->parent == nil && p->root != t) {
162 		t->prev->fwd = t->fwd;
163 		t->fwd->prev = t->prev;
164 		return;
165 	}
166 
167 	if(t->fwd != t) {
168 		f = t->fwd;
169 		s = t->parent;
170 		f->parent = s;
171 		if(s == nil)
172 			p->root = f;
173 		else {
174 			if(s->left == t)
175 				s->left = f;
176 			else
177 				s->right = f;
178 		}
179 
180 		rp = t->left;
181 		f->left = rp;
182 		if(rp != nil)
183 			rp->parent = f;
184 		rp = t->right;
185 		f->right = rp;
186 		if(rp != nil)
187 			rp->parent = f;
188 
189 		t->prev->fwd = t->fwd;
190 		t->fwd->prev = t->prev;
191 		return;
192 	}
193 
194 	if(t->left == nil)
195 		rp = t->right;
196 	else {
197 		if(t->right == nil)
198 			rp = t->left;
199 		else {
200 			f = t;
201 			rp = t->right;
202 			s = rp->left;
203 			while(s != nil) {
204 				f = rp;
205 				rp = s;
206 				s = rp->left;
207 			}
208 			if(f != t) {
209 				s = rp->right;
210 				f->left = s;
211 				if(s != nil)
212 					s->parent = f;
213 				s = t->right;
214 				rp->right = s;
215 				if(s != nil)
216 					s->parent = rp;
217 			}
218 			s = t->left;
219 			rp->left = s;
220 			s->parent = rp;
221 		}
222 	}
223 	q = t->parent;
224 	if(q == nil)
225 		p->root = rp;
226 	else {
227 		if(t == q->left)
228 			q->left = rp;
229 		else
230 			q->right = rp;
231 	}
232 	if(rp != nil)
233 		rp->parent = q;
234 }
235 
236 void
237 pooladd(Pool *p, Bhdr *q)
238 {
239 	int size;
240 	Bhdr *tp, *t;
241 
242 	q->magic = MAGIC_F;
243 
244 	q->left = nil;
245 	q->right = nil;
246 	q->parent = nil;
247 	q->fwd = q;
248 	q->prev = q;
249 
250 	t = p->root;
251 	if(t == nil) {
252 		p->root = q;
253 		return;
254 	}
255 
256 	size = q->size;
257 
258 	tp = nil;
259 	while(t != nil) {
260 		if(size == t->size) {
261 			q->prev = t->prev;
262 			q->prev->fwd = q;
263 			q->fwd = t;
264 			t->prev = q;
265 			return;
266 		}
267 		tp = t;
268 		if(size < t->size)
269 			t = t->left;
270 		else
271 			t = t->right;
272 	}
273 
274 	q->parent = tp;
275 	if(size < tp->size)
276 		tp->left = q;
277 	else
278 		tp->right = q;
279 }
280 
281 static void*
282 dopoolalloc(Pool *p, ulong asize, ulong pc)
283 {
284 	Bhdr *q, *t;
285 	int alloc, ldr, ns, frag;
286 	int osize, size;
287 
288 	if(asize >= 1024*1024*1024)	/* for sanity and to avoid overflow */
289 		return nil;
290 	size = asize;
291 	osize = size;
292 	size = (size + BHDRSIZE + p->quanta) & ~(p->quanta);
293 
294 	lock(&p->l);
295 	p->nalloc++;
296 
297 	t = p->root;
298 	q = nil;
299 	while(t) {
300 		if(t->size == size) {
301 			t = t->fwd;
302 			pooldel(p, t);
303 			t->magic = MAGIC_A;
304 			p->cursize += t->size;
305 			if(p->cursize > p->hw)
306 				p->hw = p->cursize;
307 			unlock(&p->l);
308 			if(p->monitor)
309 				MM(p->pnum, pc, (ulong)B2D(t), size);
310 			return B2D(t);
311 		}
312 		if(size < t->size) {
313 			q = t;
314 			t = t->left;
315 		}
316 		else
317 			t = t->right;
318 	}
319 	if(q != nil) {
320 		pooldel(p, q);
321 		q->magic = MAGIC_A;
322 		frag = q->size - size;
323 		if(frag < (size>>2) && frag < 0x8000) {
324 			p->cursize += q->size;
325 			if(p->cursize > p->hw)
326 				p->hw = p->cursize;
327 			unlock(&p->l);
328 			if(p->monitor)
329 				MM(p->pnum, pc, (ulong)B2D(q), size);
330 			return B2D(q);
331 		}
332 		/* Split */
333 		ns = q->size - size;
334 		q->size = size;
335 		B2T(q)->hdr = q;
336 		t = B2NB(q);
337 		t->size = ns;
338 		B2T(t)->hdr = t;
339 		pooladd(p, t);
340 		p->cursize += q->size;
341 		if(p->cursize > p->hw)
342 			p->hw = p->cursize;
343 		unlock(&p->l);
344 		if(p->monitor)
345 			MM(p->pnum, pc, (ulong)B2D(q), size);
346 		return B2D(q);
347 	}
348 
349 	ns = p->chunk;
350 	if(size > ns)
351 		ns = size;
352 	ldr = p->quanta+1;
353 
354 	alloc = ns+ldr+ldr;
355 	p->arenasize += alloc;
356 	if(p->arenasize > p->maxsize) {
357 		p->arenasize -= alloc;
358 		ns = p->maxsize-p->arenasize-ldr-ldr;
359 		ns &= ~p->quanta;
360 		if (ns < size) {
361 			if(poolcompact(p)) {
362 				unlock(&p->l);
363 				return poolalloc(p, osize);
364 			}
365 
366 			unlock(&p->l);
367 			print("arena %s too large: size %d cursize %lud arenasize %lud maxsize %lud\n",
368 			 p->name, size, p->cursize, p->arenasize, p->maxsize);
369 			return nil;
370 		}
371 		alloc = ns+ldr+ldr;
372 		p->arenasize += alloc;
373 	}
374 
375 	p->nbrk++;
376 	t = (Bhdr *)sbrk(alloc);
377 	if(t == (void*)-1) {
378 		p->nbrk--;
379 		unlock(&p->l);
380 		return nil;
381 	}
382 	/* Double alignment */
383 	t = (Bhdr *)(((ulong)t + 7) & ~7);
384 
385 	if(p->chain != nil && (char*)t-(char*)B2LIMIT(p->chain)-ldr == 0){
386 		/* can merge chains */
387 		if(0)print("merging chains %p and %p in %s\n", p->chain, t, p->name);
388 		q = B2LIMIT(p->chain);
389 		q->magic = MAGIC_A;
390 		q->size = alloc;
391 		B2T(q)->hdr = q;
392 		t = B2NB(q);
393 		t->magic = MAGIC_E;
394 		p->chain->csize += alloc;
395 		p->cursize += alloc;
396 		unlock(&p->l);
397 		poolfree(p, B2D(q));		/* for backward merge */
398 		return poolalloc(p, osize);
399 	}
400 
401 	t->magic = MAGIC_E;		/* Make a leader */
402 	t->size = ldr;
403 	t->csize = ns+ldr;
404 	t->clink = p->chain;
405 	p->chain = t;
406 	B2T(t)->hdr = t;
407 	t = B2NB(t);
408 
409 	t->magic = MAGIC_A;		/* Make the block we are going to return */
410 	t->size = size;
411 	B2T(t)->hdr = t;
412 	q = t;
413 
414 	ns -= size;			/* Free the rest */
415 	if(ns > 0) {
416 		q = B2NB(t);
417 		q->size = ns;
418 		B2T(q)->hdr = q;
419 		pooladd(p, q);
420 	}
421 	B2NB(q)->magic = MAGIC_E;	/* Mark the end of the chunk */
422 
423 	p->cursize += t->size;
424 	if(p->cursize > p->hw)
425 		p->hw = p->cursize;
426 	unlock(&p->l);
427 	if(p->monitor)
428 		MM(p->pnum, pc, (ulong)B2D(t), size);
429 	return B2D(t);
430 }
431 
432 void *
433 poolalloc(Pool *p, ulong asize)
434 {
435 	Prog *prog;
436 
437 	if(p->cursize > p->ressize && (prog = currun()) != nil && prog->flags&Prestricted)
438 		return nil;
439 	return dopoolalloc(p, asize, getcallerpc(&p));
440 }
441 
442 void
443 poolfree(Pool *p, void *v)
444 {
445 	Bhdr *b, *c;
446 	extern Bhdr *ptr;
447 
448 	D2B(b, v);
449 	if(p->monitor)
450 		MM(p->pnum|(1<<8), getcallerpc(&p), (ulong)v, b->size);
451 
452 	lock(&p->l);
453 	p->nfree++;
454 	p->cursize -= b->size;
455 	c = B2NB(b);
456 	if(c->magic == MAGIC_F) {	/* Join forward */
457 		if(c == ptr)
458 			ptr = b;
459 		pooldel(p, c);
460 		c->magic = 0;
461 		b->size += c->size;
462 		B2T(b)->hdr = b;
463 	}
464 
465 	c = B2PT(b)->hdr;
466 	if(c->magic == MAGIC_F) {	/* Join backward */
467 		if(b == ptr)
468 			ptr = c;
469 		pooldel(p, c);
470 		b->magic = 0;
471 		c->size += b->size;
472 		b = c;
473 		B2T(b)->hdr = b;
474 	}
475 	pooladd(p, b);
476 	unlock(&p->l);
477 }
478 
479 void *
480 poolrealloc(Pool *p, void *v, ulong size)
481 {
482 	Bhdr *b;
483 	void *nv;
484 	int osize;
485 
486 	if(size >= 1024*1024*1024)	/* for sanity and to avoid overflow */
487 		return nil;
488 	if(size == 0){
489 		poolfree(p, v);
490 		return nil;
491 	}
492 	SET(osize);
493 	if(v != nil){
494 		lock(&p->l);
495 		D2B(b, v);
496 		osize = b->size - BHDRSIZE;
497 		unlock(&p->l);
498 		if(osize >= size)
499 			return v;
500 	}
501 	nv = poolalloc(p, size);
502 	if(nv != nil && v != nil){
503 		memmove(nv, v, osize);
504 		poolfree(p, v);
505 	}
506 	return nv;
507 }
508 
509 ulong
510 poolmsize(Pool *p, void *v)
511 {
512 	Bhdr *b;
513 	ulong size;
514 
515 	if(v == nil)
516 		return 0;
517 	lock(&p->l);
518 	D2B(b, v);
519 	size = b->size - BHDRSIZE;
520 	unlock(&p->l);
521 	return size;
522 }
523 
524 static ulong
525 poolmax(Pool *p)
526 {
527 	Bhdr *t;
528 	ulong size;
529 
530 	lock(&p->l);
531 	size = p->maxsize - p->cursize;
532 	t = p->root;
533 	if(t != nil) {
534 		while(t->right != nil)
535 			t = t->right;
536 		if(size < t->size)
537 			size = t->size;
538 	}
539 	if(size >= BHDRSIZE)
540 		size -= BHDRSIZE;
541 	unlock(&p->l);
542 	return size;
543 }
544 
545 ulong
546 poolmaxsize(void)
547 {
548 	int i;
549 	ulong total;
550 
551 	total = 0;
552 	for(i = 0; i < nelem(table.pool); i++)
553 		total += table.pool[i].maxsize;
554 	return total;
555 }
556 
557 int
558 poolread(char *va, int count, ulong offset)
559 {
560 	Pool *p;
561 	int n, i, signed_off;
562 
563 	n = 0;
564 	signed_off = offset;
565 	for(i = 0; i < table.n; i++) {
566 		p = &table.pool[i];
567 		n += snprint(va+n, count-n, "%11lud %11lud %11lud %11lud %11lud %11d %11lud %s\n",
568 			p->cursize,
569 			p->maxsize,
570 			p->hw,
571 			p->nalloc,
572 			p->nfree,
573 			p->nbrk,
574 			poolmax(p),
575 			p->name);
576 
577 		if(signed_off > 0) {
578 			signed_off -= n;
579 			if(signed_off < 0) {
580 				memmove(va, va+n+signed_off, -signed_off);
581 				n = -signed_off;
582 			}
583 			else
584 				n = 0;
585 		}
586 
587 	}
588 	return n;
589 }
590 
591 void*
592 smalloc(size_t size)
593 {
594 	void *v;
595 
596 	for(;;){
597 		v = malloc(size);
598 		if(v != nil)
599 			break;
600 		if(0)
601 			print("smalloc waiting from %lux\n", getcallerpc(&size));
602 		osenter();
603 		osmillisleep(100);
604 		osleave();
605 	}
606 	setmalloctag(v, getcallerpc(&size));
607 	setrealloctag(v, 0);
608 	return v;
609 }
610 
611 void*
612 kmalloc(size_t size)
613 {
614 	void *v;
615 
616 	v = dopoolalloc(mainmem, size+Npadlong*sizeof(ulong), getcallerpc(&size));
617 	if(v != nil){
618 		ML(v, size, getcallerpc(&size));
619 		if(Npadlong){
620 			v = (ulong*)v+Npadlong;
621 			setmalloctag(v, getcallerpc(&size));
622 			setrealloctag(v, 0);
623 		}
624 		memset(v, 0, size);
625 		MM(0, getcallerpc(&size), (ulong)v, size);
626 	}
627 	return v;
628 }
629 
630 
631 
632 void*
633 malloc(size_t size)
634 {
635 	void *v;
636 
637 	v = poolalloc(mainmem, size+Npadlong*sizeof(ulong));
638 	if(v != nil){
639 		ML(v, size, getcallerpc(&size));
640 		if(Npadlong){
641 			v = (ulong*)v+Npadlong;
642 			setmalloctag(v, getcallerpc(&size));
643 			setrealloctag(v, 0);
644 		}
645 		memset(v, 0, size);
646 		MM(0, getcallerpc(&size), (ulong)v, size);
647 	} else
648 		print("malloc failed from %lux\n", getcallerpc(&size));
649 	return v;
650 }
651 
652 void*
653 mallocz(ulong size, int clr)
654 {
655 	void *v;
656 
657 	v = poolalloc(mainmem, size+Npadlong*sizeof(ulong));
658 	if(v != nil){
659 		ML(v, size, getcallerpc(&size));
660 		if(Npadlong){
661 			v = (ulong*)v+Npadlong;
662 			setmalloctag(v, getcallerpc(&size));
663 			setrealloctag(v, 0);
664 		}
665 		if(clr)
666 			memset(v, 0, size);
667 		MM(0, getcallerpc(&size), (ulong)v, size);
668 	} else
669 		print("mallocz failed from %lux\n", getcallerpc(&size));
670 	return v;
671 }
672 
673 void
674 free(void *v)
675 {
676 	Bhdr *b;
677 
678 	if(v != nil) {
679 		if(Npadlong)
680 			v = (ulong*)v-Npadlong;
681 		D2B(b, v);
682 		ML(v, 0, 0);
683 		MM(1<<8|0, getcallerpc(&v), (ulong)((ulong*)v+Npadlong), b->size);
684 		poolfree(mainmem, v);
685 	}
686 }
687 
688 void*
689 realloc(void *v, size_t size)
690 {
691 	void *nv;
692 
693 	if(size == 0)
694 		return malloc(size);	/* temporary change until realloc calls can be checked */
695 	if(v != nil)
696 		v = (ulong*)v-Npadlong;
697 	if(Npadlong!=0 && size!=0)
698 		size += Npadlong*sizeof(ulong);
699 	nv = poolrealloc(mainmem, v, size);
700 	ML(v, 0, 0);
701 	ML(nv, size, getcallerpc(&v));
702 	if(nv != nil) {
703 		nv = (ulong*)nv+Npadlong;
704 		setrealloctag(nv, getcallerpc(&v));
705 		if(v == nil)
706 			setmalloctag(v, getcallerpc(&v));
707 	} else
708 		print("realloc failed from %lux\n", getcallerpc(&v));
709 	return nv;
710 }
711 
712 void
713 setmalloctag(void *v, ulong pc)
714 {
715 	ulong *u;
716 
717 	USED(v);
718 	USED(pc);
719 	if(Npadlong <= MallocOffset || v == nil)
720 		return;
721 	u = v;
722 	u[-Npadlong+MallocOffset] = pc;
723 }
724 
725 ulong
726 getmalloctag(void *v)
727 {
728 	USED(v);
729 	if(Npadlong <= MallocOffset)
730 		return ~0;
731 	return ((ulong*)v)[-Npadlong+MallocOffset];
732 }
733 
734 void
735 setrealloctag(void *v, ulong pc)
736 {
737 	ulong *u;
738 
739 	USED(v);
740 	USED(pc);
741 	if(Npadlong <= ReallocOffset || v == nil)
742 		return;
743 	u = v;
744 	u[-Npadlong+ReallocOffset] = pc;
745 }
746 
747 ulong
748 getrealloctag(void *v)
749 {
750 	USED(v);
751 	if(Npadlong <= ReallocOffset)
752 		return ((ulong*)v)[-Npadlong+ReallocOffset];
753 	return ~0;
754 }
755 
756 ulong
757 msize(void *v)
758 {
759 	if(v == nil)
760 		return 0;
761 	return poolmsize(mainmem, (ulong*)v-Npadlong)-Npadlong*sizeof(ulong);
762 }
763 
764 void*
765 calloc(size_t n, size_t szelem)
766 {
767 	return malloc(n*szelem);
768 }
769 
770 /*
771 void
772 pooldump(Pool *p)
773 {
774 	Bhdr *b, *base, *limit, *ptr;
775 
776 	b = p->chain;
777 	if(b == nil)
778 		return;
779 	base = b;
780 	ptr = b;
781 	limit = B2LIMIT(b);
782 
783 	while(base != nil) {
784 		print("\tbase #%.8lux ptr #%.8lux", base, ptr);
785 		if(ptr->magic == MAGIC_A || ptr->magic == MAGIC_I)
786 			print("\tA%.5d\n", ptr->size);
787 		else if(ptr->magic == MAGIC_E)
788 			print("\tE\tL#%.8lux\tS#%.8lux\n", ptr->clink, ptr->csize);
789 		else
790 			print("\tF%.5d\tL#%.8lux\tR#%.8lux\tF#%.8lux\tP#%.8lux\tT#%.8lux\n",
791 				ptr->size, ptr->left, ptr->right, ptr->fwd, ptr->prev, ptr->parent);
792 		ptr = B2NB(ptr);
793 		if(ptr >= limit) {
794 			print("link to #%.8lux\n", base->clink);
795 			base = base->clink;
796 			if(base == nil)
797 				break;
798 			ptr = base;
799 			limit = B2LIMIT(base);
800 		}
801 	}
802 }
803 */
804 
805 void
806 poolsetcompact(Pool *p, void (*move)(void*, void*))
807 {
808 	p->move = move;
809 }
810 
811 int
812 poolcompact(Pool *pool)
813 {
814 	Bhdr *base, *limit, *ptr, *end, *next;
815 	int compacted, nb;
816 
817 	if(pool->move == nil || pool->lastfree == pool->nfree)
818 		return 0;
819 
820 	pool->lastfree = pool->nfree;
821 
822 	base = pool->chain;
823 	ptr = B2NB(base);	/* First Block in arena has clink */
824 	limit = B2LIMIT(base);
825 	compacted = 0;
826 
827 	pool->root = nil;
828 	end = ptr;
829 	while(base != nil) {
830 		next = B2NB(ptr);
831 		if(ptr->magic == MAGIC_A || ptr->magic == MAGIC_I) {
832 			if(ptr != end) {
833 				memmove(end, ptr, ptr->size);
834 				pool->move(B2D(ptr), B2D(end));
835 				compacted = 1;
836 			}
837 			end = B2NB(end);
838 		}
839 		if(next >= limit) {
840 			nb = (uchar*)limit - (uchar*)end;
841 			if(nb > 0){
842 				if(nb < pool->quanta+1){
843 					print("poolcompact: leftover too small\n");
844 					abort();
845 				}
846 				end->size = nb;
847 				B2T(end)->hdr = end;
848 				pooladd(pool, end);
849 			}
850 			base = base->clink;
851 			if(base == nil)
852 				break;
853 			ptr = B2NB(base);
854 			end = ptr;	/* could do better by copying between chains */
855 			limit = B2LIMIT(base);
856 		} else
857 			ptr = next;
858 	}
859 
860 	return compacted;
861 }
862 
863 static void
864 _poolfault(void *v, char *msg, ulong c)
865 {
866 	auditmemloc(msg, v);
867 	panic("%s %lux (from %lux/%lux)", msg, v, getcallerpc(&v), c);
868 }
869 
870 static void
871 dumpvl(char *msg, ulong *v, int n)
872 {
873 	int i, l;
874 
875 	l = print("%s at %p: ", msg, v);
876 	for (i = 0; i < n; i++) {
877 		if (l >= 60) {
878 			print("\n");
879 			l = print("    %p: ", v);
880 		}
881 		l += print(" %lux", *v++);
882 	}
883 	print("\n");
884 }
885 
886 static void
887 corrupted(char *str, char *msg, Pool *p, Bhdr *b, void *v)
888 {
889 	print("%s(%p): pool %s CORRUPT: %s at %p'%lud(magic=%lux)\n",
890 		str, v, p->name, msg, b, b->size, b->magic);
891 	dumpvl("bad Bhdr", (ulong *)((ulong)b & ~3)-4, 10);
892 }
893 
894 static void
895 _auditmemloc(char *str, void *v)
896 {
897 	Pool *p;
898 	Bhdr *bc, *ec, *b, *nb, *fb = nil;
899 	char *fmsg, *msg;
900 	ulong fsz;
901 
902 	SET(fsz);
903 	SET(fmsg);
904 	for (p = &table.pool[0]; p < &table.pool[nelem(table.pool)]; p++) {
905 		lock(&p->l);
906 		for (bc = p->chain; bc != nil; bc = bc->clink) {
907 			if (bc->magic != MAGIC_E) {
908 				unlock(&p->l);
909 				corrupted(str, "chain hdr!=MAGIC_E", p, bc, v);
910 				goto nextpool;
911 			}
912 			ec = B2LIMIT(bc);
913 			if (((Bhdr*)v >= bc) && ((Bhdr*)v < ec))
914 				goto found;
915 		}
916 		unlock(&p->l);
917 nextpool:	;
918 	}
919 	print("%s: %p not in pools\n", str, v);
920 	return;
921 
922 found:
923 	for (b = bc; b < ec; b = nb) {
924 		switch(b->magic) {
925 		case MAGIC_F:
926 			msg = "free blk";
927 			break;
928 		case MAGIC_I:
929 			msg = "immutable block";
930 			break;
931 		case MAGIC_A:
932 			msg = "block";
933 			break;
934 		default:
935 			if (b == bc && b->magic == MAGIC_E) {
936 				msg = "pool hdr";
937 				break;
938 			}
939 			unlock(&p->l);
940 			corrupted(str, "bad magic", p, b, v);
941 			goto badchunk;
942 		}
943 		if (b->size <= 0 || (b->size & p->quanta)) {
944 			unlock(&p->l);
945 			corrupted(str, "bad size", p, b, v);
946 			goto badchunk;
947 		}
948 		if (fb != nil)
949 			break;
950 		nb = B2NB(b);
951 		if ((Bhdr*)v < nb) {
952 			fb = b;
953 			fsz = b->size;
954 			fmsg = msg;
955 		}
956 	}
957 	unlock(&p->l);
958 	if (b >= ec) {
959 		if (b > ec)
960 			corrupted(str, "chain size mismatch", p, b, v);
961 		else if (b->magic != MAGIC_E)
962 			corrupted(str, "chain end!=MAGIC_E", p, b, v);
963 	}
964 badchunk:
965 	if (fb != nil) {
966 		print("%s: %p in %s:", str, v, p->name);
967 		if (fb == v)
968 			print(" is %s '%lux\n", fmsg, fsz);
969 		else
970 			print(" in %s at %p'%lux\n", fmsg, fb, fsz);
971 		dumpvl("area", (ulong *)((ulong)v & ~3)-4, 20);
972 	}
973 }
974 
975 char *
976 poolaudit(char*(*audit)(int, Bhdr *))
977 {
978 	Pool *p;
979 	Bhdr *bc, *ec, *b;
980 	char *r = nil;
981 
982 	for (p = &table.pool[0]; p < &table.pool[nelem(table.pool)]; p++) {
983 		lock(&p->l);
984 		for (bc = p->chain; bc != nil; bc = bc->clink) {
985 			if (bc->magic != MAGIC_E) {
986 				unlock(&p->l);
987 				return "bad chain hdr";
988 			}
989 			ec = B2LIMIT(bc);
990 			for (b = bc; b < ec; b = B2NB(b)) {
991 				if (b->size <= 0 || (b->size & p->quanta))
992 					r = "bad size in bhdr";
993 				else
994 					switch(b->magic) {
995 					case MAGIC_E:
996 						if (b != bc) {
997 							r = "unexpected MAGIC_E";
998 							break;
999 						}
1000 					case MAGIC_F:
1001 					case MAGIC_A:
1002 					case MAGIC_I:
1003 						r = audit(p->pnum, b);
1004 						break;
1005 					default:
1006 						r = "bad magic";
1007 					}
1008 				if (r != nil) {
1009 					unlock(&p->l);
1010 					return r;
1011 				}
1012 			}
1013 			if (b != ec || b->magic != MAGIC_E) {
1014 				unlock(&p->l);
1015 				return "bad chain ending";
1016 			}
1017 		}
1018 		unlock(&p->l);
1019 	}
1020 	return r;
1021 }
1022