diff options
author | Ian Lance Taylor <iant@golang.org> | 2016-06-07 21:46:25 -0700 |
---|---|---|
committer | Ian Lance Taylor <iant@golang.org> | 2016-06-13 21:43:19 +0000 |
commit | 84d8aff94cf48439047c7edc68ae2ea0aac6ddf5 (patch) | |
tree | 0d7fd4e7b480ce2f9972646e9abe9463c3bc7a4d /src/runtime/cpuprof.go | |
parent | 5701174c52a2d42621ec3c5c59dca3bde9a14bc6 (diff) | |
download | go-84d8aff94cf48439047c7edc68ae2ea0aac6ddf5.tar.gz go-84d8aff94cf48439047c7edc68ae2ea0aac6ddf5.zip |
runtime: collect stack trace if SIGPROF arrives on non-Go thread
Fixes #15994.
Change-Id: I5aca91ab53985ac7dcb07ce094ec15eb8ec341f8
Reviewed-on: https://go-review.googlesource.com/23891
Run-TryBot: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Diffstat (limited to 'src/runtime/cpuprof.go')
-rw-r--r-- | src/runtime/cpuprof.go | 40 |
1 files changed, 33 insertions, 7 deletions
diff --git a/src/runtime/cpuprof.go b/src/runtime/cpuprof.go index b9a6b88b0c..53082006d0 100644 --- a/src/runtime/cpuprof.go +++ b/src/runtime/cpuprof.go @@ -193,7 +193,20 @@ func SetCPUProfileRate(hz int) { // and cannot allocate memory or acquire locks that might be // held at the time of the signal, nor can it use substantial amounts // of stack. It is allowed to call evict. +//go:nowritebarrierrec func (p *cpuProfile) add(pc []uintptr) { + p.addWithFlushlog(pc, p.flushlog) +} + +// addWithFlushlog implements add and addNonGo. +// It is called from signal handlers and other limited environments +// and cannot allocate memory or acquire locks that might be +// held at the time of the signal, nor can it use substantial amounts +// of stack. It may be called by a signal handler with no g or m. +// It is allowed to call evict, passing the flushlog parameter. +//go:nosplit +//go:nowritebarrierrec +func (p *cpuProfile) addWithFlushlog(pc []uintptr, flushlog func() bool) { if len(pc) > maxCPUProfStack { pc = pc[:maxCPUProfStack] } @@ -231,7 +244,7 @@ Assoc: } } if e.count > 0 { - if !p.evict(e) { + if !p.evict(e, flushlog) { // Could not evict entry. Record lost stack. p.lost++ return @@ -248,15 +261,17 @@ Assoc: // evict copies the given entry's data into the log, so that // the entry can be reused. evict is called from add, which // is called from the profiling signal handler, so it must not -// allocate memory or block. It is safe to call flushlog. -// evict returns true if the entry was copied to the log, -// false if there was no room available. -func (p *cpuProfile) evict(e *cpuprofEntry) bool { +// allocate memory or block, and it may be called with no g or m. +// It is safe to call flushlog. evict returns true if the entry was +// copied to the log, false if there was no room available. +//go:nosplit +//go:nowritebarrierrec +func (p *cpuProfile) evict(e *cpuprofEntry, flushlog func() bool) bool { d := e.depth nslot := d + 2 log := &p.log[p.toggle] if p.nlog+nslot > len(log) { - if !p.flushlog() { + if !flushlog() { return false } log = &p.log[p.toggle] @@ -278,6 +293,7 @@ func (p *cpuProfile) evict(e *cpuprofEntry) bool { // flushlog is called from evict, called from add, called from the signal handler, // so it cannot allocate memory or block. It can try to swap logs with // the writing goroutine, as explained in the comment at the top of this file. +//go:nowritebarrierrec func (p *cpuProfile) flushlog() bool { if !atomic.Cas(&p.handoff, 0, uint32(p.nlog)) { return false @@ -299,6 +315,16 @@ func (p *cpuProfile) flushlog() bool { return true } +// addNonGo is like add, but runs on a non-Go thread. +// It can't do anything that might need a g or an m. +// With this entry point, we don't try to flush the log when evicting an +// old entry. Instead, we just drop the stack trace if we're out of space. +//go:nosplit +//go:nowritebarrierrec +func (p *cpuProfile) addNonGo(pc []uintptr) { + p.addWithFlushlog(pc, func() bool { return false }) +} + // getprofile blocks until the next block of profiling data is available // and returns it as a []byte. It is called from the writing goroutine. func (p *cpuProfile) getprofile() []byte { @@ -366,7 +392,7 @@ Flush: b := &p.hash[i] for j := range b.entry { e := &b.entry[j] - if e.count > 0 && !p.evict(e) { + if e.count > 0 && !p.evict(e, p.flushlog) { // Filled the log. Stop the loop and return what we've got. break Flush } |