]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: WriteHeapDump dumps the heap to a file.
authorKeith Randall <khr@golang.org>
Tue, 25 Mar 2014 22:09:49 +0000 (15:09 -0700)
committerKeith Randall <khr@golang.org>
Tue, 25 Mar 2014 22:09:49 +0000 (15:09 -0700)
See http://golang.org/s/go13heapdump for the file format.

LGTM=rsc
R=rsc, bradfitz, dvyukov, khr
CC=golang-codereviews
https://golang.org/cl/37540043

src/pkg/runtime/debug/garbage.go
src/pkg/runtime/heapdump.c [new file with mode: 0644]
src/pkg/runtime/iface.goc
src/pkg/runtime/malloc.h
src/pkg/runtime/mgc0.c
src/pkg/runtime/mgc0.h
src/pkg/runtime/runtime.h

index 093e7e7b1807446f6d3bccfcdd57d25998abc5bb..edb3643871e67368d0b7cd11c135d170c1e0f18e 100644 (file)
@@ -146,3 +146,8 @@ func SetMaxThreads(threads int) int {
 // SetPanicOnFault applies only to the current goroutine.
 // It returns the previous setting.
 func SetPanicOnFault(enabled bool) bool
+
+// WriteHeapDump writes a description of the heap and the objects in
+// it to the given file descriptor.
+// The heap dump format is defined at http://golang.org/s/go13heapdump.
+func WriteHeapDump(fd uintptr)
diff --git a/src/pkg/runtime/heapdump.c b/src/pkg/runtime/heapdump.c
new file mode 100644 (file)
index 0000000..db0178e
--- /dev/null
@@ -0,0 +1,890 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Implementation of runtime/debug.WriteHeapDump.  Writes all
+// objects in the heap plus additional info (roots, threads,
+// finalizers, etc.) to a file.
+
+// The format of the dumped file is described at
+// http://code.google.com/p/go-wiki/wiki/heapdump13
+
+#include "runtime.h"
+#include "arch_GOARCH.h"
+#include "malloc.h"
+#include "mgc0.h"
+#include "type.h"
+#include "typekind.h"
+#include "funcdata.h"
+#include "zaexperiment.h"
+
+extern byte data[];
+extern byte edata[];
+extern byte bss[];
+extern byte ebss[];
+extern byte gcdata[];
+extern byte gcbss[];
+
+enum {
+       FieldKindEol = 0,
+       FieldKindPtr = 1,
+       FieldKindString = 2,
+       FieldKindSlice = 3,
+       FieldKindIface = 4,
+       FieldKindEface = 5,
+
+       TagEOF = 0,
+       TagObject = 1,
+       TagOtherRoot = 2,
+       TagType = 3,
+       TagGoRoutine = 4,
+       TagStackFrame = 5,
+       TagParams = 6,
+       TagFinalizer = 7,
+       TagItab = 8,
+       TagOSThread = 9,
+       TagMemStats = 10,
+       TagQueuedFinalizer = 11,
+       TagData = 12,
+       TagBss = 13,
+       TagDefer = 14,
+       TagPanic = 15,
+
+       TypeInfo_Conservative = 127,
+};
+
+static uintptr* playgcprog(uintptr offset, uintptr *prog, void (*callback)(void*,uintptr,uintptr), void *arg);
+static void dumpfields(uintptr *prog);
+static void dumpefacetypes(void *obj, uintptr size, Type *type, uintptr kind);
+static void dumpbvtypes(BitVector *bv, byte *base);
+
+// fd to write the dump to.
+static uintptr dumpfd;
+
+// buffer of pending write data
+enum {
+       BufSize = 4096,
+};
+static byte buf[BufSize];
+static uintptr nbuf;
+
+static void
+write(byte *data, uintptr len)
+{
+       if(len + nbuf <= BufSize) {
+               runtime·memmove(buf + nbuf, data, len);
+               nbuf += len;
+               return;
+       }
+       runtime·write(dumpfd, buf, nbuf);
+       if(len >= BufSize) {
+               runtime·write(dumpfd, data, len);
+               nbuf = 0;
+       } else {
+               runtime·memmove(buf, data, len);
+               nbuf = len;
+       }
+}
+
+static void
+flush(void)
+{
+       runtime·write(dumpfd, buf, nbuf);
+       nbuf = 0;
+}
+
+// Cache of types that have been serialized already.
+// We use a type's hash field to pick a bucket.
+// Inside a bucket, we keep a list of types that
+// have been serialized so far, most recently used first.
+// Note: when a bucket overflows we may end up
+// serializing a type more than once.  That's ok.
+enum {
+       TypeCacheBuckets = 256, // must be a power of 2
+       TypeCacheAssoc = 4,
+};
+typedef struct TypeCacheBucket TypeCacheBucket;
+struct TypeCacheBucket {
+       Type *t[TypeCacheAssoc];
+};
+static TypeCacheBucket typecache[TypeCacheBuckets];
+
+// dump a uint64 in a varint format parseable by encoding/binary
+static void
+dumpint(uint64 v)
+{
+       byte buf[10];
+       int32 n;
+       n = 0;
+       while(v >= 0x80) {
+               buf[n++] = v | 0x80;
+               v >>= 7;
+       }
+       buf[n++] = v;
+       write(buf, n);
+}
+
+static void
+dumpbool(bool b)
+{
+       dumpint(b ? 1 : 0);
+}
+
+// dump varint uint64 length followed by memory contents
+static void
+dumpmemrange(byte *data, uintptr len)
+{
+       dumpint(len);
+       write(data, len);
+}
+
+static void
+dumpstr(String s)
+{
+       dumpmemrange(s.str, s.len);
+}
+
+static void
+dumpcstr(int8 *c)
+{
+       dumpmemrange((byte*)c, runtime·findnull((byte*)c));
+}
+
+// dump information for a type
+static void
+dumptype(Type *t)
+{
+       TypeCacheBucket *b;
+       int32 i, j;
+
+       if(t == nil) {
+               return;
+       }
+
+       // If we've definitely serialized the type before,
+       // no need to do it again.
+       b = &typecache[t->hash & (TypeCacheBuckets-1)];
+       if(t == b->t[0]) return;
+       for(i = 1; i < TypeCacheAssoc; i++) {
+               if(t == b->t[i]) {
+                       // Move-to-front
+                       for(j = i; j > 0; j--) {
+                               b->t[j] = b->t[j-1];
+                       }
+                       b->t[0] = t;
+                       return;
+               }
+       }
+       // Might not have been dumped yet.  Dump it and
+       // remember we did so.
+       for(j = TypeCacheAssoc-1; j > 0; j--) {
+               b->t[j] = b->t[j-1];
+       }
+       b->t[0] = t;
+       
+       // dump the type
+       dumpint(TagType);
+       dumpint((uintptr)t);
+       dumpint(t->size);
+       dumpstr(*t->string);
+       dumpbool(t->size > PtrSize || (t->kind & KindNoPointers) == 0);
+       dumpfields((uintptr*)t->gc + 1);
+}
+
+// returns true if object is scannable
+static bool
+scannable(byte *obj)
+{
+       uintptr *b, off, shift;
+
+       off = (uintptr*)obj - (uintptr*)runtime·mheap.arena_start;  // word offset
+       b = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1;
+       shift = off % wordsPerBitmapWord;
+       return ((*b >> shift) & bitScan) != 0;
+}
+
+// dump an object
+static void
+dumpobj(byte *obj, uintptr size, Type *type, uintptr kind)
+{
+       if(type != nil) {
+               dumptype(type);
+               dumpefacetypes(obj, size, type, kind);
+       }
+
+       dumpint(TagObject);
+       dumpint((uintptr)obj);
+       dumpint((uintptr)type);
+       dumpint(kind);
+       dumpmemrange(obj, size);
+}
+
+static void
+dumpotherroot(int8 *description, byte *to)
+{
+       dumpint(TagOtherRoot);
+       dumpcstr(description);
+       dumpint((uintptr)to);
+}
+
+static void
+dumpfinalizer(byte *obj, FuncVal *fn, Type* fint, PtrType *ot)
+{
+       dumpint(TagFinalizer);
+       dumpint((uintptr)obj);
+       dumpint((uintptr)fn);
+       dumpint((uintptr)fn->fn);
+       dumpint((uintptr)fint);
+       dumpint((uintptr)ot);
+}
+
+typedef struct ChildInfo ChildInfo;
+struct ChildInfo {
+       // Information passed up from the callee frame about
+       // the layout of the outargs region.
+       uintptr argoff;     // where the arguments start in the frame
+       uintptr arglen;     // size of args region
+       BitVector *args;    // if not nil, pointer map of args region
+
+       byte *sp;           // callee sp
+       uintptr depth;      // depth in call stack (0 == most recent)
+};
+
+// dump kinds & offsets of interesting fields in bv
+static void
+dumpbv(BitVector *bv, uintptr offset)
+{
+       uintptr i;
+
+       for(i = 0; i < bv->n; i += BitsPerPointer) {
+               switch(bv->data[i/32] >> i%32 & 3) {
+               case BitsDead:
+               case BitsScalar:
+                       break;
+               case BitsPointer:
+                       dumpint(FieldKindPtr);
+                       dumpint(offset + i / BitsPerPointer * PtrSize);
+                       break;
+               case BitsMultiWord:
+                       switch(bv->data[(i+BitsPerPointer)/32] >> (i+BitsPerPointer)%32 & 3) {
+                       case BitsString:
+                               dumpint(FieldKindString);
+                               dumpint(offset + i / BitsPerPointer * PtrSize);
+                               i += BitsPerPointer;
+                               break;
+                       case BitsSlice:
+                               dumpint(FieldKindSlice);
+                               dumpint(offset + i / BitsPerPointer * PtrSize);
+                               i += 2 * BitsPerPointer;
+                               break;
+                       case BitsIface:
+                               dumpint(FieldKindIface);
+                               dumpint(offset + i / BitsPerPointer * PtrSize);
+                               i += BitsPerPointer;
+                               break;
+                       case BitsEface:
+                               dumpint(FieldKindEface);
+                               dumpint(offset + i / BitsPerPointer * PtrSize);
+                               i += BitsPerPointer;
+                               break;
+                       }
+               }
+       }
+}
+
+static bool
+dumpframe(Stkframe *s, void *arg)
+{
+       Func *f;
+       ChildInfo *child;
+       uintptr pc, off, size;
+       int32 pcdata;
+       StackMap *stackmap;
+       int8 *name;
+       BitVector *bv;
+
+       child = (ChildInfo*)arg;
+       f = s->fn;
+
+       // Figure out what we can about our stack map
+       pc = s->pc;
+       if(pc != f->entry)
+               pc--;
+       pcdata = runtime·pcdatavalue(f, PCDATA_StackMapIndex, pc);
+       if(pcdata == -1) {
+               // We do not have a valid pcdata value but there might be a
+               // stackmap for this function.  It is likely that we are looking
+               // at the function prologue, assume so and hope for the best.
+               pcdata = 0;
+       }
+       stackmap = runtime·funcdata(f, FUNCDATA_LocalsPointerMaps);
+
+       // Dump any types we will need to resolve Efaces.
+       if(child->args != nil)
+               dumpbvtypes(child->args, (byte*)s->sp + child->argoff);
+       if(stackmap != nil && stackmap->n > 0) {
+               bv = runtime·stackmapdata(stackmap, pcdata);
+               dumpbvtypes(bv, s->varp - bv->n / BitsPerPointer * PtrSize);
+       } else {
+               bv = nil;
+       }
+
+       // Dump main body of stack frame.
+       dumpint(TagStackFrame);
+       dumpint(s->sp); // lowest address in frame
+       dumpint(child->depth); // # of frames deep on the stack
+       dumpint((uintptr)child->sp); // sp of child, or 0 if bottom of stack
+       dumpmemrange((byte*)s->sp, s->fp - s->sp);  // frame contents
+       dumpint(f->entry);
+       dumpint(s->pc);
+       name = runtime·funcname(f);
+       if(name == nil)
+               name = "unknown function";
+       dumpcstr(name);
+
+       // Dump fields in the outargs section
+       if(child->args != nil) {
+               dumpbv(child->args, child->argoff);
+       } else {
+               // conservative - everything might be a pointer
+               for(off = child->argoff; off < child->argoff + child->arglen; off += PtrSize) {
+                       dumpint(FieldKindPtr);
+                       dumpint(off);
+               }
+       }
+
+       // Dump fields in the local vars section
+       if(stackmap == nil) {
+               // No locals information, dump everything.
+               for(off = child->arglen; off < s->varp - (byte*)s->sp; off += PtrSize) {
+                       dumpint(FieldKindPtr);
+                       dumpint(off);
+               }
+       } else if(stackmap->n < 0) {
+               // Locals size information, dump just the locals.
+               size = -stackmap->n;
+               for(off = s->varp - size - (byte*)s->sp; off < s->varp - (byte*)s->sp; off += PtrSize) {
+                       dumpint(FieldKindPtr);
+                       dumpint(off);
+               }
+       } else if(stackmap->n > 0) {
+               // Locals bitmap information, scan just the pointers in
+               // locals.
+               dumpbv(bv, s->varp - bv->n / BitsPerPointer * PtrSize - (byte*)s->sp);
+       }
+       dumpint(FieldKindEol);
+
+       // Record arg info for parent.
+       child->argoff = s->argp - (byte*)s->sp;
+       child->arglen = s->arglen;
+       child->sp = (byte*)s->sp;
+       child->depth++;
+       stackmap = runtime·funcdata(f, FUNCDATA_ArgsPointerMaps);
+       if(stackmap != nil)
+               child->args = runtime·stackmapdata(stackmap, pcdata);
+       else
+               child->args = nil;
+       return true;
+}
+
+static void
+dumpgoroutine(G *gp)
+{
+       uintptr sp, pc, lr;
+       ChildInfo child;
+       Defer *d;
+       Panic *p;
+
+       if(gp->syscallstack != (uintptr)nil) {
+               sp = gp->syscallsp;
+               pc = gp->syscallpc;
+               lr = 0;
+       } else {
+               sp = gp->sched.sp;
+               pc = gp->sched.pc;
+               lr = gp->sched.lr;
+       }
+
+       dumpint(TagGoRoutine);
+       dumpint((uintptr)gp);
+       dumpint((uintptr)sp);
+       dumpint(gp->goid);
+       dumpint(gp->gopc);
+       dumpint(gp->status);
+       dumpbool(gp->issystem);
+       dumpbool(gp->isbackground);
+       dumpint(gp->waitsince);
+       dumpcstr(gp->waitreason);
+       dumpint((uintptr)gp->sched.ctxt);
+       dumpint((uintptr)gp->m);
+       dumpint((uintptr)gp->defer);
+       dumpint((uintptr)gp->panic);
+
+       // dump stack
+       child.args = nil;
+       child.arglen = 0;
+       child.sp = nil;
+       child.depth = 0;
+       if(!ScanStackByFrames)
+               runtime·throw("need frame info to dump stacks");
+       runtime·gentraceback(pc, sp, lr, gp, 0, nil, 0x7fffffff, dumpframe, &child, false);
+
+       // dump defer & panic records
+       for(d = gp->defer; d != nil; d = d->link) {
+               dumpint(TagDefer);
+               dumpint((uintptr)d);
+               dumpint((uintptr)gp);
+               dumpint((uintptr)d->argp);
+               dumpint((uintptr)d->pc);
+               dumpint((uintptr)d->fn);
+               dumpint((uintptr)d->fn->fn);
+               dumpint((uintptr)d->link);
+       }
+       for (p = gp->panic; p != nil; p = p->link) {
+               dumpint(TagPanic);
+               dumpint((uintptr)p);
+               dumpint((uintptr)gp);
+               dumpint((uintptr)p->arg.type);
+               dumpint((uintptr)p->arg.data);
+               dumpint((uintptr)p->defer);
+               dumpint((uintptr)p->link);
+       }
+}
+
+static void
+dumpgs(void)
+{
+       G *gp;
+       uint32 i;
+
+       // goroutines & stacks
+       for(i = 0; i < runtime·allglen; i++) {
+               gp = runtime·allg[i];
+               switch(gp->status){
+               default:
+                       runtime·printf("unexpected G.status %d\n", gp->status);
+                       runtime·throw("mark - bad status");
+               case Gdead:
+                       break;
+               case Grunnable:
+               case Gsyscall:
+               case Gwaiting:
+                       dumpgoroutine(gp);
+                       break;
+               }
+       }
+}
+
+static void
+finq_callback(FuncVal *fn, byte *obj, uintptr nret, Type *fint, PtrType *ot)
+{
+       dumpint(TagQueuedFinalizer);
+       dumpint((uintptr)obj);
+       dumpint((uintptr)fn);
+       dumpint((uintptr)fn->fn);
+       dumpint((uintptr)fint);
+       dumpint((uintptr)ot);
+       USED(&nret);
+}
+
+
+static void
+dumproots(void)
+{
+       MSpan *s, **allspans;
+       uint32 spanidx;
+       Special *sp;
+       SpecialFinalizer *spf;
+       byte *p;
+
+       // data segment
+       dumpint(TagData);
+       dumpint((uintptr)data);
+       dumpmemrange(data, edata - data);
+       dumpfields((uintptr*)gcdata + 1);
+
+       // bss segment
+       dumpint(TagBss);
+       dumpint((uintptr)bss);
+       dumpmemrange(bss, ebss - bss);
+       dumpfields((uintptr*)gcbss + 1);
+       
+       // MSpan.types
+       allspans = runtime·mheap.allspans;
+       for(spanidx=0; spanidx<runtime·mheap.nspan; spanidx++) {
+               s = allspans[spanidx];
+               if(s->state == MSpanInUse) {
+                       // The garbage collector ignores type pointers stored in MSpan.types:
+                       //  - Compiler-generated types are stored outside of heap.
+                       //  - The reflect package has runtime-generated types cached in its data structures.
+                       //    The garbage collector relies on finding the references via that cache.
+                       switch(s->types.compression) {
+                       case MTypes_Empty:
+                       case MTypes_Single:
+                               break;
+                       case MTypes_Words:
+                       case MTypes_Bytes:
+                               dumpotherroot("runtime type info", (byte*)s->types.data);
+                               break;
+                       }
+
+                       // Finalizers
+                       for(sp = s->specials; sp != nil; sp = sp->next) {
+                               if(sp->kind != KindSpecialFinalizer)
+                                       continue;
+                               spf = (SpecialFinalizer*)sp;
+                               p = (byte*)((s->start << PageShift) + spf->offset);
+                               dumpfinalizer(p, spf->fn, spf->fint, spf->ot);
+                       }
+               }
+       }
+
+       // Finalizer queue
+       runtime·iterate_finq(finq_callback);
+}
+
+// Bit vector of free marks.
+// Needs to be as big as the largest number of objects per span.
+static byte free[PageSize/8];
+
+static void
+dumpobjs(void)
+{
+       uintptr i, j, size, n, off, shift, *bitp, bits, ti, kind;
+       MSpan *s;
+       MLink *l;
+       byte *p;
+       Type *t;
+
+       for(i = 0; i < runtime·mheap.nspan; i++) {
+               s = runtime·mheap.allspans[i];
+               if(s->state != MSpanInUse)
+                       continue;
+               runtime·MSpan_EnsureSwept(s);
+               p = (byte*)(s->start << PageShift);
+               size = s->elemsize;
+               n = (s->npages << PageShift) / size;
+               if(n > PageSize/8)
+                       runtime·throw("free array doesn't have enough entries");
+               for(l = s->freelist; l != nil; l = l->next) {
+                       free[((byte*)l - p) / size] = true;
+               }
+               for(j = 0; j < n; j++, p += size) {
+                       if(free[j]) {
+                               free[j] = false;
+                               continue;
+                       }
+                       off = (uintptr*)p - (uintptr*)runtime·mheap.arena_start;
+                       bitp = (uintptr*)runtime·mheap.arena_start - off/wordsPerBitmapWord - 1;
+                       shift = off % wordsPerBitmapWord;
+                       bits = *bitp >> shift;
+
+                       // Skip FlagNoGC allocations (stacks)
+                       if((bits & bitAllocated) == 0)
+                               continue;
+
+                       // extract type and kind
+                       ti = runtime·gettype(p);
+                       t = (Type*)(ti & ~(uintptr)(PtrSize-1));
+                       kind = ti & (PtrSize-1);
+                       
+                       // dump it
+                       if(kind == TypeInfo_Chan)
+                               t = ((ChanType*)t)->elem; // use element type for chan encoding
+                       if(t == nil && scannable(p))
+                               kind = TypeInfo_Conservative; // special kind for conservatively scanned objects
+                       dumpobj(p, size, t, kind);
+               }
+       }
+}
+
+static void
+dumpparams(void)
+{
+       byte *x;
+
+       dumpint(TagParams);
+       x = (byte*)1;
+       if(*(byte*)&x == 1)
+               dumpbool(false); // little-endian ptrs
+       else
+               dumpbool(true); // big-endian ptrs
+       dumpint(PtrSize);
+       dumpint(runtime·Hchansize);
+       dumpint((uintptr)runtime·mheap.arena_start);
+       dumpint((uintptr)runtime·mheap.arena_used);
+       dumpint(thechar);
+       dumpcstr(GOEXPERIMENT);
+       dumpint(runtime·ncpu);
+}
+
+static void
+itab_callback(Itab *tab)
+{
+       Type *t;
+
+       dumpint(TagItab);
+       dumpint((uintptr)tab);
+       t = tab->type;
+       dumpbool(t->size > PtrSize || (t->kind & KindNoPointers) == 0);
+}
+
+static void
+dumpitabs(void)
+{
+       runtime·iterate_itabs(itab_callback);
+}
+
+static void
+dumpms(void)
+{
+       M *mp;
+
+       for(mp = runtime·allm; mp != nil; mp = mp->alllink) {
+               dumpint(TagOSThread);
+               dumpint((uintptr)mp);
+               dumpint(mp->id);
+               dumpint(mp->procid);
+       }
+}
+
+static void
+dumpmemstats(void)
+{
+       int32 i;
+
+       dumpint(TagMemStats);
+       dumpint(mstats.alloc);
+       dumpint(mstats.total_alloc);
+       dumpint(mstats.sys);
+       dumpint(mstats.nlookup);
+       dumpint(mstats.nmalloc);
+       dumpint(mstats.nfree);
+       dumpint(mstats.heap_alloc);
+       dumpint(mstats.heap_sys);
+       dumpint(mstats.heap_idle);
+       dumpint(mstats.heap_inuse);
+       dumpint(mstats.heap_released);
+       dumpint(mstats.heap_objects);
+       dumpint(mstats.stacks_inuse);
+       dumpint(mstats.stacks_sys);
+       dumpint(mstats.mspan_inuse);
+       dumpint(mstats.mspan_sys);
+       dumpint(mstats.mcache_inuse);
+       dumpint(mstats.mcache_sys);
+       dumpint(mstats.buckhash_sys);
+       dumpint(mstats.gc_sys);
+       dumpint(mstats.other_sys);
+       dumpint(mstats.next_gc);
+       dumpint(mstats.last_gc);
+       dumpint(mstats.pause_total_ns);
+       for(i = 0; i < 256; i++)
+               dumpint(mstats.pause_ns[i]);
+       dumpint(mstats.numgc);
+}
+
+static void
+mdump(G *gp)
+{
+       byte *hdr;
+
+       runtime·memclr((byte*)&typecache[0], sizeof(typecache));
+       hdr = (byte*)"go1.3 heap dump\n";
+       write(hdr, runtime·findnull(hdr));
+       dumpparams();
+       dumpitabs();
+       dumpobjs();
+       dumpgs();
+       dumpms();
+       dumproots();
+       dumpmemstats();
+       dumpint(TagEOF);
+       flush();
+
+       gp->param = nil;
+       gp->status = Grunning;
+       runtime·gogo(&gp->sched);
+}
+
+void
+runtime∕debug·WriteHeapDump(uintptr fd)
+{
+       // Stop the world.
+       runtime·semacquire(&runtime·worldsema, false);
+       m->gcing = 1;
+       m->locks++;
+       runtime·stoptheworld();
+
+       // Update stats so we can dump them.
+       // As a side effect, flushes all the MCaches so the MSpan.freelist
+       // lists contain all the free objects.
+       runtime·updatememstats(nil);
+
+       // Set dump file.
+       dumpfd = fd;
+
+       // Call dump routine on M stack.
+       g->status = Gwaiting;
+       g->waitreason = "dumping heap";
+       runtime·mcall(mdump);
+
+       // Reset dump file.
+       dumpfd = 0;
+
+       // Start up the world again.
+       m->gcing = 0;
+       runtime·semrelease(&runtime·worldsema);
+       runtime·starttheworld();
+       m->locks--;
+}
+
+// Runs the specified gc program.  Calls the callback for every
+// pointer-like field specified by the program and passes to the
+// callback the kind and offset of that field within the object.
+// offset is the offset in the object of the start of the program.
+// Returns a pointer to the opcode that ended the gc program (either
+// GC_END or GC_ARRAY_NEXT).
+static uintptr*
+playgcprog(uintptr offset, uintptr *prog, void (*callback)(void*,uintptr,uintptr), void *arg)
+{
+       uintptr len, elemsize, i, *end;
+
+       for(;;) {
+               switch(prog[0]) {
+               case GC_END:
+                       return prog;
+               case GC_PTR:
+                       callback(arg, FieldKindPtr, offset + prog[1]);
+                       prog += 3;
+                       break;
+               case GC_APTR:
+                       callback(arg, FieldKindPtr, offset + prog[1]);
+                       prog += 2;
+                       break;
+               case GC_ARRAY_START:
+                       len = prog[2];
+                       elemsize = prog[3];
+                       end = nil;
+                       for(i = 0; i < len; i++) {
+                               end = playgcprog(offset + prog[1] + i * elemsize, prog + 4, callback, arg);
+                               if(end[0] != GC_ARRAY_NEXT)
+                                       runtime·throw("GC_ARRAY_START did not have matching GC_ARRAY_NEXT");
+                       }
+                       prog = end + 1;
+                       break;
+               case GC_ARRAY_NEXT:
+                       return prog;
+               case GC_CALL:
+                       playgcprog(offset + prog[1], (uintptr*)((byte*)prog + *(int32*)&prog[2]), callback, arg);
+                       prog += 3;
+                       break;
+               case GC_CHAN_PTR:
+                       callback(arg, FieldKindPtr, offset + prog[1]);
+                       prog += 3;
+                       break;
+               case GC_STRING:
+                       callback(arg, FieldKindString, offset + prog[1]);
+                       prog += 2;
+                       break;
+               case GC_EFACE:
+                       callback(arg, FieldKindEface, offset + prog[1]);
+                       prog += 2;
+                       break;
+               case GC_IFACE:
+                       callback(arg, FieldKindIface, offset + prog[1]);
+                       prog += 2;
+                       break;
+               case GC_SLICE:
+                       callback(arg, FieldKindSlice, offset + prog[1]);
+                       prog += 3;
+                       break;
+               case GC_REGION:
+                       playgcprog(offset + prog[1], (uintptr*)prog[3] + 1, callback, arg);
+                       prog += 4;
+                       break;
+               default:
+                       runtime·printf("%D\n", prog[0]);
+                       runtime·throw("bad gc op");
+               }
+       }
+}
+
+static void
+dump_callback(void *p, uintptr kind, uintptr offset)
+{
+       USED(&p);
+       dumpint(kind);
+       dumpint(offset);
+}
+
+// dumpint() the kind & offset of each field in an object.
+static void
+dumpfields(uintptr *prog)
+{
+       playgcprog(0, prog, dump_callback, nil);
+       dumpint(FieldKindEol);
+}
+
+static void
+dumpeface_callback(void *p, uintptr kind, uintptr offset)
+{
+       Eface *e;
+
+       if(kind != FieldKindEface)
+               return;
+       e = (Eface*)((byte*)p + offset);
+       dumptype(e->type);
+}
+
+// The heap dump reader needs to be able to disambiguate
+// Eface entries.  So it needs to know every type that might
+// appear in such an entry.  The following two routines accomplish
+// that.
+
+// Dump all the types that appear in the type field of
+// any Eface contained in obj.
+static void
+dumpefacetypes(void *obj, uintptr size, Type *type, uintptr kind)
+{
+       uintptr i;
+
+       switch(kind) {
+       case TypeInfo_SingleObject:
+               playgcprog(0, (uintptr*)type->gc + 1, dumpeface_callback, obj);
+               break;
+       case TypeInfo_Array:
+               for(i = 0; i < size; i += type->size)
+                       playgcprog(i, (uintptr*)type->gc + 1, dumpeface_callback, obj);
+               break;
+       case TypeInfo_Chan:
+               for(i = runtime·Hchansize; i < size; i += type->size)
+                       playgcprog(i, (uintptr*)type->gc + 1, dumpeface_callback, obj);
+               break;
+       }
+}
+
+// Dump all the types that appear in the type field of
+// any Eface described by this bit vector.
+static void
+dumpbvtypes(BitVector *bv, byte *base)
+{
+       uintptr i;
+
+       for(i = 0; i < bv->n; i += BitsPerPointer) {
+               if((bv->data[i/32] >> i%32 & 3) != BitsMultiWord)
+                       continue;
+               switch(bv->data[(i+BitsPerPointer)/32] >> (i+BitsPerPointer)%32 & 3) {
+               case BitsString:
+               case BitsIface:
+                       i += BitsPerPointer;
+                       break;
+               case BitsSlice:
+                       i += 2 * BitsPerPointer;
+                       break;
+               case BitsEface:
+                       dumptype(*(Type**)(base + i / BitsPerPointer * PtrSize));
+                       i += BitsPerPointer;
+                       break;
+               }
+       }
+}
index c678ce0a2fb8febcc6fceb4d9a9cbbc9be10a997..08da8c7337075c17a8bef375fb2f426fd8cfb406 100644 (file)
@@ -134,6 +134,20 @@ out:
        return m;
 }
 
