From a62e962184cb5304bd18a4bd49edb217f9897fbd Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Mon, 19 Jul 2010 13:24:30 -0300 Subject: [PATCH] Make the GC configurable at runtime via env vars The GC offers a couple of options to debug memory problems, but they are selectable only at compile-time. Being the GC part of the compiler runtime, is not very common for the user to recompile the GC when it has a memory problem, so making this option available always is very desirable. This patch allows configuring the GC via environment variables. 4 options are available: sentinel, mem_stomp, verbose and log file. Only the first 2 are implemented right now. For example, to check a program using memory stomping and a sentinel, you can run it like this (using sh): $ D_GC_OPTS=mem_stop=1:sentinel As you can see, the value is optional for boolean options. --- rt/gc/cdgc/gc.d | 173 +++++++++++++++++++++++----------------------- rt/gc/cdgc/opts.d | 161 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 246 insertions(+), 88 deletions(-) create mode 100644 rt/gc/cdgc/opts.d diff --git a/rt/gc/cdgc/gc.d b/rt/gc/cdgc/gc.d index e7fdbd2..bb137f1 100644 --- a/rt/gc/cdgc/gc.d +++ b/rt/gc/cdgc/gc.d @@ -31,8 +31,6 @@ module rt.gc.cdgc.gc; /************** Debugging ***************************/ //debug = COLLECT_PRINTF; // turn on printf's -//debug = MEMSTOMP; // stomp on memory -//debug = SENTINEL; // add underrun/overrrun protection //debug = PTRCHECK; // more pointer checking //debug = PTRCHECK2; // thorough but slow pointer checking @@ -46,8 +44,9 @@ version = STACKGROWSDOWN; // growing the stack means subtracting from the import rt.gc.cdgc.bits: GCBits; import rt.gc.cdgc.stats: GCStats; -import alloc = rt.gc.cdgc.alloc; import rt.gc.cdgc.dynarray: DynArray; +import alloc = rt.gc.cdgc.alloc; +import opts = rt.gc.cdgc.opts; import cstdlib = tango.stdc.stdlib; import cstring = tango.stdc.string; @@ -78,7 +77,6 @@ version (GNU) static import gcc.builtins; // for __builtin_unwind_int } - struct BlkInfo { void* base; @@ -151,6 +149,7 @@ class GC void initialize() { + opts.parse(cstdlib.getenv("D_GC_OPTS")); gcLock = GCLock.classinfo; gcx = cast(Gcx*) cstdlib.calloc(1, Gcx.sizeof); if (!gcx) @@ -334,7 +333,8 @@ class GC assert(gcx); - size += SENTINEL_EXTRA; + if (opts.options.sentinel) + size += SENTINEL_EXTRA; // Compute size bin // Cache previous binsize lookup - Dave Fladebo. @@ -386,7 +386,8 @@ class GC gcx.bucket[bin] = (cast(List*)p).next; if( !(bits & BlkAttr.NO_SCAN) ) memset(p + size, 0, binsize[bin] - size); - debug (MEMSTOMP) memset(p, 0xF0, size); + if (opts.options.mem_stomp) + memset(p, 0xF0, size); } else { @@ -394,9 +395,11 @@ class GC if (!p) onOutOfMemoryError(); } - size -= SENTINEL_EXTRA; - p = sentinel_add(p); - sentinel_init(p, size); + if (opts.options.sentinel) { + size -= SENTINEL_EXTRA; + p = sentinel_add(p); + sentinel_init(p, size); + } if (bits) { @@ -481,7 +484,7 @@ class GC void *p2; size_t psize; - version (SENTINEL) + if (opts.options.sentinel) { sentinel_Invariant(p); psize = *sentinel_size(p); @@ -531,7 +534,7 @@ class GC // Shrink in place synchronized (gcLock) { - debug (MEMSTOMP) + if (opts.options.mem_stomp) memset(p + size, 0xF2, psize - size); pool.freePages(pagenum + newsz, psz - newsz); } @@ -546,9 +549,8 @@ class GC { if (i == pagenum + newsz) { - debug (MEMSTOMP) - memset(p + psize, 0xF0, - size - psize); + if (opts.options.mem_stomp) + memset(p + psize, 0xF0, size - psize); memset(pool.pagetable + pagenum + psz, B_PAGEPLUS, newsz - psz); return p; @@ -630,7 +632,7 @@ class GC } body { - version (SENTINEL) + if (opts.options.sentinel) { return 0; } @@ -660,7 +662,7 @@ class GC } if (sz < minsz) return 0; - debug (MEMSTOMP) + if (opts.options.mem_stomp) memset(p + psize, 0xF0, (psz + sz) * PAGESIZE - psize); memset(pool.pagetable + pagenum + psz, B_PAGEPLUS, sz); gcx.p_cache = null; @@ -739,8 +741,10 @@ class GC pool = gcx.findPool(p); if (!pool) // if not one of ours return; // ignore - sentinel_Invariant(p); - p = sentinel_sub(p); + if (opts.options.sentinel) { + sentinel_Invariant(p); + p = sentinel_sub(p); + } pagenum = cast(size_t)(p - pool.baseAddr) / PAGESIZE; biti = cast(size_t)(p - pool.baseAddr) / 16; gcx.clrBits(pool, biti, BlkAttr.ALL_BITS); @@ -753,7 +757,8 @@ class GC size_t n = pagenum; while (++n < pool.npages && pool.pagetable[n] == B_PAGEPLUS) npages++; - debug (MEMSTOMP) memset(p, 0xF2, npages * PAGESIZE); + if (opts.options.mem_stomp) + memset(p, 0xF2, npages * PAGESIZE); pool.freePages(pagenum, npages); } else @@ -761,7 +766,8 @@ class GC // Add to free list List *list = cast(List*)p; - debug (MEMSTOMP) memset(p, 0xF2, binsize[bin]); + if (opts.options.mem_stomp) + memset(p, 0xF2, binsize[bin]); list.next = gcx.bucket[bin]; gcx.bucket[bin] = list; @@ -834,7 +840,7 @@ class GC { assert (p); - version (SENTINEL) + if (opts.options.sentinel) { p = sentinel_sub(p); size_t size = gcx.findSize(p); @@ -936,7 +942,8 @@ class GC { assert(p); - sentinel_Invariant(p); + if (opts.options.sentinel) + sentinel_Invariant(p); debug (PTRCHECK) { Pool* pool; @@ -944,7 +951,8 @@ class GC Bins bin; size_t size; - p = sentinel_sub(p); + if (opts.options.sentinel) + p = sentinel_sub(p); pool = gcx.findPool(p); assert(pool); pagenum = cast(size_t)(p - pool.baseAddr) / PAGESIZE; @@ -1774,7 +1782,8 @@ struct Gcx 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); + if (opts.options.mem_stomp) + memset(p, 0xF1, size); return p; Lnomemory: @@ -2180,13 +2189,18 @@ struct Gcx { for (; p < ptop; p += size, biti += bitstride) { - if (pool.finals.nbits && pool.finals.testClear(biti)) - rt_finalize(cast(List *)sentinel_add(p), false/*noStack > 0*/); + if (pool.finals.nbits && pool.finals.testClear(biti)) { + if (opts.options.sentinel) + rt_finalize(cast(List *)sentinel_add(p), false/*noStack > 0*/); + else + rt_finalize(cast(List *)p, false/*noStack > 0*/); + } gcx.clrBits(pool, biti, BlkAttr.ALL_BITS); List *list = cast(List *)p; - debug (MEMSTOMP) memset(p, 0xF3, size); + if (opts.options.mem_stomp) + memset(p, 0xF3, size); } pool.pagetable[pn] = B_FREE; freed += PAGESIZE; @@ -2197,16 +2211,22 @@ struct Gcx { if (!pool.mark.test(biti)) { - sentinel_Invariant(sentinel_add(p)); + if (opts.options.sentinel) + sentinel_Invariant(sentinel_add(p)); pool.freebits.set(biti); - if (pool.finals.nbits && pool.finals.testClear(biti)) - rt_finalize(cast(List *)sentinel_add(p), false/*noStack > 0*/); + if (pool.finals.nbits && pool.finals.testClear(biti)) { + if (opts.options.sentinel) + rt_finalize(cast(List *)sentinel_add(p), false/*noStack > 0*/); + else + rt_finalize(cast(List *)p, false/*noStack > 0*/); + } clrBits(pool, biti, BlkAttr.ALL_BITS); List *list = cast(List *)p; - debug (MEMSTOMP) memset(p, 0xF3, size); + if (opts.options.mem_stomp) + memset(p, 0xF3, size); freed += size; } @@ -2218,22 +2238,28 @@ struct Gcx if (!pool.mark.test(biti)) { byte *p = pool.baseAddr + pn * PAGESIZE; - sentinel_Invariant(sentinel_add(p)); - if (pool.finals.nbits && pool.finals.testClear(biti)) - rt_finalize(sentinel_add(p), false/*noStack > 0*/); + if (opts.options.sentinel) + sentinel_Invariant(sentinel_add(p)); + if (pool.finals.nbits && pool.finals.testClear(biti)) { + if (opts.options.sentinel) + rt_finalize(sentinel_add(p), false/*noStack > 0*/); + else + rt_finalize(p, false/*noStack > 0*/); + } clrBits(pool, biti, BlkAttr.ALL_BITS); debug(COLLECT_PRINTF) printf("\tcollecting big %x\n", p); pool.pagetable[pn] = B_FREE; freedpages++; - debug (MEMSTOMP) memset(p, 0xF3, PAGESIZE); + if (opts.options.mem_stomp) + memset(p, 0xF3, PAGESIZE); while (pn + 1 < pool.npages && pool.pagetable[pn + 1] == B_PAGEPLUS) { pn++; pool.pagetable[pn] = B_FREE; freedpages++; - debug (MEMSTOMP) + if (opts.options.mem_stomp) { p += PAGESIZE; memset(p, 0xF3, PAGESIZE); @@ -2526,69 +2552,40 @@ struct Pool /* ============================ 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; - } +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; - void sentinel_Invariant(void *p) - { - assert(*sentinel_pre(p) == SENTINEL_PRE); - assert(*sentinel_post(p) == SENTINEL_POST); - } +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_add(void *p) - { - return p + 2 * size_t.sizeof; - } - - - void *sentinel_sub(void *p) - { - return p - 2 * size_t.sizeof; - } -} -else +void sentinel_init(void *p, size_t size) { - const uint SENTINEL_EXTRA = 0; - - - 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) - { - } +void sentinel_Invariant(void *p) +{ + assert(*sentinel_pre(p) == SENTINEL_PRE); + assert(*sentinel_post(p) == SENTINEL_POST); +} - void *sentinel_add(void *p) - { - return p; - } +void *sentinel_add(void *p) +{ + return p + 2 * size_t.sizeof; +} - void *sentinel_sub(void *p) - { - return p; - } +void *sentinel_sub(void *p) +{ + return p - 2 * size_t.sizeof; } diff --git a/rt/gc/cdgc/opts.d b/rt/gc/cdgc/opts.d new file mode 100644 index 0000000..ccfe9b6 --- /dev/null +++ b/rt/gc/cdgc/opts.d @@ -0,0 +1,161 @@ +/** + * This module contains the options managemente code of the garbage collector. + * + * Copyright: Copyright (C) 2010 Leandro Lucarella + * All rights reserved. + * + * License: Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: Leandro Lucarella + */ + +module rt.gc.cdgc.opts; + +import cstdlib = tango.stdc.stdlib; +import cstring = tango.stdc.string; + + +private: + + +const MAX_OPT_LEN = 256; + + +struct Options +{ + uint verbose = 0; + char[MAX_OPT_LEN] log_file = ""; + bool sentinel = false; + bool mem_stomp = false; +} + +package Options options; + + +void process_option(char* opt_name, char* opt_value) +{ + if (cstring.strcmp(opt_name, "verbose") == 0) + { + options.verbose = cstdlib.atoi(opt_value); + } + else if (cstring.strcmp(opt_name, "log_file") == 0) + { + cstring.strcpy(options.log_file.ptr, opt_value); + } + else if (cstring.strcmp(opt_name, "sentinel") == 0) + { + if (opt_value[0] == '\0') + options.sentinel = true; + else + options.sentinel = (cstdlib.atoi(opt_value) != 0); + } + else if (cstring.strcmp(opt_name, "mem_stomp") == 0) + { + if (opt_value[0] == '\0') + options.mem_stomp = true; + else + options.mem_stomp = (cstdlib.atoi(opt_value) != 0); + } +} + + +package void parse(char* opts_string) +{ + char[MAX_OPT_LEN] opt_name; + char[MAX_OPT_LEN] opt_value; + char* curr = opt_name.ptr; + size_t i = 0; + if (opts_string is null) + return; + for (; *opts_string != '\0'; opts_string++) + { + char c = *opts_string; + if (i == MAX_OPT_LEN) + { + if (c != ':') + continue; + else + i--; + } + switch (*opts_string) + { + case ':': + curr[i] = '\0'; + process_option(opt_name.ptr, opt_value.ptr); + i = 0; + opt_name[0] = '\0'; + opt_value[0] = '\0'; + curr = opt_name.ptr; + break; + case '=': + opt_name[i] = '\0'; + curr = opt_value.ptr; + i = 0; + break; + default: + curr[i] = c; + ++i; + } + } + if (i == MAX_OPT_LEN) + i--; + curr[i] = '\0'; + process_option(opt_name.ptr, opt_value.ptr); +} + + +unittest +{ + with (options) { + assert (verbose == 0); + assert (log_file[0] == '\0'); + assert (sentinel == false); + assert (mem_stomp == false); + } + parse("mem_stomp=1:verbose=2"); + with (options) { + assert (verbose == 2); + assert (log_file[0] == '\0'); + assert (sentinel == false); + assert (mem_stomp == true); + } + parse("log_file=12345 67890:verbose=1:sentinel=4:mem_stomp=0"); + with (options) { + assert (verbose == 1); + assert (cstring.strcmp(log_file.ptr, "12345 67890".ptr) == 0); + assert (sentinel == true); + assert (mem_stomp == false); + } + parse(null); + with (options) { + assert (verbose == 1); + assert (cstring.strcmp(log_file.ptr, "12345 67890".ptr) == 0); + assert (sentinel == true); + assert (mem_stomp == false); + } +} + + +// vim: set et sw=4 sts=4 : -- 2.43.0