6bd37418a3
In signal-triggered stack scan, if the signal is delivered at certain bad time (e.g. in vdso, or in the middle of setcontext?), the unwinder may not be able to unwind the whole stack, while it still reports _URC_END_OF_STACK. So we cannot rely on _URC_END_OF_STACK to tell if it successfully scanned the stack. Instead, we check the last Go frame to see it actually reached the end of the stack. For Go-created stack, this is runtime.kickoff. For C-created stack, we need to record the outermost Go frame when it enters the Go side. Also we cannot unwind the stack if the signal is delivered in the middle of runtime.gogo, halfway through a goroutine switch, where the g and the stack don't match. Give up in this case as well. Reviewed-on: https://go-review.googlesource.com/c/159098 From-SVN: r269018
163 lines
4.4 KiB
Go
163 lines
4.4 KiB
Go
// Copyright 2016 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 runtime
|
|
|
|
import (
|
|
"runtime/internal/atomic"
|
|
"unsafe"
|
|
)
|
|
|
|
// For historical reasons these functions are called as though they
|
|
// were in the syscall package.
|
|
//go:linkname Cgocall syscall.Cgocall
|
|
//go:linkname CgocallDone syscall.CgocallDone
|
|
//go:linkname CgocallBack syscall.CgocallBack
|
|
//go:linkname CgocallBackDone syscall.CgocallBackDone
|
|
|
|
// A routine that may be called by SWIG.
|
|
//go:linkname _cgo_panic _cgo_panic
|
|
|
|
// iscgo is set to true if the cgo tool sets the C variable runtime_iscgo
|
|
// to true.
|
|
var iscgo bool
|
|
|
|
// cgoHasExtraM is set on startup when an extra M is created for cgo.
|
|
// The extra M must be created before any C/C++ code calls cgocallback.
|
|
var cgoHasExtraM bool
|
|
|
|
// cgoAlwaysFalse is a boolean value that is always false.
|
|
// The cgo-generated code says if cgoAlwaysFalse { cgoUse(p) }.
|
|
// The compiler cannot see that cgoAlwaysFalse is always false,
|
|
// so it emits the test and keeps the call, giving the desired
|
|
// escape analysis result. The test is cheaper than the call.
|
|
var cgoAlwaysFalse bool
|
|
|
|
// Cgocall prepares to call from code written in Go to code written in
|
|
// C/C++. This takes the current goroutine out of the Go scheduler, as
|
|
// though it were making a system call. Otherwise the program can
|
|
// lookup if the C code blocks. The idea is to call this function,
|
|
// then immediately call the C/C++ function. After the C/C++ function
|
|
// returns, call cgocalldone. The usual Go code would look like
|
|
// syscall.Cgocall()
|
|
// defer syscall.Cgocalldone()
|
|
// cfunction()
|
|
func Cgocall() {
|
|
mp := getg().m
|
|
mp.ncgocall++
|
|
mp.ncgo++
|
|
entersyscall()
|
|
mp.incgo = true
|
|
}
|
|
|
|
// CgocallDone prepares to return to Go code from C/C++ code.
|
|
func CgocallDone() {
|
|
gp := getg()
|
|
if gp == nil {
|
|
throw("no g in CgocallDone")
|
|
}
|
|
gp.m.incgo = false
|
|
gp.m.ncgo--
|
|
|
|
// If we are invoked because the C function called _cgo_panic,
|
|
// then _cgo_panic will already have exited syscall mode.
|
|
if readgstatus(gp)&^_Gscan == _Gsyscall {
|
|
exitsyscall()
|
|
}
|
|
}
|
|
|
|
// CgocallBack is used when calling from C/C++ code into Go code.
|
|
// The usual approach is
|
|
// syscall.CgocallBack()
|
|
// defer syscall.CgocallBackDone()
|
|
// gofunction()
|
|
//go:nosplit
|
|
func CgocallBack() {
|
|
gp := getg()
|
|
if gp == nil || gp.m == nil {
|
|
needm(0)
|
|
gp = getg()
|
|
mp := gp.m
|
|
mp.dropextram = true
|
|
|
|
// This is a C-created stack.
|
|
// Record the outermost Go frame to help stack scan.
|
|
gp.entrysp = getcallersp()
|
|
}
|
|
|
|
lockOSThread()
|
|
|
|
gp.m.incgo = false
|
|
exitsyscall()
|
|
|
|
if gp.m.ncgo == 0 {
|
|
// The C call to Go came from a thread created by C.
|
|
// The C call to Go came from a thread not currently running
|
|
// any Go. In the case of -buildmode=c-archive or c-shared,
|
|
// this call may be coming in before package initialization
|
|
// is complete. Wait until it is.
|
|
<-main_init_done
|
|
}
|
|
|
|
mp := gp.m
|
|
if mp.needextram || atomic.Load(&extraMWaiters) > 0 {
|
|
mp.needextram = false
|
|
newextram()
|
|
}
|
|
}
|
|
|
|
// CgocallBackDone prepares to return to C/C++ code that has called
|
|
// into Go code.
|
|
func CgocallBackDone() {
|
|
unlockOSThread()
|
|
|
|
// We are going to stop running in Go mode and return to C mode.
|
|
// We were almost certainly called by defer; if so, clean up
|
|
// the defer struct now, before we leave Go mode. But don't
|
|
// leave Go mode if we are panicing or called from Goexit,
|
|
// since in those cases we will continue executing deferred functions.
|
|
gp := getg()
|
|
mp := gp.m
|
|
drop := false
|
|
if gp.deferring && gp._panic == nil && !gp.goexiting {
|
|
d := gp._defer
|
|
if d == nil {
|
|
throw("no defer struct when deferring")
|
|
}
|
|
gp._defer = d.link
|
|
freedefer(d)
|
|
|
|
// If we are the top level Go function called from C,
|
|
// then we need to release the m.
|
|
if mp.dropextram && mp.ncgo == 0 {
|
|
drop = true
|
|
}
|
|
}
|
|
|
|
// Don't go back to C mode if we are panicing. Just let the
|
|
// panic walk up through the Go stack.
|
|
if gp._panic == nil && !gp.goexiting {
|
|
gp.m.incgo = true
|
|
entersyscall()
|
|
}
|
|
|
|
if drop {
|
|
mp.dropextram = false
|
|
dropm()
|
|
} else if gp.deferring && gp._panic == nil && !gp.goexiting {
|
|
gp.ranCgocallBackDone = true
|
|
}
|
|
}
|
|
|
|
// _cgo_panic may be called by SWIG code to panic.
|
|
func _cgo_panic(p *byte) {
|
|
exitsyscall()
|
|
panic(gostringnocopy(p))
|
|
}
|
|
|
|
// cgo_yield exists in the gc toolchain to let TSAN deliver a signal.
|
|
// gccgo does not need this.
|
|
var cgo_yield = &_cgo_yield
|
|
var _cgo_yield unsafe.Pointer
|