|  | // 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.h" | 
|  | #include "malloc.h" | 
|  | #include "mgc0.h" | 
|  | #include "go-type.h" | 
|  |  | 
|  | #define hash __hash | 
|  | #define KindNoPointers GO_NO_POINTERS | 
|  |  | 
|  | 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, | 
|  | TagMemProf = 16, | 
|  | TagAllocSample = 17, | 
|  |  | 
|  | 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, const Type *type, uintptr kind); | 
|  |  | 
|  | // 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 | 
|  | hwrite(const 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 { | 
|  | const 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; | 
|  | hwrite(buf, n); | 
|  | } | 
|  |  | 
|  | static void | 
|  | dumpbool(bool b) | 
|  | { | 
|  | dumpint(b ? 1 : 0); | 
|  | } | 
|  |  | 
|  | // dump varint uint64 length followed by memory contents | 
|  | static void | 
|  | dumpmemrange(const byte *data, uintptr len) | 
|  | { | 
|  | dumpint(len); | 
|  | hwrite(data, len); | 
|  | } | 
|  |  | 
|  | static void | 
|  | dumpstr(String s) | 
|  | { | 
|  | dumpmemrange(s.str, s.len); | 
|  | } | 
|  |  | 
|  | static void | 
|  | dumpcstr(const int8 *c) | 
|  | { | 
|  | dumpmemrange((const byte*)c, runtime_findnull((const byte*)c)); | 
|  | } | 
|  |  | 
|  | // dump information for a type | 
|  | static void | 
|  | dumptype(const 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); | 
|  | if(t->__uncommon == nil || t->__uncommon->__pkg_path == nil || t->__uncommon->__name == nil) { | 
|  | dumpstr(*t->__reflection); | 
|  | } else { | 
|  | dumpint(t->__uncommon->__pkg_path->len + 1 + t->__uncommon->__name->len); | 
|  | hwrite(t->__uncommon->__pkg_path->str, t->__uncommon->__pkg_path->len); | 
|  | hwrite((const byte*)".", 1); | 
|  | hwrite(t->__uncommon->__name->str, t->__uncommon->__name->len); | 
|  | } | 
|  | dumpbool(t->__size > PtrSize || (t->__code & 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, const 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(const char *description, byte *to) | 
|  | { | 
|  | dumpint(TagOtherRoot); | 
|  | dumpcstr((const int8 *)description); | 
|  | dumpint((uintptr)to); | 
|  | } | 
|  |  | 
|  | static void | 
|  | dumpfinalizer(byte *obj, FuncVal *fn, const FuncType* ft, const PtrType *ot) | 
|  | { | 
|  | dumpint(TagFinalizer); | 
|  | dumpint((uintptr)obj); | 
|  | dumpint((uintptr)fn); | 
|  | dumpint((uintptr)fn->fn); | 
|  | dumpint((uintptr)ft); | 
|  | 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 args.n >= 0, pointer map of args region | 
|  |  | 
|  | byte *sp;           // callee sp | 
|  | uintptr depth;      // depth in call stack (0 == most recent) | 
|  | }; | 
|  |  | 
|  | static void | 
|  | dumpgoroutine(G *gp) | 
|  | { | 
|  | // ChildInfo child; | 
|  | Defer *d; | 
|  | Panic *p; | 
|  |  | 
|  | dumpint(TagGoRoutine); | 
|  | dumpint((uintptr)gp); | 
|  | dumpint((uintptr)0); | 
|  | dumpint(gp->goid); | 
|  | dumpint(gp->gopc); | 
|  | dumpint(gp->atomicstatus); | 
|  | dumpbool(gp->issystem); | 
|  | dumpbool(gp->isbackground); | 
|  | dumpint(gp->waitsince); | 
|  | dumpstr(gp->waitreason); | 
|  | dumpint((uintptr)0); | 
|  | dumpint((uintptr)gp->m); | 
|  | dumpint((uintptr)gp->_defer); | 
|  | dumpint((uintptr)gp->_panic); | 
|  |  | 
|  | // dump stack | 
|  | // child.args.n = -1; | 
|  | // 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->arg); | 
|  | dumpint((uintptr)d->frame); | 
|  | dumpint((uintptr)d->pfn); | 
|  | dumpint((uintptr)0); | 
|  | 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)0); | 
|  | dumpint((uintptr)p->link); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | dumpgs(void) | 
|  | { | 
|  | G *gp; | 
|  | uint32 i; | 
|  |  | 
|  | // goroutines & stacks | 
|  | for(i = 0; i < runtime_getallglen(); i++) { | 
|  | gp = runtime_getallg(i); | 
|  | switch(gp->atomicstatus){ | 
|  | default: | 
|  | runtime_printf("unexpected G.status %d\n", gp->atomicstatus); | 
|  | runtime_throw("mark - bad status"); | 
|  | case _Gdead: | 
|  | break; | 
|  | case _Grunnable: | 
|  | case _Gsyscall: | 
|  | case _Gwaiting: | 
|  | dumpgoroutine(gp); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | finq_callback(FuncVal *fn, void *obj, const FuncType *ft, const PtrType *ot) | 
|  | { | 
|  | dumpint(TagQueuedFinalizer); | 
|  | dumpint((uintptr)obj); | 
|  | dumpint((uintptr)fn); | 
|  | dumpint((uintptr)fn->fn); | 
|  | dumpint((uintptr)ft); | 
|  | dumpint((uintptr)ot); | 
|  | } | 
|  |  | 
|  |  | 
|  | 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->ft, 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 hfree[PageSize/8]; | 
|  |  | 
|  | static void | 
|  | dumpobjs(void) | 
|  | { | 
|  | uintptr i, j, size, n, off, shift, *bitp, bits, ti, kind; | 
|  | MSpan *s; | 
|  | MLink *l; | 
|  | byte *p; | 
|  | const Type *t; | 
|  |  | 
|  | for(i = 0; i < runtime_mheap.nspan; i++) { | 
|  | s = runtime_mheap.allspans[i]; | 
|  | if(s->state != MSpanInUse) | 
|  | continue; | 
|  | 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) { | 
|  | hfree[((byte*)l - p) / size] = true; | 
|  | } | 
|  | for(j = 0; j < n; j++, p += size) { | 
|  | if(hfree[j]) { | 
|  | hfree[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 = ((const ChanType*)t)->__element_type; // 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(hchanSize); | 
|  | dumpint((uintptr)runtime_mheap.arena_start); | 
|  | dumpint((uintptr)runtime_mheap.arena_used); | 
|  | dumpint(0); | 
|  | dumpcstr((const int8 *)""); | 
|  | dumpint(runtime_ncpu); | 
|  | } | 
|  |  | 
|  | static void | 
|  | dumpms(void) | 
|  | { | 
|  | M *mp; | 
|  |  | 
|  | for(mp = runtime_getallm(); mp != nil; mp = mp->alllink) { | 
|  | dumpint(TagOSThread); | 
|  | dumpint((uintptr)mp); | 
|  | dumpint(mp->id); | 
|  | dumpint(0); | 
|  | } | 
|  | } | 
|  |  | 
|  | 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 | 
|  | dumpmemprof_callback(Bucket *b, uintptr nstk, Location *stk, uintptr size, uintptr allocs, uintptr frees) | 
|  | { | 
|  | uintptr i, pc; | 
|  | byte buf[20]; | 
|  |  | 
|  | dumpint(TagMemProf); | 
|  | dumpint((uintptr)b); | 
|  | dumpint(size); | 
|  | dumpint(nstk); | 
|  | for(i = 0; i < nstk; i++) { | 
|  | pc = stk[i].pc; | 
|  | if(stk[i].function.len == 0) { | 
|  | runtime_snprintf(buf, sizeof(buf), "%X", (uint64)pc); | 
|  | dumpcstr((int8*)buf); | 
|  | dumpcstr((const int8*)"?"); | 
|  | dumpint(0); | 
|  | } else { | 
|  | dumpstr(stk[i].function); | 
|  | dumpstr(stk[i].filename); | 
|  | dumpint(stk[i].lineno); | 
|  | } | 
|  | } | 
|  | dumpint(allocs); | 
|  | dumpint(frees); | 
|  | } | 
|  |  | 
|  | static FuncVal dumpmemprof_callbackv = {(void(*)(void))dumpmemprof_callback}; | 
|  |  | 
|  | static void | 
|  | dumpmemprof(void) | 
|  | { | 
|  | MSpan *s, **allspans; | 
|  | uint32 spanidx; | 
|  | Special *sp; | 
|  | SpecialProfile *spp; | 
|  | byte *p; | 
|  |  | 
|  | runtime_iterate_memprof(&dumpmemprof_callbackv); | 
|  |  | 
|  | allspans = runtime_mheap.allspans; | 
|  | for(spanidx=0; spanidx<runtime_mheap.nspan; spanidx++) { | 
|  | s = allspans[spanidx]; | 
|  | if(s->state != MSpanInUse) | 
|  | continue; | 
|  | for(sp = s->specials; sp != nil; sp = sp->next) { | 
|  | if(sp->kind != KindSpecialProfile) | 
|  | continue; | 
|  | spp = (SpecialProfile*)sp; | 
|  | p = (byte*)((s->start << PageShift) + spp->offset); | 
|  | dumpint(TagAllocSample); | 
|  | dumpint((uintptr)p); | 
|  | dumpint((uintptr)spp->b); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | mdump(G *gp) | 
|  | { | 
|  | const byte *hdr; | 
|  | uintptr i; | 
|  | MSpan *s; | 
|  |  | 
|  | // make sure we're done sweeping | 
|  | for(i = 0; i < runtime_mheap.nspan; i++) { | 
|  | s = runtime_mheap.allspans[i]; | 
|  | if(s->state == MSpanInUse) | 
|  | runtime_MSpan_EnsureSwept(s); | 
|  | } | 
|  |  | 
|  | runtime_memclr((byte*)&typecache[0], sizeof(typecache)); | 
|  | hdr = (const byte*)"go1.3 heap dump\n"; | 
|  | hwrite(hdr, runtime_findnull(hdr)); | 
|  | dumpparams(); | 
|  | dumpobjs(); | 
|  | dumpgs(); | 
|  | dumpms(); | 
|  | dumproots(); | 
|  | dumpmemstats(); | 
|  | dumpmemprof(); | 
|  | dumpint(TagEOF); | 
|  | flush(); | 
|  |  | 
|  | gp->param = nil; | 
|  | gp->atomicstatus = _Grunning; | 
|  | runtime_gogo(gp); | 
|  | } | 
|  |  | 
|  | void runtime_debug_WriteHeapDump(uintptr) | 
|  | __asm__(GOSYM_PREFIX "runtime_debug.WriteHeapDump"); | 
|  |  | 
|  | void | 
|  | runtime_debug_WriteHeapDump(uintptr fd) | 
|  | { | 
|  | M *m; | 
|  | G *g; | 
|  |  | 
|  | // Stop the world. | 
|  | runtime_acquireWorldsema(); | 
|  | m = runtime_m(); | 
|  | m->preemptoff = runtime_gostringnocopy((const byte*)"write heap dump"); | 
|  | runtime_stopTheWorldWithSema(); | 
|  |  | 
|  | // 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 = runtime_g(); | 
|  | g->atomicstatus = _Gwaiting; | 
|  | g->waitreason = runtime_gostringnocopy((const byte*)"dumping heap"); | 
|  | runtime_mcall(mdump); | 
|  |  | 
|  | // Reset dump file. | 
|  | dumpfd = 0; | 
|  |  | 
|  | // Start up the world again. | 
|  | runtime_startTheWorldWithSema(); | 
|  | runtime_releaseWorldsema(); | 
|  | m->preemptoff = runtime_gostringnocopy(nil); | 
|  | } | 
|  |  | 
|  | // 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", (uint64)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_descriptor); | 
|  | } | 
|  | */ | 
|  |  | 
|  | // 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 __attribute__ ((unused)), uintptr size, const 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 - type->__size; i += type->__size) { | 
|  | //playgcprog(i, (uintptr*)type->gc + 1, dumpeface_callback, obj); | 
|  | } | 
|  | break; | 
|  | case TypeInfo_Chan: | 
|  | if(type->__size == 0) // channels may have zero-sized objects in them | 
|  | break; | 
|  | for(i = hchanSize; i <= size - type->__size; i += type->__size) { | 
|  | //playgcprog(i, (uintptr*)type->gc + 1, dumpeface_callback, obj); | 
|  | } | 
|  | break; | 
|  | } | 
|  | } |