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.

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

mercurial