+// call the callback for every itab that is currently allocated.
+void
+runtime·iterate_itabs(void (*callback)(Itab*))
+{
+       int32 i;
+       Itab *tab;
+
+       for(i = 0; i < nelem(hash); i++) {
+               for(tab = hash[i]; tab != nil; tab = tab->link) {
+                       callback(tab);
+               }
+       }
+}
+
 static void
 copyin(Type *t, void *src, void **dst)
 {
index 97b5a924fe48a412920abbb4b9820cde4def86e6..f4c1bef77032d524c2f48f03312be5ca7ae97688 100644 (file)
@@ -277,6 +277,7 @@ struct MStats
 
 #define mstats runtime·memStats
 extern MStats mstats;
+void   runtime·updatememstats(GCStats *stats);
 
 // Size classes.  Computed and initialized by InitSizes.
 //
index 04dd93608a5950f457d16ca001724516122e5c57..c16bac1a0d3b67fe380dcd0ab4c0df68d6be5d7e 100644 (file)
 enum {
        Debug = 0,
        CollectStats = 0,
-       ScanStackByFrames = 1,
-       IgnorePreciseGC = 0,
        ConcurrentSweep = 1,
 
-       // Four bits per word (see #defines below).
-       wordsPerBitmapWord = sizeof(void*)*8/4,
-       bitShift = sizeof(void*)*8/4,
-
        WorkbufSize     = 16*1024,
        FinBlockSize    = 4*1024,
 
@@ -146,38 +140,6 @@ clearpools(void)
        }
 }
 
