From fff63c2448109cb5a889f99dc2ab330f31d53213 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Tue, 25 Mar 2014 15:09:49 -0700 Subject: [PATCH] runtime: WriteHeapDump dumps the heap to a file. 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 | 5 + src/pkg/runtime/heapdump.c | 890 +++++++++++++++++++++++++++++++ src/pkg/runtime/iface.goc | 14 + src/pkg/runtime/malloc.h | 1 + src/pkg/runtime/mgc0.c | 62 +-- src/pkg/runtime/mgc0.h | 41 ++ src/pkg/runtime/runtime.h | 2 + 7 files changed, 973 insertions(+), 42 deletions(-) create mode 100644 src/pkg/runtime/heapdump.c diff --git a/src/pkg/runtime/debug/garbage.go b/src/pkg/runtime/debug/garbage.go index 093e7e7b18..edb3643871 100644 --- a/src/pkg/runtime/debug/garbage.go +++ b/src/pkg/runtime/debug/garbage.go @@ -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 index 0000000000..db0178eb65 --- /dev/null +++ b/src/pkg/runtime/heapdump.c @@ -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; spanidxstate == 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; + } + } +} diff --git a/src/pkg/runtime/iface.goc b/src/pkg/runtime/iface.goc index c678ce0a2f..08da8c7337 100644 --- a/src/pkg/runtime/iface.goc +++ b/src/pkg/runtime/iface.goc @@ -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) { diff --git a/src/pkg/runtime/malloc.h b/src/pkg/runtime/malloc.h index 97b5a924fe..f4c1bef770 100644 --- a/src/pkg/runtime/malloc.h +++ b/src/pkg/runtime/malloc.h @@ -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. // diff --git a/src/pkg/runtime/mgc0.c b/src/pkg/runtime/mgc0.c index 04dd93608a..c16bac1a0d 100644 --- a/src/pkg/runtime/mgc0.c +++ b/src/pkg/runtime/mgc0.c @@ -64,14 +64,8 @@ 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); diff --git a/src/pkg/runtime/mgc0.h b/src/pkg/runtime/mgc0.h index f8abe6c9c1..16000d1ae2 100644 --- a/src/pkg/runtime/mgc0.h +++ b/src/pkg/runtime/mgc0.h @@ -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) diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h index 7bd45d1a24..1a06b8a113 100644 --- a/src/pkg/runtime/runtime.h +++ b/src/pkg/runtime/runtime.h @@ -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 -- 2.48.1