1 /*- 2 * Copyright (c) 1983, 1991 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #ifndef lint 35 /*static char sccsid[] = "from: @(#)alloc.c 5.8 (Berkeley) 6/8/91";*/ 36 static char rcsid[] = "$Id: alloc.c,v 1.4 1993/08/01 19:00:53 mycroft Exp $"; 37 #endif /* not lint */ 38 39 /* 40 * tc.alloc.c from malloc.c (Caltech) 2/21/82 41 * Chris Kingsley, kingsley@cit-20. 42 * 43 * This is a very fast storage allocator. It allocates blocks of a small 44 * number of different sizes, and keeps free lists of each size. Blocks that 45 * don't exactly fit are passed up to the next larger size. In this 46 * implementation, the available sizes are 2^n-4 (or 2^n-12) bytes long. 47 * This is designed for use in a program that uses vast quantities of memory, 48 * but bombs when it runs out. 49 */ 50 51 #include <sys/types.h> 52 #include <unistd.h> 53 #include <string.h> 54 #if __STDC__ 55 # include <stdarg.h> 56 #else 57 # include <varargs.h> 58 #endif 59 60 #include "csh.h" 61 #include "extern.h" 62 63 char *memtop = NULL; /* PWP: top of current memory */ 64 char *membot = NULL; /* PWP: bottom of allocatable memory */ 65 66 #ifndef SYSMALLOC 67 68 #undef RCHECK 69 #undef DEBUG 70 71 72 #ifndef NULL 73 #define NULL 0 74 #endif 75 76 77 /* 78 * The overhead on a block is at least 4 bytes. When free, this space 79 * contains a pointer to the next free block, and the bottom two bits must 80 * be zero. When in use, the first byte is set to MAGIC, and the second 81 * byte is the size index. The remaining bytes are for alignment. 82 * If range checking is enabled and the size of the block fits 83 * in two bytes, then the top two bytes hold the size of the requested block 84 * plus the range checking words, and the header word MINUS ONE. 85 */ 86 87 #define ROUNDUP 7 88 89 #define ALIGN(a) (((a) + ROUNDUP) & ~ROUNDUP) 90 91 union overhead { 92 union overhead *ov_next; /* when free */ 93 struct { 94 u_char ovu_magic; /* magic number */ 95 u_char ovu_index; /* bucket # */ 96 #ifdef RCHECK 97 u_short ovu_size; /* actual block size */ 98 u_int ovu_rmagic; /* range magic number */ 99 #endif 100 } ovu; 101 #define ov_magic ovu.ovu_magic 102 #define ov_index ovu.ovu_index 103 #define ov_size ovu.ovu_size 104 #define ov_rmagic ovu.ovu_rmagic 105 }; 106 107 #define MAGIC 0xfd /* magic # on accounting info */ 108 #define RMAGIC 0x55555555 /* magic # on range info */ 109 #ifdef RCHECK 110 #define RSLOP sizeof (u_int) 111 #else 112 #define RSLOP 0 113 #endif 114 115 /* 116 * nextf[i] is the pointer to the next free block of size 2^(i+3). The 117 * smallest allocatable block is 8 bytes. The overhead information 118 * precedes the data area returned to the user. 119 */ 120 #define NBUCKETS 30 121 static union overhead *nextf[NBUCKETS]; 122 123 static int findbucket __P((union overhead *, int)); 124 static void morecore __P((int)); 125 126 /* 127 * nmalloc[i] is the difference between the number of mallocs and frees 128 * for a given block size. 129 */ 130 static u_int nmalloc[NBUCKETS]; 131 132 133 #ifdef DEBUG 134 #define CHECK(a, str, p) \ 135 if (a) { \ 136 xprintf(str, p); \ 137 xprintf("memtop = %lx membot = %lx.\n", memtop, membot); \ 138 abort(); \ 139 } \ 140 else 141 #else 142 #define CHECK(a, str, p) \ 143 if (a) { \ 144 xprintf(str, p); \ 145 xprintf("memtop = %lx membot = %lx.\n", memtop, membot); \ 146 return; \ 147 } \ 148 else 149 #endif 150 151 ptr_t 152 malloc(nbytes) 153 register size_t nbytes; 154 { 155 #ifndef lint 156 register union overhead *p; 157 register int bucket = 0; 158 register unsigned shiftr; 159 160 /* 161 * Convert amount of memory requested into closest block size stored in 162 * hash buckets which satisfies request. Account for space used per block 163 * for accounting. 164 */ 165 nbytes = ALIGN(ALIGN(sizeof(union overhead)) + nbytes + RSLOP); 166 shiftr = (nbytes - 1) >> 2; 167 168 /* apart from this loop, this is O(1) */ 169 while (shiftr >>= 1) 170 bucket++; 171 /* 172 * If nothing in hash bucket right now, request more memory from the 173 * system. 174 */ 175 if (nextf[bucket] == NULL) 176 morecore(bucket); 177 if ((p = (union overhead *) nextf[bucket]) == NULL) { 178 child++; 179 #ifndef DEBUG 180 stderror(ERR_NOMEM); 181 #else 182 showall(); 183 xprintf("nbytes=%d: Out of memory\n", nbytes); 184 abort(); 185 #endif 186 /* fool lint */ 187 return ((ptr_t) 0); 188 } 189 /* remove from linked list */ 190 nextf[bucket] = nextf[bucket]->ov_next; 191 p->ov_magic = MAGIC; 192 p->ov_index = bucket; 193 nmalloc[bucket]++; 194 #ifdef RCHECK 195 /* 196 * Record allocated size of block and bound space with magic numbers. 197 */ 198 if (nbytes <= 0x10000) 199 p->ov_size = nbytes - 1; 200 p->ov_rmagic = RMAGIC; 201 *((u_int *) (((caddr_t) p) + nbytes - RSLOP)) = RMAGIC; 202 #endif 203 return ((ptr_t) (((caddr_t) p) + ALIGN(sizeof(union overhead)))); 204 #else 205 if (nbytes) 206 return ((ptr_t) 0); 207 else 208 return ((ptr_t) 0); 209 #endif /* !lint */ 210 } 211 212 #ifndef lint 213 /* 214 * Allocate more memory to the indicated bucket. 215 */ 216 static void 217 morecore(bucket) 218 register int bucket; 219 { 220 register union overhead *op; 221 register int rnu; /* 2^rnu bytes will be requested */ 222 register int nblks; /* become nblks blocks of the desired size */ 223 register int siz; 224 225 if (nextf[bucket]) 226 return; 227 /* 228 * Insure memory is allocated on a page boundary. Should make getpageize 229 * call? 230 */ 231 op = (union overhead *) sbrk(0); 232 memtop = (char *) op; 233 if (membot == NULL) 234 membot = memtop; 235 if ((int) op & 0x3ff) { 236 memtop = (char *) sbrk(1024 - ((int) op & 0x3ff)); 237 memtop += 1024 - ((int) op & 0x3ff); 238 } 239 240 /* take 2k unless the block is bigger than that */ 241 rnu = (bucket <= 8) ? 11 : bucket + 3; 242 nblks = 1 << (rnu - (bucket + 3)); /* how many blocks to get */ 243 if (rnu < bucket) 244 rnu = bucket; 245 memtop = (char *) sbrk(1 << rnu); /* PWP */ 246 op = (union overhead *) memtop; 247 memtop += 1 << rnu; 248 /* no more room! */ 249 if ((int) op == -1) 250 return; 251 /* 252 * Round up to minimum allocation size boundary and deduct from block count 253 * to reflect. 254 */ 255 if (((u_int) op) & ROUNDUP) { 256 op = (union overhead *) (((u_int) op + (ROUNDUP + 1)) & ~ROUNDUP); 257 nblks--; 258 } 259 /* 260 * Add new memory allocated to that on free list for this hash bucket. 261 */ 262 nextf[bucket] = op; 263 siz = 1 << (bucket + 3); 264 while (--nblks > 0) { 265 op->ov_next = (union overhead *) (((caddr_t) op) + siz); 266 op = (union overhead *) (((caddr_t) op) + siz); 267 } 268 } 269 270 #endif 271 272 #ifdef sun 273 int 274 #else 275 void 276 #endif 277 free(cp) 278 ptr_t cp; 279 { 280 #ifndef lint 281 register int size; 282 register union overhead *op; 283 284 if (cp == NULL) 285 return; 286 CHECK(!memtop || !membot, "free(%lx) called before any allocations.", cp); 287 CHECK(cp > (ptr_t) memtop, "free(%lx) above top of memory.", cp); 288 CHECK(cp < (ptr_t) membot, "free(%lx) above top of memory.", cp); 289 op = (union overhead *) (((caddr_t) cp) - ALIGN(sizeof(union overhead))); 290 CHECK(op->ov_magic != MAGIC, "free(%lx) bad block.", cp); 291 292 #ifdef RCHECK 293 if (op->ov_index <= 13) 294 CHECK(*(u_int *) ((caddr_t) op + op->ov_size + 1 - RSLOP) != RMAGIC, 295 "free(%lx) bad range check.", cp); 296 #endif 297 CHECK(op->ov_index >= NBUCKETS, "free(%lx) bad block index.", cp); 298 size = op->ov_index; 299 op->ov_next = nextf[size]; 300 nextf[size] = op; 301 302 nmalloc[size]--; 303 304 #else 305 if (cp == NULL) 306 return; 307 #endif 308 } 309 310 ptr_t 311 calloc(i, j) 312 size_t i, j; 313 { 314 #ifndef lint 315 register char *cp, *scp; 316 317 i *= j; 318 scp = cp = (char *) xmalloc((size_t) i); 319 if (i != 0) 320 do 321 *cp++ = 0; 322 while (--i); 323 324 return (scp); 325 #else 326 if (i && j) 327 return ((ptr_t) 0); 328 else 329 return ((ptr_t) 0); 330 #endif 331 } 332 333 /* 334 * When a program attempts "storage compaction" as mentioned in the 335 * old malloc man page, it realloc's an already freed block. Usually 336 * this is the last block it freed; occasionally it might be farther 337 * back. We have to search all the free lists for the block in order 338 * to determine its bucket: 1st we make one pass thru the lists 339 * checking only the first block in each; if that fails we search 340 * ``realloc_srchlen'' blocks in each list for a match (the variable 341 * is extern so the caller can modify it). If that fails we just copy 342 * however many bytes was given to realloc() and hope it's not huge. 343 */ 344 #ifndef lint 345 int realloc_srchlen = 4; /* 4 should be plenty, -1 =>'s whole list */ 346 347 #endif /* lint */ 348 349 ptr_t 350 realloc(cp, nbytes) 351 ptr_t cp; 352 size_t nbytes; 353 { 354 #ifndef lint 355 register u_int onb; 356 union overhead *op; 357 char *res; 358 register int i; 359 int was_alloced = 0; 360 361 if (cp == NULL) 362 return (malloc(nbytes)); 363 op = (union overhead *) (((caddr_t) cp) - ALIGN(sizeof(union overhead))); 364 if (op->ov_magic == MAGIC) { 365 was_alloced++; 366 i = op->ov_index; 367 } 368 else 369 /* 370 * Already free, doing "compaction". 371 * 372 * Search for the old block of memory on the free list. First, check the 373 * most common case (last element free'd), then (this failing) the last 374 * ``realloc_srchlen'' items free'd. If all lookups fail, then assume 375 * the size of the memory block being realloc'd is the smallest 376 * possible. 377 */ 378 if ((i = findbucket(op, 1)) < 0 && 379 (i = findbucket(op, realloc_srchlen)) < 0) 380 i = 0; 381 382 onb = ALIGN(nbytes + ALIGN(sizeof(union overhead)) + RSLOP); 383 384 /* avoid the copy if same size block */ 385 if (was_alloced && (onb < (1 << (i + 3))) && (onb >= (1 << (i + 2)))) 386 return ((ptr_t) cp); 387 if ((res = malloc(nbytes)) == NULL) 388 return ((ptr_t) 0); 389 if (cp != res) /* common optimization */ 390 bcopy(cp, res, nbytes); 391 if (was_alloced) 392 free(cp); 393 return (res); 394 #else 395 if (cp && nbytes) 396 return ((ptr_t) 0); 397 else 398 return ((ptr_t) 0); 399 #endif /* !lint */ 400 } 401 402 403 404 #ifndef lint 405 /* 406 * Search ``srchlen'' elements of each free list for a block whose 407 * header starts at ``freep''. If srchlen is -1 search the whole list. 408 * Return bucket number, or -1 if not found. 409 */ 410 static int 411 findbucket(freep, srchlen) 412 union overhead *freep; 413 int srchlen; 414 { 415 register union overhead *p; 416 register int i, j; 417 418 for (i = 0; i < NBUCKETS; i++) { 419 j = 0; 420 for (p = nextf[i]; p && j != srchlen; p = p->ov_next) { 421 if (p == freep) 422 return (i); 423 j++; 424 } 425 } 426 return (-1); 427 } 428 429 #endif 430 431 432 #else /* SYSMALLOC */ 433 434 /** 435 ** ``Protected versions'' of malloc, realloc, calloc, and free 436 ** 437 ** On many systems: 438 ** 439 ** 1. malloc(0) is bad 440 ** 2. free(0) is bad 441 ** 3. realloc(0, n) is bad 442 ** 4. realloc(n, 0) is bad 443 ** 444 ** Also we call our error routine if we run out of memory. 445 **/ 446 char * 447 Malloc(n) 448 size_t n; 449 { 450 ptr_t ptr; 451 452 n = n ? n : 1; 453 454 if ((ptr = malloc(n)) == (ptr_t) 0) { 455 child++; 456 stderror(ERR_NOMEM); 457 } 458 return ((char *) ptr); 459 } 460 461 char * 462 Realloc(p, n) 463 ptr_t p; 464 size_t n; 465 { 466 ptr_t ptr; 467 468 n = n ? n : 1; 469 if ((ptr = (p ? realloc(p, n) : malloc(n))) == (ptr_t) 0) { 470 child++; 471 stderror(ERR_NOMEM); 472 } 473 return ((char *) ptr); 474 } 475 476 char * 477 Calloc(s, n) 478 size_t s, n; 479 { 480 char *sptr; 481 ptr_t ptr; 482 483 n *= s; 484 n = n ? n : 1; 485 if ((ptr = malloc(n)) == (ptr_t) 0) { 486 child++; 487 stderror(ERR_NOMEM); 488 } 489 490 sptr = (char *) ptr; 491 if (n != 0) 492 do 493 *sptr++ = 0; 494 while (--n); 495 496 return ((char *) ptr); 497 } 498 499 void 500 Free(p) 501 ptr_t p; 502 { 503 if (p) 504 free(p); 505 } 506 507 #endif /* SYSMALLOC */ 508 509 /* 510 * mstats - print out statistics about malloc 511 * 512 * Prints two lines of numbers, one showing the length of the free list 513 * for each size category, the second showing the number of mallocs - 514 * frees for each size category. 515 */ 516 void 517 showall() 518 { 519 #ifndef SYSMALLOC 520 register int i, j; 521 register union overhead *p; 522 int totfree = 0, totused = 0; 523 524 xprintf("csh current memory allocation:\nfree:\t"); 525 for (i = 0; i < NBUCKETS; i++) { 526 for (j = 0, p = nextf[i]; p; p = p->ov_next, j++); 527 xprintf(" %4d", j); 528 totfree += j * (1 << (i + 3)); 529 } 530 xprintf("\nused:\t"); 531 for (i = 0; i < NBUCKETS; i++) { 532 xprintf(" %4d", nmalloc[i]); 533 totused += nmalloc[i] * (1 << (i + 3)); 534 } 535 xprintf("\n\tTotal in use: %d, total free: %d\n", 536 totused, totfree); 537 xprintf("\tAllocated memory from 0x%lx to 0x%lx. Real top at 0x%lx\n", 538 membot, memtop, (char *) sbrk(0)); 539 #else 540 xprintf("Allocated memory from 0x%lx to 0x%lx (%ld).\n", 541 membot, memtop = (char *) sbrk(0), memtop - membot); 542 #endif /* SYSMALLOC */ 543 } 544