aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/mprof.go
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2014-11-05 23:01:48 -0500
committerRuss Cox <rsc@golang.org>2014-11-05 23:01:48 -0500
commit39bcbb353c6bf2e13eb0d3585fe82d3cab6df78d (patch)
tree6cc99e45ddff3a889208f3f238044ea4dc85fa8a /src/runtime/mprof.go
parent2d0db8e591513a1123057b8c330c946ddcb4fbc8 (diff)
downloadgo-39bcbb353c6bf2e13eb0d3585fe82d3cab6df78d.tar.gz
go-39bcbb353c6bf2e13eb0d3585fe82d3cab6df78d.zip
runtime: avoid gentraceback of self on user goroutine stack
Gentraceback may grow the stack. One of the gentraceback wrappers may grow the stack. One of the gentraceback callback calls may grow the stack. Various stack pointers are stored in various stack locations as type uintptr during the execution of these calls. If the stack does grow, these stack pointers will not be updated and will start trying to decode stack memory that is no longer valid. It may be possible to change the type of the stack pointer variables to be unsafe.Pointer, but that's pretty subtle and may still have problems, even if we catch every last one. An easier, more obviously correct fix is to require that gentraceback of the currently running goroutine must run on the g0 stack, not on the goroutine's own stack. Not doing this causes faults when you set StackFromSystem = 1 StackFaultOnFree = 1 The new check in gentraceback will catch future lapses. The more general problem is calling getcallersp but then calling a function that might relocate the stack, which would invalidate the result of getcallersp. Add note to stubs.go declaration of getcallersp explaining the problem, and check all existing calls to getcallersp. Most needed fixes. This affects Callers, Stack, and nearly all the runtime profiling routines. It does not affect stack copying directly nor garbage collection. LGTM=khr R=khr, bradfitz CC=golang-codereviews, r https://golang.org/cl/167060043
Diffstat (limited to 'src/runtime/mprof.go')
-rw-r--r--src/runtime/mprof.go43
1 files changed, 28 insertions, 15 deletions
diff --git a/src/runtime/mprof.go b/src/runtime/mprof.go
index d64e3be695..d409c6c306 100644
--- a/src/runtime/mprof.go
+++ b/src/runtime/mprof.go
@@ -528,8 +528,6 @@ var allgs []*g // proc.c
// Most clients should use the runtime/pprof package instead
// of calling GoroutineProfile directly.
func GoroutineProfile(p []StackRecord) (n int, ok bool) {
- sp := getcallersp(unsafe.Pointer(&p))
- pc := getcallerpc(unsafe.Pointer(&p))
n = NumGoroutine()
if n <= len(p) {
@@ -542,7 +540,11 @@ func GoroutineProfile(p []StackRecord) (n int, ok bool) {
if n <= len(p) {
ok = true
r := p
- saveg(pc, sp, gp, &r[0])
+ sp := getcallersp(unsafe.Pointer(&p))
+ pc := getcallerpc(unsafe.Pointer(&p))
+ onM(func() {
+ saveg(pc, sp, gp, &r[0])
+ })
r = r[1:]
for _, gp1 := range allgs {
if gp1 == gp || readgstatus(gp1) == _Gdead {
@@ -573,8 +575,6 @@ func saveg(pc, sp uintptr, gp *g, r *StackRecord) {
// 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 {
- sp := getcallersp(unsafe.Pointer(&buf))
- pc := getcallerpc(unsafe.Pointer(&buf))
mp := acquirem()
gp := mp.curg
if all {
@@ -589,14 +589,19 @@ func Stack(buf []byte, all bool) int {
n := 0
if len(buf) > 0 {
- gp.writebuf = buf[0:0:len(buf)]
- goroutineheader(gp)
- traceback(pc, sp, 0, gp)
- if all {
- tracebackothers(gp)
- }
- n = len(gp.writebuf)
- gp.writebuf = nil
+ sp := getcallersp(unsafe.Pointer(&buf))
+ pc := getcallerpc(unsafe.Pointer(&buf))
+ onM(func() {
+ g0 := getg()
+ g0.writebuf = buf[0:0:len(buf)]
+ goroutineheader(gp)
+ traceback(pc, sp, 0, gp)
+ if all {
+ tracebackothers(gp)
+ }
+ n = len(g0.writebuf)
+ g0.writebuf = nil
+ })
}
if all {
@@ -623,7 +628,11 @@ func tracealloc(p unsafe.Pointer, size uintptr, typ *_type) {
}
if gp.m.curg == nil || gp == gp.m.curg {
goroutineheader(gp)
- traceback(getcallerpc(unsafe.Pointer(&p)), getcallersp(unsafe.Pointer(&p)), 0, gp)
+ pc := getcallerpc(unsafe.Pointer(&p))
+ sp := getcallersp(unsafe.Pointer(&p))
+ onM(func() {
+ traceback(pc, sp, 0, gp)
+ })
} else {
goroutineheader(gp.m.curg)
traceback(^uintptr(0), ^uintptr(0), 0, gp.m.curg)
@@ -639,7 +648,11 @@ func tracefree(p unsafe.Pointer, size uintptr) {
gp.m.traceback = 2
print("tracefree(", p, ", ", hex(size), ")\n")
goroutineheader(gp)
- traceback(getcallerpc(unsafe.Pointer(&p)), getcallersp(unsafe.Pointer(&p)), 0, gp)
+ pc := getcallerpc(unsafe.Pointer(&p))
+ sp := getcallersp(unsafe.Pointer(&p))
+ onM(func() {
+ traceback(pc, sp, 0, gp)
+ })
print("\n")
gp.m.traceback = 0
unlock(&tracelock)