mirror of
https://bitbucket.org/cosmicvoids/vide_public.git
synced 2025-07-04 23:31:12 +00:00
1067 lines
30 KiB
C
1067 lines
30 KiB
C
/* 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 <stdio.h>
|
|
#include <stddef.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#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 <stdlib.h>
|
|
#include <string.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#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 = (N<chunksize) ? chunksize : N;
|
|
got = extend_mem(askfor);
|
|
/* If we couldn't get a full-size */
|
|
/* chunk, remember that, and don't */
|
|
/* ask again. */
|
|
if(got < (int)askfor){
|
|
/* We have exhausted all our memory. */
|
|
chunksize = 0;
|
|
}
|
|
/* The cast is important here, so the */
|
|
/* comparison is signed */
|
|
if(got < (int)N){
|
|
/* We couldn't get what we needed */
|
|
if(debug_abort_nomem) {
|
|
malloc_print();
|
|
Error("Out of memory in malloc\n");
|
|
}
|
|
return (void *)0;
|
|
}
|
|
/* extend_mem will link in a block immediately */
|
|
/* after avail, so we will find it in the */
|
|
/* next time around the outside loop */
|
|
}else{
|
|
looped = 1;
|
|
p = avail.link; /* ????????? */
|
|
continue;
|
|
}
|
|
}
|
|
p = p->link;
|
|
}
|
|
|
|
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 <ulimit.h>
|
|
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 <sys/time.h>
|
|
#include <sys/resource.h>
|
|
|
|
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 */
|
|
|