]> git.llucax.com Git - software/dgc/cdgc.git/commitdiff
Make the GC configurable at runtime via env vars
authorLeandro Lucarella <llucax@gmail.com>
Mon, 19 Jul 2010 16:24:30 +0000 (13:24 -0300)
committerLeandro Lucarella <llucax@gmail.com>
Tue, 20 Jul 2010 02:50:03 +0000 (23:50 -0300)
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
rt/gc/cdgc/opts.d [new file with mode: 0644]

index e7fdbd2832b9b4208d8f1c5fb55b79972f8a5aa5..bb137f1b7cb984daafc4b7bfa126153b7741317a 100644 (file)
@@ -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 (file)
index 0000000..ccfe9b6
--- /dev/null
@@ -0,0 +1,161 @@
+/**
+ * This module contains the options managemente code of the garbage collector.
+ *
+ * Copyright: Copyright (C) 2010 Leandro Lucarella <http://www.llucax.com.ar/>
+ *            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 :