diff options
Diffstat (limited to 'src/pkg/runtime/mprof.goc')
-rw-r--r-- | src/pkg/runtime/mprof.goc | 146 |
1 files changed, 138 insertions, 8 deletions
diff --git a/src/pkg/runtime/mprof.goc b/src/pkg/runtime/mprof.goc index 517f96a31..0bbce8583 100644 --- a/src/pkg/runtime/mprof.goc +++ b/src/pkg/runtime/mprof.goc @@ -7,8 +7,9 @@ package runtime #include "runtime.h" +#include "arch_GOARCH.h" #include "malloc.h" -#include "defs.h" +#include "defs_GOOS_GOARCH.h" #include "type.h" // NOTE(rsc): Everything here could use cas if contention became an issue. @@ -25,6 +26,10 @@ struct Bucket uintptr frees; uintptr alloc_bytes; uintptr free_bytes; + uintptr recent_allocs; // since last gc + uintptr recent_frees; + uintptr recent_alloc_bytes; + uintptr recent_free_bytes; uintptr hash; uintptr nstk; uintptr stk[1]; @@ -38,7 +43,7 @@ static uintptr bucketmem; // Return the bucket for stk[0:nstk], allocating new bucket if needed. static Bucket* -stkbucket(uintptr *stk, int32 nstk) +stkbucket(uintptr *stk, int32 nstk, bool alloc) { int32 i; uintptr h; @@ -65,6 +70,9 @@ stkbucket(uintptr *stk, int32 nstk) runtime·mcmp((byte*)b->stk, (byte*)stk, nstk*sizeof stk[0]) == 0) return b; + if(!alloc) + return nil; + b = runtime·mallocgc(sizeof *b + nstk*sizeof stk[0], FlagNoProfiling, 0, 1); bucketmem += sizeof *b + nstk*sizeof stk[0]; runtime·memmove(b->stk, stk, nstk*sizeof stk[0]); @@ -77,6 +85,26 @@ stkbucket(uintptr *stk, int32 nstk) return b; } +// Record that a gc just happened: all the 'recent' statistics are now real. +void +runtime·MProf_GC(void) +{ + Bucket *b; + + runtime·lock(&proflock); + for(b=buckets; b; b=b->allnext) { + b->allocs += b->recent_allocs; + b->frees += b->recent_frees; + b->alloc_bytes += b->recent_alloc_bytes; + b->free_bytes += b->recent_free_bytes; + b->recent_allocs = 0; + b->recent_frees = 0; + b->recent_alloc_bytes = 0; + b->recent_free_bytes = 0; + } + runtime·unlock(&proflock); +} + // Map from pointer to Bucket* that allocated it. // Three levels: // Linked-list hash table for top N-20 bits. @@ -197,9 +225,9 @@ runtime·MProf_Malloc(void *p, uintptr size) m->nomemprof++; nstk = runtime·callers(1, stk, 32); runtime·lock(&proflock); - b = stkbucket(stk, nstk); - b->allocs++; - b->alloc_bytes += size; + b = stkbucket(stk, nstk, true); + b->recent_allocs++; + b->recent_alloc_bytes += size; setaddrbucket((uintptr)p, b); runtime·unlock(&proflock); m->nomemprof--; @@ -218,8 +246,8 @@ runtime·MProf_Free(void *p, uintptr size) runtime·lock(&proflock); b = getaddrbucket((uintptr)p); if(b != nil) { - b->frees++; - b->free_bytes += size; + b->recent_frees++; + b->recent_free_bytes += size; } runtime·unlock(&proflock); m->nomemprof--; @@ -229,7 +257,7 @@ runtime·MProf_Free(void *p, uintptr size) // Go interface to profile data. (Declared in extern.go) // Assumes Go sizeof(int) == sizeof(int32) -// Must match MemProfileRecord in extern.go. +// Must match MemProfileRecord in debug.go. typedef struct Record Record; struct Record { int64 alloc_bytes, free_bytes; @@ -272,3 +300,105 @@ func MemProfile(p Slice, include_inuse_zero bool) (n int32, ok bool) { } runtime·unlock(&proflock); } + +// Must match StackRecord in debug.go. +typedef struct TRecord TRecord; +struct TRecord { + uintptr stk[32]; +}; + +func ThreadCreateProfile(p Slice) (n int32, ok bool) { + TRecord *r; + M *first, *m; + + first = runtime·atomicloadp(&runtime·allm); + n = 0; + for(m=first; m; m=m->alllink) + n++; + ok = false; + if(n <= p.len) { + ok = true; + r = (TRecord*)p.array; + for(m=first; m; m=m->alllink) { + runtime·memmove(r->stk, m->createstack, sizeof r->stk); + r++; + } + } +} + +func Stack(b Slice, all bool) (n int32) { + byte *pc, *sp; + + sp = runtime·getcallersp(&b); + pc = runtime·getcallerpc(&b); + + if(all) { + runtime·semacquire(&runtime·worldsema); + m->gcing = 1; + runtime·stoptheworld(); + } + + if(b.len == 0) + n = 0; + else{ + g->writebuf = (byte*)b.array; + g->writenbuf = b.len; + runtime·goroutineheader(g); + runtime·traceback(pc, sp, 0, g); + if(all) + runtime·tracebackothers(g); + n = b.len - g->writenbuf; + g->writebuf = nil; + g->writenbuf = 0; + } + + if(all) { + m->gcing = 0; + runtime·semrelease(&runtime·worldsema); + runtime·starttheworld(false); + } +} + +static void +saveg(byte *pc, byte *sp, G *g, TRecord *r) +{ + int32 n; + + n = runtime·gentraceback(pc, sp, 0, g, 0, r->stk, nelem(r->stk)); + if(n < nelem(r->stk)) + r->stk[n] = 0; +} + +func GoroutineProfile(b Slice) (n int32, ok bool) { + byte *pc, *sp; + TRecord *r; + G *gp; + + sp = runtime·getcallersp(&b); + pc = runtime·getcallerpc(&b); + + ok = false; + n = runtime·gcount(); + if(n <= b.len) { + runtime·semacquire(&runtime·worldsema); + m->gcing = 1; + runtime·stoptheworld(); + + n = runtime·gcount(); + if(n <= b.len) { + ok = true; + r = (TRecord*)b.array; + saveg(pc, sp, g, r++); + for(gp = runtime·allg; gp != nil; gp = gp->alllink) { + if(gp == g || gp->status == Gdead) + continue; + saveg(gp->sched.pc, gp->sched.sp, gp, r++); + } + } + + m->gcing = 0; + runtime·semrelease(&runtime·worldsema); + runtime·starttheworld(false); + } +} + |