xref: /inferno-os/emu/port/alloc.c (revision 5d0c4cf3fc288434c41cba52dd998ab1d7375a7b)
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,   64*1024*1024+256, 31, 4*1024*1024, 1, 63*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 #ifdef __NetBSD__
383 	/* Align allocations to 16 bytes */
384 	{
385 		const size_t off = __builtin_offsetof(struct Bhdr, u.data)
386 					+ Npadlong*sizeof(ulong);
387 		struct assert_align {
388 			unsigned int align_ok : (off % 8 == 0) ? 1 : -1;
389 		};
390 
391 		const ulong align = (off - 1) % 16;
392 		t = (Bhdr *)(((ulong)t + align) & ~align);
393 	}
394 #else
395 	/* Double alignment */
396 	t = (Bhdr *)(((ulong)t + 7) & ~7);
397 #endif
398 	if(p->chain != nil && (char*)t-(char*)B2LIMIT(p->chain)-ldr == 0){
399 		/* can merge chains */
400 		if(0)print("merging chains %p and %p in %s\n", p->chain, t, p->name);
401 		q = B2LIMIT(p->chain);
402 		q->magic = MAGIC_A;
403 		q->size = alloc;
404 		B2T(q)->hdr = q;
405 		t = B2NB(q);
406 		t->magic = MAGIC_E;
407 		p->chain->csize += alloc;
408 		p->cursize += alloc;
409 		unlock(&p->l);
410 		poolfree(p, B2D(q));		/* for backward merge */
411 		return poolalloc(p, osize);
412 	}
413 
414 	t->magic = MAGIC_E;		/* Make a leader */
415 	t->size = ldr;
416 	t->csize = ns+ldr;
417 	t->clink = p->chain;
418 	p->chain = t;
419 	B2T(t)->hdr = t;
420 	t = B2NB(t);
421 
422 	t->magic = MAGIC_A;		/* Make the block we are going to return */
423 	t->size = size;
424 	B2T(t)->hdr = t;
425 	q = t;
426 
427 	ns -= size;			/* Free the rest */
428 	if(ns > 0) {
429 		q = B2NB(t);
430 		q->size = ns;
431 		B2T(q)->hdr = q;
432 		pooladd(p, q);
433 	}
434 	B2NB(q)->magic = MAGIC_E;	/* Mark the end of the chunk */
435 
436 	p->cursize += t->size;
437 	if(p->cursize > p->hw)
438 		p->hw = p->cursize;
439 	unlock(&p->l);
440 	if(p->monitor)
441 		MM(p->pnum, pc, (ulong)B2D(t), size);
442 	return B2D(t);
443 }
444 
445 void *
446 poolalloc(Pool *p, ulong asize)
447 {
448 	Prog *prog;
449 
450 	if(p->cursize > p->ressize && (prog = currun()) != nil && prog->flags&Prestricted)
451 		return nil;
452 	return dopoolalloc(p, asize, getcallerpc(&p));
453 }
454 
455 void
456 poolfree(Pool *p, void *v)
457 {
458 	Bhdr *b, *c;
459 	extern Bhdr *ptr;
460 
461 	D2B(b, v);
462 	if(p->monitor)
463 		MM(p->pnum|(1<<8), getcallerpc(&p), (ulong)v, b->size);
464 
465 	lock(&p->l);
466 	p->nfree++;
467 	p->cursize -= b->size;
468 	c = B2NB(b);
469 	if(c->magic == MAGIC_F) {	/* Join forward */
470 		if(c == ptr)
471 			ptr = b;
472 		pooldel(p, c);
473 		c->magic = 0;
474 		b->size += c->size;
475 		B2T(b)->hdr = b;
476 	}
477 
478 	c = B2PT(b)->hdr;
479 	if(c->magic == MAGIC_F) {	/* Join backward */
480 		if(b == ptr)
481 			ptr = c;
482 		pooldel(p, c);
483 		b->magic = 0;
484 		c->size += b->size;
485 		b = c;
486 		B2T(b)->hdr = b;
487 	}
488 	pooladd(p, b);
489 	unlock(&p->l);
490 }
491 
492 void *
493 poolrealloc(Pool *p, void *v, ulong size)
494 {
495 	Bhdr *b;
496 	void *nv;
497 	int osize;
498 
499 	if(size >= 1024*1024*1024)	/* for sanity and to avoid overflow */
500 		return nil;
501 	if(size == 0){
502 		poolfree(p, v);
503 		return nil;
504 	}
505 	SET(osize);
506 	if(v != nil){
507 		lock(&p->l);
508 		D2B(b, v);
509 		osize = b->size - BHDRSIZE;
510 		unlock(&p->l);
511 		if(osize >= size)
512 			return v;
513 	}
514 	nv = poolalloc(p, size);
515 	if(nv != nil && v != nil){
516 		memmove(nv, v, osize);
517 		poolfree(p, v);
518 	}
519 	return nv;
520 }
521 
522 ulong
523 poolmsize(Pool *p, void *v)
524 {
525 	Bhdr *b;
526 	ulong size;
527 
528 	if(v == nil)
529 		return 0;
530 	lock(&p->l);
531 	D2B(b, v);
532 	size = b->size - BHDRSIZE;
533 	unlock(&p->l);
534 	return size;
535 }
536 
537 static ulong
538 poolmax(Pool *p)
539 {
540 	Bhdr *t;
541 	ulong size;
542 
543 	lock(&p->l);
544 	size = p->maxsize - p->cursize;
545 	t = p->root;
546 	if(t != nil) {
547 		while(t->right != nil)
548 			t = t->right;
549 		if(size < t->size)
550 			size = t->size;
551 	}
552 	if(size >= BHDRSIZE)
553 		size -= BHDRSIZE;
554 	unlock(&p->l);
555 	return size;
556 }
557 
558 ulong
559 poolmaxsize(void)
560 {
561 	int i;
562 	ulong total;
563 
564 	total = 0;
565 	for(i = 0; i < nelem(table.pool); i++)
566 		total += table.pool[i].maxsize;
567 	return total;
568 }
569 
570 int
571 poolread(char *va, int count, ulong offset)
572 {
573 	Pool *p;
574 	int n, i, signed_off;
575 
576 	n = 0;
577 	signed_off = offset;
578 	for(i = 0; i < table.n; i++) {
579 		p = &table.pool[i];
580 		n += snprint(va+n, count-n, "%11lud %11lud %11lud %11lud %11lud %11d %11lud %s\n",
581 			p->cursize,
582 			p->maxsize,
583 			p->hw,
584 			p->nalloc,
585 			p->nfree,
586 			p->nbrk,
587 			poolmax(p),
588 			p->name);
589 
590 		if(signed_off > 0) {
591 			signed_off -= n;
592 			if(signed_off < 0) {
593 				memmove(va, va+n+signed_off, -signed_off);
594 				n = -signed_off;
595 			}
596 			else
597 				n = 0;
598 		}
599 
600 	}
601 	return n;
602 }
603 
604 void*
605 smalloc(size_t size)
606 {
607 	void *v;
608 
609 	for(;;){
610 		v = malloc(size);
611 		if(v != nil)
612 			break;
613 		if(0)
614 			print("smalloc waiting from %lux\n", getcallerpc(&size));
615 		osenter();
616 		osmillisleep(100);
617 		osleave();
618 	}
619 	setmalloctag(v, getcallerpc(&size));
620 	setrealloctag(v, 0);
621 	return v;
622 }
623 
624 void*
625 kmalloc(size_t size)
626 {
627 	void *v;
628 
629 	v = dopoolalloc(mainmem, size+Npadlong*sizeof(ulong), getcallerpc(&size));
630 	if(v != nil){
631 		ML(v, size, getcallerpc(&size));
632 		if(Npadlong){
633 			v = (ulong*)v+Npadlong;
634 			setmalloctag(v, getcallerpc(&size));
635 			setrealloctag(v, 0);
636 		}
637 		memset(v, 0, size);
638 		MM(0, getcallerpc(&size), (ulong)v, size);
639 	}
640 	return v;
641 }
642 
643 
644 
645 void*
646 malloc(size_t size)
647 {
648 	void *v;
649 
650 	v = poolalloc(mainmem, size+Npadlong*sizeof(ulong));
651 	if(v != nil){
652 		ML(v, size, getcallerpc(&size));
653 		if(Npadlong){
654 			v = (ulong*)v+Npadlong;
655 			setmalloctag(v, getcallerpc(&size));
656 			setrealloctag(v, 0);
657 		}
658 		memset(v, 0, size);
659 		MM(0, getcallerpc(&size), (ulong)v, size);
660 	} else
661 		print("malloc failed from %lux\n", getcallerpc(&size));
662 	return v;
663 }
664 
665 void*
666 mallocz(ulong size, int clr)
667 {
668 	void *v;
669 
670 	v = poolalloc(mainmem, size+Npadlong*sizeof(ulong));
671 	if(v != nil){
672 		ML(v, size, getcallerpc(&size));
673 		if(Npadlong){
674 			v = (ulong*)v+Npadlong;
675 			setmalloctag(v, getcallerpc(&size));
676 			setrealloctag(v, 0);
677 		}
678 		if(clr)
679 			memset(v, 0, size);
680 		MM(0, getcallerpc(&size), (ulong)v, size);
681 	} else
682 		print("mallocz failed from %lux\n", getcallerpc(&size));
683 	return v;
684 }
685 
686 void
687 free(void *v)
688 {
689 	Bhdr *b;
690 
691 	if(v != nil) {
692 		if(Npadlong)
693 			v = (ulong*)v-Npadlong;
694 		D2B(b, v);
695 		ML(v, 0, 0);
696 		MM(1<<8|0, getcallerpc(&v), (ulong)((ulong*)v+Npadlong), b->size);
697 		poolfree(mainmem, v);
698 	}
699 }
700 
701 void*
702 realloc(void *v, size_t size)
703 {
704 	void *nv;
705 
706 	if(size == 0)
707 		return malloc(size);	/* temporary change until realloc calls can be checked */
708 	if(v != nil)
709 		v = (ulong*)v-Npadlong;
710 	if(Npadlong!=0 && size!=0)
711 		size += Npadlong*sizeof(ulong);
712 	nv = poolrealloc(mainmem, v, size);
713 	ML(v, 0, 0);
714 	ML(nv, size, getcallerpc(&v));
715 	if(nv != nil) {
716 		nv = (ulong*)nv+Npadlong;
717 		setrealloctag(nv, getcallerpc(&v));
718 		if(v == nil)
719 			setmalloctag(v, getcallerpc(&v));
720 	} else
721 		print("realloc failed from %lux\n", getcallerpc(&v));
722 	return nv;
723 }
724 
725 void
726 setmalloctag(void *v, ulong pc)
727 {
728 	ulong *u;
729 
730 	USED(v);
731 	USED(pc);
732 	if(Npadlong <= MallocOffset || v == nil)
733 		return;
734 	u = v;
735 	u[-Npadlong+MallocOffset] = pc;
736 }
737 
738 ulong
739 getmalloctag(void *v)
740 {
741 	USED(v);
742 	if(Npadlong <= MallocOffset)
743 		return ~0;
744 	return ((ulong*)v)[-Npadlong+MallocOffset];
745 }
746 
747 void
748 setrealloctag(void *v, ulong pc)
749 {
750 	ulong *u;
751 
752 	USED(v);
753 	USED(pc);
754 	if(Npadlong <= ReallocOffset || v == nil)
755 		return;
756 	u = v;
757 	u[-Npadlong+ReallocOffset] = pc;
758 }
759 
760 ulong
761 getrealloctag(void *v)
762 {
763 	USED(v);
764 	if(Npadlong <= ReallocOffset)
765 		return ((ulong*)v)[-Npadlong+ReallocOffset];
766 	return ~0;
767 }
768 
769 ulong
770 msize(void *v)
771 {
772 	if(v == nil)
773 		return 0;
774 	return poolmsize(mainmem, (ulong*)v-Npadlong)-Npadlong*sizeof(ulong);
775 }
776 
777 void*
778 calloc(size_t n, size_t szelem)
779 {
780 	return malloc(n*szelem);
781 }
782 
783 /*
784 void
785 pooldump(Pool *p)
786 {
787 	Bhdr *b, *base, *limit, *ptr;
788 
789 	b = p->chain;
790 	if(b == nil)
791 		return;
792 	base = b;
793 	ptr = b;
794 	limit = B2LIMIT(b);
795 
796 	while(base != nil) {
797 		print("\tbase #%.8lux ptr #%.8lux", base, ptr);
798 		if(ptr->magic == MAGIC_A || ptr->magic == MAGIC_I)
799 			print("\tA%.5d\n", ptr->size);
800 		else if(ptr->magic == MAGIC_E)
801 			print("\tE\tL#%.8lux\tS#%.8lux\n", ptr->clink, ptr->csize);
802 		else
803 			print("\tF%.5d\tL#%.8lux\tR#%.8lux\tF#%.8lux\tP#%.8lux\tT#%.8lux\n",
804 				ptr->size, ptr->left, ptr->right, ptr->fwd, ptr->prev, ptr->parent);
805 		ptr = B2NB(ptr);
806 		if(ptr >= limit) {
807 			print("link to #%.8lux\n", base->clink);
808 			base = base->clink;
809 			if(base == nil)
810 				break;
811 			ptr = base;
812 			limit = B2LIMIT(base);
813 		}
814 	}
815 }
816 */
817 
818 void
819 poolsetcompact(Pool *p, void (*move)(void*, void*))
820 {
821 	p->move = move;
822 }
823 
824 int
825 poolcompact(Pool *pool)
826 {
827 	Bhdr *base, *limit, *ptr, *end, *next;
828 	int compacted, nb;
829 
830 	if(pool->move == nil || pool->lastfree == pool->nfree)
831 		return 0;
832 
833 	pool->lastfree = pool->nfree;
834 
835 	base = pool->chain;
836 	ptr = B2NB(base);	/* First Block in arena has clink */
837 	limit = B2LIMIT(base);
838 	compacted = 0;
839 
840 	pool->root = nil;
841 	end = ptr;
842 	while(base != nil) {
843 		next = B2NB(ptr);
844 		if(ptr->magic == MAGIC_A || ptr->magic == MAGIC_I) {
845 			if(ptr != end) {
846 				memmove(end, ptr, ptr->size);
847 				pool->move(B2D(ptr), B2D(end));
848 				compacted = 1;
849 			}
850 			end = B2NB(end);
851 		}
852 		if(next >= limit) {
853 			nb = (uchar*)limit - (uchar*)end;
854 			if(nb > 0){
855 				if(nb < pool->quanta+1){
856 					print("poolcompact: leftover too small\n");
857 					abort();
858 				}
859 				end->size = nb;
860 				B2T(end)->hdr = end;
861 				pooladd(pool, end);
862 			}
863 			base = base->clink;
864 			if(base == nil)
865 				break;
866 			ptr = B2NB(base);
867 			end = ptr;	/* could do better by copying between chains */
868 			limit = B2LIMIT(base);
869 		} else
870 			ptr = next;
871 	}
872 
873 	return compacted;
874 }
875 
876 static void
877 _poolfault(void *v, char *msg, ulong c)
878 {
879 	auditmemloc(msg, v);
880 	panic("%s %lux (from %lux/%lux)", msg, v, getcallerpc(&v), c);
881 }
882 
883 static void
884 dumpvl(char *msg, ulong *v, int n)
885 {
886 	int i, l;
887 
888 	l = print("%s at %p: ", msg, v);
889 	for (i = 0; i < n; i++) {
890 		if (l >= 60) {
891 			print("\n");
892 			l = print("    %p: ", v);
893 		}
894 		l += print(" %lux", *v++);
895 	}
896 	print("\n");
897 }
898 
899 static void
900 corrupted(char *str, char *msg, Pool *p, Bhdr *b, void *v)
901 {
902 	print("%s(%p): pool %s CORRUPT: %s at %p'%lud(magic=%lux)\n",
903 		str, v, p->name, msg, b, b->size, b->magic);
904 	dumpvl("bad Bhdr", (ulong *)((ulong)b & ~3)-4, 10);
905 }
906 
907 static void
908 _auditmemloc(char *str, void *v)
909 {
910 	Pool *p;
911 	Bhdr *bc, *ec, *b, *nb, *fb = nil;
912 	char *fmsg, *msg;
913 	ulong fsz;
914 
915 	SET(fsz);
916 	SET(fmsg);
917 	for (p = &table.pool[0]; p < &table.pool[nelem(table.pool)]; p++) {
918 		lock(&p->l);
919 		for (bc = p->chain; bc != nil; bc = bc->clink) {
920 			if (bc->magic != MAGIC_E) {
921 				unlock(&p->l);
922 				corrupted(str, "chain hdr!=MAGIC_E", p, bc, v);
923 				goto nextpool;
924 			}
925 			ec = B2LIMIT(bc);
926 			if (((Bhdr*)v >= bc) && ((Bhdr*)v < ec))
927 				goto found;
928 		}
929 		unlock(&p->l);
930 nextpool:	;
931 	}
932 	print("%s: %p not in pools\n", str, v);
933 	return;
934 
935 found:
936 	for (b = bc; b < ec; b = nb) {
937 		switch(b->magic) {
938 		case MAGIC_F:
939 			msg = "free blk";
940 			break;
941 		case MAGIC_I:
942 			msg = "immutable block";
943 			break;
944 		case MAGIC_A:
945 			msg = "block";
946 			break;
947 		default:
948 			if (b == bc && b->magic == MAGIC_E) {
949 				msg = "pool hdr";
950 				break;
951 			}
952 			unlock(&p->l);
953 			corrupted(str, "bad magic", p, b, v);
954 			goto badchunk;
955 		}
956 		if (b->size <= 0 || (b->size & p->quanta)) {
957 			unlock(&p->l);
958 			corrupted(str, "bad size", p, b, v);
959 			goto badchunk;
960 		}
961 		if (fb != nil)
962 			break;
963 		nb = B2NB(b);
964 		if ((Bhdr*)v < nb) {
965 			fb = b;
966 			fsz = b->size;
967 			fmsg = msg;
968 		}
969 	}
970 	unlock(&p->l);
971 	if (b >= ec) {
972 		if (b > ec)
973 			corrupted(str, "chain size mismatch", p, b, v);
974 		else if (b->magic != MAGIC_E)
975 			corrupted(str, "chain end!=MAGIC_E", p, b, v);
976 	}
977 badchunk:
978 	if (fb != nil) {
979 		print("%s: %p in %s:", str, v, p->name);
980 		if (fb == v)
981 			print(" is %s '%lux\n", fmsg, fsz);
982 		else
983 			print(" in %s at %p'%lux\n", fmsg, fb, fsz);
984 		dumpvl("area", (ulong *)((ulong)v & ~3)-4, 20);
985 	}
986 }
987 
988 char *
989 poolaudit(char*(*audit)(int, Bhdr *))
990 {
991 	Pool *p;
992 	Bhdr *bc, *ec, *b;
993 	char *r = nil;
994 
995 	for (p = &table.pool[0]; p < &table.pool[nelem(table.pool)]; p++) {
996 		lock(&p->l);
997 		for (bc = p->chain; bc != nil; bc = bc->clink) {
998 			if (bc->magic != MAGIC_E) {
999 				unlock(&p->l);
1000 				return "bad chain hdr";
1001 			}
1002 			ec = B2LIMIT(bc);
1003 			for (b = bc; b < ec; b = B2NB(b)) {
1004 				if (b->size <= 0 || (b->size & p->quanta))
1005 					r = "bad size in bhdr";
1006 				else
1007 					switch(b->magic) {
1008 					case MAGIC_E:
1009 						if (b != bc) {
1010 							r = "unexpected MAGIC_E";
1011 							break;
1012 						}
1013 					case MAGIC_F:
1014 					case MAGIC_A:
1015 					case MAGIC_I:
1016 						r = audit(p->pnum, b);
1017 						break;
1018 					default:
1019 						r = "bad magic";
1020 					}
1021 				if (r != nil) {
1022 					unlock(&p->l);
1023 					return r;
1024 				}
1025 			}
1026 			if (b != ec || b->magic != MAGIC_E) {
1027 				unlock(&p->l);
1028 				return "bad chain ending";
1029 			}
1030 		}
1031 		unlock(&p->l);
1032 	}
1033 	return r;
1034 }
1035