aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/cpuprof.go
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2016-06-07 21:46:25 -0700
committerIan Lance Taylor <iant@golang.org>2016-06-13 21:43:19 +0000
commit84d8aff94cf48439047c7edc68ae2ea0aac6ddf5 (patch)
tree0d7fd4e7b480ce2f9972646e9abe9463c3bc7a4d /src/runtime/cpuprof.go
parent5701174c52a2d42621ec3c5c59dca3bde9a14bc6 (diff)
downloadgo-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.go40
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
}