diff --git a/src/pkg/runtime/heapdump.c b/src/pkg/runtime/heapdump.c index 9132e2c186..42d1601aa1 100644 --- a/src/pkg/runtime/heapdump.c +++ b/src/pkg/runtime/heapdump.c @@ -49,6 +49,8 @@ enum { TagBss = 13, TagDefer = 14, TagPanic = 15, + TagMemProf = 16, + TagAllocSample = 17, TypeInfo_Conservative = 127, }; @@ -689,6 +691,74 @@ dumpmemstats(void) 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; + + runtime·iterate_memprof(dumpmemprof_callback); + + allspans = runtime·mheap.allspans; + for(spanidx=0; spanidxstate != 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) { @@ -713,6 +783,7 @@ mdump(G *gp) dumpms(); dumproots(); dumpmemstats(); + dumpmemprof(); dumpint(TagEOF); flush(); diff --git a/src/pkg/runtime/malloc.h b/src/pkg/runtime/malloc.h index dbea7ad135..798c130ad5 100644 --- a/src/pkg/runtime/malloc.h +++ b/src/pkg/runtime/malloc.h @@ -570,6 +570,7 @@ enum void runtime·MProf_Malloc(void*, uintptr); void runtime·MProf_Free(Bucket*, uintptr, bool); void runtime·MProf_GC(void); +void runtime·iterate_memprof(void (*callback)(Bucket*, uintptr, uintptr*, uintptr, uintptr, uintptr)); int32 runtime·gcprocs(void); void runtime·helpgc(int32 nproc); void runtime·gchelper(void); diff --git a/src/pkg/runtime/mprof.goc b/src/pkg/runtime/mprof.goc index a1659a7b42..9c23a16f88 100644 --- a/src/pkg/runtime/mprof.goc +++ b/src/pkg/runtime/mprof.goc @@ -309,6 +309,18 @@ func MemProfile(p Slice, include_inuse_zero bool) (n int, ok bool) { runtime·unlock(&proflock); } +void +runtime·iterate_memprof(void (*callback)(Bucket*, uintptr, uintptr*, uintptr, uintptr, uintptr)) +{ + Bucket *b; + + runtime·lock(&proflock); + for(b=mbuckets; b; b=b->allnext) { + callback(b, b->nstk, b->stk, b->size, b->allocs, b->frees); + } + runtime·unlock(&proflock); +} + // Must match BlockProfileRecord in debug.go. typedef struct BRecord BRecord; struct BRecord {