From: Ian Lance Taylor Date: Fri, 14 Oct 2016 22:51:46 +0000 (+0000) Subject: runtime: copy mprof code from Go 1.7 runtime X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=1f0be9ee86f63bac9c4541a9cfaf52cb5ae5e89a;p=gcc.git 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 --- diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index b2f0413da89..2e09ec1db85 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -993840643e27e52cda7e86e6a775f54443ea5d07 +ec3dc927da71d15cac48a13c0fb0c1f94572d0d2 The first line of this file holds the git revision number of the last merge done from the gofrontend repository. diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index daa1c92cce6..261129faa0c 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -10308,7 +10308,7 @@ Array_index_expression::do_determine_type(const Type_context*) // Check types of an array index. void -Array_index_expression::do_check_types(Gogo*) +Array_index_expression::do_check_types(Gogo* gogo) { Numeric_constant nc; unsigned long v; @@ -10427,7 +10427,18 @@ Array_index_expression::do_check_types(Gogo*) if (!this->array_->is_addressable()) this->report_error(_("slice of unaddressable value")); else - this->array_->address_taken(true); + { + bool escapes = true; + + // When compiling the runtime, a slice operation does not + // cause local variables to escape. When escape analysis + // becomes the default, this should be changed to make it an + // error if we have a slice operation that escapes. + if (gogo->compiling_runtime() && gogo->package_name() == "runtime") + escapes = false; + + this->array_->address_taken(escapes); + } } } diff --git a/libgo/Makefile.am b/libgo/Makefile.am index dee6fbcc25c..f90331fc529 100644 --- a/libgo/Makefile.am +++ b/libgo/Makefile.am @@ -478,7 +478,6 @@ runtime_files = \ runtime/go-signal.c \ runtime/go-strcmp.c \ runtime/go-strslice.c \ - runtime/go-traceback.c \ runtime/go-type-complex.c \ runtime/go-type-eface.c \ runtime/go-type-float.c \ @@ -515,7 +514,6 @@ runtime_files = \ go-iface.c \ lfstack.c \ malloc.c \ - mprof.c \ netpoll.c \ rdebug.c \ reflect.c \ diff --git a/libgo/Makefile.in b/libgo/Makefile.in index c811312f21d..1955ede5b5d 100644 --- a/libgo/Makefile.in +++ b/libgo/Makefile.in @@ -253,17 +253,17 @@ am__objects_6 = go-append.lo go-assert.lo go-assert-interface.lo \ go-nanotime.lo go-now.lo go-new.lo go-nosys.lo go-panic.lo \ go-recover.lo go-reflect-call.lo go-runtime-error.lo \ go-setenv.lo go-signal.lo go-strcmp.lo go-strslice.lo \ - go-traceback.lo go-type-complex.lo go-type-eface.lo \ - go-type-float.lo go-type-identity.lo go-type-interface.lo \ - go-type-string.lo go-typedesc-equal.lo go-unsafe-new.lo \ - go-unsafe-newarray.lo go-unsafe-pointer.lo go-unsetenv.lo \ - go-unwind.lo go-varargs.lo env_posix.lo heapdump.lo mcache.lo \ - mcentral.lo $(am__objects_1) mfixalloc.lo mgc0.lo mheap.lo \ - msize.lo $(am__objects_2) panic.lo parfor.lo print.lo proc.lo \ + go-type-complex.lo go-type-eface.lo go-type-float.lo \ + go-type-identity.lo go-type-interface.lo go-type-string.lo \ + go-typedesc-equal.lo go-unsafe-new.lo go-unsafe-newarray.lo \ + go-unsafe-pointer.lo go-unsetenv.lo go-unwind.lo go-varargs.lo \ + env_posix.lo heapdump.lo mcache.lo mcentral.lo \ + $(am__objects_1) mfixalloc.lo mgc0.lo mheap.lo msize.lo \ + $(am__objects_2) panic.lo parfor.lo print.lo proc.lo \ runtime.lo signal_unix.lo thread.lo $(am__objects_3) yield.lo \ - $(am__objects_4) go-iface.lo lfstack.lo malloc.lo mprof.lo \ - netpoll.lo rdebug.lo reflect.lo runtime1.lo sigqueue.lo \ - time.lo $(am__objects_5) + $(am__objects_4) go-iface.lo lfstack.lo malloc.lo netpoll.lo \ + rdebug.lo reflect.lo runtime1.lo sigqueue.lo time.lo \ + $(am__objects_5) am_libgo_llgo_la_OBJECTS = $(am__objects_6) libgo_llgo_la_OBJECTS = $(am_libgo_llgo_la_OBJECTS) libgo_llgo_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ @@ -877,7 +877,6 @@ runtime_files = \ runtime/go-signal.c \ runtime/go-strcmp.c \ runtime/go-strslice.c \ - runtime/go-traceback.c \ runtime/go-type-complex.c \ runtime/go-type-eface.c \ runtime/go-type-float.c \ @@ -914,7 +913,6 @@ runtime_files = \ go-iface.c \ lfstack.c \ malloc.c \ - mprof.c \ netpoll.c \ rdebug.c \ reflect.c \ @@ -1593,7 +1591,6 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-signal.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-strcmp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-strslice.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-traceback.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-type-complex.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-type-eface.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-type-float.Plo@am__quote@ @@ -1620,7 +1617,6 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mfixalloc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mgc0.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mheap.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mprof.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/msize.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netpoll.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netpoll_epoll.Plo@am__quote@ @@ -1986,13 +1982,6 @@ go-strslice.lo: runtime/go-strslice.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o go-strslice.lo `test -f 'runtime/go-strslice.c' || echo '$(srcdir)/'`runtime/go-strslice.c -go-traceback.lo: runtime/go-traceback.c -@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT go-traceback.lo -MD -MP -MF $(DEPDIR)/go-traceback.Tpo -c -o go-traceback.lo `test -f 'runtime/go-traceback.c' || echo '$(srcdir)/'`runtime/go-traceback.c -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-traceback.Tpo $(DEPDIR)/go-traceback.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/go-traceback.c' object='go-traceback.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o go-traceback.lo `test -f 'runtime/go-traceback.c' || echo '$(srcdir)/'`runtime/go-traceback.c - go-type-complex.lo: runtime/go-type-complex.c @am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT go-type-complex.lo -MD -MP -MF $(DEPDIR)/go-type-complex.Tpo -c -o go-type-complex.lo `test -f 'runtime/go-type-complex.c' || echo '$(srcdir)/'`runtime/go-type-complex.c @am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/go-type-complex.Tpo $(DEPDIR)/go-type-complex.Plo diff --git a/libgo/go/runtime/debug.go b/libgo/go/runtime/debug.go index 56d19d7a4cf..56e307fb3f5 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 00000000000..8d110317003 --- /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 25b5b79de20..660d81e47f3 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 30a0f559a92..e1c28fac543 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 00000000000..4f3d7c039be --- /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 +} diff --git a/libgo/runtime/go-traceback.c b/libgo/runtime/go-traceback.c deleted file mode 100644 index 7b33cca8681..00000000000 --- a/libgo/runtime/go-traceback.c +++ /dev/null @@ -1,37 +0,0 @@ -/* go-traceback.c -- stack backtrace for Go. - - Copyright 2012 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. */ - -#include "config.h" - -#include "runtime.h" - -/* Print a stack trace for the current goroutine. */ - -void -runtime_traceback () -{ - Location locbuf[100]; - int32 c; - - c = runtime_callers (1, locbuf, nelem (locbuf), false); - runtime_printtrace (locbuf, c, true); -} - -void -runtime_printtrace (Location *locbuf, int32 c, bool current) -{ - int32 i; - - for (i = 0; i < c; ++i) - { - if (runtime_showframe (locbuf[i].function, current)) - { - runtime_printf ("%S\n", locbuf[i].function); - runtime_printf ("\t%S:%D\n", locbuf[i].filename, - (int64) locbuf[i].lineno); - } - } -} diff --git a/libgo/runtime/malloc.h b/libgo/runtime/malloc.h index 12a25b57677..011eaa91d07 100644 --- a/libgo/runtime/malloc.h +++ b/libgo/runtime/malloc.h @@ -303,7 +303,7 @@ struct SpecialFinalizer }; // The described object is being heap profiled. -typedef struct Bucket Bucket; // from mprof.goc +typedef struct bucket Bucket; // from mprof.go typedef struct SpecialProfile SpecialProfile; struct SpecialProfile { @@ -414,7 +414,8 @@ void runtime_MHeap_Scavenger(void*); void runtime_MHeap_SplitSpan(MHeap *h, MSpan *s); void* runtime_mallocgc(uintptr size, uintptr typ, uint32 flag); -void* runtime_persistentalloc(uintptr size, uintptr align, uint64 *stat); +void* runtime_persistentalloc(uintptr size, uintptr align, uint64 *stat) + __asm__(GOSYM_PREFIX "runtime.persistentalloc"); int32 runtime_mlookup(void *v, byte **base, uintptr *size, MSpan **s); void runtime_gc(int32 force); uintptr runtime_sweepone(void); @@ -428,12 +429,15 @@ void runtime_markspan(void *v, uintptr size, uintptr n, bool leftover); void runtime_unmarkspan(void *v, uintptr size); void runtime_purgecachedstats(MCache*); void* runtime_cnew(const Type*) - __asm__(GOSYM_PREFIX "runtime.newobject"); + __asm__(GOSYM_PREFIX "runtime.newobject"); void* runtime_cnewarray(const Type*, intgo) - __asm__(GOSYM_PREFIX "runtime.newarray"); -void runtime_tracealloc(void*, uintptr, uintptr); -void runtime_tracefree(void*, uintptr); -void runtime_tracegc(void); + __asm__(GOSYM_PREFIX "runtime.newarray"); +void runtime_tracealloc(void*, uintptr, uintptr) + __asm__ (GOSYM_PREFIX "runtime.tracealloc"); +void runtime_tracefree(void*, uintptr) + __asm__ (GOSYM_PREFIX "runtime.tracefree"); +void runtime_tracegc(void) + __asm__ (GOSYM_PREFIX "runtime.tracegc"); uintptr runtime_gettype(void*); @@ -455,10 +459,14 @@ struct Obj uintptr ti; // type info }; -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, Location*, uintptr, uintptr, uintptr)); +void runtime_MProf_Malloc(void*, uintptr) + __asm__ (GOSYM_PREFIX "runtime.mProf_Malloc"); +void runtime_MProf_Free(Bucket*, uintptr, bool) + __asm__ (GOSYM_PREFIX "runtime.mProf_Free"); +void runtime_MProf_GC(void) + __asm__ (GOSYM_PREFIX "runtime.mProf_GC"); +void runtime_iterate_memprof(void (*callback)(Bucket*, uintptr, Location*, uintptr, uintptr, uintptr)) + __asm__ (GOSYM_PREFIX "runtime.iterate_memprof"); int32 runtime_gcprocs(void); void runtime_helpgc(int32 nproc); void runtime_gchelper(void); @@ -467,7 +475,8 @@ G* runtime_wakefing(void); extern bool runtime_fingwait; extern bool runtime_fingwake; -void runtime_setprofilebucket(void *p, Bucket *b); +void runtime_setprofilebucket(void *p, Bucket *b) + __asm__ (GOSYM_PREFIX "runtime.setprofilebucket"); struct __go_func_type; struct __go_ptr_type; @@ -533,7 +542,6 @@ int32 runtime_setgcpercent(int32); #define PoisonStack ((uintptr)0x6868686868686868ULL) struct Workbuf; -void runtime_MProf_Mark(struct Workbuf**, void (*)(struct Workbuf**, Obj)); void runtime_proc_scan(struct Workbuf**, void (*)(struct Workbuf**, Obj)); void runtime_time_scan(struct Workbuf**, void (*)(struct Workbuf**, Obj)); void runtime_netpoll_scan(struct Workbuf**, void (*)(struct Workbuf**, Obj)); diff --git a/libgo/runtime/mgc0.c b/libgo/runtime/mgc0.c index 48776787828..2c8e5a8e643 100644 --- a/libgo/runtime/mgc0.c +++ b/libgo/runtime/mgc0.c @@ -1277,7 +1277,6 @@ markroot(ParFor *desc, uint32 i) enqueue1(&wbuf, (Obj){(byte*)&runtime_allp, sizeof runtime_allp, 0}); enqueue1(&wbuf, (Obj){(byte*)&work, sizeof work, 0}); runtime_proc_scan(&wbuf, enqueue1); - runtime_MProf_Mark(&wbuf, enqueue1); runtime_time_scan(&wbuf, enqueue1); runtime_netpoll_scan(&wbuf, enqueue1); break; diff --git a/libgo/runtime/mprof.goc b/libgo/runtime/mprof.goc deleted file mode 100644 index c4966a4ba81..00000000000 --- a/libgo/runtime/mprof.goc +++ /dev/null @@ -1,564 +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 -#include "runtime.h" -#include "arch.h" -#include "malloc.h" -#include "defs.h" -#include "go-type.h" -#include "go-string.h" - -// NOTE(rsc): Everything here could use cas if contention became an issue. -static Lock proflock; - -// All memory allocations are local and do not escape outside of the profiler. -// The profiler is forbidden from referring to garbage-collected memory. - -enum { MProf, BProf }; // profile types - -// Per-call-stack profiling information. -// Lookup by hashing call stack into a linked-list hash table. -struct Bucket -{ - Bucket *next; // next in hash list - Bucket *allnext; // next in list of all mbuckets/bbuckets - int32 typ; - // Generally unions can break precise GC, - // this one is fine because it does not contain pointers. - union - { - struct // typ == MProf - { - // 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. - uintptr allocs; - uintptr frees; - uintptr alloc_bytes; - uintptr free_bytes; - - uintptr prev_allocs; // since last but one till last gc - uintptr prev_frees; - uintptr prev_alloc_bytes; - uintptr prev_free_bytes; - - uintptr recent_allocs; // since last gc till now - uintptr recent_frees; - uintptr recent_alloc_bytes; - uintptr recent_free_bytes; - - }; - struct // typ == BProf - { - int64 count; - int64 cycles; - }; - }; - uintptr hash; // hash of size + stk - uintptr size; - uintptr nstk; - Location stk[1]; -}; -enum { - BuckHashSize = 179999, -}; -static Bucket **buckhash; -static Bucket *mbuckets; // memory profile buckets -static Bucket *bbuckets; // blocking profile buckets -static uintptr bucketmem; - -// Return the bucket for stk[0:nstk], allocating new bucket if needed. -static Bucket* -stkbucket(int32 typ, uintptr size, Location *stk, int32 nstk, bool alloc) -{ - int32 i, j; - uintptr h; - Bucket *b; - - if(buckhash == nil) { - buckhash = runtime_SysAlloc(BuckHashSize*sizeof buckhash[0], &mstats()->buckhash_sys); - if(buckhash == nil) - runtime_throw("runtime: cannot allocate memory"); - } - - // Hash stack. - h = 0; - for(i=0; i>6; - } - // hash in size - h += size; - h += h<<10; - h ^= h>>6; - // finalize - h += h<<3; - h ^= h>>11; - - i = h%BuckHashSize; - for(b = buckhash[i]; b; b=b->next) { - if(b->typ == typ && b->hash == h && b->size == size && b->nstk == (uintptr)nstk) { - for(j = 0; j < nstk; j++) { - if(b->stk[j].pc != stk[j].pc || - b->stk[j].lineno != stk[j].lineno || - !__go_strings_equal(b->stk[j].filename, stk[j].filename)) - break; - } - if (j == nstk) - return b; - } - } - - if(!alloc) - return nil; - - b = runtime_persistentalloc(sizeof *b + nstk*sizeof stk[0], 0, &mstats()->buckhash_sys); - bucketmem += sizeof *b + nstk*sizeof stk[0]; - runtime_memmove(b->stk, stk, nstk*sizeof stk[0]); - b->typ = typ; - b->hash = h; - b->size = size; - b->nstk = nstk; - b->next = buckhash[i]; - buckhash[i] = b; - if(typ == MProf) { - b->allnext = mbuckets; - mbuckets = b; - } else { - b->allnext = bbuckets; - bbuckets = b; - } - return b; -} - -static void -MProf_GC(void) -{ - Bucket *b; - - for(b=mbuckets; b; b=b->allnext) { - b->allocs += b->prev_allocs; - b->frees += b->prev_frees; - b->alloc_bytes += b->prev_alloc_bytes; - b->free_bytes += b->prev_free_bytes; - - b->prev_allocs = b->recent_allocs; - b->prev_frees = b->recent_frees; - b->prev_alloc_bytes = b->recent_alloc_bytes; - b->prev_free_bytes = b->recent_free_bytes; - - b->recent_allocs = 0; - b->recent_frees = 0; - b->recent_alloc_bytes = 0; - b->recent_free_bytes = 0; - } -} - -// Record that a gc just happened: all the 'recent' statistics are now real. -void -runtime_MProf_GC(void) -{ - runtime_lock(&proflock); - MProf_GC(); - runtime_unlock(&proflock); -} - -// Called by malloc to record a profiled block. -void -runtime_MProf_Malloc(void *p, uintptr size) -{ - Location stk[32]; - Bucket *b; - int32 nstk; - - nstk = runtime_callers(1, stk, nelem(stk), false); - runtime_lock(&proflock); - b = stkbucket(MProf, size, stk, nstk, true); - b->recent_allocs++; - b->recent_alloc_bytes += size; - runtime_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. - runtime_setprofilebucket(p, b); -} - -// Called when freeing a profiled block. -void -runtime_MProf_Free(Bucket *b, uintptr size, bool freed) -{ - runtime_lock(&proflock); - if(freed) { - b->recent_frees++; - b->recent_free_bytes += size; - } else { - b->prev_frees++; - b->prev_free_bytes += size; - } - runtime_unlock(&proflock); -} - -int64 runtime_blockprofilerate; // in CPU ticks - -void runtime_SetBlockProfileRate(intgo) __asm__ (GOSYM_PREFIX "runtime.SetBlockProfileRate"); - -void -runtime_SetBlockProfileRate(intgo rate) -{ - int64 r; - - if(rate <= 0) - r = 0; // disable profiling - else { - // convert ns to cycles, use float64 to prevent overflow during multiplication - r = (float64)rate*runtime_tickspersecond()/(1000*1000*1000); - if(r == 0) - r = 1; - } - runtime_atomicstore64((uint64*)&runtime_blockprofilerate, r); -} - -void -runtime_blockevent(int64 cycles, int32 skip) -{ - int32 nstk; - int64 rate; - Location stk[32]; - Bucket *b; - - if(cycles <= 0) - return; - rate = runtime_atomicload64((uint64*)&runtime_blockprofilerate); - if(rate <= 0 || (rate > cycles && runtime_fastrand1()%rate > cycles)) - return; - - nstk = runtime_callers(skip, stk, nelem(stk), false); - runtime_lock(&proflock); - b = stkbucket(BProf, 0, stk, nstk, true); - b->count++; - b->cycles += cycles; - runtime_unlock(&proflock); -} - -// Go interface to profile data. (Declared in debug.go) - -// Must match MemProfileRecord in debug.go. -typedef struct Record Record; -struct Record { - int64 alloc_bytes, free_bytes; - int64 alloc_objects, free_objects; - uintptr stk[32]; -}; - -// Write b's data to r. -static void -record(Record *r, Bucket *b) -{ - uint32 i; - - r->alloc_bytes = b->alloc_bytes; - r->free_bytes = b->free_bytes; - r->alloc_objects = b->allocs; - r->free_objects = b->frees; - for(i=0; instk && istk); i++) - r->stk[i] = b->stk[i].pc; - for(; istk); i++) - r->stk[i] = 0; -} - -func MemProfile(p Slice, include_inuse_zero bool) (n int, ok bool) { - Bucket *b; - Record *r; - bool clear; - - runtime_lock(&proflock); - n = 0; - clear = true; - for(b=mbuckets; b; b=b->allnext) { - if(include_inuse_zero || b->alloc_bytes != b->free_bytes) - n++; - if(b->allocs != 0 || b->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; b=b->allnext) - if(include_inuse_zero || b->alloc_bytes != b->free_bytes) - n++; - } - ok = false; - if(n <= p.__count) { - ok = true; - r = (Record*)p.__values; - for(b=mbuckets; b; b=b->allnext) - if(include_inuse_zero || b->alloc_bytes != b->free_bytes) - record(r++, b); - } - runtime_unlock(&proflock); -} - -void -runtime_MProf_Mark(struct Workbuf **wbufp, void (*enqueue1)(struct Workbuf**, Obj)) -{ - // buckhash is not allocated via mallocgc. - enqueue1(wbufp, (Obj){(byte*)&mbuckets, sizeof mbuckets, 0}); - enqueue1(wbufp, (Obj){(byte*)&bbuckets, sizeof bbuckets, 0}); -} - -void -runtime_iterate_memprof(void (*callback)(Bucket*, uintptr, Location*, 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 { - int64 count; - int64 cycles; - uintptr stk[32]; -}; - -func BlockProfile(p Slice) (n int, ok bool) { - Bucket *b; - BRecord *r; - int32 i; - - runtime_lock(&proflock); - n = 0; - for(b=bbuckets; b; b=b->allnext) - n++; - ok = false; - if(n <= p.__count) { - ok = true; - r = (BRecord*)p.__values; - for(b=bbuckets; b; b=b->allnext, r++) { - r->count = b->count; - r->cycles = b->cycles; - for(i=0; (uintptr)instk && (uintptr)istk); i++) - r->stk[i] = b->stk[i].pc; - for(; (uintptr)istk); i++) - r->stk[i] = 0; - } - } - runtime_unlock(&proflock); -} - -// Must match StackRecord in debug.go. -typedef struct TRecord TRecord; -struct TRecord { - uintptr stk[32]; -}; - -func ThreadCreateProfile(p Slice) (n int, ok bool) { - TRecord *r; - M *first, *mp; - int32 i; - - first = runtime_atomicloadp(&runtime_allm); - n = 0; - for(mp=first; mp; mp=mp->alllink) - n++; - ok = false; - if(n <= p.__count) { - ok = true; - r = (TRecord*)p.__values; - for(mp=first; mp; mp=mp->alllink) { - for(i = 0; (uintptr)i < nelem(r->stk); i++) { - r->stk[i] = mp->createstack[i].pc; - } - r++; - } - } -} - -func Stack(b Slice, all bool) (n int) { - byte *pc; - bool enablegc = false; - - pc = (byte*)(uintptr)runtime_getcallerpc(&b); - - if(all) { - runtime_acquireWorldsema(); - runtime_m()->gcing = 1; - runtime_stopTheWorldWithSema(); - enablegc = mstats()->enablegc; - mstats()->enablegc = false; - } - - if(b.__count == 0) - n = 0; - else{ - G* g = runtime_g(); - g->writebuf.__values = b.__values; - g->writebuf.__count = 0; - g->writebuf.__capacity = b.__count; - USED(pc); - runtime_goroutineheader(g); - runtime_traceback(); - runtime_printcreatedby(g); - if(all) - runtime_tracebackothers(g); - n = g->writebuf.__count; - g->writebuf.__values = nil; - g->writebuf.__count = 0; - g->writebuf.__capacity = 0; - } - - if(all) { - runtime_m()->gcing = 0; - mstats()->enablegc = enablegc; - runtime_releaseWorldsema(); - runtime_startTheWorldWithSema(); - } -} - -static void -saveg(G *gp, TRecord *r) -{ - int32 n, i; - Location locstk[nelem(r->stk)]; - - if(gp == runtime_g()) { - n = runtime_callers(0, locstk, nelem(r->stk), false); - for(i = 0; i < n; i++) - r->stk[i] = locstk[i].pc; - } - else { - // FIXME: Not implemented. - n = 0; - } - if((size_t)n < nelem(r->stk)) - r->stk[n] = 0; -} - -func GoroutineProfile(b Slice) (n int, ok bool) { - uintptr i; - TRecord *r; - G *gp; - - ok = false; - n = runtime_gcount(); - if(n <= b.__count) { - runtime_acquireWorldsema(); - runtime_m()->gcing = 1; - runtime_stopTheWorldWithSema(); - - n = runtime_gcount(); - if(n <= b.__count) { - G* g = runtime_g(); - ok = true; - r = (TRecord*)b.__values; - saveg(g, r++); - for(i = 0; i < runtime_allglen; i++) { - gp = runtime_allg[i]; - if(gp == g || gp->atomicstatus == _Gdead) - continue; - saveg(gp, r++); - } - } - - runtime_m()->gcing = 0; - runtime_releaseWorldsema(); - runtime_startTheWorldWithSema(); - } -} - -// Tracing of alloc/free/gc. - -static Lock tracelock; - -static const char* -typeinfoname(int32 typeinfo) -{ - if(typeinfo == TypeInfo_SingleObject) - return "single object"; - else if(typeinfo == TypeInfo_Array) - return "array"; - else if(typeinfo == TypeInfo_Chan) - return "channel"; - runtime_throw("typinfoname: unknown type info"); - return nil; -} - -void -runtime_tracealloc(void *p, uintptr size, uintptr typ) -{ - const char *name; - Type *type; - - runtime_lock(&tracelock); - runtime_m()->traceback = 2; - type = (Type*)(typ & ~3); - name = typeinfoname(typ & 3); - if(type == nil) - runtime_printf("tracealloc(%p, %p, %s)\n", p, size, name); - else - runtime_printf("tracealloc(%p, %p, %s of %S)\n", p, size, name, *type->__reflection); - if(runtime_m()->curg == nil || runtime_g() == runtime_m()->curg) { - runtime_goroutineheader(runtime_g()); - runtime_traceback(); - } else { - runtime_goroutineheader(runtime_m()->curg); - runtime_traceback(); - } - runtime_printf("\n"); - runtime_m()->traceback = 0; - runtime_unlock(&tracelock); -} - -void -runtime_tracefree(void *p, uintptr size) -{ - runtime_lock(&tracelock); - runtime_m()->traceback = 2; - runtime_printf("tracefree(%p, %p)\n", p, size); - runtime_goroutineheader(runtime_g()); - runtime_traceback(); - runtime_printf("\n"); - runtime_m()->traceback = 0; - runtime_unlock(&tracelock); -} - -void -runtime_tracegc(void) -{ - runtime_lock(&tracelock); - runtime_m()->traceback = 2; - runtime_printf("tracegc()\n"); - // running on m->g0 stack; show all non-g0 goroutines - runtime_tracebackothers(runtime_g()); - runtime_printf("end tracegc\n"); - runtime_printf("\n"); - runtime_m()->traceback = 0; - runtime_unlock(&tracelock); -} diff --git a/libgo/runtime/proc.c b/libgo/runtime/proc.c index 246ab7d1b02..7d65c4b3b72 100644 --- a/libgo/runtime/proc.c +++ b/libgo/runtime/proc.c @@ -657,68 +657,13 @@ runtime_main(void* dummy __attribute__((unused))) *(int32*)0 = 0; } -void -runtime_goroutineheader(G *gp) -{ - String status; - int64 waitfor; - - switch(gp->atomicstatus) { - case _Gidle: - status = runtime_gostringnocopy((const byte*)"idle"); - break; - case _Grunnable: - status = runtime_gostringnocopy((const byte*)"runnable"); - break; - case _Grunning: - status = runtime_gostringnocopy((const byte*)"running"); - break; - case _Gsyscall: - status = runtime_gostringnocopy((const byte*)"syscall"); - break; - case _Gwaiting: - if(gp->waitreason.len > 0) - status = gp->waitreason; - else - status = runtime_gostringnocopy((const byte*)"waiting"); - break; - default: - status = runtime_gostringnocopy((const byte*)"???"); - break; - } - - // approx time the G is blocked, in minutes - waitfor = 0; - if((gp->atomicstatus == _Gwaiting || gp->atomicstatus == _Gsyscall) && gp->waitsince != 0) - waitfor = (runtime_nanotime() - gp->waitsince) / (60LL*1000*1000*1000); - - if(waitfor < 1) - runtime_printf("goroutine %D [%S]:\n", gp->goid, status); - else - runtime_printf("goroutine %D [%S, %D minutes]:\n", gp->goid, status, waitfor); -} - -void -runtime_printcreatedby(G *g) -{ - if(g != nil && g->gopc != 0 && g->goid != 1) { - String fn; - String file; - intgo line; - - if(__go_file_line(g->gopc - 1, -1, &fn, &file, &line)) { - runtime_printf("created by %S\n", fn); - runtime_printf("\t%S:%D\n", file, (int64) line); - } - } -} - void runtime_tracebackothers(G * volatile me) { G * volatile gp; Traceback tb; int32 traceback; + Slice slice; volatile uintptr i; tb.gp = me; @@ -739,7 +684,10 @@ runtime_tracebackothers(G * volatile me) runtime_gogo(gp); } - runtime_printtrace(tb.locbuf, tb.c, false); + slice.__values = &tb.locbuf[0]; + slice.__count = tb.c; + slice.__capacity = tb.c; + runtime_printtrace(slice, nil); runtime_printcreatedby(gp); } @@ -780,7 +728,10 @@ runtime_tracebackothers(G * volatile me) runtime_gogo(gp); } - runtime_printtrace(tb.locbuf, tb.c, false); + slice.__values = &tb.locbuf[0]; + slice.__count = tb.c; + slice.__capacity = tb.c; + runtime_printtrace(slice, nil); runtime_printcreatedby(gp); } } @@ -3597,3 +3548,28 @@ sync_runtime_doSpin() { runtime_procyield(ACTIVE_SPIN_CNT); } + +// For Go code to look at variables, until we port proc.go. + +extern M** runtime_go_allm(void) + __asm__ (GOSYM_PREFIX "runtime.allm"); + +M** +runtime_go_allm() +{ + return &runtime_allm; +} + +extern Slice runtime_go_allgs(void) + __asm__ (GOSYM_PREFIX "runtime.allgs"); + +Slice +runtime_go_allgs() +{ + Slice s; + + s.__values = runtime_allg; + s.__count = runtime_allglen; + s.__capacity = allgcap; + return s; +} diff --git a/libgo/runtime/runtime.c b/libgo/runtime/runtime.c index 8e6f1f50524..70331f4767e 100644 --- a/libgo/runtime/runtime.c +++ b/libgo/runtime/runtime.c @@ -90,18 +90,6 @@ runtime_cputicks(void) #endif } -bool -runtime_showframe(String s, bool current) -{ - static int32 traceback = -1; - - if(current && runtime_m()->throwing > 0) - return 1; - if(traceback < 0) - traceback = runtime_gotraceback(nil); - return traceback > 1 || (__builtin_memchr(s.str, '.', s.len) != nil && __builtin_memcmp(s.str, "runtime.", 7) != 0); -} - // Called to initialize a new m (including the bootstrap m). // Called on the parent thread (main thread in case of bootstrap), can allocate memory. void diff --git a/libgo/runtime/runtime.h b/libgo/runtime/runtime.h index 27b0649f30f..be19e9527cc 100644 --- a/libgo/runtime/runtime.h +++ b/libgo/runtime/runtime.h @@ -89,7 +89,7 @@ typedef struct __go_interface_type InterfaceType; typedef struct __go_map_type MapType; typedef struct __go_channel_type ChanType; -typedef struct traceback Traceback; +typedef struct tracebackg Traceback; typedef struct location Location; @@ -261,8 +261,10 @@ enum { }; void runtime_hashinit(void); -void runtime_traceback(void); -void runtime_tracebackothers(G*); +void runtime_traceback(void) + __asm__ (GOSYM_PREFIX "runtime.traceback"); +void runtime_tracebackothers(G*) + __asm__ (GOSYM_PREFIX "runtime.tracebackothers"); enum { // The maximum number of frames we print for a traceback @@ -325,8 +327,10 @@ void runtime_sigenable(uint32 sig); void runtime_sigdisable(uint32 sig); void runtime_sigignore(uint32 sig); int32 runtime_gotraceback(bool *crash); -void runtime_goroutineheader(G*); -void runtime_printtrace(Location*, int32, bool); +void runtime_goroutineheader(G*) + __asm__ (GOSYM_PREFIX "runtime.goroutineheader"); +void runtime_printtrace(Slice, G*) + __asm__ (GOSYM_PREFIX "runtime.printtrace"); #define runtime_open(p, f, m) open((p), (f), (m)) #define runtime_read(d, v, n) read((d), (v), (n)) #define runtime_write(d, v, n) write((d), (v), (n)) @@ -561,8 +565,8 @@ void runtime_lockOSThread(void); void runtime_unlockOSThread(void); bool runtime_lockedOSThread(void); -bool runtime_showframe(String, bool); -void runtime_printcreatedby(G*); +void runtime_printcreatedby(G*) + __asm__(GOSYM_PREFIX "runtime.printcreatedby"); uintptr runtime_memlimit(void);