nsprpub/pr/src/malloc/prmalloc.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "primpl.h"
     8 /*
     9 ** We override malloc etc. on any platform which has preemption +
    10 ** nspr20 user level threads.  When we're debugging, we can make our
    11 ** version of malloc fail occasionally.
    12 */
    13 #ifdef _PR_OVERRIDE_MALLOC
    15 /*
    16 ** Thread safe version of malloc, calloc, realloc, free
    17 */
    18 #include <stdarg.h>
    20 #ifdef DEBUG
    21 #define SANITY
    22 #define EXTRA_SANITY
    23 #else
    24 #undef SANITY
    25 #undef EXTRA_SANITY
    26 #endif
    28 /* Forward decls */
    29 void *_PR_UnlockedMalloc(size_t size);
    30 void _PR_UnlockedFree(void *ptr);
    31 void *_PR_UnlockedRealloc(void *ptr, size_t size);
    32 void *_PR_UnlockedCalloc(size_t n, size_t elsize);
    34 /************************************************************************/
    36 /*
    37  * ----------------------------------------------------------------------------
    38  * "THE BEER-WARE LICENSE" (Revision 42):
    39  * <phk@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
    40  * can do whatever you want with this stuff. If we meet some day, and you think
    41  * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
    42  * ----------------------------------------------------------------------------
    43  *
    44  */
    46 /*
    47  * Defining SANITY will enable some checks which will tell you if the users
    48  * program did botch something
    49  */
    51 /*
    52  * Defining EXTRA_SANITY will enable some checks which are mostly related
    53  * to internal conditions in malloc.c
    54  */
    56 /*
    57  * Very verbose progress on stdout...
    58  */
    59 #if 0
    60 #  define TRACE(foo)    printf  foo
    61 static int malloc_event;
    62 #else
    63 #  define TRACE(foo)	
    64 #endif
    66 /* XXX Pick a number, any number */
    67 #   define malloc_pagesize		4096UL
    68 #   define malloc_pageshift		12UL
    70 #ifdef XP_UNIX
    71 #include <stdio.h>
    72 #include <stdlib.h>
    73 #include <unistd.h>
    74 #include <string.h>
    75 #include <errno.h>
    76 #include <sys/types.h>
    77 #include <sys/mman.h>
    78 #endif
    80 /*
    81  * This structure describes a page's worth of chunks.
    82  */
    84 struct pginfo {
    85     struct pginfo	*next;	/* next on the free list */
    86     char		*page;	/* Pointer to the page */
    87     u_short		size;	/* size of this page's chunks */
    88     u_short		shift;	/* How far to shift for this size chunks */
    89     u_short		free;	/* How many free chunks */
    90     u_short		total;	/* How many chunk */
    91     u_long		bits[1]; /* Which chunks are free */
    92 };
    94 struct pgfree {
    95     struct pgfree	*next;	/* next run of free pages */
    96     struct pgfree	*prev;	/* prev run of free pages */
    97     char		*page;	/* pointer to free pages */
    98     char		*end;	/* pointer to end of free pages */
    99     u_long		size;	/* number of bytes free */
   100 };
   102 /*
   103  * How many bits per u_long in the bitmap.
   104  * Change only if not 8 bits/byte
   105  */
   106 #define	MALLOC_BITS	(8*sizeof(u_long))
   108 /*
   109  * Magic values to put in the page_directory
   110  */
   111 #define MALLOC_NOT_MINE	((struct pginfo*) 0)
   112 #define MALLOC_FREE 	((struct pginfo*) 1)
   113 #define MALLOC_FIRST	((struct pginfo*) 2)
   114 #define MALLOC_FOLLOW	((struct pginfo*) 3)
   115 #define MALLOC_MAGIC	((struct pginfo*) 4)
   117 /*
   118  * Set to one when malloc_init has been called
   119  */
   120 static	unsigned	initialized;
   122 /*
   123  * The size of a page.
   124  * Must be a integral multiplum of the granularity of mmap(2).
   125  * Your toes will curl if it isn't a power of two
   126  */
   127 #define malloc_pagemask	((malloc_pagesize)-1)
   129 /*
   130  * The size of the largest chunk.
   131  * Half a page.
   132  */
   133 #define malloc_maxsize	((malloc_pagesize)>>1)
   135 /*
   136  * malloc_pagesize == 1 << malloc_pageshift
   137  */
   138 #ifndef malloc_pageshift
   139 static	unsigned	malloc_pageshift;
   140 #endif /* malloc_pageshift */
   142 /*
   143  * The smallest allocation we bother about.
   144  * Must be power of two
   145  */
   146 #ifndef malloc_minsize
   147 static	unsigned  malloc_minsize;
   148 #endif /* malloc_minsize */
   150 /*
   151  * The largest chunk we care about.
   152  * Must be smaller than pagesize
   153  * Must be power of two
   154  */
   155 #ifndef malloc_maxsize
   156 static	unsigned  malloc_maxsize;
   157 #endif /* malloc_maxsize */
   159 #ifndef malloc_cache
   160 static	unsigned  malloc_cache;
   161 #endif /* malloc_cache */
   163 /*
   164  * The offset from pagenumber to index into the page directory
   165  */
   166 static	u_long  malloc_origo;
   168 /*
   169  * The last index in the page directory we care about
   170  */
   171 static	u_long  last_index;
   173 /*
   174  * Pointer to page directory.
   175  * Allocated "as if with" malloc
   176  */
   177 static	struct	pginfo **page_dir;
   179 /*
   180  * How many slots in the page directory
   181  */
   182 static	unsigned	malloc_ninfo;
   184 /*
   185  * Free pages line up here 
   186  */
   187 static struct pgfree	free_list;
   189 /*
   190  * Abort() if we fail to get VM ?
   191  */
   192 static int malloc_abort;
   194 #ifdef SANITY
   195 /*
   196  * Are we trying to die ?
   197  */
   198 static int suicide;
   199 #endif
   201 /*
   202  * dump statistics
   203  */
   204 static int malloc_stats;
   206 /*
   207  * always realloc ?
   208  */
   209 static int malloc_realloc;
   211 /*
   212  * my last break.
   213  */
   214 static void *malloc_brk;
   216 /*
   217  * one location cache for free-list holders
   218  */
   219 static struct pgfree *px;
   221 static int set_pgdir(void *ptr, struct  pginfo *info);
   222 static int extend_page_directory(u_long index);
   224 #ifdef SANITY
   225 void
   226 malloc_dump(FILE *fd)
   227 {
   228     struct pginfo **pd;
   229     struct pgfree *pf;
   230     int j;
   232     pd = page_dir;
   234     /* print out all the pages */
   235     for(j=0;j<=last_index;j++) {
   236 	fprintf(fd,"%08lx %5d ",(j+malloc_origo) << malloc_pageshift,j);
   237 	if (pd[j] == MALLOC_NOT_MINE) {
   238 	    for(j++;j<=last_index && pd[j] == MALLOC_NOT_MINE;j++)
   239 		;
   240 	    j--;
   241 	    fprintf(fd,".. %5d not mine\n",	j);
   242 	} else if (pd[j] == MALLOC_FREE) {
   243 	    for(j++;j<=last_index && pd[j] == MALLOC_FREE;j++)
   244 		;
   245 	    j--;
   246 	    fprintf(fd,".. %5d free\n", j);
   247 	} else if (pd[j] == MALLOC_FIRST) {
   248 	    for(j++;j<=last_index && pd[j] == MALLOC_FOLLOW;j++)
   249 		;
   250 	    j--;
   251 	    fprintf(fd,".. %5d in use\n", j);
   252 	} else if (pd[j] < MALLOC_MAGIC) {
   253 	    fprintf(fd,"(%p)\n", pd[j]);
   254 	} else {
   255 	    fprintf(fd,"%p %d (of %d) x %d @ %p --> %p\n",
   256 		pd[j],pd[j]->free, pd[j]->total, 
   257 		pd[j]->size, pd[j]->page, pd[j]->next);
   258 	}
   259     }
   261     for(pf=free_list.next; pf; pf=pf->next) {
   262 	fprintf(fd,"Free: @%p [%p...%p[ %ld ->%p <-%p\n",
   263 		pf,pf->page,pf->end,pf->size,pf->prev,pf->next);
   264 	if (pf == pf->next) {
   265 		fprintf(fd,"Free_list loops.\n");
   266 		break;
   267 	}
   268     }
   270     /* print out various info */
   271     fprintf(fd,"Minsize\t%d\n",malloc_minsize);
   272     fprintf(fd,"Maxsize\t%ld\n",malloc_maxsize);
   273     fprintf(fd,"Pagesize\t%ld\n",malloc_pagesize);
   274     fprintf(fd,"Pageshift\t%ld\n",malloc_pageshift);
   275     fprintf(fd,"FirstPage\t%ld\n",malloc_origo);
   276     fprintf(fd,"LastPage\t%ld %lx\n",last_index+malloc_pageshift,
   277 	(last_index + malloc_pageshift) << malloc_pageshift);
   278     fprintf(fd,"Break\t%ld\n",(u_long)sbrk(0) >> malloc_pageshift);
   279 }
   281 static void wrterror(char *fmt, ...)
   282 {
   283     char *q = "malloc() error: ";
   284     char buf[100];
   285     va_list ap;
   287     suicide = 1;
   289     va_start(ap, fmt);
   290     PR_vsnprintf(buf, sizeof(buf), fmt, ap);
   291     va_end(ap);
   292     fputs(q, stderr);
   293     fputs(buf, stderr);
   295     malloc_dump(stderr);
   296     PR_Abort();
   297 }
   299 static void wrtwarning(char *fmt, ...)
   300 {
   301     char *q = "malloc() warning: ";
   302     char buf[100];
   303     va_list ap;
   305     va_start(ap, fmt);
   306     PR_vsnprintf(buf, sizeof(buf), fmt, ap);
   307     va_end(ap);
   308     fputs(q, stderr);
   309     fputs(buf, stderr);
   310 }
   311 #endif /* SANITY */
   314 /*
   315  * Allocate a number of pages from the OS
   316  */
   317 static caddr_t
   318 map_pages(int pages, int update)
   319 {
   320     caddr_t result,tail;
   322     result = ((caddr_t)sbrk(0)) + malloc_pagemask - 1;
   323     result = (caddr_t) ((u_long)result & ~malloc_pagemask);
   324     tail = result + (pages << malloc_pageshift);
   325     if (!brk(tail)) {
   326 	last_index = ((u_long)tail >> malloc_pageshift) - malloc_origo -1;
   327 	malloc_brk = tail;
   328 	TRACE(("%6d S %p .. %p\n",malloc_event++, result, tail));
   329 	if (!update || last_index < malloc_ninfo ||
   330 	  extend_page_directory(last_index))
   331 	    return result;
   332     }
   333     TRACE(("%6d s %d %p %d\n",malloc_event++,pages,sbrk(0),errno));
   334 #ifdef EXTRA_SANITY
   335     wrterror("map_pages fails\n");
   336 #endif
   337     return 0;
   338 }
   340 #define set_bit(_pi,_bit) \
   341     (_pi)->bits[(_bit)/MALLOC_BITS] |= 1L<<((_bit)%MALLOC_BITS)
   343 #define clr_bit(_pi,_bit) \
   344     (_pi)->bits[(_bit)/MALLOC_BITS] &= ~(1L<<((_bit)%MALLOC_BITS));
   346 #define tst_bit(_pi,_bit) \
   347     ((_pi)->bits[(_bit)/MALLOC_BITS] & (1L<<((_bit)%MALLOC_BITS)))
   349 /*
   350  * Extend page directory
   351  */
   352 static int
   353 extend_page_directory(u_long index)
   354 {
   355     struct  pginfo **young, **old;
   356     int i;
   358     TRACE(("%6d E %lu\n",malloc_event++,index));
   360     /* Make it this many pages */
   361     i = index * sizeof *page_dir;
   362     i /= malloc_pagesize;
   363     i += 2;
   365     /* Get new pages, if you used this much mem you don't care :-) */
   366     young = (struct pginfo**) map_pages(i,0);
   367     if (!young)
   368 	return 0;
   370     /* Copy the old stuff */
   371     memset(young, 0, i * malloc_pagesize);
   372     memcpy(young, page_dir,
   373 	    malloc_ninfo * sizeof *page_dir);
   375     /* register the new size */
   376     malloc_ninfo = i * malloc_pagesize / sizeof *page_dir;
   378     /* swap the pointers */
   379     old = page_dir;
   380     page_dir = young;
   382     /* Mark the pages */
   383     index = ((u_long)young >> malloc_pageshift) - malloc_origo;
   384     page_dir[index] = MALLOC_FIRST;
   385     while (--i) {
   386 	page_dir[++index] = MALLOC_FOLLOW;
   387     }
   389     /* Now free the old stuff */
   390     _PR_UnlockedFree(old);
   391     return 1;
   392 }
   394 /*
   395  * Set entry in page directory.
   396  * Extend page directory if need be.
   397  */
   398 static int
   399 set_pgdir(void *ptr, struct  pginfo *info)
   400 {
   401     u_long index = ((u_long)ptr >> malloc_pageshift) - malloc_origo;
   403     if (index >= malloc_ninfo && !extend_page_directory(index))
   404 	return 0;
   405     page_dir[index] = info;
   406     return 1;
   407 }
   409 /*
   410  * Initialize the world
   411  */
   412 static void
   413 malloc_init (void)
   414 {
   415     int i;
   416     char *p;
   418     TRACE(("%6d I\n",malloc_event++));
   419 #ifdef DEBUG
   420     for (p=getenv("MALLOC_OPTIONS"); p && *p; p++) {
   421 	switch (*p) {
   422 	    case 'a': malloc_abort = 0; break;
   423 	    case 'A': malloc_abort = 1; break;
   424 	    case 'd': malloc_stats = 0; break;
   425 	    case 'D': malloc_stats = 1; break;
   426 	    case 'r': malloc_realloc = 0; break;
   427 	    case 'R': malloc_realloc = 1; break;
   428 	    default:
   429 		wrtwarning("Unknown chars in MALLOC_OPTIONS\n");
   430 		break;
   431 	}
   432     }
   433 #endif
   435 #ifndef malloc_pagesize
   436     /* determine our pagesize */
   437     malloc_pagesize = getpagesize();
   438 #endif /* malloc_pagesize */
   440 #ifndef malloc_pageshift
   441     /* determine how much we shift by to get there */
   442     for (i = malloc_pagesize; i > 1; i >>= 1)
   443 	malloc_pageshift++;
   444 #endif /* malloc_pageshift */
   446 #ifndef malloc_cache
   447     malloc_cache = 50 << malloc_pageshift;	
   448 #endif /* malloc_cache */
   450 #ifndef malloc_minsize
   451     /*
   452      * find the smallest size allocation we will bother about.
   453      * this is determined as the smallest allocation that can hold
   454      * it's own pginfo;
   455      */
   456     i = 2;
   457     for(;;) {
   458 	int j;
   460 	/* Figure out the size of the bits */
   461 	j = malloc_pagesize/i;
   462 	j /= 8;
   463 	if (j < sizeof(u_long))
   464 		j = sizeof (u_long);
   465 	if (sizeof(struct pginfo) + j - sizeof (u_long) <= i)
   466 		break;
   467 	i += i;
   468     }
   469     malloc_minsize = i;
   470 #endif /* malloc_minsize */
   473     /* Allocate one page for the page directory */
   474     page_dir = (struct pginfo **) map_pages(1,0);
   475 #ifdef SANITY
   476     if (!page_dir)
   477 	wrterror("fatal: my first mmap failed.  (check limits ?)\n");
   478 #endif
   480     /*
   481      * We need a maximum of malloc_pageshift buckets, steal these from the
   482      * front of the page_directory;
   483      */
   484     malloc_origo = (u_long) page_dir >> malloc_pageshift;
   485     malloc_origo -= malloc_pageshift;
   487     /* Clear it */
   488     memset(page_dir,0,malloc_pagesize);
   490     /* Find out how much it tells us */
   491     malloc_ninfo = malloc_pagesize / sizeof *page_dir;
   493     /* Plug the page directory into itself */
   494     i = set_pgdir(page_dir,MALLOC_FIRST);
   495 #ifdef SANITY
   496     if (!i)
   497 	wrterror("fatal: couldn't set myself in the page directory\n");
   498 #endif
   500     /* Been here, done that */
   501     initialized++;
   502 }
   504 /*
   505  * Allocate a number of complete pages
   506  */
   507 static void *malloc_pages(size_t size)
   508 {
   509     void *p,*delay_free = 0;
   510     int i;
   511     struct pgfree *pf;
   512     u_long index;
   514     /* How many pages ? */
   515     size += (malloc_pagesize-1);
   516     size &= ~malloc_pagemask;
   518     p = 0;
   519     /* Look for free pages before asking for more */
   520     for(pf = free_list.next; pf; pf = pf->next) {
   521 #ifdef EXTRA_SANITY
   522 	if (pf->page == pf->end)
   523 	    wrterror("zero entry on free_list\n");
   524 	if (pf->page > pf->end) {
   525 	    TRACE(("%6d !s %p %p %p <%d>\n",malloc_event++,
   526 		pf,pf->page,pf->end,__LINE__));
   527 	    wrterror("sick entry on free_list\n");
   528 	}
   529 	if ((void*)pf->page >= (void*)sbrk(0))
   530 	    wrterror("entry on free_list past brk\n");
   531 	if (page_dir[((u_long)pf->page >> malloc_pageshift) - malloc_origo] 
   532 	  != MALLOC_FREE) {
   533 	    TRACE(("%6d !f %p %p %p <%d>\n",malloc_event++,
   534 		pf,pf->page,pf->end,__LINE__));
   535 	    wrterror("non-free first page on free-list\n");
   536 	}
   537 	if (page_dir[((u_long)pf->end >> malloc_pageshift) - 1 - malloc_origo] 
   538 	  != MALLOC_FREE)
   539 	    wrterror("non-free last page on free-list\n");
   540 #endif /* EXTRA_SANITY */
   541 	if (pf->size < size) 
   542 	    continue;
   543 	else if (pf->size == size) {
   544 	    p = pf->page;
   545 	    if (pf->next)
   546 		    pf->next->prev = pf->prev;
   547 	    pf->prev->next = pf->next;
   548 	    delay_free = pf;
   549 	    break;
   550 	} else {
   551 	    p = pf->page;
   552 	    pf->page += size;
   553 	    pf->size -= size;
   554 	    break;
   555         }
   556     }
   557 #ifdef EXTRA_SANITY
   558     if (p && page_dir[((u_long)p >> malloc_pageshift) - malloc_origo] 
   559       != MALLOC_FREE) {
   560 	wrterror("allocated non-free page on free-list\n");
   561     }
   562 #endif /* EXTRA_SANITY */
   564     size >>= malloc_pageshift;
   566     /* Map new pages */
   567     if (!p)
   568 	p = map_pages(size,1);
   570     if (p) {
   571 	/* Mark the pages in the directory */
   572 	index = ((u_long)p >> malloc_pageshift) - malloc_origo;
   573 	page_dir[index] = MALLOC_FIRST;
   574 	for (i=1;i<size;i++)
   575 	    page_dir[index+i] = MALLOC_FOLLOW;
   576     }
   577     if (delay_free) {
   578 	if (!px) 
   579 	    px = (struct pgfree*)delay_free;
   580 	else
   581 	    _PR_UnlockedFree(delay_free);
   582     }
   583     return p;
   584 }
   586 /*
   587  * Allocate a page of fragments
   588  */
   590 static int
   591 malloc_make_chunks(int bits)
   592 {
   593     struct  pginfo *bp;
   594     void *pp;
   595     int i,k,l;
   597     /* Allocate a new bucket */
   598     pp = malloc_pages(malloc_pagesize);
   599     if (!pp)
   600 	return 0;
   601     l = sizeof *bp - sizeof(u_long);
   602     l += sizeof(u_long) *
   603 	(((malloc_pagesize >> bits)+MALLOC_BITS-1) / MALLOC_BITS);
   604     if ((1<<(bits)) <= l+l) {
   605 	bp = (struct  pginfo *)pp;
   606     } else {
   607 	bp = (struct  pginfo *)_PR_UnlockedMalloc(l);
   608     }
   609     if (!bp)
   610 	return 0;
   611     bp->size = (1<<bits);
   612     bp->shift = bits;
   613     bp->total = bp->free = malloc_pagesize >> bits;
   614     bp->next = page_dir[bits];
   615     bp->page = (char*)pp;
   616     i = set_pgdir(pp,bp);
   617     if (!i)
   618 	return 0;
   620     /* We can safely assume that there is nobody in this chain */
   621     page_dir[bits] = bp;
   623     /* set all valid bits in the bits */
   624     k = bp->total;
   625     i = 0;
   626 /*
   627     for(;k-i >= MALLOC_BITS; i += MALLOC_BITS)
   628 	bp->bits[i / MALLOC_BITS] = ~0;
   629 */
   630     for(; i < k; i++)
   631 	set_bit(bp,i);
   633     if (bp != pp)
   634 	return 1;
   636     /* We may have used the first ones already */
   637     for(i=0;l > 0;i++) {
   638 	clr_bit(bp,i);
   639 	bp->free--;
   640 	bp->total--;
   641 	l -= (1 << bits);
   642     }
   643     return 1;
   644 }
   646 /*
   647  * Allocate a fragment
   648  */
   649 static void *malloc_bytes(size_t size)
   650 {
   651     size_t s;
   652     int j;
   653     struct  pginfo *bp;
   654     int k;
   655     u_long *lp, bf;
   657     /* Don't bother with anything less than this */
   658     if (size < malloc_minsize) {
   659 	size = malloc_minsize;
   660     }
   662     /* Find the right bucket */
   663     j = 1;
   664     s = size - 1;
   665     while (s >>= 1) {
   666         j++;
   667     }
   669     /* If it's empty, make a page more of that size chunks */
   670     if (!page_dir[j] && !malloc_make_chunks(j))
   671 	return 0;
   673     /* Find first word of bitmap which isn't empty */
   674     bp = page_dir[j];
   675     for (lp = bp->bits; !*lp; lp++)
   676 	;
   678     /* Find that bit */
   679     bf = *lp;
   680     k = 0;
   681     while ((bf & 1) == 0) {
   682 	bf >>= 1;
   683 	k++;
   684     }
   686     *lp ^= 1L<<k;                       /* clear it */
   687     bp->free--;
   688     if (!bp->free) {
   689 	page_dir[j] = bp->next;
   690 	bp->next = 0;
   691     }
   692     k += (lp - bp->bits)*MALLOC_BITS;
   693     return bp->page + (k << bp->shift);
   694 }
   696 void *_PR_UnlockedMalloc(size_t size)
   697 {
   698     void *result;
   700     /* Round up to a multiple of 8 bytes */
   701     if (size & 7) {
   702 	size = size + 8 - (size & 7);
   703     }
   705     if (!initialized)
   706 	malloc_init();
   708 #ifdef SANITY
   709     if (suicide)
   710 	PR_Abort();
   711 #endif
   713     if (size <= malloc_maxsize)
   714 	result =  malloc_bytes(size);
   715     else
   716 	result =  malloc_pages(size);
   717 #ifdef SANITY
   718     if (malloc_abort && !result)
   719 	wrterror("malloc() returns NULL\n");
   720 #endif
   721     TRACE(("%6d M %p %d\n",malloc_event++,result,size));
   723     return result;
   724 }
   726 void *_PR_UnlockedMemalign(size_t alignment, size_t size)
   727 {
   728     void *result;
   730     /*
   731      * alignment has to be a power of 2
   732      */
   734     if ((size <= alignment) && (alignment <= malloc_maxsize))
   735 		size = alignment;	
   736     else
   737            	size += alignment - 1;	
   739     /* Round up to a multiple of 8 bytes */
   740     if (size & 7) {
   741 	size = size + 8 - (size & 7);
   742     }
   744     if (!initialized)
   745 	malloc_init();
   747 #ifdef SANITY
   748     if (suicide)
   749 	abort();
   750 #endif
   752     if (size <= malloc_maxsize)
   753 	result =  malloc_bytes(size);
   754     else
   755 	result =  malloc_pages(size);
   756 #ifdef SANITY
   757     if (malloc_abort && !result)
   758 	wrterror("malloc() returns NULL\n");
   759 #endif
   760     TRACE(("%6d A %p %d\n",malloc_event++,result,size));
   762     if ((u_long)result & (alignment - 1))
   763     	return ((void *)(((u_long)result + alignment)  & ~(alignment - 1)));
   764     else
   765     	return result;
   766 }
   768 void *_PR_UnlockedCalloc(size_t n, size_t nelem)
   769 {
   770     void *p;
   772     /* Compute total size and then round up to a double word amount */
   773     n *= nelem;
   774     if (n & 7) {
   775 	n = n + 8 - (n & 7);
   776     }
   778     /* Get the memory */
   779     p = _PR_UnlockedMalloc(n);
   780     if (p) {
   781 	/* Zero it */
   782 	memset(p, 0, n);
   783     }
   784     return p;
   785 }
   787 /*
   788  * Change an allocation's size
   789  */
   790 void *_PR_UnlockedRealloc(void *ptr, size_t size)
   791 {
   792     void *p;
   793     u_long osize,page,index,tmp_index;
   794     struct pginfo **mp;
   796     if (!initialized)
   797 	malloc_init();
   799 #ifdef SANITY
   800     if (suicide)
   801 	PR_Abort();
   802 #endif
   804     /* used as free() */
   805     TRACE(("%6d R %p %d\n",malloc_event++, ptr, size));
   806     if (ptr && !size) {
   807 	_PR_UnlockedFree(ptr);
   808 	return _PR_UnlockedMalloc (1);
   809     }
   811     /* used as malloc() */
   812     if (!ptr) {
   813 	p = _PR_UnlockedMalloc(size);
   814 	return p;
   815     }
   817     /* Find the page directory entry for the page in question */
   818     page = (u_long)ptr >> malloc_pageshift;
   819     index = page - malloc_origo;
   821     /*
   822      * check if memory was allocated by memalign
   823      */
   824     tmp_index = index;
   825     while (page_dir[tmp_index] == MALLOC_FOLLOW)
   826 	tmp_index--;
   827     if (tmp_index != index) {
   828 	/*
   829 	 * memalign-allocated memory
   830 	 */
   831 	index = tmp_index;
   832 	page = index + malloc_origo;			
   833 	ptr = (void *) (page << malloc_pageshift);
   834     }
   835     TRACE(("%6d R2 %p %d\n",malloc_event++, ptr, size));
   837     /* make sure it makes sense in some fashion */
   838     if (index < malloc_pageshift || index > last_index) {
   839 #ifdef SANITY
   840 	wrtwarning("junk pointer passed to realloc()\n");
   841 #endif
   842 	return 0;
   843     }
   845     /* find the size of that allocation, and see if we need to relocate */
   846     mp = &page_dir[index];
   847     if (*mp == MALLOC_FIRST) {
   848 	osize = malloc_pagesize;
   849 	while (mp[1] == MALLOC_FOLLOW) {
   850 	    osize += malloc_pagesize;
   851 	    mp++;
   852 	}
   853         if (!malloc_realloc && 
   854 		size < osize && 
   855 		size > malloc_maxsize &&
   856 	    size > (osize - malloc_pagesize)) {
   857 	    return ptr;
   858 	}
   859     } else if (*mp >= MALLOC_MAGIC) {
   860 	osize = (*mp)->size;
   861 	if (!malloc_realloc &&
   862 		size < osize && 
   863 	    (size > (*mp)->size/2 || (*mp)->size == malloc_minsize)) {
   864 	    return ptr;
   865 	}
   866     } else {
   867 #ifdef SANITY
   868 	wrterror("realloc() of wrong page.\n");
   869 #endif
   870     }
   872     /* try to reallocate */
   873     p = _PR_UnlockedMalloc(size);
   875     if (p) {
   876 	/* copy the lesser of the two sizes */
   877 	if (osize < size)
   878 	    memcpy(p,ptr,osize);
   879 	else
   880 	    memcpy(p,ptr,size);
   881 	_PR_UnlockedFree(ptr);
   882     }
   883 #ifdef DEBUG
   884     else if (malloc_abort)
   885 	wrterror("realloc() returns NULL\n");
   886 #endif
   888     return p;
   889 }
   891 /*
   892  * Free a sequence of pages
   893  */
   895 static void
   896 free_pages(char *ptr, u_long page, int index, struct pginfo *info)
   897 {
   898     int i;
   899     struct pgfree *pf,*pt;
   900     u_long l;
   901     char *tail;
   903     TRACE(("%6d FP %p %d\n",malloc_event++, ptr, page));
   904     /* Is it free already ? */
   905     if (info == MALLOC_FREE) {
   906 #ifdef SANITY
   907 	wrtwarning("freeing free page at %p.\n", ptr);
   908 #endif
   909 	return;
   910     }
   912 #ifdef SANITY
   913     /* Is it not the right place to begin ? */
   914     if (info != MALLOC_FIRST)
   915 	wrterror("freeing wrong page.\n");
   917     /* Is this really a pointer to a page ? */
   918     if ((u_long)ptr & malloc_pagemask)
   919 	wrterror("freeing messed up page pointer.\n");
   920 #endif
   922     /* Count how many pages it is anyway */
   923     page_dir[index] = MALLOC_FREE;
   924     for (i = 1; page_dir[index+i] == MALLOC_FOLLOW; i++)
   925 	page_dir[index + i] = MALLOC_FREE;
   927     l = i << malloc_pageshift;
   929     tail = ptr+l;
   931     /* add to free-list */
   932     if (!px)
   933 	px = (struct pgfree*)_PR_UnlockedMalloc(sizeof *pt);
   934     /* XXX check success */
   935     px->page = ptr;
   936     px->end =  tail;
   937     px->size = l;
   938     if (!free_list.next) {
   939 	px->next = free_list.next;
   940 	px->prev = &free_list;
   941 	free_list.next = px;
   942 	pf = px;
   943 	px = 0;
   944     } else {
   945 	tail = ptr+l;
   946 	for(pf = free_list.next; pf->next && pf->end < ptr; pf = pf->next)
   947 	    ;
   948 	for(; pf; pf = pf->next) {
   949 	    if (pf->end == ptr ) {
   950 		/* append to entry */
   951 		pf->end += l;
   952 		pf->size += l;
   953 		if (pf->next && pf->end == pf->next->page ) {
   954 		    pt = pf->next;
   955 		    pf->end = pt->end;
   956 		    pf->size += pt->size;
   957 		    pf->next = pt->next;
   958 		    if (pf->next)
   959 			pf->next->prev = pf;
   960 		    _PR_UnlockedFree(pt);
   961 		}
   962 	    } else if (pf->page == tail) {
   963 		/* prepend to entry */
   964 		pf->size += l;
   965 		pf->page = ptr;
   966 	    } else if (pf->page > ptr) {
   967 		px->next = pf;
   968 		px->prev = pf->prev;
   969 		pf->prev = px;
   970 		px->prev->next = px;
   971 		pf = px;
   972 		px = 0;
   973 	    } else if (!pf->next) {
   974 		px->next = 0;
   975 		px->prev = pf;
   976 		pf->next = px;
   977 		pf = px;
   978 		px = 0;
   979 	    } else {
   980 		continue;
   981 	    }
   982 	    break;
   983 	}
   984     }
   985     if (!pf->next &&
   986       pf->size > malloc_cache &&
   987       pf->end == malloc_brk &&
   988       malloc_brk == (void*)sbrk(0)) {
   989 	pf->end = pf->page + malloc_cache;
   990 	pf->size = malloc_cache;
   991 	TRACE(("%6d U %p %d\n",malloc_event++,pf->end,pf->end - pf->page));
   992 	brk(pf->end);
   993 	malloc_brk = pf->end;
   994 	/* Find the page directory entry for the page in question */
   995 	page = (u_long)pf->end >> malloc_pageshift;
   996 	index = page - malloc_origo;
   997 	/* Now update the directory */
   998 	for(i=index;i <= last_index;)
   999 	    page_dir[i++] = MALLOC_NOT_MINE;
  1000 	last_index = index - 1;
  1004 /*
  1005  * Free a chunk, and possibly the page it's on, if the page becomes empty.
  1006  */
  1008 static void
  1009 free_bytes(void *ptr, u_long page, int index, struct pginfo *info)
  1011     int i;
  1012     struct pginfo **mp;
  1013     void *vp;
  1015     /* Make sure that pointer is multiplum of chunk-size */
  1016 #ifdef SANITY
  1017     if ((u_long)ptr & (info->size - 1))
  1018 	wrterror("freeing messed up chunk pointer\n");
  1019 #endif
  1021     /* Find the chunk number on the page */
  1022     i = ((u_long)ptr & malloc_pagemask) >> info->shift;
  1024     /* See if it's free already */
  1025     if (tst_bit(info,i)) {
  1026 #ifdef SANITY
  1027 	wrtwarning("freeing free chunk at %p\n", ptr);
  1028 #endif
  1029 	return;
  1032     /* Mark it free */
  1033     set_bit(info,i);
  1034     info->free++;
  1036     /* If the page was full before, we need to put it on the queue now */
  1037     if (info->free == 1) {
  1038 	mp = page_dir + info->shift;
  1039 	while (*mp && (*mp)->next && (*mp)->next->page < info->page)
  1040 	    mp = &(*mp)->next;
  1041 	info->next = *mp;
  1042 	*mp = info;
  1043 	return;
  1046     /* If this page isn't empty, don't do anything. */
  1047     if (info->free != info->total)
  1048 	return;
  1050     /* We may want to keep at least one page of each size chunks around.  */
  1051     mp = page_dir + info->shift;
  1052     if (0 && (*mp == info) && !info->next)
  1053 	return;
  1055     /* Find & remove this page in the queue */
  1056     while (*mp != info) {
  1057 	mp = &((*mp)->next);
  1058 #ifdef EXTRA_SANITY
  1059 	if (!*mp) {
  1060 		TRACE(("%6d !q %p\n",malloc_event++,info));
  1061 		wrterror("Not on queue\n");
  1063 #endif
  1065     *mp = info->next;
  1067     /* Free the page & the info structure if need be */
  1068     set_pgdir(info->page,MALLOC_FIRST);
  1069     if((void*)info->page == (void*)info) {
  1070 	_PR_UnlockedFree(info->page);
  1071     } else {
  1072 	vp = info->page;
  1073 	_PR_UnlockedFree(info);
  1074 	_PR_UnlockedFree(vp);
  1078 void _PR_UnlockedFree(void *ptr)
  1080     u_long page;
  1081     struct pginfo *info;
  1082     int index, tmp_index;
  1084     TRACE(("%6d F %p\n",malloc_event++,ptr));
  1085     /* This is legal */
  1086     if (!ptr)
  1087 	return;
  1089 #ifdef SANITY
  1090     /* There wouldn't be anything to free */
  1091     if (!initialized) {
  1092 	wrtwarning("free() called before malloc() ever got called\n");
  1093 	return;
  1095 #endif
  1097 #ifdef SANITY
  1098     if (suicide)
  1099 	PR_Abort();
  1100 #endif
  1102     /* Find the page directory entry for the page in question */
  1103     page = (u_long)ptr >> malloc_pageshift;
  1104     index = page - malloc_origo;
  1106     /*
  1107      * check if memory was allocated by memalign
  1108      */
  1109     tmp_index = index;
  1110     while (page_dir[tmp_index] == MALLOC_FOLLOW)
  1111 	tmp_index--;
  1112     if (tmp_index != index) {
  1113 	/*
  1114 	 * memalign-allocated memory
  1115 	 */
  1116 	index = tmp_index;
  1117 	page = index + malloc_origo;			
  1118 	ptr = (void *) (page << malloc_pageshift);
  1120     /* make sure it makes sense in some fashion */
  1121     if (index < malloc_pageshift) {
  1122 #ifdef SANITY
  1123 	wrtwarning("junk pointer %p (low) passed to free()\n", ptr);
  1124 #endif
  1125 	return;
  1127     if (index > last_index) {
  1128 #ifdef SANITY
  1129 	wrtwarning("junk pointer %p (high) passed to free()\n", ptr);
  1130 #endif
  1131 	return;
  1134     /* handle as page-allocation or chunk allocation */
  1135     info = page_dir[index];
  1136     if (info < MALLOC_MAGIC)
  1137         free_pages((char*)ptr, page, index, info);
  1138     else 
  1139 	free_bytes(ptr,page,index,info);
  1140     return;
  1142 #endif /* _PR_OVERRIDE_MALLOC */

mercurial