diff options
author | Tianon Gravi <admwiggin@gmail.com> | 2015-01-15 11:54:00 -0700 |
---|---|---|
committer | Tianon Gravi <admwiggin@gmail.com> | 2015-01-15 11:54:00 -0700 |
commit | f154da9e12608589e8d5f0508f908a0c3e88a1bb (patch) | |
tree | f8255d51e10c6f1e0ed69702200b966c9556a431 /src/runtime/heapdump.c | |
parent | 8d8329ed5dfb9622c82a9fbec6fd99a580f9c9f6 (diff) | |
download | golang-upstream/1.4.tar.gz |
Imported Upstream version 1.4upstream/1.4
Diffstat (limited to 'src/runtime/heapdump.c')
-rw-r--r-- | src/runtime/heapdump.c | 864 |
1 files changed, 864 insertions, 0 deletions
diff --git a/src/runtime/heapdump.c b/src/runtime/heapdump.c new file mode 100644 index 000000000..7eba8c005 --- /dev/null +++ b/src/runtime/heapdump.c @@ -0,0 +1,864 @@ +// 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://golang.org/s/go14heapdump. + +#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" +#include "textflag.h" + +extern byte runtime·data[]; +extern byte runtime·edata[]; +extern byte runtime·bss[]; +extern byte runtime·ebss[]; + +enum { + FieldKindEol = 0, + FieldKindPtr = 1, + FieldKindIface = 2, + FieldKindEface = 3, + + 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, +}; + +static uintptr* playgcprog(uintptr offset, uintptr *prog, void (*callback)(void*,uintptr,uintptr), void *arg); +static void dumpfields(BitVector bv); +static void dumpbvtypes(BitVector *bv, byte *base); +static BitVector makeheapobjbv(byte *p, uintptr size); + +// fd to write the dump to. +static uintptr dumpfd; + +#pragma dataflag NOPTR /* tmpbuf not a heap pointer at least */ +static byte *tmpbuf; +static uintptr tmpbufsize; + +// buffer of pending write data +enum { + BufSize = 4096, +}; +#pragma dataflag NOPTR +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]; +}; +#pragma dataflag NOPTR /* only initialized and used while world is stopped */ +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); + if(t->x == nil || t->x->pkgPath == nil || t->x->name == nil) { + dumpstr(*t->string); + } else { + dumpint(t->x->pkgPath->len + 1 + t->x->name->len); + write(t->x->pkgPath->str, t->x->pkgPath->len); + write((byte*)".", 1); + write(t->x->name->str, t->x->name->len); + } + dumpbool((t->kind & KindDirectIface) == 0 || (t->kind & KindNoPointers) == 0); +} + +// dump an object +static void +dumpobj(byte *obj, uintptr size, BitVector bv) +{ + dumpbvtypes(&bv, obj); + dumpint(TagObject); + dumpint((uintptr)obj); + dumpmemrange(obj, size); + dumpfields(bv); +} + +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 args.n >= 0, 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->bytedata[i/8] >> i%8 & 3) { + case BitsDead: + // BitsDead has already been processed in makeheapobjbv. + // We should only see it in stack maps, in which case we should continue processing. + break; + case BitsScalar: + break; + case BitsPointer: + dumpint(FieldKindPtr); + dumpint(offset + i / BitsPerPointer * PtrSize); + break; + case BitsMultiWord: + switch(bv->bytedata[(i+BitsPerPointer)/8] >> (i+BitsPerPointer)%8 & 3) { + default: + runtime·throw("unexpected garbage collection bits"); + 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.n >= 0) + dumpbvtypes(&child->args, (byte*)s->sp + child->argoff); + if(stackmap != nil && stackmap->n > 0) { + bv = runtime·stackmapdata(stackmap, pcdata); + dumpbvtypes(&bv, (byte*)(s->varp - bv.n / BitsPerPointer * PtrSize)); + } else { + bv.n = -1; + } + + // 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); + dumpint(s->continpc); + name = runtime·funcname(f); + if(name == nil) + name = "unknown function"; + dumpcstr(name); + + // Dump fields in the outargs section + if(child->args.n >= 0) { + 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 - 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 - s->sp; off < s->varp - 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 - s->sp); + } + dumpint(FieldKindEol); + + // Record arg info for parent. + child->argoff = s->argp - s->fp; + 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.n = -1; + return true; +} + +static void +dumpgoroutine(G *gp) +{ + uintptr sp, pc, lr; + ChildInfo child; + Defer *d; + Panic *p; + bool (*fn)(Stkframe*, void*); + + if(gp->syscallsp != (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(runtime·readgstatus(gp)); + dumpbool(gp->issystem); + dumpbool(false); // isbackground + dumpint(gp->waitsince); + dumpstr(gp->waitreason); + dumpint((uintptr)gp->sched.ctxt); + 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; + fn = dumpframe; + runtime·gentraceback(pc, sp, lr, gp, 0, nil, 0x7fffffff, &fn, &child, 0); + + // 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(0); // was p->defer, no longer recorded + dumpint((uintptr)p->link); + } +} + +static void +dumpgs(void) +{ + G *gp; + uint32 i; + uint32 status; + + // goroutines & stacks + for(i = 0; i < runtime·allglen; i++) { + gp = runtime·allg[i]; + status = runtime·readgstatus(gp); // The world is stopped so gp will not be in a scan state. + switch(status){ + default: + runtime·printf("runtime: unexpected G.status %d\n", status); + runtime·throw("dumpgs in STW - 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 + dumpbvtypes(&runtime·gcdatamask, runtime·data); + dumpint(TagData); + dumpint((uintptr)runtime·data); + dumpmemrange(runtime·data, runtime·edata - runtime·data); + dumpfields(runtime·gcdatamask); + + // bss segment + dumpbvtypes(&runtime·gcbssmask, runtime·bss); + dumpint(TagBss); + dumpint((uintptr)runtime·bss); + dumpmemrange(runtime·bss, runtime·ebss - runtime·bss); + dumpfields(runtime·gcbssmask); + + // MSpan.types + allspans = runtime·mheap.allspans; + for(spanidx=0; spanidx<runtime·mheap.nspan; spanidx++) { + s = allspans[spanidx]; + if(s->state == MSpanInUse) { + // Finalizers + for(sp = s->specials; sp != nil; sp = sp->next) { + if(sp->kind != KindSpecialFinalizer) + continue; + spf = (SpecialFinalizer*)sp; + p = (byte*)((s->start << PageShift) + spf->special.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. +#pragma dataflag NOPTR +static byte free[PageSize/8]; + +static void +dumpobjs(void) +{ + uintptr i, j, size, n; + MSpan *s; + MLink *l; + byte *p; + + 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 > nelem(free)) + 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; + } + dumpobj(p, size, makeheapobjbv(p, size)); + } + } +} + +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((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; + + t = tab->type; + // Dump a map from itab* to the type of its data field. + // We want this map so we can deduce types of interface referents. + if((t->kind & KindDirectIface) == 0) { + // indirect - data slot is a pointer to t. + dumptype(t->ptrto); + dumpint(TagItab); + dumpint((uintptr)tab); + dumpint((uintptr)t->ptrto); + } else if((t->kind & KindNoPointers) == 0) { + // t is pointer-like - data slot is a t. + dumptype(t); + dumpint(TagItab); + dumpint((uintptr)tab); + dumpint((uintptr)t); + } else { + // Data slot is a scalar. Dump type just for fun. + // With pointer-only interfaces, this shouldn't happen. + dumptype(t); + dumpint(TagItab); + dumpint((uintptr)tab); + dumpint((uintptr)t); + } +} + +static void +dumpitabs(void) +{ + void (*fn)(Itab*); + + fn = itab_callback; + runtime·iterate_itabs(&fn); +} + +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 +dumpmemprof_callback(Bucket *b, uintptr nstk, uintptr *stk, uintptr size, uintptr allocs, uintptr frees) +{ + uintptr i, pc; + Func *f; + byte buf[20]; + String file; + int32 line; + + dumpint(TagMemProf); + dumpint((uintptr)b); + dumpint(size); + dumpint(nstk); + for(i = 0; i < nstk; i++) { + pc = stk[i]; + f = runtime·findfunc(pc); + if(f == nil) { + runtime·snprintf(buf, sizeof(buf), "%X", (uint64)pc); + dumpcstr((int8*)buf); + dumpcstr("?"); + dumpint(0); + } else { + dumpcstr(runtime·funcname(f)); + // TODO: Why do we need to back up to a call instruction here? + // Maybe profiler should do this. + if(i > 0 && pc > f->entry) { + if(thechar == '6' || thechar == '8') + pc--; + else + pc -= 4; // arm, etc + } + line = runtime·funcline(f, pc, &file); + dumpstr(file); + dumpint(line); + } + } + dumpint(allocs); + dumpint(frees); +} + +static void +dumpmemprof(void) +{ + MSpan *s, **allspans; + uint32 spanidx; + Special *sp; + SpecialProfile *spp; + byte *p; + void (*fn)(Bucket*, uintptr, uintptr*, uintptr, uintptr, uintptr); + + fn = dumpmemprof_callback; + runtime·iterate_memprof(&fn); + + 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->special.offset); + dumpint(TagAllocSample); + dumpint((uintptr)p); + dumpint((uintptr)spp->b); + } + } +} + +static void +mdump(void) +{ + 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 = (byte*)"go1.4 heap dump\n"; + write(hdr, runtime·findnull(hdr)); + dumpparams(); + dumpitabs(); + dumpobjs(); + dumpgs(); + dumpms(); + dumproots(); + dumpmemstats(); + dumpmemprof(); + dumpint(TagEOF); + flush(); +} + +void +runtime·writeheapdump_m(void) +{ + uintptr fd; + + fd = g->m->scalararg[0]; + g->m->scalararg[0] = 0; + + runtime·casgstatus(g->m->curg, Grunning, Gwaiting); + g->waitreason = runtime·gostringnocopy((byte*)"dumping heap"); + + // 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. + mdump(); + + // Reset dump file. + dumpfd = 0; + if(tmpbuf != nil) { + runtime·SysFree(tmpbuf, tmpbufsize, &mstats.other_sys); + tmpbuf = nil; + tmpbufsize = 0; + } + + runtime·casgstatus(g->m->curg, Gwaiting, Grunning); +} + +// dumpint() the kind & offset of each field in an object. +static void +dumpfields(BitVector bv) +{ + dumpbv(&bv, 0); + dumpint(FieldKindEol); +} + +// 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 routine accomplishes that. + +// 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->bytedata[i/8] >> i%8 & 3) != BitsMultiWord) + continue; + switch(bv->bytedata[(i+BitsPerPointer)/8] >> (i+BitsPerPointer)%8 & 3) { + default: + runtime·throw("unexpected garbage collection bits"); + case BitsIface: + i += BitsPerPointer; + break; + case BitsEface: + dumptype(*(Type**)(base + i / BitsPerPointer * PtrSize)); + i += BitsPerPointer; + break; + } + } +} + +static BitVector +makeheapobjbv(byte *p, uintptr size) +{ + uintptr off, nptr, i; + byte shift, *bitp, bits; + bool mw; + + // Extend the temp buffer if necessary. + nptr = size/PtrSize; + if(tmpbufsize < nptr*BitsPerPointer/8+1) { + if(tmpbuf != nil) + runtime·SysFree(tmpbuf, tmpbufsize, &mstats.other_sys); + tmpbufsize = nptr*BitsPerPointer/8+1; + tmpbuf = runtime·sysAlloc(tmpbufsize, &mstats.other_sys); + if(tmpbuf == nil) + runtime·throw("heapdump: out of memory"); + } + + // Copy and compact the bitmap. + mw = false; + for(i = 0; i < nptr; i++) { + off = (uintptr*)(p + i*PtrSize) - (uintptr*)runtime·mheap.arena_start; + bitp = runtime·mheap.arena_start - off/wordsPerBitmapByte - 1; + shift = (off % wordsPerBitmapByte) * gcBits; + bits = (*bitp >> (shift + 2)) & BitsMask; + if(!mw && bits == BitsDead) + break; // end of heap object + mw = !mw && bits == BitsMultiWord; + tmpbuf[i*BitsPerPointer/8] &= ~(BitsMask<<((i*BitsPerPointer)%8)); + tmpbuf[i*BitsPerPointer/8] |= bits<<((i*BitsPerPointer)%8); + } + return (BitVector){i*BitsPerPointer, tmpbuf}; +} |