diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2016-10-14 22:51:46 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2016-10-14 22:51:46 +0000 |
commit | 1f0be9ee86f63bac9c4541a9cfaf52cb5ae5e89a (patch) | |
tree | 584ab0cd64a2743fa7198ca34c7b13282c1c0ad7 /libgo/go | |
parent | 2045acd902fd8028514a72c58c98dba11749b8ad (diff) | |
download | gcc-1f0be9ee86f63bac9c4541a9cfaf52cb5ae5e89a.zip gcc-1f0be9ee86f63bac9c4541a9cfaf52cb5ae5e89a.tar.gz gcc-1f0be9ee86f63bac9c4541a9cfaf52cb5ae5e89a.tar.bz2 |
runtime: copy mprof code from Go 1.7 runtime
Also create a gccgo version of some of the traceback code in
traceback_gccgo.go, replacing some code currently in C.
This required modifying the compiler so that when compiling the runtime
package a slice expression does not cause a local array variable to
escape to the heap.
Reviewed-on: https://go-review.googlesource.com/31230
From-SVN: r241189
Diffstat (limited to 'libgo/go')
-rw-r--r-- | libgo/go/runtime/debug.go | 151 | ||||
-rw-r--r-- | libgo/go/runtime/mprof.go | 689 | ||||
-rw-r--r-- | libgo/go/runtime/runtime2.go | 17 | ||||
-rw-r--r-- | libgo/go/runtime/stubs.go | 26 | ||||
-rw-r--r-- | libgo/go/runtime/traceback_gccgo.go | 164 |
5 files changed, 875 insertions, 172 deletions
diff --git a/libgo/go/runtime/debug.go b/libgo/go/runtime/debug.go index 56d19d7..56e307f 100644 --- a/libgo/go/runtime/debug.go +++ b/libgo/go/runtime/debug.go @@ -4,18 +4,6 @@ package runtime -// Breakpoint executes a breakpoint trap. -func Breakpoint() - -// LockOSThread wires the calling goroutine to its current operating system thread. -// Until the calling goroutine exits or calls UnlockOSThread, it will always -// execute in that thread, and no other goroutine can. -func LockOSThread() - -// UnlockOSThread unwires the calling goroutine from its fixed operating system thread. -// If the calling goroutine has not called LockOSThread, UnlockOSThread is a no-op. -func UnlockOSThread() - // GOMAXPROCS sets the maximum number of CPUs that can be executing // simultaneously and returns the previous setting. If n < 1, it does not // change the current setting. @@ -36,145 +24,6 @@ func NumCgoCall() int64 // NumGoroutine returns the number of goroutines that currently exist. func NumGoroutine() int -// 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) - -// 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:] -} - -// 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) - -// 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) - -// CPUProfile returns the next chunk of binary CPU profiling stack trace data, -// blocking until data is available. If profiling is turned off and all the profile -// data accumulated while it was on has been returned, CPUProfile returns nil. -// The caller must save the returned data before calling CPUProfile again. -// -// Most clients should use the runtime/pprof package or -// the testing package's -test.cpuprofile flag instead of calling -// CPUProfile directly. -func CPUProfile() []byte - -// SetCPUProfileRate sets the CPU profiling rate to hz samples per second. -// If hz <= 0, SetCPUProfileRate turns off profiling. -// If the profiler is on, the rate cannot be changed without first turning it off. -// -// Most clients should use the runtime/pprof package or -// the testing package's -test.cpuprofile flag instead of calling -// SetCPUProfileRate directly. -func SetCPUProfileRate(hz int) - -// 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) - -// 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) - -// 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 - // Get field tracking information. Only fields with a tag go:"track" // are tracked. This function will add every such field that is // referenced to the map. The keys in the map will be diff --git a/libgo/go/runtime/mprof.go b/libgo/go/runtime/mprof.go new file mode 100644 index 0000000..8d11031 --- /dev/null +++ b/libgo/go/runtime/mprof.go @@ -0,0 +1,689 @@ +// 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 ( + "runtime/internal/atomic" + "unsafe" +) + +// Export temporarily for gccgo's C code to call: +//go:linkname mProf_Malloc runtime.mProf_Malloc +//go:linkname mProf_Free runtime.mProf_Free +//go:linkname mProf_GC runtime.mProf_GC +//go:linkname tracealloc runtime.tracealloc +//go:linkname tracefree runtime.tracefree +//go:linkname tracegc runtime.tracegc +//go:linkname iterate_memprof runtime.iterate_memprof + +// 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(location{}) + switch typ { + default: + throw("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() []location { + stk := (*[maxStack]location)(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 { + throw("bad use of bucket.mp") + } + data := add(unsafe.Pointer(b), unsafe.Sizeof(*b)+b.nstk*unsafe.Sizeof(location{})) + return (*memRecord)(data) +} + +// bp returns the blockRecord associated with the blockProfile bucket b. +func (b *bucket) bp() *blockRecord { + if b.typ != blockProfile { + throw("bad use of bucket.bp") + } + data := add(unsafe.Pointer(b), unsafe.Sizeof(*b)+b.nstk*unsafe.Sizeof(location{})) + return (*blockRecord)(data) +} + +// Return the bucket for stk[0:nstk], allocating new bucket if needed. +func stkbucket(typ bucketType, size uintptr, stk []location, alloc bool) *bucket { + if buckhash == nil { + buckhash = (*[buckHashSize]*bucket)(sysAlloc(unsafe.Sizeof(*buckhash), &memstats.buckhash_sys)) + if buckhash == nil { + throw("runtime: cannot allocate memory") + } + } + + // Hash stack. + var h uintptr + for _, loc := range stk { + h += loc.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 eqslice(x, y []location) 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]location + nstk := callers(4, 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. + systemstack(func() { + setprofilebucket(p, b) + }) +} + +// Called when freeing a profiled block. +func mProf_Free(b *bucket, size uintptr) { + lock(&proflock) + mp := b.mp() + 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 + } + } + + atomic.Store64(&blockprofilerate, uint64(r)) +} + +func blockevent(cycles int64, skip int) { + if cycles <= 0 { + cycles = 1 + } + rate := int64(atomic.Load64(&blockprofilerate)) + if rate <= 0 || (rate > cycles && int64(fastrand1())%rate > cycles) { + return + } + gp := getg() + var nstk int + var stk [maxStack]location + if gp.m.curg == nil || gp.m.curg == gp { + nstk = callers(skip, stk[:]) + } else { + // FIXME: This should get a traceback of gp.m.curg. + // nstk = gcallers(gp.m.curg, skip, stk[:]) + nstk = callers(skip, 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 a profile of memory allocated and freed per allocation +// site. +// +// 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. +// +// The returned profile may be up to two garbage collection cycles old. +// This is to avoid skewing the profile toward allocations; because +// allocations happen in real time but frees are delayed until the garbage +// collector performs sweeping, the profile only accounts for allocations +// that have had a chance to be freed by the garbage collector. +// +// 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) + for i, loc := range b.stk() { + if i >= len(r.Stack0) { + break + } + r.Stack0[i] = loc.pc + } + for i := int(b.nstk); i < len(r.Stack0); i++ { + r.Stack0[i] = 0 + } +} + +func iterate_memprof(fn func(*bucket, uintptr, *location, uintptr, uintptr, uintptr)) { + lock(&proflock) + for b := mbuckets; b != nil; b = b.allnext { + mp := b.mp() + fn(b, 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 = bp.count + r.Cycles = bp.cycles + i := 0 + var loc location + for i, loc = range b.stk() { + if i >= len(r.Stack0) { + break + } + r.Stack0[i] = loc.pc + } + 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)(atomic.Loadp(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 j := range mp.createstack { + p[i].Stack0[j] = mp.createstack[j].pc + } + i++ + } + } + return +} + +// 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) { + gp := getg() + + isOK := func(gp1 *g) bool { + // Checking isSystemGoroutine here makes GoroutineProfile + // consistent with both NumGoroutine and Stack. + return gp1 != gp && readgstatus(gp1) != _Gdead && !isSystemGoroutine(gp1) + } + + stopTheWorld("profile") + + n = 1 + for _, gp1 := range allgs() { + if isOK(gp1) { + n++ + } + } + + if n <= len(p) { + ok = true + r := p + + // Save current goroutine. + saveg(gp, &r[0]) + r = r[1:] + + // Save other goroutines. + for _, gp1 := range allgs() { + if isOK(gp1) { + if len(r) == 0 { + // Should be impossible, but better to return a + // truncated profile than to crash the entire process. + break + } + saveg(gp1, &r[0]) + r = r[1:] + } + } + } + + startTheWorld() + + return n, ok +} + +func saveg(gp *g, r *StackRecord) { + if gp == getg() { + var locbuf [32]location + n := callers(1, locbuf[:]) + for i := 0; i < n; i++ { + r.Stack0[i] = locbuf[i].pc + } + if n < len(r.Stack0) { + r.Stack0[n] = 0 + } + } else { + // FIXME: Not implemented. + r.Stack0[0] = 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 { + stopTheWorld("stack trace") + } + + n := 0 + if len(buf) > 0 { + gp := getg() + // Force traceback=1 to override GOTRACEBACK setting, + // so that Stack's results are consistent. + // GOTRACEBACK is only about crash dumps. + gp.m.traceback = 1 + gp.writebuf = buf[0:0:len(buf)] + goroutineheader(gp) + traceback() + if all { + tracebackothers(gp) + } + gp.m.traceback = 0 + n = len(gp.writebuf) + gp.writebuf = nil + } + + if all { + 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) + traceback() + } else { + goroutineheader(gp.m.curg) + // FIXME: Can't do traceback of other g. + } + 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) + traceback() + 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) +} diff --git a/libgo/go/runtime/runtime2.go b/libgo/go/runtime/runtime2.go index 25b5b79..660d81e 100644 --- a/libgo/go/runtime/runtime2.go +++ b/libgo/go/runtime/runtime2.go @@ -394,7 +394,7 @@ type g struct { issystem bool // do not output in stack dump isbackground bool // ignore in deadlock detector - traceback *traceback // stack traceback buffer + traceback *tracebackg // stack traceback buffer context g_ucontext_t // saved context for setcontext stackcontext [10]unsafe.Pointer // split-stack context @@ -801,21 +801,6 @@ var ( // array. type g_ucontext_t [(_sizeof_ucontext_t + 15) / unsafe.Sizeof(unsafe.Pointer(nil))]unsafe.Pointer -// traceback is used to collect stack traces from other goroutines. -type traceback struct { - gp *g - locbuf [_TracebackMaxFrames]location - c int -} - -// location is a location in the program, used for backtraces. -type location struct { - pc uintptr - filename string - function string - lineno int -} - // cgoMal tracks allocations made by _cgo_allocate // FIXME: _cgo_allocate has been removed from gc and can probably be // removed from gccgo too. diff --git a/libgo/go/runtime/stubs.go b/libgo/go/runtime/stubs.go index 30a0f55..e1c28fa 100644 --- a/libgo/go/runtime/stubs.go +++ b/libgo/go/runtime/stubs.go @@ -307,11 +307,6 @@ func gopark(func(*g, unsafe.Pointer) bool, unsafe.Pointer, string, byte, int) func goparkunlock(*mutex, string, byte, int) func goready(*g, int) -// Temporary for gccgo until we port mprof.go. -var blockprofilerate uint64 - -func blockevent(cycles int64, skip int) {} - // Temporary hack for gccgo until we port proc.go. //go:nosplit func acquireSudog() *sudog { @@ -428,3 +423,24 @@ func sysAlloc(n uintptr, sysStat *uint64) unsafe.Pointer func cpuprofAdd(stk []uintptr) { cpuprof.add(stk) } + +// For gccgo until we port proc.go. +func Breakpoint() +func LockOSThread() +func UnlockOSThread() +func allm() *m +func allgs() []*g + +//go:nosplit +func readgstatus(gp *g) uint32 { + return atomic.Load(&gp.atomicstatus) +} + +// Temporary for gccgo until we port malloc.go +func persistentalloc(size, align uintptr, sysStat *uint64) unsafe.Pointer + +// Temporary for gccgo until we port mheap.go +func setprofilebucket(p unsafe.Pointer, b *bucket) + +// Currently in proc.c. +func tracebackothers(*g) diff --git a/libgo/go/runtime/traceback_gccgo.go b/libgo/go/runtime/traceback_gccgo.go new file mode 100644 index 0000000..4f3d7c0 --- /dev/null +++ b/libgo/go/runtime/traceback_gccgo.go @@ -0,0 +1,164 @@ +// Copyright 2016 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. + +// Traceback support for gccgo. +// The actual traceback code is written in C. + +package runtime + +import ( + "runtime/internal/sys" + _ "unsafe" // for go:linkname +) + +// For gccgo, use go:linkname to rename compiler-called functions to +// themselves, so that the compiler will export them. +// These are temporary for C runtime code to call. +//go:linkname traceback runtime.traceback +//go:linkname printtrace runtime.printtrace +//go:linkname goroutineheader runtime.goroutineheader +//go:linkname printcreatedby runtime.printcreatedby + +func printcreatedby(gp *g) { + // Show what created goroutine, except main goroutine (goid 1). + pc := gp.gopc + tracepc := pc // back up to CALL instruction for funcfileline. + entry := funcentry(tracepc) + if entry != 0 && tracepc > entry { + tracepc -= sys.PCQuantum + } + function, file, line := funcfileline(tracepc, -1) + if function != "" && showframe(function, gp) && gp.goid != 1 { + print("created by ", function, "\n") + print("\t", file, ":", line) + if entry != 0 && pc > entry { + print(" +", hex(pc-entry)) + } + print("\n") + } +} + +// tracebackg is used to collect stack traces from other goroutines. +type tracebackg struct { + gp *g + locbuf [_TracebackMaxFrames]location + c int +} + +// location is a location in the program, used for backtraces. +type location struct { + pc uintptr + filename string + function string + lineno int +} + +//extern runtime_callers +func c_callers(skip int32, locbuf *location, max int32, keepThunks bool) int32 + +// callers returns a stack trace of the current goroutine. +// The gc version of callers takes []uintptr, but we take []location. +func callers(skip int, locbuf []location) int { + n := c_callers(int32(skip), &locbuf[0], int32(len(locbuf)), false) + return int(n) +} + +// traceback prints a traceback of the current goroutine. +// This differs from the gc version, which is given pc, sp, lr and g and +// can print a traceback of any goroutine. +func traceback() { + var locbuf [100]location + c := c_callers(1, &locbuf[0], int32(len(locbuf)), false) + printtrace(locbuf[:c], getg()) +} + +// printtrace prints a traceback from locbuf. +func printtrace(locbuf []location, gp *g) { + for i := range locbuf { + if showframe(locbuf[i].function, gp) { + print(locbuf[i].function, "\n\t", locbuf[i].filename, ":", locbuf[i].lineno) + } + } +} + +// showframe returns whether to print a frame in a traceback. +// name is the function name. +func showframe(name string, gp *g) bool { + g := getg() + if g.m.throwing > 0 && gp != nil && (gp == g.m.curg || gp == g.m.caughtsig.ptr()) { + return true + } + level, _, _ := gotraceback() + + // Special case: always show runtime.gopanic frame, so that we can + // see where a panic started in the middle of a stack trace. + // See golang.org/issue/5832. + // __go_panic is the current gccgo name. + if name == "runtime.gopanic" || name == "__go_panic" { + return true + } + + return level > 1 || contains(name, ".") && (!hasprefix(name, "runtime.") || isExportedRuntime(name)) +} + +// isExportedRuntime reports whether name is an exported runtime function. +// It is only for runtime functions, so ASCII A-Z is fine. +func isExportedRuntime(name string) bool { + const n = len("runtime.") + return len(name) > n && name[:n] == "runtime." && 'A' <= name[n] && name[n] <= 'Z' +} + +var gStatusStrings = [...]string{ + _Gidle: "idle", + _Grunnable: "runnable", + _Grunning: "running", + _Gsyscall: "syscall", + _Gwaiting: "waiting", + _Gdead: "dead", + _Gcopystack: "copystack", +} + +func goroutineheader(gp *g) { + gpstatus := readgstatus(gp) + + isScan := gpstatus&_Gscan != 0 + gpstatus &^= _Gscan // drop the scan bit + + // Basic string status + var status string + if 0 <= gpstatus && gpstatus < uint32(len(gStatusStrings)) { + status = gStatusStrings[gpstatus] + } else { + status = "???" + } + + // Override. + if gpstatus == _Gwaiting && gp.waitreason != "" { + status = gp.waitreason + } + + // approx time the G is blocked, in minutes + var waitfor int64 + if (gpstatus == _Gwaiting || gpstatus == _Gsyscall) && gp.waitsince != 0 { + waitfor = (nanotime() - gp.waitsince) / 60e9 + } + print("goroutine ", gp.goid, " [", status) + if isScan { + print(" (scan)") + } + if waitfor >= 1 { + print(", ", waitfor, " minutes") + } + if gp.lockedm != nil { + print(", locked to thread") + } + print("]:\n") +} + +// isSystemGoroutine reports whether the goroutine g must be omitted in +// stack dumps and deadlock detector. +func isSystemGoroutine(gp *g) bool { + // FIXME. + return false +} |