/* Combine malloc.c, prt_mem.c and _malloc.h into one file. */ /* Then remove abort() in favor of Error(). - johns, 12/24/92*/ /* Throw out the whole thing if USE_SYSTEM_MALLOC is defined. */ #ifdef USE_SYSTEM_MALLOC #include #include #include #include #include "Msgs.h" #ifndef __INSIGHT__ int malloc_debug(int i){ return -1; } int malloc_verify(void){return 0;} void malloc_print(void){Msg_do("Can't print malloc structures for system malloc\n");} #endif size_t malloc_avail(void){return -1;} size_t malloc_heapsz(void) { char fname[128]; char line[128]; FILE *fp; int ret; size_t size = 0; sprintf(fname, "/proc/%d/status", getpid()); if ((fp = fopen(fname, "r")) == NULL) return 0; while (fgets(line, sizeof(line), fp)) { ret = sscanf(line, "VmPeak: %ld kB", &size); if (ret == 1) break; } fclose(fp); return size*1024; } size_t malloc_used(void) { char fname[128]; char line[128]; FILE *fp; int ret; size_t size = 0; sprintf(fname, "/proc/%d/status", getpid()); if ((fp = fopen(fname, "r")) == NULL) return 0; while (fgets(line, sizeof(line), fp)) { ret = sscanf(line, "VmRSS: %ld kB", &size); if (ret == 1) break; } fclose(fp); return size*1024; } /* Do not define malloc, calloc, realloc or free!!! */ #else /* USE_SYSTEM_MALLOC */ #include #include #include #include #include "malloc.h" #include "protos.h" #include "Msgs.h" #include "error.h" #define ALIGN 8 /* Knuth's_c must be >= (sizeof(fheader_t) + sizeof(aheader_t)) */ /* See p. 438 of Knuth, and the soln to exercise 12. */ /* When equal to the above amount, */ /* the control data in a free block created */ /* at the end of a not-quite-filled block completely fills the block. */ /* Since an aheader_t is smaller than an fheader_t, you can still */ /* get it if you ask for <= (sizeof(fheader_t) - sizeof(aheader_t)) */ #define KNUTHS_C (sizeof(fheader_t) + sizeof(aheader_t)) #define FREE '-' #define INUSE '+' /* The CHUNKSIZE is the amount of memory allocated */ /* by each call to sbrk. It should be reasonably large, */ /* but not so large that it is likely to fail. */ #ifdef __NCUBE1__ #define CHUNKSIZE (16*1024) #else #define CHUNKSIZE (256*1024*1024) #endif /* MAXCHUNKS is the maximum number of chunks */ /* that may be obtained. The value is used */ /* exclusively for the size of an array with */ /* information describing each chunk. With a */ /* little cleverness, we could chain the chunks */ /* together, but it hardly seems worth it since */ /* the only purpose of this information is to */ /* allow debugging/printing of diagnostics. */ /* Now that chunks get coallesced when sbrk has */ /* not been called by the user, there is even less */ /* reason for MAXCHUNKS to be large... */ #define MAXCHUNKS 8 /* If any of these typedefs are changed, be sure that */ /* sizeof(header_t) and sizeof(trailer_t) both are multiples */ /* of ALIGN */ typedef int tag_t; /* An aheader is the header at the beginning of an */ /* allocated block. It must be identical to an fheader, */ /* except that allocated fields don't need linking info */ /* WARNING: If this struct is changed, it must also be */ /* changed in ../stdio/prt_mem.c!!!! */ typedef struct aheader{ tag_t tag; size_t size; } aheader_t ; typedef struct fheader{ tag_t tag; /* The tag is a single bit */ size_t size; /* it should be merged into size */ struct fheader *link; struct fheader *prev; } fheader_t; typedef struct trailer{ tag_t tag; size_t size; } trailer_t; /* Each piece of memory is preceded by a header, and followed */ /* by a trailer. If the piece is a free block, the tag fields */ /* in the header and trailer are FREE, otherwise, they are INUSE */ /* For free blocks, the size field in the trailer is valid, but it */ /* is not necessarily valid for allocated blocks. */ /* The point of all this is to make freeing an object take */ /* constant time. One can examine the trailer that immediately */ /* precedes the to-be-freed object, and determine whether the */ /* block immediately below should be coalesced. Similarly for */ /* the block immediately above. */ /* The avail fheader_t is special. Its link field */ /* points to the front of the free list. Its prev field */ /* points to the end of the free list, and its space field */ /* contains the total available space in the free list, so */ /* mall_avail runs quickly. Unfortunately, mall_avail can't*/ /* give any info about fragmentation. It also doesn't */ /* call getrlimit, although it might be useful to ask the */ /* OS exactly how much farther we will be able to extend */ /* memory. */ /* Export */ /* In addition to malloc, calloc, realloc, free */ char malloc_errstring[256]; int malloc_verify(void); void malloc_print(void); int malloc_debug(int); /* The avail fheader_t is special. Its link field */ /* points to the front of the free list. Its prev field */ /* points to the end of the free list, and its space field */ /* contains the total available space in the free list, so */ /* mall_avail runs quickly. Unfortunately, mall_avail can't*/ /* give any info about fragmentation. */ static fheader_t avail = { FREE, /* tag */ 0, /* size */ &avail, /* link */ &avail /* prev */ }; /* The last entry in the chunk_tbl is special */ struct _chunk_desc{ size_t size; void *begin; }; static fheader_t *rover = &avail; static size_t chunksize = CHUNKSIZE; static int debug_lev = 1; static int debug_verify; static int debug_abort_nomem; static int debug_test; static fheader_t *top_of_mem, *bottom_of_mem; static int n_free_blocks; static struct _chunk_desc _chunk_tbl[MAXCHUNKS + 1]; static int _nchunks; static int extend_mem(size_t n); static int verify_blk(fheader_t *p); /* We use these system-dependent functions. */ extern void *sbrk(int incr); extern int brk(void *addr); static void *getmaxbrk(void); void *calloc(size_t nmemb, size_t size) { void *p; /* What are we supposed to do here??? Should we round up the size */ /* so all elements are aligned? I don't think so, but I'm not */ /* confident. */ /* if(size > 1 && size % ALIGN) size += ALIGN - size%ALIGN; */ p = malloc(size*nmemb); if(p) (void)memset(p, 0, size*nmemb); return p; } void *malloc(size_t size) /* Implement Knuth's Algorithm A, modified for doubly linked */ /* lists, and using the A4' step to avoid small blocks. */ /* It may be found on p. 598 in the soln. to problem 12. */ { size_t N, k; int looped; fheader_t *p, *p1; aheader_t *L; trailer_t *trl; size_t askfor; int got; if(debug_verify && malloc_verify()){ Error("bad malloc structures: %s\n", malloc_errstring); } if(size == 0) return (void *)0; if(size%ALIGN) size += ALIGN - size%ALIGN; /* If we allocate a block that's too small, we will have */ /* BIG problems when we try to free it!! */ if(size + sizeof(aheader_t) < sizeof(fheader_t)) N = sizeof(fheader_t) + sizeof(trailer_t); else N = size + sizeof(aheader_t) + sizeof(trailer_t); p = rover; looped = 0; while(p->size < N || p == &avail){ if(p == &avail){ if(looped){ askfor = (Nlink; } if(debug_test && verify_blk(p)){ Error("Bad block (%#lx) in malloc: %s\n", (unsigned long)p, malloc_errstring); return (void *)0; } rover = p->link; k = p->size - N; #if 0 /* This code allocates the new block at the top of */ /* the free block. This has very bad consequences */ /* for fragmentation when sbrk is called many times */ /* to get new chunks. Thus, the improved code below. */ if(k < KNUTHS_C){ avail.size -= p->size; p->prev->link = rover; rover->prev = p->prev; L = (aheader_t *)p; n_free_blocks--; }else{ /* Here, we split the large block */ avail.size -= N; p->size = k; L = (aheader_t *)((char *)p + k); trl = (trailer_t *)L - 1; trl->size = k; trl->tag = FREE; L->size = N; } L->tag = INUSE; trl = (trailer_t *)((char *)L + L->size) - 1; trl->tag = INUSE; trl->size = L->size; #else L = (aheader_t *)p; if(k < KNUTHS_C){ avail.size -= p->size; p->prev->link = rover; rover->prev = p->prev; n_free_blocks--; }else{ /* Here, we split a large free block into an allocated block */ /* and a smaller free block. */ /* Knuth's method is considerably easier, since */ /* it leaves the free block essentially alone. There */ /* is no need to poke around in the free list and fix pointers */ /* Undoubtedly, the correct thing is to make the linked lists */ /* out of the trailers instead... Maybe in some other life */ avail.size -= N; L->size = N; p1 = (fheader_t *)((char *)L + N); p1->tag = FREE; p1->size = k; p1->link = p->link; p1->prev = p->prev; p->prev->link = p1; p->link->prev = p1; trl = (trailer_t *)((char *)p1 + k) - 1; trl->size = k; /* the tag is FREE already */ } L->tag = INUSE; trl = (trailer_t *)((char *)L + L->size) - 1; trl->tag = INUSE; trl->size = L->size; #endif return (void *)(L+1); } void *realloc(void *ptr, size_t size) { void *ret; aheader_t *p0; fheader_t *p, *p1, *p2; trailer_t *trl, *trl0; size_t N; size_t ptrsz; int k; if(debug_verify && malloc_verify()){ Error("Bad structures in realloc: %s\n", malloc_errstring); return (void *)0; } if(size == 0){ if(ptr) free(ptr); return (void *)0; } if(ptr == (void *)0) return malloc(size); if(size%ALIGN) size += ALIGN - size%ALIGN; /* If we allocate a block that's too small, we will have */ /* BIG problems when we try to free it!! */ if(size + sizeof(aheader_t) < sizeof(fheader_t)) N = sizeof(fheader_t) + sizeof(trailer_t); else N = size + sizeof(aheader_t) + sizeof(trailer_t); /* This code is very similar to that in free. */ /* They should probably be combined in a common subroutine */ p0 = (aheader_t *) ((aheader_t *)ptr - 1); /* How much data is actuall stored under ptr??? */ /* I think this is correct even for 'small' N */ ptrsz = p0->size - (sizeof(aheader_t) + sizeof(trailer_t)); p = (fheader_t *)((char *)p0 + p0->size); if(debug_test && verify_blk(p)){ Error("Bad blk (%#lx) in realloc: %s\n", (unsigned long)p, malloc_errstring); return (void *)0; } if(p->tag == FREE){ /* The next block is free. Combine them */ p1 = p->link; p2 = p->prev; p1->prev = p2; p2->link = p1; p0->size += p->size; avail.size -= p->size; /* p just got removed from the free list. */ /* Make sure it's not equal to rover */ if(p == rover) rover = &avail; p = (fheader_t *)( (char *)p + p->size ); trl = (trailer_t *)p - 1; trl->tag = INUSE; trl->size = p0->size; n_free_blocks--; } trl = (trailer_t *)p0 -1; #ifdef UNFRAGMENT /* Combine this block and the one before it whenever possible */ if(trl->tag == FREE && (trl->size + p0->size) > N){ #else /* Only combine this block with the one before it if we don't have */ /* enough space yet. */ if(trl->tag == FREE && (p0->size < N) && (trl->size + p0->size) > N){ #endif /* In the event that the previous blk is free, and it doesn't meet */ /* our needs, we will coalesce it when the current block */ /* is freed, postponing any irreversible damage until after */ /* new space has been successfully found */ p = (fheader_t *)((char *)p0 - trl->size); p1 = p->link; p2 = p->prev; p1->prev = p2; p2->link = p1; avail.size -= p->size; p->size += p0->size; /* p just got removed from the free list. */ /* Make sure it's not equal to rover */ if(p == rover) rover = &avail; /* Copy the data from p0 to p */ if( (char *)((aheader_t*)p+1) < (char *)ptr - ptrsz ) memcpy(((aheader_t *)p+1), ptr, ptrsz); else memmove(((aheader_t *)p+1), ptr, ptrsz); p->tag = INUSE; p0 = (aheader_t *)p; trl0 = ((trailer_t *)((char *)p + p->size)) -1; trl0->size = p->size; n_free_blocks--; } if(debug_test && verify_blk((fheader_t *)p0)){ Error("Bad block (p0) (%#lx) in realloc: %s\n", (unsigned long)p0, malloc_errstring); return (void *)0; } k = p0->size - N; if(k<0){ ret = malloc(size); if(ret){ memcpy(ret, ptr, p0->size - sizeof(aheader_t)-sizeof(trailer_t)); free(ptr); }else if(debug_abort_nomem){ Error("Out of mem in realloc\n"); } /* Fortunately, we didn't combine the preceding block */ /* with the given block unless we were sure that would */ /* result in enough space. Thus, we didn't molest the data */ /* or the list structures in any way that would need to */ /* be reversed. */ return ret; }else if(k >= KNUTHS_C){ /* First, shrink this node. */ p0->size = N; p1 = (fheader_t *)((char *)p0 +N); trl = (trailer_t *)p1 - 1; trl->tag = INUSE; trl->size = N; /* then create a new free node at the top of this block */ n_free_blocks++; avail.size += k; p1->size = k; p1->tag = FREE; p1->link = avail.link; p1->prev = &avail; avail.link->prev = p1; avail.link = p1; trl = (trailer_t *) ((char *)p1 + k) -1; trl->tag = FREE; trl->size = k; }/* else there is nothing to do. The change is not significant */ return (void *)((aheader_t *)p0 + 1); } void free(void *ptr) /* Implement Knuth's Algorithm C, p. 442 */ { fheader_t *p, *p0, *p1, *p2; trailer_t *trl; if(debug_verify && malloc_verify()){ Error("Bad structures in free: %s\n", malloc_errstring); return; } if(ptr == (void *)0) return; p0 = (fheader_t *) ((aheader_t *)ptr - 1); if(debug_test && verify_blk(p0)){ Error("Bad block (%#lx) in free: %s\n", (unsigned long)p0, malloc_errstring); } trl = (trailer_t *)p0 -1; avail.size += p0->size; if(trl->tag == FREE){ /* The preceding block is free. Combine them */ p = (fheader_t *)((char *)p0 - trl->size); p1 = p->link; p2 = p->prev; p1->prev = p2; p2->link = p1; p->size += p0->size; /* p just got removed from the free list. */ /* Make sure it's not equal to rover */ if(p == rover) rover = &avail; p0 = p; n_free_blocks--; } p = (fheader_t *)((char *)p0 + p0->size); if(debug_test && verify_blk(p)){ Error("Bad block (p) (%#lx) in free: %s\n", (unsigned long)p, malloc_errstring); } if(p->tag == FREE){ /* The anteceding block is free. Combine them */ p1 = p->link; p2 = p->prev; p1->prev = p2; p2->link = p1; p0->size += p->size; /* p just got removed from the free list. */ /* Make sure it's not equal to rover */ if(p == rover) rover = &avail; p = (fheader_t *)( (char *)p + p->size ); n_free_blocks--; } /* link the new block into the front of the avail list */ trl = (trailer_t *)p - 1; trl->size = p0->size; trl->tag = FREE; p0->link = avail.link; p0->prev = &avail; avail.link->prev = p0; avail.link = p0; p0->tag = FREE; n_free_blocks++; } size_t malloc_avail(void) { /* The space unallocated in the blocks we have appropriated, */ /* minus the space left that the OS will give us. */ /* If the OS won't tell us, we have to return -1, which is */ /* more likely to mean we don't know than that there is none! */ void *memlim = getmaxbrk(); void *curtop = sbrk(0); if((long)memlim == -1 || (long)curtop == -1) return -1; return avail.size + ((char *)memlim - (char *)curtop); } size_t malloc_heapsz(void) { void *curtop = sbrk(0); return((long)curtop - (long)_chunk_tbl[0].begin); } size_t malloc_used(void) { return malloc_heapsz()-avail.size; } static int extend_mem(size_t n) /* Try to obtain a block of at least size n */ /* from the OS using sbrk. Return the size */ /* of the block actually obtained. */ { trailer_t *trl0; aheader_t *hdr0; aheader_t *hdr1; fheader_t *hdr; trailer_t *trl; char *old_top; void *max_brk, *begin; Msgf(("extend_mem(%ld)\n", (long)n)); /* Check structure sizes */ if (sizeof(aheader_t) % ALIGN != 0) Error("sizeof(aheader_t) (%d) not multiple of ALIGN (%d)\n", (int)sizeof(aheader_t), ALIGN); if (sizeof(aheader_t) != sizeof(trailer_t)) Error("sizeof(aheader_t) (%d) != sizeof(trailer_t) (%d)\n", (int)sizeof(aheader_t), (int)sizeof(trailer_t)); /* First, try to get a chunk of memory */ /* If the system won't give us one of the */ /* size we ask for, then halve the size */ /* until we get something */ /* Technically, sbrk returns a caddr_t, which */ /* is typedef'ed to be a char *. It returns */ /* an error condition, however, by returning the */ /* integer -1, so the casts are necessary. */ while (1) { if(n < (sizeof(fheader_t) + sizeof(trailer_t))) { Error("no more memory!\n"); } begin = sbrk(n + sizeof(aheader_t) +sizeof(trailer_t)); if ((long int)begin != -1L) break; Msgf(("sbrk(%ld) returns -1\n", (long)(n+sizeof(aheader_t) + sizeof(trailer_t)))); n = n/2; } old_top = (char *)top_of_mem; if(bottom_of_mem == 0){ /* The first time through here, remember the lowest possible */ /* malloc'ed location */ bottom_of_mem = (fheader_t *)(begin); } /* Assume sbrk is strictly increasing, so the end of memory */ /* is represented by the end of the most recent sbrk call */ top_of_mem = (fheader_t *)((char*)begin + n+sizeof(aheader_t)+sizeof(trailer_t)); if(begin == old_top){ /* put a fake INUSE block at the top */ Msgf(("Adding extended mem to previous chunk\n")); hdr1 = ((aheader_t *)top_of_mem) - 1; hdr1->size = sizeof(aheader_t); hdr1->tag = INUSE; /* Now change the hdr that used to be at the top of memory */ hdr0 = ((aheader_t *)old_top) - 1; hdr0->tag = INUSE; hdr0->size = n + sizeof(aheader_t) + sizeof(trailer_t); /* Now put a consistent trailer at the end of it */ trl0 = ((trailer_t *)hdr1) - 1; trl0->tag = INUSE; trl0->size = hdr0->size; /* And now free it to connect it with the free-list */ /* This might change trl0->size */ free((void *)(hdr0 + 1)); /* Finally, record that the chunk has grown */ #if 0 /* wrong? */ _chunk_tbl[_nchunks-1].size += n + sizeof(trailer_t); #else _chunk_tbl[_nchunks-1].size += n+sizeof(aheader_t)+sizeof(trailer_t); #endif Msgf(("extend_mem returns %lu\n", (unsigned long)trl0->size)); return trl0->size; } /* Now we've got a big chunk of memory */ /* Prepare it by placing INUSE markers at */ /* both ends, and a header after the INUSE */ /* marker at the beginning. */ trl0 = (trailer_t *)begin; trl0->tag = INUSE; hdr0 = (aheader_t *)((char *)begin + n + sizeof(trailer_t)); hdr0->tag = INUSE; /* WARNING! These two lines will keep verify_blk happy */ /* if it is asked to verify the markers at the beginning */ /* and end of memory. They RELY ON THE FACT that: */ /* sizeof(header_t) == sizeof(trailer_t) */ hdr0->size = sizeof(aheader_t); trl0->size = sizeof(trailer_t); hdr = (fheader_t *)(trl0 + 1); /* the header of the new free block */ trl = ((trailer_t *)hdr0) - 1; /* the trailer of the new free block */ /* Now link the header into the free chain */ hdr->link = avail.link; hdr->prev = &avail; avail.link->prev = hdr; avail.link = hdr; hdr->tag = FREE; hdr->size = n; trl->tag = FREE; trl->size = n; avail.size += n; n_free_blocks++; /* Now record info about this chunk in the table */ if(_nchunks < MAXCHUNKS){ _chunk_tbl[_nchunks].size = n; _chunk_tbl[_nchunks].begin = (void *)hdr; _nchunks++; }else{ /* There's no more room in the table! */ /* Don't panic, though, just give up on */ /* recording the sizes of individual chunks for */ /* posterity. */ _chunk_tbl[MAXCHUNKS].size += n; } Msgf(("extend_mem returns %lu\n", (unsigned long)n)); return n; } /* These two are patterned after the diagnostic version of */ /* malloc available on SUNs. Malloc_debug(lev) sets the debugging */ /* level to its arg. Values are: */ /* 0 : the default case */ /* 1 : abort if any problem is detected in malloc's data structures */ /* Level 1 does not actively seek out problems, but if it happens upon */ /* one, it calls abort. This is the default level. It should */ /* be only marginally slower than level 0. */ /* 2 : Examine the entire data structure on every call of malloc/calloc/ */ /* realloc/free. This may be VERY slow. */ /* 3 : Same as 1, but also abort when malloc would return NULL. */ /* 4 : Same as 2, but also abort when malloc would return NULL. */ /* Neither of the last two abort if 0 bytes are requested */ /* malloc_verify() performs a very thorough of the entire malloc */ /* data structures. If all is well, it returns 0, otherwise it */ /* returns 1 */ /* Whenever an error is detected, the external (void *) malloc_bad_block */ /* is set to point to the bad block. It points to the value that */ /* would have been returned by malloc, i.e. sizeof(aheader_t) past */ /* the address of the header. If the error is a global one that */ /* cannot be blamed on a single block, e.g. sizes not adding up, */ /* malloc_bad_block points to the static avail. */ int malloc_debug(int level) { int ret; if(level > 4 || level < 0) return -1; ret = debug_lev; debug_lev = level; switch(debug_lev){ case 0: debug_test = 0; debug_verify = 0; debug_abort_nomem = 0; break; case 1: debug_test = 1; debug_verify = 0; debug_abort_nomem = 0; break; case 2: debug_test = 1; debug_verify = 1; debug_abort_nomem = 0; break; case 3: debug_test = 1; debug_verify = 0; debug_abort_nomem = 1; break; case 4: debug_test = 1; debug_verify = 1; debug_abort_nomem = 1; break; } return ret; } int malloc_verify(void) { fheader_t *last; fheader_t *hdr; int i; int n_free_blocks1 = 0; int n_free_blocks2 = 0; size_t sz_free = 0; /* This is very similar to the loop in prt_mem... */ /* we loop over all chunks, and all blocks in the chunk, */ /* calling verify_blk for each one. We also count the */ /* number of free blocks, which we compare with a scan down the */ /* linked list of free blocks starting at avail. */ for(i=0; i<_nchunks; i++){ hdr = (fheader_t *)_chunk_tbl[i].begin; last = (fheader_t *)((char *)hdr + _chunk_tbl[i].size); while(hdr < last){ if(verify_blk(hdr)){ return 1; } if(hdr->tag == FREE){ n_free_blocks1++; sz_free += hdr->size; } /* Avoid infinite loops by counting free blocks */ if(n_free_blocks1 > n_free_blocks){ sprintf(malloc_errstring, "nfree_blocks1(%d) > nfreeblocks(%d)\n", n_free_blocks1, n_free_blocks); return 1; } hdr = (fheader_t *)((char *)hdr + hdr->size); } if( hdr != last ){ SeriousWarning("Possible too-long chunk: hdr=%#lx != last=%#lx\n", (unsigned long)hdr, (unsigned long)last); } } /* Only make this test if the chunk_tbl contains all the relevant */ /* information */ if((_nchunks != MAXCHUNKS) && (sz_free != avail.size || n_free_blocks != n_free_blocks1)){ sprintf(malloc_errstring, "Sizes don't add up, sz_free=%ld, avail.size=%ld, n_free_blocks=%d, n_free_blocks1=%d!\n", (long)sz_free, (long)avail.size, n_free_blocks, n_free_blocks1); SeriousWarning("%s", malloc_errstring); /* return 1; */ } /* Now scan the linked list of free blocks and make sure */ /* it is the right length, and that its size adds up too */ sz_free = 0; for(hdr = avail.link; hdr != &avail; hdr = hdr->link){ n_free_blocks2++; sz_free += hdr->size; /* avoid infinite loops this way */ if(n_free_blocks2 > n_free_blocks1){ sprintf(malloc_errstring, "Too many links in chain\n"); return 1; } } if(sz_free != avail.size || n_free_blocks2 != n_free_blocks){ sprintf(malloc_errstring, "Sizes or counts don't add up!\n"); return 1; } return 0; } static int verify_blk(fheader_t *p) { fheader_t *l; trailer_t *tp; /* rely on the two variables top_of_mem and bottom_of_mem */ /* being set before entering here... */ /* How slow is this????? Is it unreasonable to call it */ /* every time through the linked list in malloc??? */ if(p >= top_of_mem || p < bottom_of_mem){ sprintf(malloc_errstring, "ptr (%#lx) outside of memory\n", (unsigned long)p); return 1; } tp = ((trailer_t *)((char *)p + p->size)) - 1; /* make sure the size isn't preposterous */ if((fheader_t *)tp > top_of_mem){ sprintf(malloc_errstring, "block goes past (%#lx) top of memory\n", (unsigned long)tp); return 1; } /* Verify that the flag is ok */ if((p->tag != FREE && p->tag != INUSE)){ sprintf(malloc_errstring, "Bad magic byte in hdr %#lx\n", (unsigned long)p); return 1; } /* Verify that the header and trailer match */ if(p->tag != tp->tag){ sprintf(malloc_errstring, "Tags don't match %#lx, %#lx\n", (unsigned long)p, (unsigned long)tp); return 1; } /* Check that the sizes agree in header and trailer */ if(p->size != tp->size){ sprintf(malloc_errstring, "Sizes don't match %#lx, %#lx\n", (unsigned long)p, (unsigned long)tp); return 1; } /* Check that the size is reasonable */ /* Accepting sizes exactly equal to sizeof(trailer_t) */ /* allows the "dummy" headers at both ends of each chunk */ /* to pass. It hardly seems worth the effort of looking through */ /* the chunk_tbl, to verify that we are actually looking at */ /* such a block */ if(p->size < sizeof(fheader_t) + sizeof(trailer_t) && p->size != sizeof(trailer_t)){ sprintf(malloc_errstring, "Size doesn't make sense p=%#lx\n", (unsigned long)p); return 1; } /* Verify that the forward and backward pointers are reasonable */ if(p->tag == FREE){ l = p->link; if(l != &avail && (l >= top_of_mem || l < bottom_of_mem)){ sprintf(malloc_errstring, "Link ptr (%#lx) of %#lx out of range\n", (unsigned long)l, (unsigned long)p); return 1; } l = p->prev; if(l != &avail && (l >= top_of_mem || l < bottom_of_mem)){ sprintf(malloc_errstring, "Prev ptr (%#lx) of %#lx out of range\n", (unsigned long)l, (unsigned long)p); return 1; } } /* Amazing, all is well. */ return 0; } void malloc_print(void) /* Print the map of allocated memory. */ /* Beware that printf might call malloc if */ /* the buffer for the file, fp, is extensible */ /* This will have dire consequences. */ /* For the moment, a solution is to guarantee that */ /* fprintf is linked with any paralib program via */ /* the trick in stdio/data.c */ { aheader_t *last; aheader_t *hdr; fheader_t *fhdr; int ch, i; int nfb; Msg_do("Malloc_print called. heapsz: %ld, avail: %ld, used: %ld\n", (long)malloc_heapsz(), (long)malloc_avail(), (long)malloc_used()); Msg_do( "Memory map:\n"); for(i=0; i<_nchunks; i++){ Msg_do( "Chunk %d of size %lu\n", i, (unsigned long)_chunk_tbl[i].size); Msg_do( "address size [allocated|free]\n"); hdr = (aheader_t *)_chunk_tbl[i].begin; last = (aheader_t *)((char *)hdr + _chunk_tbl[i].size); while(hdr < last){ if( verify_blk((fheader_t *)hdr) ){ Msg_do( "Bad block (%#lx): %s\n", (unsigned long)hdr, malloc_errstring); break; } /* ch is 'f' for free, 'a' for allocated */ /* We print out the address that would have been */ /* returned by malloc, and the maximum possible */ /* size of USER memory in the block. Due to */ /* rounding, this may be more than he asked for, */ /* but the size of any headers and/or trailers is */ /* subtracted. Note the use of the %p conversion */ /* specifier. */ ch = (hdr->tag == INUSE)? 'a' : 'f'; Msg_do( "%#lx %lu %c\n", (unsigned long)(hdr+1), (unsigned long)(hdr->size - sizeof(aheader_t) - sizeof(trailer_t)), ch); hdr = (aheader_t *)((char *)hdr + hdr->size); } Msg_do( "\n"); } if(_nchunks == MAXCHUNKS){ Msg_do( "There are more chunks with\n"); Msg_do( "a total size of %lu. I don't\n", (unsigned long)_chunk_tbl[MAXCHUNKS].size); Msg_do( "have detailed info about them though.\n"); } Msg_do("Free list:\n"); nfb = 0; for(fhdr = avail.link; fhdr != &avail; fhdr = fhdr->link){ nfb++; ch = (fhdr->tag == INUSE)? 'a' : 'f'; if( verify_blk(fhdr) ){ Msg_do("Bad block (%#lx): %s\n", (unsigned long)fhdr, malloc_errstring); } Msg_do( "%#lx %lu %c\n", (unsigned long)((aheader_t *)fhdr+1), (unsigned long)(fhdr->size - sizeof(aheader_t) - sizeof(trailer_t)), ch); /* avoid infinite loops this way */ if(nfb > n_free_blocks){ Msg_do("Too many free blocks. Possible loop\n"); break; } } } /* This uses the same discredited idea that we abandoned */ /* in the tree11/sysdep.c: define a GETMAXBRK_DEFINED symbol */ /* as soon as we find a system-predicate we like. When we hit the */ /* end, complain if we haven't yet defined GETMAXBRK_DEFINED. */ #undef GETMAXBRK_DEFINED #if defined(__hpux) #define GETMAXBRK_DEFINED #include static void *getmaxbrk(void){ (void *)ulimit(UL_GETMAXBRK); } #endif #if defined(sun)||defined(__PARAGON__)||defined(linux) /* is getrlimit a sunos'ism, a bsd'ism or what??? */ #define GETMAXBRK_DEFINED #include #include extern char etext; static void * getmaxbrk(void) { struct rlimit rl; if(getrlimit(RLIMIT_DATA, &rl)) return (void *)-1; if(rl.rlim_max == RLIM_INFINITY){ return (void *)-1; }else{ return &etext + 2750LL*1024LL*1024LL; } } #endif /* sun||PARAGON */ #ifdef __DELTA__ #define GETMAXBRK_DEFINED #define BEGIN 0x10000000 static void * getmaxbrk(void){ return (void *)(BEGIN + 12*1024*1024); /* 12Meg */ } #endif #ifndef GETMAXBRK_DEFINED static void * getmaxbrk(void) { return (void *)-1; } #endif /* GETMAXBRK_DEFINED */ #endif /* USE_SYSTEM_MALLOC */