mirror of
https://github.com/golang/go
synced 2024-09-15 22:20:06 +00:00
runtime: fix crash in runtime.GoroutineProfile
This is a possible Go 1.2.1 candidate. Fixes #6946. R=iant, r CC=golang-dev https://golang.org/cl/41640043
This commit is contained in:
parent
c134ce272f
commit
bc135f6492
|
@ -1515,27 +1515,7 @@ scanframe(Stkframe *frame, void *arg)
|
|||
static void
|
||||
scanstack(G* gp, void *scanbuf)
|
||||
{
|
||||
uintptr pc;
|
||||
uintptr sp;
|
||||
uintptr lr;
|
||||
|
||||
if(gp->syscallstack != (uintptr)nil) {
|
||||
// Scanning another goroutine that is about to enter or might
|
||||
// have just exited a system call. It may be executing code such
|
||||
// as schedlock and may have needed to start a new stack segment.
|
||||
// Use the stack segment and stack pointer at the time of
|
||||
// the system call instead, since that won't change underfoot.
|
||||
sp = gp->syscallsp;
|
||||
pc = gp->syscallpc;
|
||||
lr = 0;
|
||||
} else {
|
||||
// Scanning another goroutine's stack.
|
||||
// The goroutine is usually asleep (the world is stopped).
|
||||
sp = gp->sched.sp;
|
||||
pc = gp->sched.pc;
|
||||
lr = gp->sched.lr;
|
||||
}
|
||||
runtime·gentraceback(pc, sp, lr, gp, 0, nil, 0x7fffffff, scanframe, scanbuf, false);
|
||||
runtime·gentraceback(~(uintptr)0, ~(uintptr)0, 0, gp, 0, nil, 0x7fffffff, scanframe, scanbuf, false);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
@ -528,7 +528,7 @@ saveg(uintptr pc, uintptr sp, G *gp, TRecord *r)
|
|||
{
|
||||
int32 n;
|
||||
|
||||
n = runtime·gentraceback((uintptr)pc, (uintptr)sp, 0, gp, 0, r->stk, nelem(r->stk), nil, nil, false);
|
||||
n = runtime·gentraceback(pc, sp, 0, gp, 0, r->stk, nelem(r->stk), nil, nil, false);
|
||||
if(n < nelem(r->stk))
|
||||
r->stk[n] = 0;
|
||||
}
|
||||
|
@ -556,7 +556,7 @@ func GoroutineProfile(b Slice) (n int, ok bool) {
|
|||
for(gp = runtime·allg; gp != nil; gp = gp->alllink) {
|
||||
if(gp == g || gp->status == Gdead)
|
||||
continue;
|
||||
saveg(gp->sched.pc, gp->sched.sp, gp, r++);
|
||||
saveg(~(uintptr)0, ~(uintptr)0, gp, r++);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -276,7 +276,7 @@ runtime·tracebackothers(G *me)
|
|||
if((gp = m->curg) != nil && gp != me) {
|
||||
runtime·printf("\n");
|
||||
runtime·goroutineheader(gp);
|
||||
runtime·traceback(gp->sched.pc, gp->sched.sp, gp->sched.lr, gp);
|
||||
runtime·traceback(~(uintptr)0, ~(uintptr)0, 0, gp);
|
||||
}
|
||||
|
||||
for(gp = runtime·allg; gp != nil; gp = gp->alllink) {
|
||||
|
@ -290,7 +290,7 @@ runtime·tracebackothers(G *me)
|
|||
runtime·printf("\tgoroutine running on other thread; stack unavailable\n");
|
||||
runtime·printcreatedby(gp);
|
||||
} else
|
||||
runtime·traceback(gp->sched.pc, gp->sched.sp, gp->sched.lr, gp);
|
||||
runtime·traceback(~(uintptr)0, ~(uintptr)0, 0, gp);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
56
src/pkg/runtime/runtime_unix_test.go
Normal file
56
src/pkg/runtime/runtime_unix_test.go
Normal file
|
@ -0,0 +1,56 @@
|
|||
// Copyright 2013 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.
|
||||
|
||||
// Only works on systems with syscall.Close.
|
||||
// We need a fast system call to provoke the race,
|
||||
// and Close(-1) is nearly universally fast.
|
||||
|
||||
// +build darwin dragonfly freebsd linux netbsd openbsd plan9
|
||||
|
||||
package runtime_test
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGoroutineProfile(t *testing.T) {
|
||||
// GoroutineProfile used to use the wrong starting sp for
|
||||
// goroutines coming out of system calls, causing possible
|
||||
// crashes.
|
||||
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(100))
|
||||
|
||||
var stop uint32
|
||||
defer atomic.StoreUint32(&stop, 1) // in case of panic
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 4; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
for atomic.LoadUint32(&stop) == 0 {
|
||||
syscall.Close(-1)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
|
||||
max := 10000
|
||||
if testing.Short() {
|
||||
max = 100
|
||||
}
|
||||
stk := make([]runtime.StackRecord, 100)
|
||||
for n := 0; n < max; n++ {
|
||||
_, ok := runtime.GoroutineProfile(stk)
|
||||
if !ok {
|
||||
t.Fatalf("GoroutineProfile failed")
|
||||
}
|
||||
}
|
||||
|
||||
// If the program didn't crash, we passed.
|
||||
atomic.StoreUint32(&stop, 1)
|
||||
wg.Wait()
|
||||
}
|
|
@ -20,6 +20,18 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip,
|
|||
Stktop *stk;
|
||||
String file;
|
||||
|
||||
if(pc0 == ~(uintptr)0 && sp0 == ~(uintptr)0) { // Signal to fetch saved values from gp.
|
||||
if(gp->syscallstack != (uintptr)nil) {
|
||||
pc0 = gp->syscallpc;
|
||||
sp0 = gp->syscallsp;
|
||||
lr0 = 0;
|
||||
} else {
|
||||
pc0 = gp->sched.pc;
|
||||
sp0 = gp->sched.sp;
|
||||
lr0 = gp->sched.lr;
|
||||
}
|
||||
}
|
||||
|
||||
nprint = 0;
|
||||
runtime·memclr((byte*)&frame, sizeof frame);
|
||||
frame.pc = pc0;
|
||||
|
|
|
@ -30,6 +30,16 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip,
|
|||
String file;
|
||||
|
||||
USED(lr0);
|
||||
|
||||
if(pc0 == ~(uintptr)0 && sp0 == ~(uintptr)0) { // Signal to fetch saved values from gp.
|
||||
if(gp->syscallstack != (uintptr)nil) {
|
||||
pc0 = gp->syscallpc;
|
||||
sp0 = gp->syscallsp;
|
||||
} else {
|
||||
pc0 = gp->sched.pc;
|
||||
sp0 = gp->sched.sp;
|
||||
}
|
||||
}
|
||||
|
||||
nprint = 0;
|
||||
runtime·memclr((byte*)&frame, sizeof frame);
|
||||
|
|
Loading…
Reference in a new issue