/** This is a modified version of the gcx.d file (tested on freebsd) - it basically implements :getAllocLoc() which returns a string which is a pretty good estiamte of the method/line number of where the memory was allocated. - this is then used by the Logger to store where the memory was allocated - then I've modifed log_collect so it just dumps out all the places it knows that memory has been allocated. */ // // Copyright (C) 2001-2006 by Digital Mars // All Rights Reserved // Written by Walter Bright // www.digitalmars.com /* NOTE: This file has been patched from the original DMD distribution to work with the GDC compiler. Modified by David Friedman, July 2006 */ // D Garbage Collector implementation /************** Debugging ***************************/ debug = PRINTF; // turn on printf's //debug = COLLECT_PRINTF; // turn on printf's //debug = THREADINVARIANT; // check thread integrity debug = LOGGING; // log allocations / frees //debug = MEMSTOMP; // stomp on memory //debug = SENTINEL; // add underrun/overrrun protection //debug = PTRCHECK; // more pointer checking //debug = PTRCHECK2; // thorough but slow pointer checking /*************** Configuration *********************/ version = STACKGROWSDOWN; // growing the stack means subtracting from the stack pointer // (use for Intel X86 CPUs) // else growing the stack means adding to the stack pointer version = MULTI_THREADED; // produce multithreaded version /***************************************************/ debug (PRINTF) import std.c.stdio; import std.c.stdio; import std.c.stdlib; import std.c.string; import gcbits; import std.outofmemory; import std.gc; import gcstats; version (GNU) { private import gcc.builtins; } version (Win32) { import win32; import std.c.windows.windows; } else version (GNU) { private import gcgcc; } else version (linux) { import gclinux; } /*version (BigEndian) private import std.intrinsic;*/ version (MULTI_THREADED) { import std.thread; } //alias GC* gc_t; alias GC gc_t; version (X86) version (D_InlineAsm) { version = Asm86; } /* ======================= Leak Detector =========================== */ import std.c.linux.linux; debug (LOGGING) { extern(C) { int backtrace (void **buffer, int size); char ** backtrace_symbols (void **buffer, int size); char *strdup( char *s); bool isdigit(char c) { return (c >= '0' && c <= '9'); } static int extractidentifiers(char** output, char** mangled) { int i = -1; while (isdigit(**mangled)) { i = strtol(*mangled, mangled, 10); if (strlen(*mangled) < i) return -1; memcpy(*output, *mangled, i); *mangled += i; *output += i + 1; (*output)[-1] = '.'; } if (**mangled == '\0' || i == -1) return -1; (*output)--; return 1; } static void append(char** dest, char* src) { int i = strlen(src); for(;i>0; i--) { *(*dest)++ = *src++; } } static int extracttypeinfo(char** dest, char** id) { if (**id == '\0') return -1; // Extract the type info: switch (*(*id)++) { // array, static array, dynamic array: case 'A': case 'G': case 'H': if (extracttypeinfo(dest, id) == -1) return -1; append(dest, "[]"); return 1; // pointer: case 'P': if (extracttypeinfo(dest, id) == -1) return -1; append(dest, "*"); return 1; // reference: case 'R': if (extracttypeinfo(dest, id) == -1) return -1; append(dest, "&"); return 1; // return value: case 'Z': return extracttypeinfo(dest, id); // out: case 'J': append(dest, "out "); return extracttypeinfo(dest, id); // inout: case 'K': append(dest, "inout "); return extracttypeinfo(dest, id); // enum: case 'E': case 'T': case 'D': case 'C': case 'S': case 'I': return extractidentifiers(dest, id); // basic types: case 'n': append(dest, "none"); return 1; // ever used? case 'v': append(dest, "void"); return 1; case 'g': append(dest, "byte"); return 1; case 'h': append(dest, "ubyte"); return 1; case 's': append(dest, "short"); return 1; case 't': append(dest, "ushort"); return 1; case 'i': append(dest, "int"); return 1; case 'k': append(dest, "uint"); return 1; case 'l': append(dest, "long"); return 1; case 'm': append(dest, "ulong"); return 1; case 'f': append(dest, "float"); return 1; case 'd': append(dest, "double"); return 1; case 'e': append(dest, "real"); return 1; // imaginary and complex: case 'o': append(dest, "ifloat"); return 1; case 'p': append(dest, "idouble"); return 1; case 'j': append(dest, "ireal"); return 1; case 'q': append(dest, "cfloat"); return 1; case 'r': append(dest, "cdouble"); return 1; case 'c': append(dest, "creal"); return 1; // other types: case 'b': append(dest, "bit"); return 1; case 'a': append(dest, "char"); return 1; case 'u': append(dest, "wchar"); return 1; case 'w': append(dest, "dchar"); return 1; // typeinfo, error, instance: case '@': return extractidentifiers(dest, id); // BUG: is this right? default: append(dest, "unknown"); return 1; } } char* d_demangle(char* mangled) { char *symbol = mangled; char *output = cast(char*)malloc(strlen(mangled)+20); char *orig = output; char isFunc = 0; if (mangled == null) { free(output); return null; } else if (strcmp(mangled, "_Dmain") == 0) { free(output); return strdup("D main"); } if (symbol == strstr(symbol, "_D")) { symbol += 2; isFunc = 1; } else if (symbol == strstr(symbol, "__Class_")) { symbol += 8; } else if (symbol == strstr(symbol, "__init_")) { symbol += 7; } else if (symbol == strstr(symbol, "__vtbl_")) { symbol += 7; } else if (symbol == strstr(symbol, "__modctor_")) { symbol += 10; } else if (symbol == strstr(symbol, "__moddtor_")) { symbol += 10; } else if (symbol == strstr(symbol, "__ModuleInfo_")) { symbol += 13; } else { free(orig); return null; } if (extractidentifiers(&output, &symbol) < 0) { free(orig); return null; } append(&output, "("); if (isFunc == 1 && *symbol == 'F') { symbol++; while (*symbol != '\0' && *symbol != 'Z') { if (isFunc == 1) { isFunc++; } else { append(&output, ", "); } if (extracttypeinfo(&output, &symbol) < 0) { free(orig); return null; } } } append(&output, ")"); //Doesn't display the return type, but wouldn't be too hard to do. // add the line number... while (*symbol != '+') symbol++; *output = ':'; output++; symbol++; while (isdigit(*symbol)) { *output = *symbol; output++; symbol++; } *output = '\0'; output = strdup(orig); free(orig); return output; } char* getAllocLoc () { void *array[6]; size_t size; char * file; char **strings; size_t i; bool gotx; size = backtrace (cast(void**)array, 6); if (size < 4) { return null; } strings = backtrace_symbols (cast(void**)array, size); for (i = 3 ; i < size; i++ ){ file = strings[i]; // bit specific.. - D?M == our appli? if (*(file+11) != '_') continue; if (*(file+12) != 'D') continue; if (*(file+13) == 'm') continue; // _Dmain if (*(file+14) == 's') continue; // not std.... // find -< // find > char* b = strchr(strings[i], '<'); char* e = strchr(strings[i], '>'); b++; file = d_demangle(b); free (strings); return file; } free (strings); return null; } } // end extern struct Log { void *p; size_t size; uint line; char *file; void *parent; void print() { printf("\tsize = %d, parent = %x - %s ", size, parent, file); //if (file) //{ // printf("%s(%u)", file, line); // } printf("\n"); } } struct LogArray { size_t dim; size_t allocdim; Log *data; void Dtor() { if (data) std.c.stdlib.free(data); data = null; } void reserve(size_t nentries) { assert(dim <= allocdim); if (allocdim - dim < nentries) { allocdim = (dim + nentries) * 2; assert(dim + nentries <= allocdim); if (!data) { data = cast(Log *)std.c.stdlib.malloc(allocdim * Log.sizeof); } else { Log *newdata; newdata = cast(Log *)std.c.stdlib.malloc(allocdim * Log.sizeof); assert(newdata); memcpy(newdata, data, dim * Log.sizeof); std.c.stdlib.free(data); data = newdata; } assert(!allocdim || data); } } void push(Log log) { reserve(1); data[dim++] = log; } void remove(size_t i) { memmove(data + i, data + i + 1, (dim - i) * Log.sizeof); dim--; } size_t find(void *p) { for (size_t i = 0; i < dim; i++) { if (data[i].p == p) return i; } return ~0u; // not found } void copy(LogArray *from) { reserve(from.dim - dim); assert(from.dim <= allocdim); memcpy(data, from.data, from.dim * Log.sizeof); dim = from.dim; } } } /* ============================ GC =============================== */ //alias int size_t; alias void (*GC_FINALIZER)(void *p, void *dummy); class GCLock { } // just a dummy so we can get a global lock const uint GCVERSION = 1; // increment every time we change interface // to GC. class GC { // For passing to debug code static uint line; static char *file; uint gcversion = GCVERSION; Gcx *gcx; // implementation static ClassInfo gcLock; // global lock void initialize() { gcLock = GCLock.classinfo; gcx = cast(Gcx *)std.c.stdlib.calloc(1, Gcx.sizeof); gcx.initialize(); version (Win32) { setStackBottom(win32.os_query_stackBottom()); } else version (GNU) { setStackBottom(gcgcc.os_query_stackBottom()); } else version (linux) { setStackBottom(gclinux.os_query_stackBottom()); } } void Dtor() { version (linux) { //debug(PRINTF) printf("Thread %x ", pthread_self()); //debug(PRINTF) printf("GC.Dtor()\n"); } if (gcx) { gcx.Dtor(); std.c.stdlib.free(gcx); gcx = null; } } invariant { if (gcx) gcx.thread_Invariant(); } void *malloc(size_t size) { void *p; if (std.thread.Thread.nthreads == 1) { /* Single-threaded no-sync - Dave Fladebo. * The reason this works is because none of the gc code * can start up a new thread from within mallocNoSync(). * Skip the sync for speed reasons. */ return mallocNoSync(size); } else synchronized (gcLock) { p = mallocNoSync(size); } return p; } void *mallocNoSync(size_t size) { void *p = null; Bins bin; debug(PRINTF) if (size > 256) printf("GC::malloc(size = %d)\n", size); assert(gcx); //debug(PRINTF) printf("gcx.self = %x, pthread_self() = %x\n", gcx.self, pthread_self()); if (size > 160000) { return null; } if (size) { size += SENTINEL_EXTRA; // Compute size bin // Cache previous binsize lookup - Dave Fladebo. static size_t lastsize = -1; static Bins lastbin; if (size == lastsize) bin = lastbin; else { bin = gcx.findBin(size); lastsize = size; lastbin = bin; } if (bin < B_PAGE) { p = gcx.bucket[bin]; if (p == null) { if (!gcx.allocPage(bin) && !gcx.disabled) // try to find a new page { if (std.thread.Thread.nthreads == 1) { /* Then we haven't locked it yet. Be sure * and lock for a collection, since a finalizer * may start a new thread. */ synchronized (gcLock) { gcx.fullcollectshell(); } } else if (!gcx.fullcollectshell()) // collect to find a new page { //gcx.newPool(1); } } if (!gcx.bucket[bin] && !gcx.allocPage(bin)) { int result; gcx.newPool(1); // allocate new pool to find a new page result = gcx.allocPage(bin); if (!result) return null; } p = gcx.bucket[bin]; } // Return next item from free list gcx.bucket[bin] = (cast(List *)p).next; version(GNU) memset(p + size, 0, binsize[bin] - size); else // 'inline' memset - Dave Fladebo. foreach(inout byte b; cast(byte[])(p + size)[0..binsize[bin] - size]) { b = 0; } //debug(PRINTF) printf("\tmalloc => %x\n", p); debug (MEMSTOMP) memset(p, 0xF0, size); } else { p = gcx.bigAlloc(size); if (!p) return null; } size -= SENTINEL_EXTRA; p = sentinel_add(p); sentinel_init(p, size); gcx.log_malloc(p, size); } return p; } void *calloc(size_t size, size_t n) { size_t len; void *p; len = size * n; p = malloc(len); if (p) { //debug(PRINTF) printf("calloc: %x len %d\n", p, len); memset(p, 0, len); } return p; } void *realloc(void *p, size_t size) { if (!size) { if (p) { free(p); p = null; } } else if (!p) { p = malloc(size); } else { void *p2; size_t psize; //debug(PRINTF) printf("GC::realloc(p = %x, size = %u)\n", p, size); version (SENTINEL) { sentinel_Invariant(p); psize = *sentinel_size(p); if (psize != size) { p2 = malloc(size); if (psize < size) size = psize; //debug(PRINTF) printf("\tcopying %d bytes\n",size); memcpy(p2, p, size); p = p2; } } else { psize = gcx.findSize(p); // find allocated size if (psize < size || // if new size is bigger psize > size * 2) // or less than half { p2 = malloc(size); if (psize < size) size = psize; //debug(PRINTF) printf("\tcopying %d bytes\n",size); memcpy(p2, p, size); p = p2; } } } return p; } void free(void *p) { Pool *pool; uint pagenum; Bins bin; uint biti; if (!p) return; // Find which page it is in pool = gcx.findPool(p); if (!pool) // if not one of ours return; // ignore sentinel_Invariant(p); p = sentinel_sub(p); pagenum = (p - pool.baseAddr) / PAGESIZE; synchronized (gcLock) { biti = cast(uint)(p - pool.baseAddr) / 16; pool.noptrs.clear(biti); if (pool.finals.nbits && gcx.finalizer) { if (pool.finals.testClear(biti)) { (*gcx.finalizer)(sentinel_add(p), null); } } bin = cast(Bins)pool.pagetable[pagenum]; if (bin == B_PAGE) // if large alloc { int npages; uint n; // Free pages npages = 1; n = pagenum; while (++n < pool.ncommitted && pool.pagetable[n] == B_PAGEPLUS) npages++; debug (MEMSTOMP) memset(p, 0xF2, npages * PAGESIZE); pool.freePages(pagenum, npages); } else { // Add to free list List *list = cast(List *)p; debug (MEMSTOMP) memset(p, 0xF2, binsize[bin]); list.next = gcx.bucket[bin]; gcx.bucket[bin] = list; } } gcx.log_free(sentinel_add(p)); } /**************************************** * Determine the allocated size of pointer p. * If p is an interior pointer or not a gc allocated pointer, * return 0. */ size_t capacity(void *p) { version (SENTINEL) { p = sentinel_sub(p); size_t size = gcx.findSize(p); // Check for interior pointer // This depends on: // 1) size is a power of 2 for less than PAGESIZE values // 2) base of memory pool is aligned on PAGESIZE boundary if (cast(size_t)p & (size - 1) & (PAGESIZE - 1)) size = 0; return size ? size - SENTINAL_EXTRA : 0; } else { if (p == gcx.p_cache) return gcx.size_cache; size_t size = gcx.findSize(p); // Check for interior pointer // This depends on: // 1) size is a power of 2 for less than PAGESIZE values // 2) base of memory pool is aligned on PAGESIZE boundary if (cast(size_t)p & (size - 1) & (PAGESIZE - 1)) size = 0; else { gcx.p_cache = p; gcx.size_cache = size; } return size; } } /**************************************** * Verify that pointer p: * 1) belongs to this memory pool * 2) points to the start of an allocated piece of memory * 3) is not on a free list */ void check(void *p) { if (p) { synchronized (gcLock) { sentinel_Invariant(p); debug (PTRCHECK) { Pool *pool; uint pagenum; Bins bin; size_t size; p = sentinel_sub(p); pool = gcx.findPool(p); assert(pool); pagenum = (p - pool.baseAddr) / PAGESIZE; bin = cast(Bins)pool.pagetable[pagenum]; assert(bin <= B_PAGE); size = binsize[bin]; assert((cast(size_t)p & (size - 1)) == 0); debug (PTRCHECK2) { if (bin < B_PAGE) { // Check that p is not on a free list List *list; for (list = gcx.bucket[bin]; list; list = list.next) { assert(cast(void *)list != p); } } } } } } } void setStackBottom(void *p) { version (STACKGROWSDOWN) { //p = (void *)((uint *)p + 4); if (p > gcx.stackBottom) { //debug(PRINTF) printf("setStackBottom(%x)\n", p); gcx.stackBottom = p; } } else { //p = (void *)((uint *)p - 4); if (p < gcx.stackBottom) { //debug(PRINTF) printf("setStackBottom(%x)\n", p); gcx.stackBottom = cast(char *)p; } } } static void scanStaticData(gc_t g) { void *pbot; void *ptop; size_t nbytes; //debug(PRINTF) printf("+GC.scanStaticData()\n"); os_query_staticdataseg(&pbot, &nbytes); ptop = pbot + nbytes; version (GNU) { if (pbot) { g.addRange(pbot, ptop); } } else { g.addRange(pbot, ptop); } //debug(PRINTF) printf("-GC.scanStaticData()\n"); } static void unscanStaticData(gc_t g) { void *pbot; size_t nbytes; os_query_staticdataseg(&pbot, &nbytes); version (GNU) { if (pbot) { g.removeRange(pbot); } } else { g.removeRange(pbot); } } void addRoot(void *p) // add p to list of roots { synchronized (gcLock) { gcx.addRoot(p); } } void removeRoot(void *p) // remove p from list of roots { synchronized (gcLock) { gcx.removeRoot(p); } } void addRange(void *pbot, void *ptop) // add range to scan for roots { //debug(PRINTF) printf("+GC.addRange(pbot = x%x, ptop = x%x)\n", pbot, ptop); synchronized (gcLock) { gcx.addRange(pbot, ptop); } //debug(PRINTF) printf("-GC.addRange()\n"); } void removeRange(void *pbot) // remove range { synchronized (gcLock) { gcx.removeRange(pbot); } } void fullCollect() // do full garbage collection { debug(PRINTF) printf("GC.fullCollect()\n"); synchronized (gcLock) { gcx.fullcollectshell(); } version (none) { GCStats stats; getStats(stats); debug(PRINTF) printf("poolsize = %x, usedsize = %x, freelistsize = %x\n", stats.poolsize, stats.usedsize, stats.freelistsize); } gcx.log_collect(); } void fullCollectNoStack() // do full garbage collection { gcx.noStack++; fullCollect(); gcx.noStack--; } void genCollect() // do generational garbage collection { synchronized (gcLock) { gcx.fullcollectshell(); } gcx.log_collect(); } void minimize() // minimize physical memory usage { synchronized (gcLock) { gcx.minimize(); } } void setFinalizer(void *p, GC_FINALIZER pFn) { synchronized (gcLock) { gcx.finalizer = pFn; gcx.doFinalize(p); } } void hasPointers(void *p) { synchronized (gcLock) { gcx.HasPointers(p); } } void hasNoPointers(void *p) { if (!gcx.conservative) { synchronized (gcLock) { gcx.HasNoPointers(p); } } } void setV1_0() { gcx.conservative = 1; } void enable() { synchronized (gcLock) { assert(gcx.disabled > 0); gcx.disabled--; } } void disable() { synchronized (gcLock) { gcx.disabled++; } } /***************************************** * Retrieve statistics about garbage collection. * Useful for debugging and tuning. */ void getStats(out GCStats stats) { size_t psize = 0; size_t usize = 0; size_t flsize = 0; size_t n; size_t bsize = 0; //debug(PRINTF) printf("getStats()\n"); memset(&stats, 0, GCStats.sizeof); synchronized (gcLock) { for (n = 0; n < gcx.npools; n++) { Pool *pool = gcx.pooltable[n]; psize += pool.ncommitted * PAGESIZE; for (uint j = 0; j < pool.ncommitted; j++) { Bins bin = cast(Bins)pool.pagetable[j]; if (bin == B_FREE) stats.freeblocks++; else if (bin == B_PAGE) stats.pageblocks++; else if (bin < B_PAGE) bsize += PAGESIZE; } } for (n = 0; n < B_PAGE; n++) { //debug(PRINTF) printf("bin %d\n", n); for (List *list = gcx.bucket[n]; list; list = list.next) { //debug(PRINTF) printf("\tlist %x\n", list); flsize += binsize[n]; } } } usize = bsize - flsize; stats.poolsize = psize; stats.usedsize = bsize - flsize; stats.freelistsize = flsize; } } /* ============================ Gcx =============================== */ enum { PAGESIZE = 4096, COMMITSIZE = (4096*16), POOLSIZE = (4096*256), } enum { B_16, B_32, B_64, B_128, B_256, B_512, B_1024, B_2048, B_PAGE, // start of large alloc B_PAGEPLUS, // continuation of large alloc B_FREE, // free page B_UNCOMMITTED, // memory not committed for this page B_MAX } alias ubyte Bins; struct List { List *next; } struct Range { void *pbot; void *ptop; } const uint binsize[B_MAX] = [ 16,32,64,128,256,512,1024,2048,4096 ]; const uint notbinsize[B_MAX] = [ ~(16u-1),~(32u-1),~(64u-1),~(128u-1),~(256u-1), ~(512u-1),~(1024u-1),~(2048u-1),~(4096u-1) ]; /* ============================ Gcx =============================== */ struct Gcx { debug (THREADINVARIANT) { pthread_t self; void thread_Invariant() { if (self != pthread_self()) printf("thread_Invariant(): gcx = %x, self = %x, pthread_self() = %x\n", this, self, pthread_self()); assert(self == pthread_self()); } } else { void thread_Invariant() { } } void *p_cache; size_t size_cache; size_t nroots; size_t rootdim; void **roots; size_t nranges; size_t rangedim; Range *ranges; uint conservative; // !=0 means conservative behavior uint noStack; // !=0 means don't scan stack uint log; // turn on logging uint anychanges; void *stackBottom; uint inited; int disabled; // turn off collections if >0 byte *minAddr; // min(baseAddr) byte *maxAddr; // max(topAddr) uint npools; Pool **pooltable; List *bucket[B_MAX]; // free list for each size GC_FINALIZER finalizer; // finalizer function (one per GC) void initialize() { int dummy; (cast(byte *)this)[0 .. Gcx.sizeof] = 0; stackBottom = cast(char *)&dummy; log_init(); debug (THREADINVARIANT) self = pthread_self(); //printf("gcx = %p, self = %x\n", this, self); inited = 1; } void Dtor() { inited = 0; for (uint i = 0; i < npools; i++) { Pool *pool = pooltable[i]; pool.Dtor(); std.c.stdlib.free(pool); } if (pooltable) std.c.stdlib.free(pooltable); if (roots) std.c.stdlib.free(roots); if (ranges) std.c.stdlib.free(ranges); } void Invariant() { } invariant { if (inited) { //printf("Gcx.invariant(): this = %p\n", this); uint i; // Assure we're called on the right thread debug (THREADINVARIANT) assert(self == pthread_self()); for (i = 0; i < npools; i++) { Pool *pool = pooltable[i]; pool.Invariant(); if (i == 0) { assert(minAddr == pool.baseAddr); } if (i + 1 < npools) { assert(pool.opCmp(pooltable[i + 1]) < 0); } else if (i + 1 == npools) { assert(maxAddr == pool.topAddr); } } if (roots) { assert(rootdim != 0); assert(nroots <= rootdim); } if (ranges) { assert(rangedim != 0); assert(nranges <= rangedim); for (i = 0; i < nranges; i++) { assert(ranges[i].pbot); assert(ranges[i].ptop); assert(ranges[i].pbot <= ranges[i].ptop); } } for (i = 0; i < B_PAGE; i++) { for (List *list = bucket[i]; list; list = list.next) { } } } } /*************************************** */ void addRoot(void *p) { if (nroots == rootdim) { size_t newdim = rootdim * 2 + 16; void **newroots; newroots = cast(void **)std.c.stdlib.malloc(newdim * newroots[0].sizeof); assert(newroots); if (roots) { memcpy(newroots, roots, nroots * newroots[0].sizeof); std.c.stdlib.free(roots); } roots = newroots; rootdim = newdim; } roots[nroots] = p; nroots++; } void removeRoot(void *p) { size_t i; for (i = nroots; i--;) { if (roots[i] == p) { nroots--; memmove(roots + i, roots + i + 1, (nroots - i) * roots[0].sizeof); return; } } assert(0); } /*************************************** */ void addRange(void *pbot, void *ptop) { debug(THREADINVARIANT) { debug(PRINTF) printf("Thread %x ", pthread_self()); } debug(PRINTF) printf("%x.Gcx::addRange(%x, %x), nranges = %d\n", this, pbot, ptop, nranges); if (nranges == rangedim) { size_t newdim = rangedim * 2 + 16; Range *newranges; newranges = cast(Range *)std.c.stdlib.malloc(newdim * newranges[0].sizeof); assert(newranges); if (ranges) { memcpy(newranges, ranges, nranges * newranges[0].sizeof); std.c.stdlib.free(ranges); } ranges = newranges; rangedim = newdim; } ranges[nranges].pbot = pbot; ranges[nranges].ptop = ptop; nranges++; } void removeRange(void *pbot) { debug(THREADINVARIANT) { debug(PRINTF) printf("Thread %x ", pthread_self()); } debug(PRINTF) printf("%x.Gcx.removeRange(%x), nranges = %d\n", this, pbot, nranges); for (size_t i = nranges; i--;) { if (ranges[i].pbot == pbot) { nranges--; memmove(ranges + i, ranges + i + 1, (nranges - i) * ranges[0].sizeof); return; } } debug(PRINTF) printf("Wrong thread\n"); // This is a fatal error, but ignore it. // The problem is that we can get a Close() call on a thread // other than the one the range was allocated on. //assert(zero); } /******************************* * Find Pool that pointer is in. * Return null if not in a Pool. * Assume pooltable[] is sorted. */ Pool *findPool(void *p) { if (p >= minAddr && p < maxAddr) { if (npools == 1) { return pooltable[0]; } for (uint i = 0; i < npools; i++) { Pool *pool; pool = pooltable[i]; if (p < pool.topAddr) { if (pool.baseAddr <= p) return pool; break; } } } return null; } /******************************* * Find size of pointer p. * Returns 0 if not a gc'd pointer */ size_t findSize(void *p) { Pool *pool; size_t size = 0; pool = findPool(p); if (pool) { uint pagenum; Bins bin; pagenum = (cast(uint)(p - pool.baseAddr)) / PAGESIZE; bin = cast(Bins)pool.pagetable[pagenum]; size = binsize[bin]; if (bin == B_PAGE) { uint npages = pool.ncommitted; ubyte* pt; uint i; pt = &pool.pagetable[0]; for (i = pagenum + 1; i < npages; i++) { if (pt[i] != B_PAGEPLUS) break; } size = (i - pagenum) * PAGESIZE; } } return size; } /******************************* * Compute bin for size. */ static Bins findBin(size_t size) { Bins bin; if (size <= 256) { if (size <= 64) { if (size <= 16) bin = B_16; else if (size <= 32) bin = B_32; else bin = B_64; } else { if (size <= 128) bin = B_128; else bin = B_256; } } else { if (size <= 1024) { if (size <= 512) bin = B_512; else bin = B_1024; } else { if (size <= 2048) bin = B_2048; else bin = B_PAGE; } } return bin; } /**************************************** * Allocate a chunk of memory that is larger than a page. * Return null if out of memory. */ void *bigAlloc(size_t size) { Pool *pool; uint npages; uint n; uint pn; uint freedpages; void *p; int state; npages = (size + PAGESIZE - 1) / PAGESIZE; for (state = 0; ; ) { // This code could use some refinement when repeatedly // allocating very large arrays. for (n = 0; n < npools; n++) { pool = pooltable[n]; pn = pool.allocPages(npages); if (pn != ~0u) goto L1; } // Failed switch (state) { case 0: if (disabled) { state = 1; continue; } // Try collecting freedpages = fullcollectshell(); if (freedpages >= npools * ((POOLSIZE / PAGESIZE) / 4)) { state = 1; continue; } // Allocate new pool pool = newPool(npages); if (!pool) { state = 2; continue; } pn = pool.allocPages(npages); assert(pn != ~0u); goto L1; case 1: // Allocate new pool pool = newPool(npages); if (!pool) goto Lnomemory; pn = pool.allocPages(npages); assert(pn != ~0u); goto L1; case 2: goto Lnomemory; } } L1: pool.pagetable[pn] = B_PAGE; if (npages > 1) memset(&pool.pagetable[pn + 1], B_PAGEPLUS, npages - 1); p = pool.baseAddr + pn * PAGESIZE; memset(cast(char *)p + size, 0, npages * PAGESIZE - size); debug (MEMSTOMP) memset(p, 0xF1, size); //debug(PRINTF) printf("\tp = %x\n", p); return p; Lnomemory: assert(0); return null; } /*********************************** * Allocate a new pool with at least npages in it. * Sort it into pooltable[]. * Return null if failed. */ Pool *newPool(uint npages) { Pool *pool; Pool **newpooltable; uint newnpools; uint i; debug(PRINTF) printf("************Gcx::newPool(npages = %d)****************\n", npages); // Round up to COMMITSIZE pages npages = (npages + (COMMITSIZE/PAGESIZE) - 1) & ~(COMMITSIZE/PAGESIZE - 1); // Minimum of POOLSIZE if (npages < POOLSIZE/PAGESIZE) npages = POOLSIZE/PAGESIZE; // Allocate successively larger pools up to 8 megs if (npools) { uint n; n = npools; if (n > 8) n = 8; // cap pool size at 8 megs n *= (POOLSIZE / PAGESIZE); if (npages < n) npages = n; } pool = cast(Pool *)std.c.stdlib.calloc(1, Pool.sizeof); if (pool) { pool.initialize(npages); if (!pool.baseAddr) goto Lerr; newnpools = npools + 1; newpooltable = cast(Pool **)std.c.stdlib.realloc(pooltable, newnpools * (Pool *).sizeof); if (!newpooltable) goto Lerr; // Sort pool into newpooltable[] for (i = 0; i < npools; i++) { if (pool.opCmp(newpooltable[i]) < 0) break; } memmove(newpooltable + i + 1, newpooltable + i, (npools - i) * (Pool *).sizeof); newpooltable[i] = pool; pooltable = newpooltable; npools = newnpools; minAddr = pooltable[0].baseAddr; maxAddr = pooltable[npools - 1].topAddr; } return pool; Lerr: pool.Dtor(); std.c.stdlib.free(pool); return null; } /******************************* * Allocate a page of bin's. * Returns: * 0 failed */ int allocPage(Bins bin) { Pool *pool; uint n; uint pn; byte *p; byte *ptop; //debug(PRINTF) printf("Gcx::allocPage(bin = %d)\n", bin); for (n = 0; n < npools; n++) { pool = pooltable[n]; pn = pool.allocPages(1); if (pn != ~0u) goto L1; } return 0; // failed L1: pool.pagetable[pn] = cast(ubyte)bin; // Convert page to free list size_t size = binsize[bin]; List **b = &bucket[bin]; p = pool.baseAddr + pn * PAGESIZE; ptop = p + PAGESIZE; for (; p < ptop; p += size) { (cast(List *)p).next = *b; *b = cast(List *)p; } return 1; } /************************************ * Search a range of memory values and mark any pointers into the GC pool. */ void mark(void *pbot, void *ptop) { void **p1 = cast(void **)pbot; void **p2 = cast(void **)ptop; uint changes = 0; //if (log) debug(PRINTF) printf("Gcx::mark(%x .. %x)\n", pbot, ptop); for (; p1 < p2; p1++) { Pool *pool; byte *p = cast(byte *)(*p1); //if (log) debug(PRINTF) printf("\tmark %x\n", p); if (p >= minAddr) { pool = findPool(p); if (pool) { size_t offset = cast(uint)(p - pool.baseAddr); uint biti; uint pn = offset / PAGESIZE; Bins bin = cast(Bins)pool.pagetable[pn]; //debug(PRINTF) printf("\t\tfound pool %x, base=%x, pn = %d, bin = %d, biti = x%x\n", pool, pool.baseAddr, pn, bin, biti); // Adjust bit to be at start of allocated memory block if (bin <= B_PAGE) { biti = (offset & notbinsize[bin]) >> 4; //debug(PRINTF) printf("\t\tbiti = x%x\n", biti); } else if (bin == B_PAGEPLUS) { do { --pn; } while (cast(Bins)pool.pagetable[pn] == B_PAGEPLUS); biti = pn * (PAGESIZE / 16); } else { // Don't mark bits in B_FREE or B_UNCOMMITTED pages continue; } //debug(PRINTF) printf("\t\tmark(x%x) = %d\n", biti, pool.mark.test(biti)); if (!pool.mark.test(biti)) { //if (log) debug(PRINTF) printf("\t\tmarking %x\n", p); pool.mark.set(biti); if (!pool.noptrs.test(biti)) { pool.scan.set(biti); changes = 1; } log_parent(sentinel_add(pool.baseAddr + biti * 16), sentinel_add(pbot)); } } } } anychanges |= changes; } /********************************* * Return number of full pages free'd. */ size_t fullcollectshell() { // The purpose of the 'shell' is to ensure all the registers // get put on the stack so they'll be scanned void *sp; size_t result; version (GNU) { __builtin_unwind_init(); sp = & sp; } else { asm { pushad ; mov sp[EBP],ESP ; } } result = fullcollect(sp); version (GNU) { // nothing to do } else { asm { popad ; } } return result; } void minimize() { size_t n; size_t pn; Pool* pool; size_t ncommitted; //printf("Gcx::minimize(INnpools = %d)\n", npools); for (n = 0; n < npools; n++) { pool = pooltable[n]; ncommitted = pool.ncommitted; for (pn = 0; pn < ncommitted; pn++) { if (cast(Bins)pool.pagetable[pn] != B_FREE) break; } if (pn < ncommitted) { n++; continue; } pool.Dtor(); std.c.stdlib.free(pool); std.c.string.memmove(pooltable + n, pooltable + n + 1, (--npools - n) * (Pool*).sizeof); minAddr = pooltable[0].baseAddr; maxAddr = pooltable[npools - 1].topAddr; } //printf("Gcx::minimize(OUTnpools = %d)\n", npools); } size_t fullcollect(void *stackTop) { uint n; Pool *pool; debug(COLLECT_PRINTF) printf("Gcx.fullcollect()\n"); Thread.pauseAll(); p_cache = null; size_cache = 0; anychanges = 0; for (n = 0; n < npools; n++) { pool = pooltable[n]; pool.mark.zero(); pool.scan.zero(); pool.freebits.zero(); } // Mark each free entry, so it doesn't get scanned for (n = 0; n < B_PAGE; n++) { for (List *list = bucket[n]; list; list = list.next) { pool = findPool(list); assert(pool); pool.freebits.set(cast(uint)(cast(byte *)list - pool.baseAddr) / 16); } } for (n = 0; n < npools; n++) { pool = pooltable[n]; pool.mark.copy(&pool.freebits); } version (MULTI_THREADED) { // Scan stacks and registers for each paused thread Thread[] threads = Thread.getAll(); //thread_id id = cast(thread_id) GetCurrentThread(); for (n = 0; n < threads.length; n++) { Thread t = threads[n]; if (t && t.getState() == Thread.TS.RUNNING) { if (noStack && threads.length == 1) break; version (Win32) { CONTEXT context; context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL; if (!GetThreadContext(t.hdl, &context)) { assert(0); } debug (PRINTF) printf("mt scan stack bot = %x, top = %x\n", context.Esp, t.stackBottom); mark(cast(void *)context.Esp, t.stackBottom); mark(&context.Edi, &context.Eip); } else version (GNU) { if (t.isSelf()) t.stackTop = Thread.getESP(); //%%fry printf("top=%08x bot=%08x ext=%08x\n", t.stackTop, t.stackBottom, Thread.getESP());//%%try version (STACKGROWSDOWN) mark(t.stackTop, t.stackBottom); else mark(t.stackBottom, t.stackTop); } else version (linux) { // The registers are already stored in the stack //printf("Thread: ESP = x%x, stackBottom = x%x, isSelf = %d\n", Thread.getESP(), t.stackBottom, t.isSelf()); if (t.isSelf()) t.stackTop = Thread.getESP(); version (STACKGROWSDOWN) mark(t.stackTop, t.stackBottom); else mark(t.stackBottom, t.stackTop); } } } } else { if (!noStack) { // Scan stack for main thread debug(PRINTF) printf(" scan stack bot = %x, top = %x\n", stackTop, stackBottom); version (STACKGROWSDOWN) mark(stackTop, stackBottom); else mark(stackBottom, stackTop); } } // Scan roots[] debug(COLLECT_PRINTF) printf("scan roots[]\n"); mark(roots, roots + nroots); // Scan ranges[] debug(COLLECT_PRINTF) printf("scan ranges[]\n"); //log++; for (n = 0; n < nranges; n++) { debug(COLLECT_PRINTF) printf("\t%x .. %x\n", ranges[n].pbot, ranges[n].ptop); mark(ranges[n].pbot, ranges[n].ptop); } //log--; debug(COLLECT_PRINTF) printf("\tscan heap\n"); while (anychanges) { anychanges = 0; for (n = 0; n < npools; n++) { uint *bbase; uint *b; uint *btop; pool = pooltable[n]; bbase = pool.scan.base(); btop = bbase + pool.scan.nwords; for (b = bbase; b < btop;) { Bins bin; uint pn; uint u; uint bitm; byte *o; bitm = *b; if (!bitm) { b++; continue; } *b = 0; o = pool.baseAddr + (b - bbase) * 32 * 16; /* version (BigEndian) bitm = bswap(bitm); */ if (!(bitm & 0xFFFF)) { bitm >>= 16; o += 16 * 16; } for (; bitm; o += 16, bitm >>= 1) { if (!(bitm & 1)) continue; pn = (o - pool.baseAddr) / PAGESIZE; bin = cast(Bins)pool.pagetable[pn]; if (bin < B_PAGE) { mark(o, o + binsize[bin]); } else if (bin == B_PAGE || bin == B_PAGEPLUS) { if (bin == B_PAGEPLUS) { while (pool.pagetable[pn - 1] != B_PAGE) pn--; } u = 1; while (pn + u < pool.ncommitted && pool.pagetable[pn + u] == B_PAGEPLUS) u++; mark(o, o + u * PAGESIZE); } } } } } // Free up everything not marked debug(COLLECT_PRINTF) printf("\tfree'ing\n"); size_t freedpages = 0; size_t freed = 0; for (n = 0; n < npools; n++) { uint pn; uint ncommitted; uint *bbase; pool = pooltable[n]; bbase = pool.mark.base(); ncommitted = pool.ncommitted; for (pn = 0; pn < ncommitted; pn++, bbase += PAGESIZE / (32 * 16)) { Bins bin = cast(Bins)pool.pagetable[pn]; if (bin < B_PAGE) { byte *p; byte *ptop; uint biti; uint bitstride; uint size = binsize[bin]; p = pool.baseAddr + pn * PAGESIZE; ptop = p + PAGESIZE; biti = pn * (PAGESIZE/16); bitstride = size / 16; version(none) // BUG: doesn't work because freebits() must also be cleared { // If free'd entire page if (bbase[0] == 0 && bbase[1] == 0 && bbase[2] == 0 && bbase[3] == 0 && bbase[4] == 0 && bbase[5] == 0 && bbase[6] == 0 && bbase[7] == 0) { for (; p < ptop; p += size, biti += bitstride) { if (finalizer && pool.finals.nbits && pool.finals.testClear(biti)) { (*finalizer)(cast(List *)sentinel_add(p), null); } List *list = cast(List *)p; //debug(PRINTF) printf("\tcollecting %x\n", list); log_free(sentinel_add(list)); debug (MEMSTOMP) memset(p, 0xF3, size); } pool.pagetable[pn] = B_FREE; freed += PAGESIZE; //debug(PRINTF) printf("freeing entire page %d\n", pn); continue; } } for (; p < ptop; p += size, biti += bitstride) { if (!pool.mark.test(biti)) { sentinel_Invariant(sentinel_add(p)); pool.freebits.set(biti); pool.noptrs.clear(biti); if (finalizer && pool.finals.nbits && pool.finals.testClear(biti)) { (*finalizer)(cast(List *)sentinel_add(p), null); } List *list = cast(List *)p; //debug(PRINTF) printf("\tcollecting %x\n", list); log_free(sentinel_add(list)); debug (MEMSTOMP) memset(p, 0xF3, size); freed += size; } } } else if (bin == B_PAGE) { uint biti = pn * (PAGESIZE / 16); if (!pool.mark.test(biti)) { byte *p = pool.baseAddr + pn * PAGESIZE; sentinel_Invariant(sentinel_add(p)); pool.noptrs.clear(biti); if (finalizer && pool.finals.nbits && pool.finals.testClear(biti)) { (*finalizer)(sentinel_add(p), null); } debug(COLLECT_PRINTF) printf("\tcollecting big %x\n", p); log_free(sentinel_add(p)); pool.pagetable[pn] = B_FREE; freedpages++; debug (MEMSTOMP) memset(p, 0xF3, PAGESIZE); while (pn + 1 < ncommitted && pool.pagetable[pn + 1] == B_PAGEPLUS) { pn++; pool.pagetable[pn] = B_FREE; freedpages++; debug (MEMSTOMP) { p += PAGESIZE; memset(p, 0xF3, PAGESIZE); } } } } } } // Zero buckets bucket[] = null; // Free complete pages, rebuild free list debug(COLLECT_PRINTF) printf("\tfree complete pages\n"); size_t recoveredpages = 0; for (n = 0; n < npools; n++) { uint pn; uint ncommitted; pool = pooltable[n]; ncommitted = pool.ncommitted; for (pn = 0; pn < ncommitted; pn++) { Bins bin = cast(Bins)pool.pagetable[pn]; uint biti; uint u; if (bin < B_PAGE) { uint size = binsize[bin]; uint bitstride = size / 16; uint bitbase = pn * (PAGESIZE / 16); uint bittop = bitbase + (PAGESIZE / 16); byte *p; biti = bitbase; for (biti = bitbase; biti < bittop; biti += bitstride) { if (!pool.freebits.test(biti)) goto Lnotfree; } pool.pagetable[pn] = B_FREE; recoveredpages++; continue; Lnotfree: p = pool.baseAddr + pn * PAGESIZE; for (u = 0; u < PAGESIZE; u += size) { biti = bitbase + u / 16; if (pool.freebits.test(biti)) { List *list; list = cast(List *)(p + u); if (list.next != bucket[bin]) // avoid unnecessary writes list.next = bucket[bin]; bucket[bin] = list; } } } } } debug(COLLECT_PRINTF) printf("recovered pages = %d\n", recoveredpages); debug(COLLECT_PRINTF) printf("\tfree'd %u bytes, %u pages from %u pools\n", freed, freedpages, npools); Thread.resumeAll(); return freedpages + recoveredpages; } /********************************* * Run finalizer on p when it is free'd. */ void doFinalize(void *p) { Pool *pool = findPool(p); assert(pool); // Only allocate finals[] if we actually need it if (!pool.finals.nbits) pool.finals.alloc(pool.mark.nbits); pool.finals.set((p - pool.baseAddr) / 16); } /********************************* * Indicate that block pointed to by p has possible pointers * to GC allocated memory in it. */ void HasPointers(void *p) { Pool *pool = findPool(p); assert(pool); pool.noptrs.clear((p - pool.baseAddr) / 16); } /********************************* * Indicate that block pointed to by p has no possible pointers * to GC allocated memory in it. */ void HasNoPointers(void *p) { //printf("HasNoPointers(%p)\n", p); Pool *pool = findPool(p); assert(pool); pool.noptrs.set((p - pool.baseAddr) / 16); } /***** Leak Detector ******/ debug (LOGGING) { LogArray current; LogArray prev; void log_init() { //debug(PRINTF) printf("+log_init()\n"); current.reserve(1000); prev.reserve(1000); //debug(PRINTF) printf("-log_init()\n"); } void log_malloc(void *p, size_t size) { //print_trace(); //debug(PRINTF) //printf("+log_malloc(p = %x, size = %d)\n", p, size); Log log; log.p = p; log.size = size; log.file = getAllocLoc(); log.line = log.file ? 1 : 0; log.parent = null; //GC.line = 0; //GC.file = null; current.push(log); //debug(PRINTF) printf("-log_malloc()\n"); } void log_free(void *p) { //debug(PRINTF) printf("+log_free(%x)\n", p); size_t i; i = current.find(p); if (i == ~0u) { debug(PRINTF) printf("free'ing unallocated memory %x\n", p); } else free(current.data[i].file); current.remove(i); //debug(PRINTF) printf("-log_free()\n"); } void log_collect() { char* fpos = getAllocLoc(); debug(PRINTF) printf("+log_collect(%s)\n",fpos); // Print everything in current that is not in prev // list all the current memory allocated and from where... // debug(PRINTF) printf("Allocated Memory"); size_t usize; for (size_t i = 0; i < current.dim; i++) { if (current.data[i].line > 0) current.data[i].print(); else usize+=current.data[i].size; } debug(PRINTF) printf("other unknown unfreed memory: --------------------------------%d \n", usize); /* debug(PRINTF) printf("New pointers this cycle: --------------------------------\n"); size_t used = 0; for (size_t i = 0; i < current.dim; i++) { size_t j; j = prev.find(current.data[i].p); if (j == ~0u) current.data[i].print(); else used++; } debug(PRINTF) printf("All roots this cycle: --------------------------------\n"); for (size_t i = 0; i < current.dim; i++) { void *p; size_t j; p = current.data[i].p; if (!findPool(current.data[i].parent)) { j = prev.find(current.data[i].p); if (j == ~0u) debug(PRINTF) printf("N"); else debug(PRINTF) printf(" ");; current.data[i].print(); } } debug(PRINTF) printf("Used = %d-------------------------------------------------\n", used); */ prev.copy(¤t); debug(PRINTF) printf("-log_collect(%s)\n",fpos); free(fpos); } void log_parent(void *p, void *parent) { //debug(PRINTF) printf("+log_parent()\n"); size_t i; i = current.find(p); if (i == ~0u) { debug(PRINTF) printf("parent'ing unallocated memory %x, parent = %x\n", p, parent); Pool *pool; pool = findPool(p); assert(pool); size_t offset = cast(uint)(p - pool.baseAddr); size_t biti; uint pn = offset / PAGESIZE; Bins bin = cast(Bins)pool.pagetable[pn]; biti = (offset & notbinsize[bin]); debug(PRINTF) printf("\tbin = %d, offset = x%x, biti = x%x\n", bin, offset, biti); } else { current.data[i].parent = parent; } //debug(PRINTF) printf("-log_parent()\n"); } } else { void log_init() { } void log_malloc(void *p, size_t size) { } void log_free(void *p) { } void log_collect() { } void log_parent(void *p, void *parent) { } } }; /* ============================ Pool =============================== */ struct Pool { byte* baseAddr; byte* topAddr; GCBits mark; // entries already scanned, or should not be scanned GCBits scan; // entries that need to be scanned GCBits finals; // entries that need finalizer run on them GCBits freebits; // entries that are on the free list GCBits noptrs; // entries that do not contain pointers uint npages; uint ncommitted; // ncommitted <= npages ubyte* pagetable; void initialize(uint npages) { size_t poolsize; //debug(PRINTF) printf("Pool::Pool(%u)\n", npages); poolsize = npages * PAGESIZE; assert(poolsize >= POOLSIZE); baseAddr = cast(byte *)os_mem_map(poolsize); // Some of the code depends on page alignment of memory pools assert((cast(uint)baseAddr & (PAGESIZE - 1)) == 0); if (!baseAddr) { //debug(PRINTF) printf("GC fail: poolsize = x%x, errno = %d\n", poolsize, errno); //debug(PRINTF) printf("message = '%s'\n", sys_errlist[errno]); npages = 0; poolsize = 0; } //assert(baseAddr); topAddr = baseAddr + poolsize; mark.alloc(poolsize / 16); scan.alloc(poolsize / 16); freebits.alloc(poolsize / 16); noptrs.alloc(poolsize / 16); pagetable = cast(ubyte*)std.c.stdlib.malloc(npages); memset(pagetable, B_UNCOMMITTED, npages); this.npages = npages; ncommitted = 0; } void Dtor() { if (baseAddr) { int result; if (ncommitted) { result = os_mem_decommit(baseAddr, 0, ncommitted * PAGESIZE); assert(result == 0); ncommitted = 0; } if (npages) { result = os_mem_unmap(baseAddr, npages * PAGESIZE); assert(result == 0); npages = 0; } baseAddr = null; topAddr = null; } if (pagetable) std.c.stdlib.free(pagetable); mark.Dtor(); scan.Dtor(); finals.Dtor(); freebits.Dtor(); } void Invariant() { } invariant { //mark.Invariant(); //scan.Invariant(); //finals.Invariant(); //freebits.Invariant(); if (baseAddr) { //if (baseAddr + npages * PAGESIZE != topAddr) //printf("baseAddr = %p, npages = %d, topAddr = %p\n", baseAddr, npages, topAddr); assert(baseAddr + npages * PAGESIZE == topAddr); assert(ncommitted <= npages); } for (uint i = 0; i < npages; i++) { Bins bin = cast(Bins)pagetable[i]; assert(bin < B_MAX); } } /************************************** * Allocate n pages from Pool. * Returns ~0u on failure. */ uint allocPages(uint n) { uint i; uint n2; //debug(PRINTF) printf("Pool::allocPages(n = %d)\n", n); n2 = n; for (i = 0; i < ncommitted; i++) { if (pagetable[i] == B_FREE) { if (--n2 == 0) { //debug(PRINTF) printf("\texisting pn = %d\n", i - n + 1); return i - n + 1; } } else n2 = n; } if (ncommitted + n <= npages) { uint tocommit; tocommit = (n + (COMMITSIZE/PAGESIZE) - 1) & ~(COMMITSIZE/PAGESIZE - 1); if (ncommitted + tocommit > npages) tocommit = npages - ncommitted; //debug(PRINTF) printf("\tlooking to commit %d more pages\n", tocommit); //fflush(stdout); if (os_mem_commit(baseAddr, ncommitted * PAGESIZE, tocommit * PAGESIZE) == 0) { memset(pagetable + ncommitted, B_FREE, tocommit); i = ncommitted; ncommitted += tocommit; while (i && pagetable[i - 1] == B_FREE) i--; return i; } //debug(PRINTF) printf("\tfailed to commit %d pages\n", tocommit); } return ~0u; } /********************************** * Free npages pages starting with pagenum. */ void freePages(uint pagenum, uint npages) { memset(&pagetable[pagenum], B_FREE, npages); } /*************************** * Used for sorting pooltable[] */ int opCmp(Pool *p2) { if (baseAddr < p2.baseAddr) return -1; else return cast(int)(baseAddr > p2.baseAddr); } } /* ============================ SENTINEL =============================== */ version (SENTINEL) { const size_t SENTINEL_PRE = cast(size_t) 0xF4F4F4F4F4F4F4F4UL; // 32 or 64 bits const ubyte SENTINEL_POST = 0xF5; // 8 bits const uint SENTINEL_EXTRA = 2 * size_t.sizeof + 1; size_t* sentinel_size(void *p) { return &(cast(size_t *)p)[-2]; } size_t* sentinel_pre(void *p) { return &(cast(size_t *)p)[-1]; } ubyte* sentinel_post(void *p) { return &(cast(ubyte *)p)[sentinel_size(p)]; } void sentinel_init(void *p, size_t size) { *sentinel_size(p) = size; *sentinel_pre(p) = SENTINEL_PRE; *sentinel_post(p) = SENTINEL_POST; } void sentinel_Invariant(void *p) { assert(*sentinel_pre(p) == SENTINEL_PRE); assert(*sentinel_post(p) == SENTINEL_POST); } void *sentinel_add(void *p) { return p + 2 * size_t.sizeof; } void *sentinel_sub(void *p) { return p - 2 * size_t.sizeof; } } else { const uint SENTINEL_EXTRA = 0; void sentinel_init(void *p, size_t size) { } void sentinel_Invariant(void *p) { } void *sentinel_add(void *p) { return p; } void *sentinel_sub(void *p) { return p; } }