mirror of
https://github.com/golang/go
synced 2024-11-24 06:20:02 -07:00
runtime: bypass scheduler when doing traceback for goroutine profile
When acquire a goroutine profile, we stop the world then acquire a stack trace for each goroutine. When cgo traceback is used, the traceback code may call the cgo traceback function using cgocall. As the world is stopped, cgocall will be blocked at exitsyscall, causing a deadlock. Bypass the scheduler (using asmcgocall) to fix this. Change-Id: Ic4e596adc3711310b6a983d73786d697ef15dd72 Reviewed-on: https://go-review.googlesource.com/c/go/+/362757 Trust: Cherry Mui <cherryyz@google.com> Run-TryBot: Cherry Mui <cherryyz@google.com> Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
parent
47b3ab5ede
commit
8c73f80400
@ -702,3 +702,11 @@ func TestNeedmDeadlock(t *testing.T) {
|
||||
t.Fatalf("want %s, got %s\n", want, output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCgoTracebackGoroutineProfile(t *testing.T) {
|
||||
output := runTestProg(t, "testprogcgo", "GoroutineProfile")
|
||||
want := "OK\n"
|
||||
if output != want {
|
||||
t.Fatalf("want %s, got %s\n", want, output)
|
||||
}
|
||||
}
|
||||
|
@ -805,7 +805,11 @@ func goroutineProfileWithLabels(p []StackRecord, labels []unsafe.Pointer) (n int
|
||||
// truncated profile than to crash the entire process.
|
||||
return
|
||||
}
|
||||
saveg(^uintptr(0), ^uintptr(0), gp1, &r[0])
|
||||
// saveg calls gentraceback, which may call cgo traceback functions.
|
||||
// The world is stopped, so it cannot use cgocall (which will be
|
||||
// blocked at exitsyscall). Do it on the system stack so it won't
|
||||
// call into the schedular (see traceback.go:cgoContextPCs).
|
||||
systemstack(func() { saveg(^uintptr(0), ^uintptr(0), gp1, &r[0]) })
|
||||
if labels != nil {
|
||||
lbl[0] = gp1.labels
|
||||
lbl = lbl[1:]
|
||||
|
46
src/runtime/testdata/testprogcgo/gprof.go
vendored
Normal file
46
src/runtime/testdata/testprogcgo/gprof.go
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
package main
|
||||
|
||||
// Test taking a goroutine profile with C traceback.
|
||||
|
||||
/*
|
||||
// Defined in gprof_c.c.
|
||||
void CallGoSleep(void);
|
||||
void gprofCgoTraceback(void* parg);
|
||||
void gprofCgoContext(void* parg);
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func init() {
|
||||
register("GoroutineProfile", GoroutineProfile)
|
||||
}
|
||||
|
||||
func GoroutineProfile() {
|
||||
runtime.SetCgoTraceback(0, unsafe.Pointer(C.gprofCgoTraceback), unsafe.Pointer(C.gprofCgoContext), nil)
|
||||
|
||||
go C.CallGoSleep()
|
||||
go C.CallGoSleep()
|
||||
go C.CallGoSleep()
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
prof := pprof.Lookup("goroutine")
|
||||
prof.WriteTo(io.Discard, 1)
|
||||
fmt.Println("OK")
|
||||
}
|
||||
|
||||
//export GoSleep
|
||||
func GoSleep() {
|
||||
time.Sleep(time.Hour)
|
||||
}
|
29
src/runtime/testdata/testprogcgo/gprof_c.c
vendored
Normal file
29
src/runtime/testdata/testprogcgo/gprof_c.c
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
// The C definitions for gprof.go. That file uses //export so
|
||||
// it can't put function definitions in the "C" import comment.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// Functions exported from Go.
|
||||
extern void GoSleep();
|
||||
|
||||
struct cgoContextArg {
|
||||
uintptr_t context;
|
||||
};
|
||||
|
||||
void gprofCgoContext(void *arg) {
|
||||
((struct cgoContextArg*)arg)->context = 1;
|
||||
}
|
||||
|
||||
void gprofCgoTraceback(void *arg) {
|
||||
// spend some time here so the P is more likely to be retaken.
|
||||
for (volatile int i = 0; i < 123456789; i++);
|
||||
}
|
||||
|
||||
void CallGoSleep() {
|
||||
GoSleep();
|
||||
}
|
Loading…
Reference in New Issue
Block a user