diff options
Diffstat (limited to 'libgo/go/runtime/mprof.go')
-rw-r--r-- | libgo/go/runtime/mprof.go | 668 |
1 files changed, 0 insertions, 668 deletions
diff --git a/libgo/go/runtime/mprof.go b/libgo/go/runtime/mprof.go deleted file mode 100644 index f4da45f5c30..00000000000 --- a/libgo/go/runtime/mprof.go +++ /dev/null @@ -1,668 +0,0 @@ -// Copyright 2009 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. - -// Malloc profiling. -// Patterned after tcmalloc's algorithms; shorter code. - -package runtime - -import ( - "unsafe" -) - -// NOTE(rsc): Everything here could use cas if contention became an issue. -var proflock mutex - -// All memory allocations are local and do not escape outside of the profiler. -// The profiler is forbidden from referring to garbage-collected memory. - -const ( - // profile types - memProfile bucketType = 1 + iota - blockProfile - - // size of bucket hash table - buckHashSize = 179999 - - // max depth of stack to record in bucket - maxStack = 32 -) - -type bucketType int - -// A bucket holds per-call-stack profiling information. -// The representation is a bit sleazy, inherited from C. -// This struct defines the bucket header. It is followed in -// memory by the stack words and then the actual record -// data, either a memRecord or a blockRecord. -// -// Per-call-stack profiling information. -// Lookup by hashing call stack into a linked-list hash table. -type bucket struct { - next *bucket - allnext *bucket - typ bucketType // memBucket or blockBucket - hash uintptr - size uintptr - nstk uintptr -} - -// A memRecord is the bucket data for a bucket of type memProfile, -// part of the memory profile. -type memRecord struct { - // The following complex 3-stage scheme of stats accumulation - // is required to obtain a consistent picture of mallocs and frees - // for some point in time. - // The problem is that mallocs come in real time, while frees - // come only after a GC during concurrent sweeping. So if we would - // naively count them, we would get a skew toward mallocs. - // - // Mallocs are accounted in recent stats. - // Explicit frees are accounted in recent stats. - // GC frees are accounted in prev stats. - // After GC prev stats are added to final stats and - // recent stats are moved into prev stats. - allocs uintptr - frees uintptr - alloc_bytes uintptr - free_bytes uintptr - - // changes between next-to-last GC and last GC - prev_allocs uintptr - prev_frees uintptr - prev_alloc_bytes uintptr - prev_free_bytes uintptr - - // changes since last GC - recent_allocs uintptr - recent_frees uintptr - recent_alloc_bytes uintptr - recent_free_bytes uintptr -} - -// A blockRecord is the bucket data for a bucket of type blockProfile, -// part of the blocking profile. -type blockRecord struct { - count int64 - cycles int64 -} - -var ( - mbuckets *bucket // memory profile buckets - bbuckets *bucket // blocking profile buckets - buckhash *[179999]*bucket - bucketmem uintptr -) - -// newBucket allocates a bucket with the given type and number of stack entries. -func newBucket(typ bucketType, nstk int) *bucket { - size := unsafe.Sizeof(bucket{}) + uintptr(nstk)*unsafe.Sizeof(uintptr(0)) - switch typ { - default: - gothrow("invalid profile bucket type") - case memProfile: - size += unsafe.Sizeof(memRecord{}) - case blockProfile: - size += unsafe.Sizeof(blockRecord{}) - } - - b := (*bucket)(persistentalloc(size, 0, &memstats.buckhash_sys)) - bucketmem += size - b.typ = typ - b.nstk = uintptr(nstk) - return b -} - -// stk returns the slice in b holding the stack. -func (b *bucket) stk() []uintptr { - stk := (*[maxStack]uintptr)(add(unsafe.Pointer(b), unsafe.Sizeof(*b))) - return stk[:b.nstk:b.nstk] -} - -// mp returns the memRecord associated with the memProfile bucket b. -func (b *bucket) mp() *memRecord { - if b.typ != memProfile { - gothrow("bad use of bucket.mp") - } - data := add(unsafe.Pointer(b), unsafe.Sizeof(*b)+b.nstk*unsafe.Sizeof(uintptr(0))) - return (*memRecord)(data) -} - -// bp returns the blockRecord associated with the blockProfile bucket b. -func (b *bucket) bp() *blockRecord { - if b.typ != blockProfile { - gothrow("bad use of bucket.bp") - } - data := add(unsafe.Pointer(b), unsafe.Sizeof(*b)+b.nstk*unsafe.Sizeof(uintptr(0))) - return (*blockRecord)(data) -} - -// Return the bucket for stk[0:nstk], allocating new bucket if needed. -func stkbucket(typ bucketType, size uintptr, stk []uintptr, alloc bool) *bucket { - if buckhash == nil { - buckhash = (*[buckHashSize]*bucket)(sysAlloc(unsafe.Sizeof(*buckhash), &memstats.buckhash_sys)) - if buckhash == nil { - gothrow("runtime: cannot allocate memory") - } - } - - // Hash stack. - var h uintptr - for _, pc := range stk { - h += pc - h += h << 10 - h ^= h >> 6 - } - // hash in size - h += size - h += h << 10 - h ^= h >> 6 - // finalize - h += h << 3 - h ^= h >> 11 - - i := int(h % buckHashSize) - for b := buckhash[i]; b != nil; b = b.next { - if b.typ == typ && b.hash == h && b.size == size && eqslice(b.stk(), stk) { - return b - } - } - - if !alloc { - return nil - } - - // Create new bucket. - b := newBucket(typ, len(stk)) - copy(b.stk(), stk) - b.hash = h - b.size = size - b.next = buckhash[i] - buckhash[i] = b - if typ == memProfile { - b.allnext = mbuckets - mbuckets = b - } else { - b.allnext = bbuckets - bbuckets = b - } - return b -} - -func sysAlloc(n uintptr, stat *uint64) unsafe.Pointer - -func eqslice(x, y []uintptr) bool { - if len(x) != len(y) { - return false - } - for i, xi := range x { - if xi != y[i] { - return false - } - } - return true -} - -func mprof_GC() { - for b := mbuckets; b != nil; b = b.allnext { - mp := b.mp() - mp.allocs += mp.prev_allocs - mp.frees += mp.prev_frees - mp.alloc_bytes += mp.prev_alloc_bytes - mp.free_bytes += mp.prev_free_bytes - - mp.prev_allocs = mp.recent_allocs - mp.prev_frees = mp.recent_frees - mp.prev_alloc_bytes = mp.recent_alloc_bytes - mp.prev_free_bytes = mp.recent_free_bytes - - mp.recent_allocs = 0 - mp.recent_frees = 0 - mp.recent_alloc_bytes = 0 - mp.recent_free_bytes = 0 - } -} - -// Record that a gc just happened: all the 'recent' statistics are now real. -func mProf_GC() { - lock(&proflock) - mprof_GC() - unlock(&proflock) -} - -// Called by malloc to record a profiled block. -func mProf_Malloc(p unsafe.Pointer, size uintptr) { - var stk [maxStack]uintptr - nstk := callers(4, &stk[0], len(stk)) - lock(&proflock) - b := stkbucket(memProfile, size, stk[:nstk], true) - mp := b.mp() - mp.recent_allocs++ - mp.recent_alloc_bytes += size - unlock(&proflock) - - // Setprofilebucket locks a bunch of other mutexes, so we call it outside of proflock. - // This reduces potential contention and chances of deadlocks. - // Since the object must be alive during call to mProf_Malloc, - // it's fine to do this non-atomically. - setprofilebucket(p, b) -} - -func setprofilebucket_m() // mheap.c - -func setprofilebucket(p unsafe.Pointer, b *bucket) { - g := getg() - g.m.ptrarg[0] = p - g.m.ptrarg[1] = unsafe.Pointer(b) - onM(setprofilebucket_m) -} - -// Called when freeing a profiled block. -func mProf_Free(b *bucket, size uintptr, freed bool) { - lock(&proflock) - mp := b.mp() - if freed { - mp.recent_frees++ - mp.recent_free_bytes += size - } else { - mp.prev_frees++ - mp.prev_free_bytes += size - } - unlock(&proflock) -} - -var blockprofilerate uint64 // in CPU ticks - -// SetBlockProfileRate controls the fraction of goroutine blocking events -// that are reported in the blocking profile. The profiler aims to sample -// an average of one blocking event per rate nanoseconds spent blocked. -// -// To include every blocking event in the profile, pass rate = 1. -// To turn off profiling entirely, pass rate <= 0. -func SetBlockProfileRate(rate int) { - var r int64 - if rate <= 0 { - r = 0 // disable profiling - } else if rate == 1 { - r = 1 // profile everything - } else { - // convert ns to cycles, use float64 to prevent overflow during multiplication - r = int64(float64(rate) * float64(tickspersecond()) / (1000 * 1000 * 1000)) - if r == 0 { - r = 1 - } - } - - atomicstore64(&blockprofilerate, uint64(r)) -} - -func blockevent(cycles int64, skip int) { - if cycles <= 0 { - cycles = 1 - } - rate := int64(atomicload64(&blockprofilerate)) - if rate <= 0 || (rate > cycles && int64(fastrand1())%rate > cycles) { - return - } - gp := getg() - var nstk int - var stk [maxStack]uintptr - if gp.m.curg == nil || gp.m.curg == gp { - nstk = callers(skip, &stk[0], len(stk)) - } else { - nstk = gcallers(gp.m.curg, skip, &stk[0], len(stk)) - } - lock(&proflock) - b := stkbucket(blockProfile, 0, stk[:nstk], true) - b.bp().count++ - b.bp().cycles += cycles - unlock(&proflock) -} - -// Go interface to profile data. - -// A StackRecord describes a single execution stack. -type StackRecord struct { - Stack0 [32]uintptr // stack trace for this record; ends at first 0 entry -} - -// Stack returns the stack trace associated with the record, -// a prefix of r.Stack0. -func (r *StackRecord) Stack() []uintptr { - for i, v := range r.Stack0 { - if v == 0 { - return r.Stack0[0:i] - } - } - return r.Stack0[0:] -} - -// MemProfileRate controls the fraction of memory allocations -// that are recorded and reported in the memory profile. -// The profiler aims to sample an average of -// one allocation per MemProfileRate bytes allocated. -// -// To include every allocated block in the profile, set MemProfileRate to 1. -// To turn off profiling entirely, set MemProfileRate to 0. -// -// The tools that process the memory profiles assume that the -// profile rate is constant across the lifetime of the program -// and equal to the current value. Programs that change the -// memory profiling rate should do so just once, as early as -// possible in the execution of the program (for example, -// at the beginning of main). -var MemProfileRate int = 512 * 1024 - -// A MemProfileRecord describes the live objects allocated -// by a particular call sequence (stack trace). -type MemProfileRecord struct { - AllocBytes, FreeBytes int64 // number of bytes allocated, freed - AllocObjects, FreeObjects int64 // number of objects allocated, freed - Stack0 [32]uintptr // stack trace for this record; ends at first 0 entry -} - -// InUseBytes returns the number of bytes in use (AllocBytes - FreeBytes). -func (r *MemProfileRecord) InUseBytes() int64 { return r.AllocBytes - r.FreeBytes } - -// InUseObjects returns the number of objects in use (AllocObjects - FreeObjects). -func (r *MemProfileRecord) InUseObjects() int64 { - return r.AllocObjects - r.FreeObjects -} - -// Stack returns the stack trace associated with the record, -// a prefix of r.Stack0. -func (r *MemProfileRecord) Stack() []uintptr { - for i, v := range r.Stack0 { - if v == 0 { - return r.Stack0[0:i] - } - } - return r.Stack0[0:] -} - -// MemProfile returns n, the number of records in the current memory profile. -// If len(p) >= n, MemProfile copies the profile into p and returns n, true. -// If len(p) < n, MemProfile does not change p and returns n, false. -// -// If inuseZero is true, the profile includes allocation records -// where r.AllocBytes > 0 but r.AllocBytes == r.FreeBytes. -// These are sites where memory was allocated, but it has all -// been released back to the runtime. -// -// Most clients should use the runtime/pprof package or -// the testing package's -test.memprofile flag instead -// of calling MemProfile directly. -func MemProfile(p []MemProfileRecord, inuseZero bool) (n int, ok bool) { - lock(&proflock) - clear := true - for b := mbuckets; b != nil; b = b.allnext { - mp := b.mp() - if inuseZero || mp.alloc_bytes != mp.free_bytes { - n++ - } - if mp.allocs != 0 || mp.frees != 0 { - clear = false - } - } - if clear { - // Absolutely no data, suggesting that a garbage collection - // has not yet happened. In order to allow profiling when - // garbage collection is disabled from the beginning of execution, - // accumulate stats as if a GC just happened, and recount buckets. - mprof_GC() - mprof_GC() - n = 0 - for b := mbuckets; b != nil; b = b.allnext { - mp := b.mp() - if inuseZero || mp.alloc_bytes != mp.free_bytes { - n++ - } - } - } - if n <= len(p) { - ok = true - idx := 0 - for b := mbuckets; b != nil; b = b.allnext { - mp := b.mp() - if inuseZero || mp.alloc_bytes != mp.free_bytes { - record(&p[idx], b) - idx++ - } - } - } - unlock(&proflock) - return -} - -// Write b's data to r. -func record(r *MemProfileRecord, b *bucket) { - mp := b.mp() - r.AllocBytes = int64(mp.alloc_bytes) - r.FreeBytes = int64(mp.free_bytes) - r.AllocObjects = int64(mp.allocs) - r.FreeObjects = int64(mp.frees) - copy(r.Stack0[:], b.stk()) - for i := int(b.nstk); i < len(r.Stack0); i++ { - r.Stack0[i] = 0 - } -} - -func iterate_memprof(fn func(*bucket, uintptr, *uintptr, uintptr, uintptr, uintptr)) { - lock(&proflock) - for b := mbuckets; b != nil; b = b.allnext { - mp := b.mp() - fn(b, uintptr(b.nstk), &b.stk()[0], b.size, mp.allocs, mp.frees) - } - unlock(&proflock) -} - -// BlockProfileRecord describes blocking events originated -// at a particular call sequence (stack trace). -type BlockProfileRecord struct { - Count int64 - Cycles int64 - StackRecord -} - -// BlockProfile returns n, the number of records in the current blocking profile. -// If len(p) >= n, BlockProfile copies the profile into p and returns n, true. -// If len(p) < n, BlockProfile does not change p and returns n, false. -// -// Most clients should use the runtime/pprof package or -// the testing package's -test.blockprofile flag instead -// of calling BlockProfile directly. -func BlockProfile(p []BlockProfileRecord) (n int, ok bool) { - lock(&proflock) - for b := bbuckets; b != nil; b = b.allnext { - n++ - } - if n <= len(p) { - ok = true - for b := bbuckets; b != nil; b = b.allnext { - bp := b.bp() - r := &p[0] - r.Count = int64(bp.count) - r.Cycles = int64(bp.cycles) - i := copy(r.Stack0[:], b.stk()) - for ; i < len(r.Stack0); i++ { - r.Stack0[i] = 0 - } - p = p[1:] - } - } - unlock(&proflock) - return -} - -// ThreadCreateProfile returns n, the number of records in the thread creation profile. -// If len(p) >= n, ThreadCreateProfile copies the profile into p and returns n, true. -// If len(p) < n, ThreadCreateProfile does not change p and returns n, false. -// -// Most clients should use the runtime/pprof package instead -// of calling ThreadCreateProfile directly. -func ThreadCreateProfile(p []StackRecord) (n int, ok bool) { - first := (*m)(atomicloadp(unsafe.Pointer(&allm))) - for mp := first; mp != nil; mp = mp.alllink { - n++ - } - if n <= len(p) { - ok = true - i := 0 - for mp := first; mp != nil; mp = mp.alllink { - for s := range mp.createstack { - p[i].Stack0[s] = uintptr(mp.createstack[s]) - } - i++ - } - } - return -} - -var allgs []*g // proc.c - -// GoroutineProfile returns n, the number of records in the active goroutine stack profile. -// If len(p) >= n, GoroutineProfile copies the profile into p and returns n, true. -// If len(p) < n, GoroutineProfile does not change p and returns n, false. -// -// Most clients should use the runtime/pprof package instead -// of calling GoroutineProfile directly. -func GoroutineProfile(p []StackRecord) (n int, ok bool) { - - n = NumGoroutine() - if n <= len(p) { - gp := getg() - semacquire(&worldsema, false) - gp.m.gcing = 1 - onM(stoptheworld) - - n = NumGoroutine() - if n <= len(p) { - ok = true - r := p - sp := getcallersp(unsafe.Pointer(&p)) - pc := getcallerpc(unsafe.Pointer(&p)) - onM(func() { - saveg(pc, sp, gp, &r[0]) - }) - r = r[1:] - for _, gp1 := range allgs { - if gp1 == gp || readgstatus(gp1) == _Gdead { - continue - } - saveg(^uintptr(0), ^uintptr(0), gp1, &r[0]) - r = r[1:] - } - } - - gp.m.gcing = 0 - semrelease(&worldsema) - onM(starttheworld) - } - - return n, ok -} - -func saveg(pc, sp uintptr, gp *g, r *StackRecord) { - n := gentraceback(pc, sp, 0, gp, 0, &r.Stack0[0], len(r.Stack0), nil, nil, 0) - if n < len(r.Stack0) { - r.Stack0[n] = 0 - } -} - -// Stack formats a stack trace of the calling goroutine into buf -// and returns the number of bytes written to buf. -// If all is true, Stack formats stack traces of all other goroutines -// into buf after the trace for the current goroutine. -func Stack(buf []byte, all bool) int { - if all { - semacquire(&worldsema, false) - gp := getg() - gp.m.gcing = 1 - onM(stoptheworld) - } - - n := 0 - if len(buf) > 0 { - gp := getg() - sp := getcallersp(unsafe.Pointer(&buf)) - pc := getcallerpc(unsafe.Pointer(&buf)) - onM(func() { - g0 := getg() - g0.writebuf = buf[0:0:len(buf)] - goroutineheader(gp) - traceback(pc, sp, 0, gp) - if all { - tracebackothers(gp) - } - n = len(g0.writebuf) - g0.writebuf = nil - }) - } - - if all { - gp := getg() - gp.m.gcing = 0 - semrelease(&worldsema) - onM(starttheworld) - } - return n -} - -// Tracing of alloc/free/gc. - -var tracelock mutex - -func tracealloc(p unsafe.Pointer, size uintptr, typ *_type) { - lock(&tracelock) - gp := getg() - gp.m.traceback = 2 - if typ == nil { - print("tracealloc(", p, ", ", hex(size), ")\n") - } else { - print("tracealloc(", p, ", ", hex(size), ", ", *typ._string, ")\n") - } - if gp.m.curg == nil || gp == gp.m.curg { - goroutineheader(gp) - pc := getcallerpc(unsafe.Pointer(&p)) - sp := getcallersp(unsafe.Pointer(&p)) - onM(func() { - traceback(pc, sp, 0, gp) - }) - } else { - goroutineheader(gp.m.curg) - traceback(^uintptr(0), ^uintptr(0), 0, gp.m.curg) - } - print("\n") - gp.m.traceback = 0 - unlock(&tracelock) -} - -func tracefree(p unsafe.Pointer, size uintptr) { - lock(&tracelock) - gp := getg() - gp.m.traceback = 2 - print("tracefree(", p, ", ", hex(size), ")\n") - goroutineheader(gp) - pc := getcallerpc(unsafe.Pointer(&p)) - sp := getcallersp(unsafe.Pointer(&p)) - onM(func() { - traceback(pc, sp, 0, gp) - }) - print("\n") - gp.m.traceback = 0 - unlock(&tracelock) -} - -func tracegc() { - lock(&tracelock) - gp := getg() - gp.m.traceback = 2 - print("tracegc()\n") - // running on m->g0 stack; show all non-g0 goroutines - tracebackothers(gp) - print("end tracegc\n") - print("\n") - gp.m.traceback = 0 - unlock(&tracelock) -} |