-// Bits in per-word bitmap.
-// #defines because enum might not be able to hold the values.
-//
-// Each word in the bitmap describes wordsPerBitmapWord words
-// of heap memory.  There are 4 bitmap bits dedicated to each heap word,
-// so on a 64-bit system there is one bitmap word per 16 heap words.
-// The bits in the word are packed together by type first, then by
-// heap location, so each 64-bit bitmap word consists of, from top to bottom,
-// the 16 bitMarked bits for the corresponding heap words,
-// then the 16 bitScan/bitBlockBoundary bits, then the 16 bitAllocated bits.
-// This layout makes it easier to iterate over the bits of a given type.
-//
-// The bitmap starts at mheap.arena_start and extends *backward* from
-// there.  On a 64-bit system the off'th word in the arena is tracked by
-// the off/16+1'th word before mheap.arena_start.  (On a 32-bit system,
-// the only difference is that the divisor is 8.)
-//
-// To pull out the bits corresponding to a given pointer p, we use:
-//
-//     off = p - (uintptr*)mheap.arena_start;  // word offset
-//     b = (uintptr*)mheap.arena_start - off/wordsPerBitmapWord - 1;
-//     shift = off % wordsPerBitmapWord
-//     bits = *b >> shift;
-//     /* then test bits & bitAllocated, bits & bitMarked, etc. */
-//
-#define bitAllocated           ((uintptr)1<<(bitShift*0))      /* block start; eligible for garbage collection */
-#define bitScan                        ((uintptr)1<<(bitShift*1))      /* when bitAllocated is set */
-#define bitMarked              ((uintptr)1<<(bitShift*2))      /* when bitAllocated is set */
-#define bitBlockBoundary       ((uintptr)1<<(bitShift*1))      /* when bitAllocated is NOT set - mark for FlagNoGC objects */
-
-#define bitMask (bitAllocated | bitScan | bitMarked)
-
 // Holding worldsema grants an M the right to try to stop the world.
 // The procedure is:
 //
