/* memory allocation routines * Copyright 1991 Phil Karn, KA9Q * * Adapted from alloc routine in K&R; memory statistics and interrupt * protection added for use with net package. Must be used in place of * standard Turbo-C library routines because the latter check for stack/heap * collisions. This causes erroneous failures because process stacks are * allocated off the heap. */ #include #include #include #include "global.h" #include "mbuf.h" #include "proc.h" #include "cmdparse.h" static unsigned long Memfail; /* Count of allocation failures */ static unsigned long Allocs; /* Total allocations */ static unsigned long Frees; /* Total frees */ static unsigned long Invalid; /* Total calls to free with garbage arg */ static unsigned long Intalloc; /* Calls to malloc with ints disabled */ static unsigned long Intfree; /* Calls to free with ints disabled */ static int Memwait; /* Number of tasks waiting for memory */ static unsigned long Yellows; /* Yellow alert garbage collections */ static unsigned long Reds; /* Red alert garbage collections */ unsigned long Availmem; /* Heap memory, ABLKSIZE units */ static unsigned long Morecores; static unsigned long Sizes[16]; static int dostat __ARGS((int argc,char *argv[],void *p)); static int dofreelist __ARGS((int argc,char *argv[],void *p)); static int doibufsize __ARGS((int argc,char *argv[],void *p)); static int donibufs __ARGS((int argc,char *argv[],void *p)); static int dothresh __ARGS((int argc,char *argv[],void *p)); static int dosizes __ARGS((int argc,char *argv[],void *p)); struct cmds Memcmds[] = { "freelist", dofreelist, 0, 0, NULLCHAR, "ibufsize", doibufsize, 0, 0, NULLCHAR, "nibufs", donibufs, 0, 0, NULLCHAR, "sizes", dosizes, 0, 0, NULLCHAR, "status", dostat, 0, 0, NULLCHAR, "thresh", dothresh, 0, 0, NULLCHAR, NULLCHAR, }; #ifdef LARGEDATA #define HUGE huge #else #define HUGE #endif union header { struct { union header HUGE *ptr; unsigned long size; } s; long l[2]; }; typedef union header HEADER; #define NULLHDR (HEADER HUGE *)NULL #define ABLKSIZE (sizeof (HEADER)) static HEADER HUGE *morecore __ARGS((unsigned nu)); static HEADER Base; static HEADER HUGE *Allocp = NULLHDR; static unsigned long Heapsize; /* Allocate block of 'nb' bytes */ void * malloc(nb) unsigned nb; { register HEADER HUGE *p, HUGE *q; register unsigned nu; int i; if(!istate()) Intalloc++; if(nb == 0) return NULL; /* Record the size of this request */ if((i = log2(nb)) >= 0) Sizes[i]++; /* Round up to full block, then add one for header */ nu = ((nb + ABLKSIZE - 1) / ABLKSIZE) + 1; if((q = Allocp) == NULLHDR){ Base.s.ptr = Allocp = q = &Base; Base.s.size = 1; } for(p = q->s.ptr; ; q = p, p = p->s.ptr){ if(p->s.size >= nu){ /* This chunk is at least as large as we need */ if(p->s.size <= nu + 1){ /* This is either a perfect fit (size == nu) * or the free chunk is just one unit larger. * In either case, alloc the whole thing, * because there's no point in keeping a free * block only large enough to hold the header. */ q->s.ptr = p->s.ptr; } else { /* Carve out piece from end of entry */ p->s.size -= nu; p += p->s.size; p->s.size = nu; } #ifdef circular Allocp = q; #endif p->s.ptr = p; /* for auditing */ Allocs++; Availmem -= p->s.size; p++; break; } if(p == Allocp && ((p = morecore(nu)) == NULLHDR)){ Memfail++; break; } } #ifdef LARGEDATA /* On the brain-damaged Intel CPUs in "large data" model, * make sure the pointer's offset field isn't null * (unless the entire pointer is null). * The Turbo C compiler and certain * library functions like strrchr() assume this. */ if(FP_OFF(p) == 0 && FP_SEG(p) != 0){ /* Return denormalized but equivalent pointer */ return (void *)MK_FP(FP_SEG(p)-1,16); } #endif return (void *)p; } /* Get more memory from the system and put it on the heap */ static HEADER HUGE * morecore(nu) unsigned nu; { register char HUGE *cp; register HEADER HUGE *up; Morecores++; if ((int)(cp = (char HUGE *)sbrk(nu * ABLKSIZE)) == -1) return NULLHDR; up = (HEADER *)cp; up->s.size = nu; up->s.ptr = up; /* satisfy audit */ free((void *)(up + 1)); Heapsize += nu*ABLKSIZE; Frees--; /* Nullify increment inside free() */ return Allocp; } /* Put memory block back on heap */ void free(blk) void *blk; { register HEADER HUGE *p, HUGE *q; unsigned short HUGE *ptr; if(!istate()) Intfree++; if(blk == NULL) return; /* Required by ANSI */ p = (HEADER HUGE *)blk - 1; /* Audit check */ if(p->s.ptr != p){ ptr = (unsigned short *)&blk; printf("free: WARNING! invalid pointer (0x%lx) proc %s\n", ptol(blk),Curproc->name); stktrace(); Invalid++; log(-1,"free: WARNING! invalid pointer (0x%lx) pc = 0x%x %x proc %s\n", ptol(blk),ptr[-1],ptr[-2],Curproc->name); return; } Availmem += p->s.size; /* Search the free list looking for the right place to insert */ for(q = Allocp; !(p > q && p < q->s.ptr); q = q->s.ptr){ /* Highest address on circular list? */ if(q >= q->s.ptr && (p > q || p < q->s.ptr)) break; } if(p + p->s.size == q->s.ptr){ /* Combine with front of this entry */ p->s.size += q->s.ptr->s.size; p->s.ptr = q->s.ptr->s.ptr; } else { /* Link to front of this entry */ p->s.ptr = q->s.ptr; } if(q + q->s.size == p){ /* Combine with end of this entry */ q->s.size += p->s.size; q->s.ptr = p->s.ptr; } else { /* Link to end of this entry */ q->s.ptr = p; } #ifdef circular Allocp = q; #endif Frees++; if(Memwait != 0) psignal(&Memwait,0); } #ifdef notdef /* Not presently used */ /* Move existing block to new area */ void * realloc(area,size) void *area; unsigned size; { unsigned osize; HEADER HUGE *hp; char HUGE *cp; hp = ((HEADER *)area) - 1; osize = (hp->s.size -1) * ABLKSIZE; free(area); if((cp = malloc(size)) != NULL && cp != area) memcpy((char *)cp,(char *)area,size>osize? osize : size); return cp; } #endif /* Allocate block of cleared memory */ void * calloc(nelem,size) unsigned nelem; /* Number of elements */ unsigned size; /* Size of each element */ { register unsigned i; register char *cp; i = nelem * size; if((cp = malloc(i)) != NULL) memset(cp,0,i); return cp; } /* Version of malloc() that waits if necessary for memory to become available */ void * mallocw(nb) unsigned nb; { register void *p; while((p = malloc(nb)) == NULL){ Memwait++; pwait(&Memwait); Memwait--; } return p; } /* Version of calloc that waits if necessary for memory to become available */ void * callocw(nelem,size) unsigned nelem; /* Number of elements */ unsigned size; /* Size of each element */ { register unsigned i; register char *cp; i = nelem * size; cp = mallocw(i); memset(cp,0,i); return cp; } /* Return available memory on our heap plus available system memory */ unsigned long availmem() { return Availmem * ABLKSIZE + coreleft(); } /* Print heap stats */ static int dostat(argc,argv,envp) int argc; char *argv[]; void *envp; { tprintf("heap size %lu avail %lu (%lu%%) morecores %lu coreleft %lu\n", Heapsize,Availmem * ABLKSIZE,100L*Availmem*ABLKSIZE/Heapsize, Morecores,coreleft()); tprintf("allocs %lu frees %lu (diff %lu) alloc fails %lu invalid frees %lu\n", Allocs,Frees,Allocs-Frees,Memfail,Invalid); tprintf("interrupts-off calls to malloc %lu free %lu\n",Intalloc,Intfree); tprintf("garbage collections yellow %lu red %lu\n",Yellows,Reds); iqstat(); return 0; } /* Print heap free list */ static int dofreelist(argc,argv,envp) int argc; char *argv[]; void *envp; { HEADER HUGE *p; int i = 0; for(p = Base.s.ptr;p != &Base;p = p->s.ptr){ tprintf("%5lx %6lu",ptol((void *)p),p->s.size * ABLKSIZE); if(++i == 4){ i = 0; if(tprintf("\n") == EOF) return 0; } else tprintf(" | "); } if(i != 0) tprintf("\n"); return 0; } static int dosizes(argc,argv,p) int argc; char *argv[]; void *p; { int i; for(i=0;i<16;i += 4){ tprintf("N>=%5u:%7ld| N>=%5u:%7ld| N>=%5u:%7ld| N>=%5u:%7ld\n", 1<