os161-1.99
 All Data Structures
malloc.c
00001 /*
00002  * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
00003  *      The President and Fellows of Harvard College.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the University nor the names of its contributors
00014  *    may be used to endorse or promote products derived from this software
00015  *    without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
00018  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED.  IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
00021  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00022  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00023  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00024  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00025  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00026  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00027  * SUCH DAMAGE.
00028  */
00029 
00030 /*
00031  * User-level malloc and free implementation.
00032  *
00033  * This is a basic first-fit allocator. It's intended to be simple and
00034  * easy to follow. It performs abysmally if the heap becomes larger than
00035  * physical memory. To get (much) better out-of-core performance, port
00036  * the kernel's malloc. :-)
00037  */
00038 
00039 #include <stdlib.h>
00040 #include <unistd.h>
00041 #include <err.h>
00042 #include <stdint.h>  // for uintptr_t on non-OS/161 platforms
00043 
00044 #undef MALLOCDEBUG
00045 
00046 #if defined(__mips__) || defined(__i386__)
00047 #define MALLOC32
00048 #elif defined(__alpha__)
00049 #define MALLOC64
00050 #else
00051 #error "please fix me"
00052 #endif
00053 
00054 /*
00055  * malloc block header.
00056  *
00057  * mh_prevblock is the downwards offset to the previous header, 0 if this
00058  * is the bottom of the heap.
00059  *
00060  * mh_nextblock is the upwards offset to the next header.
00061  *
00062  * mh_pad is unused.
00063  * mh_inuse is 1 if the block is in use, 0 if it is free.
00064  * mh_magic* should always be a fixed value.
00065  *
00066  * MBLOCKSIZE should equal sizeof(struct mheader) and be a power of 2.
00067  * MBLOCKSHIFT is the log base 2 of MBLOCKSIZE.
00068  * MMAGIC is the value for mh_magic*.
00069  */
00070 struct mheader {
00071 
00072 #if defined(MALLOC32)
00073 #define MBLOCKSIZE 8
00074 #define MBLOCKSHIFT 3
00075 #define MMAGIC 2
00076         /*
00077          * 32-bit platform. size_t is 32 bits (4 bytes). 
00078          * Block size is 8 bytes.
00079          */
00080         unsigned mh_prevblock:29;
00081         unsigned mh_pad:1;
00082         unsigned mh_magic1:2;
00083 
00084         unsigned mh_nextblock:29;
00085         unsigned mh_inuse:1;
00086         unsigned mh_magic2:2;
00087 
00088 #elif defined(MALLOC64)
00089 #define MBLOCKSIZE 16
00090 #define MBLOCKSHIFT 4
00091 #define MMAGIC 6
00092         /*
00093          * 64-bit platform. size_t is 64 bits (8 bytes)
00094          * Block size is 16 bytes.
00095          */
00096         unsigned mh_prevblock:62;
00097         unsigned mh_pad:1;
00098         unsigned mh_magic1:3;
00099 
00100         unsigned mh_nextblock:62;
00101         unsigned mh_inuse:1;
00102         unsigned mh_magic2:3;
00103 
00104 #else
00105 #error "please fix me"
00106 #endif
00107 };
00108 
00109 /*
00110  * Operator macros on struct mheader.
00111  *
00112  * M_NEXT/PREVOFF:      return offset to next/previous header
00113  * M_NEXT/PREV:         return next/previous header
00114  * 
00115  * M_DATA:              return data pointer of a header
00116  * M_SIZE:              return data size of a header
00117  *
00118  * M_OK:                true if the magic values are correct
00119  * 
00120  * M_MKFIELD:           prepare a value for mh_next/prevblock.
00121  *                      (value should include the header size)
00122  */
00123 
00124 #define M_NEXTOFF(mh)   ((size_t)(((size_t)((mh)->mh_nextblock))<<MBLOCKSHIFT))
00125 #define M_PREVOFF(mh)   ((size_t)(((size_t)((mh)->mh_prevblock))<<MBLOCKSHIFT))
00126 #define M_NEXT(mh)      ((struct mheader *)(((char*)(mh))+M_NEXTOFF(mh)))
00127 #define M_PREV(mh)      ((struct mheader *)(((char*)(mh))-M_PREVOFF(mh)))
00128 
00129 #define M_DATA(mh)      ((void *)((mh)+1))
00130 #define M_SIZE(mh)      (M_NEXTOFF(mh)-MBLOCKSIZE)
00131 
00132 #define M_OK(mh)        ((mh)->mh_magic1==MMAGIC && (mh)->mh_magic2==MMAGIC)
00133 
00134 #define M_MKFIELD(off)  ((off)>>MBLOCKSHIFT)
00135 
00136 ////////////////////////////////////////////////////////////
00137 
00138 /*
00139  * Static variables - the bottom and top addresses of the heap.
00140  */
00141 static uintptr_t __heapbase, __heaptop;
00142 
00143 /*
00144  * Setup function.
00145  */
00146 static
00147 void
00148 __malloc_init(void)
00149 {
00150         void *x;
00151 
00152         /*
00153          * Check various assumed properties of the sizes.
00154          */
00155         if (sizeof(struct mheader) != MBLOCKSIZE) {
00156                 errx(1, "malloc: Internal error - MBLOCKSIZE wrong");
00157         }
00158         if ((MBLOCKSIZE & (MBLOCKSIZE-1))!=0) {
00159                 errx(1, "malloc: Internal error - MBLOCKSIZE not power of 2");
00160         }
00161         if (1<<MBLOCKSHIFT != MBLOCKSIZE) {
00162                 errx(1, "malloc: Internal error - MBLOCKSHIFT wrong");
00163         }
00164 
00165         /* init should only be called once. */
00166         if (__heapbase!=0 || __heaptop!=0) {
00167                 errx(1, "malloc: Internal error - bad init call");
00168         }
00169 
00170         /* Use sbrk to find the base of the heap. */
00171         x = sbrk(0);
00172         if (x==(void *)-1) {
00173                 err(1, "malloc: initial sbrk failed");
00174         }
00175         if (x==(void *) 0) {
00176                 errx(1, "malloc: Internal error - heap began at 0");
00177         }
00178         __heapbase = __heaptop = (uintptr_t)x;
00179 
00180         /*
00181          * Make sure the heap base is aligned the way we want it.
00182          * (On OS/161, it will begin on a page boundary. But on 
00183          * an arbitrary Unix, it may not be, as traditionally it
00184          * begins at _end.)
00185          */
00186 
00187         if (__heapbase % MBLOCKSIZE != 0) {
00188                 size_t adjust = MBLOCKSIZE - (__heapbase % MBLOCKSIZE);
00189                 x = sbrk(adjust);
00190                 if (x==(void *)-1) {
00191                         err(1, "malloc: sbrk failed aligning heap base");
00192                 }
00193                 if ((uintptr_t)x != __heapbase) {
00194                         err(1, "malloc: heap base moved during init");
00195                 }
00196 #ifdef MALLOCDEBUG
00197                 warnx("malloc: adjusted heap base upwards by %lu bytes",
00198                       (unsigned long) adjust);
00199 #endif
00200                 __heapbase += adjust;
00201                 __heaptop = __heapbase;
00202         }
00203 }
00204 
00205 ////////////////////////////////////////////////////////////
00206 
00207 #ifdef MALLOCDEBUG
00208 
00209 /*
00210  * Debugging print function to iterate and dump the entire heap.
00211  */
00212 static
00213 void
00214 __malloc_dump(void)
00215 {
00216         struct mheader *mh;
00217         uintptr_t i;
00218         size_t rightprevblock;
00219 
00220         warnx("heap: ************************************************");
00221 
00222         rightprevblock = 0;
00223         for (i=__heapbase; i<__heaptop; i += M_NEXTOFF(mh)) {
00224                 mh = (struct mheader *) i;
00225                 if (!M_OK(mh)) {
00226                         errx(1, "malloc: Heap corrupt; header at 0x%lx"
00227                              " has bad magic bits",
00228                              (unsigned long) i);
00229                 }
00230                 if (mh->mh_prevblock != rightprevblock) {
00231                         errx(1, "malloc: Heap corrupt; header at 0x%lx"
00232                              " has bad previous-block size %lu "
00233                              "(should be %lu)",
00234                              (unsigned long) i, 
00235                              (unsigned long) mh->mh_prevblock << MBLOCKSHIFT,
00236                              (unsigned long) rightprevblock << MBLOCKSHIFT);
00237                 }
00238                 rightprevblock = mh->mh_nextblock;
00239 
00240                 warnx("heap: 0x%lx 0x%-6lx (next: 0x%lx) %s",
00241                       (unsigned long) i + MBLOCKSIZE,
00242                       (unsigned long) M_SIZE(mh),
00243                       (unsigned long) (i+M_NEXTOFF(mh)),
00244                       mh->mh_inuse ? "INUSE" : "FREE");
00245         }
00246         if (i!=__heaptop) {
00247                 errx(1, "malloc: Heap corrupt; ran off end");
00248         }
00249 
00250         warnx("heap: ************************************************");
00251 }
00252 
00253 #endif /* MALLOCDEBUG */
00254 
00255 ////////////////////////////////////////////////////////////
00256 
00257 /*
00258  * Get more memory (at the top of the heap) using sbrk, and 
00259  * return a pointer to it.
00260  */
00261 static
00262 void *
00263 __malloc_sbrk(size_t size)
00264 {
00265         void *x;
00266 
00267         x = sbrk(size);
00268         if (x == (void *)-1) {
00269                 return NULL;
00270         }
00271 
00272         if ((uintptr_t)x != __heaptop) {
00273                 errx(1, "malloc: Internal error - "
00274                      "heap top moved itself from 0x%lx to 0x%lx",
00275                      (unsigned long) __heaptop,
00276                      (unsigned long) (uintptr_t) x);
00277         }
00278         __heaptop += size;
00279         return x;
00280 }
00281 
00282 /*
00283  * Make a new (free) block from the block passed in, leaving size
00284  * bytes for data in the current block. size must be a multiple of
00285  * MBLOCKSIZE.
00286  *
00287  * Only split if the excess space is at least twice the blocksize -
00288  * one blocksize to hold a header and one for data.
00289  */
00290 static
00291 void
00292 __malloc_split(struct mheader *mh, size_t size)
00293 {
00294         struct mheader *mhnext, *mhnew;
00295         size_t oldsize;
00296 
00297         if (size % MBLOCKSIZE != 0) {
00298                 errx(1, "malloc: Internal error (size %lu passed to split)",
00299                      (unsigned long) size);
00300         }
00301 
00302         if (M_SIZE(mh) - size < 2*MBLOCKSIZE) {
00303                 /* no room */
00304                 return;
00305         }
00306 
00307         mhnext = M_NEXT(mh);
00308 
00309         oldsize = M_SIZE(mh);
00310         mh->mh_nextblock = M_MKFIELD(size + MBLOCKSIZE);
00311         
00312         mhnew = M_NEXT(mh);
00313         if (mhnew==mhnext) {
00314                 errx(1, "malloc: Internal error (split screwed up?)");
00315         }
00316 
00317         mhnew->mh_prevblock = M_MKFIELD(size + MBLOCKSIZE);
00318         mhnew->mh_pad = 0;
00319         mhnew->mh_magic1 = MMAGIC;
00320         mhnew->mh_nextblock = M_MKFIELD(oldsize - size);
00321         mhnew->mh_inuse = 0;
00322         mhnew->mh_magic2 = MMAGIC;
00323 
00324         if (mhnext != (struct mheader *) __heaptop) {
00325                 mhnext->mh_prevblock = mhnew->mh_nextblock;
00326         }
00327 }
00328 
00329 /*
00330  * malloc itself.
00331  */
00332 void *
00333 malloc(size_t size)
00334 {
00335         struct mheader *mh;
00336         uintptr_t i;
00337         size_t rightprevblock;
00338 
00339         if (__heapbase==0) {
00340                 __malloc_init();
00341         }
00342         if (__heapbase==0 || __heaptop==0 || __heapbase > __heaptop) {
00343                 warnx("malloc: Internal error - local data corrupt");
00344                 errx(1, "malloc: heapbase 0x%lx; heaptop 0x%lx", 
00345                      (unsigned long) __heapbase, (unsigned long) __heaptop);
00346         }
00347 
00348 #ifdef MALLOCDEBUG
00349         warnx("malloc: about to allocate %lu (0x%lx) bytes", 
00350               (unsigned long) size, (unsigned long) size);
00351         __malloc_dump();
00352 #endif
00353 
00354         /* Round size up to an integral number of blocks. */
00355         size = ((size + MBLOCKSIZE - 1) & ~(size_t)(MBLOCKSIZE-1));
00356 
00357         /*
00358          * First-fit search algorithm for available blocks.
00359          * Check to make sure the next/previous sizes all agree.
00360          */
00361         rightprevblock = 0;
00362         for (i=__heapbase; i<__heaptop; i += M_NEXTOFF(mh)) {
00363                 mh = (struct mheader *) i;
00364                 if (!M_OK(mh)) {
00365                         errx(1, "malloc: Heap corrupt; header at 0x%lx"
00366                              " has bad magic bits",
00367                              (unsigned long) i);
00368                 }
00369                 if (mh->mh_prevblock != rightprevblock) {
00370                         errx(1, "malloc: Heap corrupt; header at 0x%lx"
00371                              " has bad previous-block size %lu "
00372                              "(should be %lu)",
00373                              (unsigned long) i, 
00374                              (unsigned long) mh->mh_prevblock << MBLOCKSHIFT,
00375                              (unsigned long) rightprevblock << MBLOCKSHIFT);
00376                 }
00377                 rightprevblock = mh->mh_nextblock;
00378 
00379                 /* Can't allocate a block that's in use. */
00380                 if (mh->mh_inuse) {
00381                         continue;
00382                 }
00383 
00384                 /* Can't allocate a block that isn't big enough. */
00385                 if (M_SIZE(mh) < size) {
00386                         continue;
00387                 }
00388 
00389                 /* Try splitting block. */
00390                 __malloc_split(mh, size);
00391 
00392                 /*
00393                  * Now, allocate.
00394                  */
00395                 mh->mh_inuse = 1;
00396 
00397 #ifdef MALLOCDEBUG
00398                 warnx("malloc: allocating at %p", M_DATA(mh));
00399                 __malloc_dump();
00400 #endif
00401                 return M_DATA(mh);
00402         }
00403         if (i!=__heaptop) {
00404                 errx(1, "malloc: Heap corrupt; ran off end");
00405         }
00406 
00407         /*
00408          * Didn't find anything. Expand the heap.
00409          */
00410 
00411         mh = __malloc_sbrk(size + MBLOCKSIZE);
00412         if (mh == NULL) {
00413                 return NULL;
00414         }
00415 
00416         mh->mh_prevblock = rightprevblock;
00417         mh->mh_magic1 = MMAGIC;
00418         mh->mh_magic2 = MMAGIC;
00419         mh->mh_pad = 0;
00420         mh->mh_inuse = 1;
00421         mh->mh_nextblock = M_MKFIELD(size + MBLOCKSIZE);
00422 
00423 #ifdef MALLOCDEBUG
00424         warnx("malloc: allocating at %p", M_DATA(mh));
00425         __malloc_dump();
00426 #endif
00427         return M_DATA(mh);
00428 }
00429 
00430 ////////////////////////////////////////////////////////////
00431 
00432 /*
00433  * Clear a range of memory with 0xdeadbeef.
00434  * ptr must be suitably aligned.
00435  */
00436 static
00437 void
00438 __malloc_deadbeef(void *ptr, size_t size)
00439 {
00440         uint32_t *x = ptr;
00441         size_t i, n = size/sizeof(uint32_t);
00442         for (i=0; i<n; i++) {
00443                 x[i] = 0xdeadbeef;
00444         }
00445 }
00446 
00447 /*
00448  * Attempt to merge two adjacent blocks (mh below mhnext).
00449  */
00450 static
00451 void
00452 __malloc_trymerge(struct mheader *mh, struct mheader *mhnext)
00453 {
00454         struct mheader *mhnextnext;
00455 
00456         if (mh->mh_nextblock != mhnext->mh_prevblock) {
00457                 errx(1, "free: Heap corrupt (%p and %p inconsistent)",
00458                      mh, mhnext);
00459         }
00460         if (mh->mh_inuse || mhnext->mh_inuse) {
00461                 /* can't merge */
00462                 return;
00463         }
00464 
00465         mhnextnext = M_NEXT(mhnext);
00466 
00467         mh->mh_nextblock = M_MKFIELD(MBLOCKSIZE + M_SIZE(mh) +
00468                                      MBLOCKSIZE + M_SIZE(mhnext));
00469 
00470         if (mhnextnext != (struct mheader *)__heaptop) {
00471                 mhnextnext->mh_prevblock = mh->mh_nextblock;
00472         }
00473 
00474         /* Deadbeef out the memory used by the now-obsolete header */
00475         __malloc_deadbeef(mhnext, sizeof(struct mheader));
00476 }
00477 
00478 /*
00479  * The actual free() implementation.
00480  */
00481 void
00482 free(void *x)
00483 {
00484         struct mheader *mh, *mhnext, *mhprev;
00485 
00486         if (x==NULL) {
00487                 /* safest practice */
00488                 return;
00489         }
00490 
00491         /* Consistency check. */
00492         if (__heapbase==0 || __heaptop==0 || __heapbase > __heaptop) {
00493                 warnx("free: Internal error - local data corrupt");
00494                 errx(1, "free: heapbase 0x%lx; heaptop 0x%lx", 
00495                      (unsigned long) __heapbase, (unsigned long) __heaptop);
00496         }
00497 
00498         /* Don't allow freeing pointers that aren't on the heap. */
00499         if ((uintptr_t)x < __heapbase || (uintptr_t)x >= __heaptop) {
00500                 errx(1, "free: Invalid pointer %p freed (out of range)", x);
00501         }
00502 
00503 #ifdef MALLOCDEBUG
00504         warnx("free: about to free %p", x);
00505         __malloc_dump();
00506 #endif
00507 
00508         mh = ((struct mheader *)x)-1;
00509         if (!M_OK(mh)) {
00510                 errx(1, "free: Invalid pointer %p freed (corrupt header)", x);
00511         }
00512 
00513         if (!mh->mh_inuse) {
00514                 errx(1, "free: Invalid pointer %p freed (already free)", x);
00515         }
00516 
00517         /* mark it free */
00518         mh->mh_inuse = 0;
00519 
00520         /* wipe it */
00521         __malloc_deadbeef(M_DATA(mh), M_SIZE(mh));
00522 
00523         /* Try merging with the block above (but not if we're at the top) */
00524         mhnext = M_NEXT(mh);
00525         if (mhnext != (struct mheader *)__heaptop) {
00526                 __malloc_trymerge(mh, mhnext);
00527         }
00528 
00529         /* Try merging with the block below (but not if we're at the bottom) */
00530         if (mh != (struct mheader *)__heapbase) {
00531                 mhprev = M_PREV(mh);
00532                 __malloc_trymerge(mhprev, mh);
00533         }
00534 
00535 #ifdef MALLOCDEBUG
00536         warnx("free: freed %p", x);
00537         __malloc_dump();
00538 #endif
00539 }
 All Data Structures