@@ -1270,6 +1232,7 @@ markroot(ParFor *desc, uint32 i)
 
        USED(&desc);
        wbuf = getempty(nil);
+       // Note: if you add a case here, please also update heapdump.c:dumproots.
        switch(i) {
        case RootData:
                enqueue1(&wbuf, (Obj){data, edata - data, (uintptr)gcdata});
@@ -1714,6 +1677,21 @@ runtime·queuefinalizer(byte *p, FuncVal *fn, uintptr nret, Type *fint, PtrType
        runtime·unlock(&gclock);
 }
 
+void
+runtime·iterate_finq(void (*callback)(FuncVal*, byte*, uintptr, Type*, PtrType*))
+{
+       FinBlock *fb;
+       Finalizer *f;
+       uintptr i;
+
+       for(fb = allfin; fb; fb = fb->alllink) {
+               for(i = 0; i < fb->cnt; i++) {
+                       f = &fb->fin[i];
+                       callback(f->fn, f->arg, f->nret, f->fint, f->ot);
+               }
+       }
+}
+
 void
 runtime·MSpan_EnsureSwept(MSpan *s)
 {
@@ -2120,8 +2098,8 @@ flushallmcaches(void)
        }
 }
 
-static void
-updatememstats(GCStats *stats)
+void
+runtime·updatememstats(GCStats *stats)
 {
        M *mp;
        MSpan *s;
@@ -2388,7 +2366,7 @@ gc(struct gc_args *args)
 
        if(runtime·debug.gctrace) {
                heap1 = mstats.heap_alloc;
-               updatememstats(&stats);
+               runtime·updatememstats(&stats);
                if(heap1 != mstats.heap_alloc) {
                        runtime·printf("runtime: mstats skew: heap=%D/%D\n", heap1, mstats.heap_alloc);
                        runtime·throw("mstats skew");
@@ -2488,7 +2466,7 @@ runtime·ReadMemStats(MStats *stats)
        runtime·semacquire(&runtime·worldsema, false);
        m->gcing = 1;
        runtime·stoptheworld();
-       updatememstats(nil);
+       runtime·updatememstats(nil);
        // Size of the trailing by_size array differs between Go and C,
        // NumSizeClasses was changed, but we can not change Go struct because of backward compatibility.
        runtime·memcopy(runtime·sizeof_C_MStats, stats, &mstats);
index f8abe6c9c1c235ff71952c8c56ab330f11329617..16000d1ae2ea8b7ce613a55455e376d460bff270 100644 (file)
@@ -44,3 +44,44 @@ enum {
        //  - at most GC_STACK_CAPACITY allocations because of GC_ARRAY_START
        GC_STACK_CAPACITY = 8,  
 };
+
+enum {
+       ScanStackByFrames = 1,
+       IgnorePreciseGC = 0,
+
+       // Four bits per word (see #defines below).
+       wordsPerBitmapWord = sizeof(void*)*8/4,
+       bitShift = sizeof(void*)*8/4,
+};
+
+// Bits in per-word bitmap.
+// #defines because enum might not be able to hold the values.
+//
+// Each word in the bitmap describes wordsPerBitmapWord words
+// of heap memory.  There are 4 bitmap bits dedicated to each heap word,
+// so on a 64-bit system there is one bitmap word per 16 heap words.
+// The bits in the word are packed together by type first, then by
+// heap location, so each 64-bit bitmap word consists of, from top to bottom,
+// the 16 bitMarked bits for the corresponding heap words,
+// then the 16 bitScan/bitBlockBoundary bits, then the 16 bitAllocated bits.
+// This layout makes it easier to iterate over the bits of a given type.
+//
+// The bitmap starts at mheap.arena_start and extends *backward* from
+// there.  On a 64-bit system the off'th word in the arena is tracked by
+// the off/16+1'th word before mheap.arena_start.  (On a 32-bit system,
+// the only difference is that the divisor is 8.)
+//
+// To pull out the bits corresponding to a given pointer p, we use:
+//
+//     off = p - (uintptr*)mheap.arena_start;  // word offset
+//     b = (uintptr*)mheap.arena_start - off/wordsPerBitmapWord - 1;
+//     shift = off % wordsPerBitmapWord
+//     bits = *b >> shift;
+//     /* then test bits & bitAllocated, bits & bitMarked, etc. */
+//
+#define bitAllocated           ((uintptr)1<<(bitShift*0))      /* block start; eligible for garbage collection */
+#define bitScan                        ((uintptr)1<<(bitShift*1))      /* when bitAllocated is set */
+#define bitMarked              ((uintptr)1<<(bitShift*2))      /* when bitAllocated is set */
+#define bitBlockBoundary       ((uintptr)1<<(bitShift*1))      /* when bitAllocated is NOT set - mark for FlagNoGC objects */
+
+#define bitMask (bitAllocated | bitScan | bitMarked)
index 7bd45d1a2473002654c7de853b507d6686c76fe3..1a06b8a1132502e118ddd74aa792feeb5783160e 100644 (file)
@@ -950,6 +950,8 @@ void        _rt0_go(void);
 void*  runtime·funcdata(Func*, int32);
 int32  runtime·setmaxthreads(int32);
 G*     runtime·timejump(void);
+void   runtime·iterate_itabs(void (*callback)(Itab*));
+void   runtime·iterate_finq(void (*callback)(FuncVal*, byte*, uintptr, Type*, PtrType*));
 
 #pragma        varargck        argpos  runtime·printf 1
 #pragma        varargck        type    "c"     int32