If we're in a libc call and get a trap, don't try to traceback the libc call.
Start from the state we had at entry to libc.
If there are multiple libc calls outstanding, remember the outermost one.
Fixes #26393
Change-Id: Icfe8794b95bf3bfd1a0679b456dcde2481dcabf3
Reviewed-on: https://go-review.googlesource.com/124195
Reviewed-by: Austin Clements <austin@google.com>
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
if gp != nil {
mp = gp.m
}
- if mp != nil {
+ if mp != nil && mp.libcallsp == 0 {
mp.libcallg.set(gp)
mp.libcallpc = getcallerpc()
// sp must be the last, because once async cpu profiler finds
// all three values to be non-zero, it will use them
mp.libcallsp = getcallersp()
+ } else {
+ mp = nil // See comment in sys_darwin.go:libcCall
}
var libcall libcall
if gp != nil {
mp = gp.m
}
- if mp != nil {
+ if mp != nil && mp.libcallsp == 0 {
mp.libcallg.set(gp)
mp.libcallpc = getcallerpc()
// sp must be the last, because once async cpu profiler finds
// all three values to be non-zero, it will use them
mp.libcallsp = getcallersp()
+ } else {
+ mp = nil
}
var libcall libcall
if gp != nil {
mp = gp.m
}
- if mp != nil {
+ if mp != nil && mp.libcallsp == 0 {
mp.libcallg.set(gp)
mp.libcallpc = getcallerpc()
// sp must be the last, because once async cpu profiler finds
// all three values to be non-zero, it will use them
mp.libcallsp = getcallersp()
+ } else {
+ mp = nil
}
var libcall libcall
if gp != nil {
mp = gp.m
}
- if mp != nil {
+ if mp != nil && mp.libcallsp == 0 {
mp.libcallg.set(gp)
mp.libcallpc = getcallerpc()
// sp must be the last, because once async cpu profiler finds
// all three values to be non-zero, it will use them
mp.libcallsp = getcallersp()
+ } else {
+ mp = nil
}
var libcall libcall
if gp != nil {
mp = gp.m
}
- if mp != nil {
+ if mp != nil && mp.libcallsp == 0 {
mp.libcallg.set(gp)
mp.libcallpc = getcallerpc()
// sp must be the last, because once async cpu profiler finds
// all three values to be non-zero, it will use them
mp.libcallsp = getcallersp()
+ } else {
+ mp = nil
}
var libcall libcall
if gp != nil {
mp = gp.m
}
- if mp != nil {
+ if mp != nil && mp.libcallsp == 0 {
mp.libcallg.set(gp)
mp.libcallpc = getcallerpc()
// sp must be the last, because once async cpu profiler finds
// all three values to be non-zero, it will use them
mp.libcallsp = getcallersp()
+ } else {
+ mp = nil
}
var libcall libcall
if gp != nil {
mp = gp.m
}
- if mp != nil {
+ if mp != nil && mp.libcallsp == 0 {
mp.libcallg.set(gp)
mp.libcallpc = getcallerpc()
// sp must be the last, because once async cpu profiler finds
// all three values to be non-zero, it will use them
mp.libcallsp = getcallersp()
+ } else {
+ mp = nil
}
var libcall libcall
gp := getg()
mp := gp.m
mp.libcall.fn = uintptr(unsafe.Pointer(fn))
-
- if mp.profilehz != 0 {
+ resetLibcall := false
+ if mp.profilehz != 0 && mp.libcallsp == 0 {
// leave pc/sp for cpu profiler
mp.libcallg.set(gp)
mp.libcallpc = getcallerpc()
// sp must be the last, because once async cpu profiler finds
// all three values to be non-zero, it will use them
mp.libcallsp = getcallersp()
+ resetLibcall = true // See comment in sys_darwin.go:libcCall
}
asmcgocall(asmstdcallAddr, unsafe.Pointer(&mp.libcall))
- mp.libcallsp = 0
+ if resetLibcall {
+ mp.libcallsp = 0
+ }
return mp.libcall.r1
}
if gp != nil {
mp = gp.m
}
- if mp != nil {
+ if mp != nil && mp.libcallsp == 0 {
mp.libcallg.set(gp)
mp.libcallpc = getcallerpc()
// sp must be the last, because once async cpu profiler finds
// all three values to be non-zero, it will use them
mp.libcallsp = getcallersp()
+ } else {
+ // Make sure we don't reset libcallsp. This makes
+ // libcCall reentrant; We remember the g/pc/sp for the
+ // first call on an M, until that libcCall instance
+ // returns. Reentrance only matters for signals, as
+ // libc never calls back into Go. The tricky case is
+ // where we call libcX from an M and record g/pc/sp.
+ // Before that call returns, a signal arrives on the
+ // same M and the signal handling code calls another
+ // libc function. We don't want that second libcCall
+ // from within the handler to be recorded, and we
+ // don't want that call's completion to zero
+ // libcallsp.
+ // We don't need to set libcall* while we're in a sighandler
+ // (even if we're not currently in libc) because we block all
+ // signals while we're handling a signal. That includes the
+ // profile signal, which is the one that uses the libcall* info.
+ mp = nil
}
res := asmcgocall(fn, arg)
if mp != nil {
// the initial PC must not be rewound to the previous instruction.
// (All the saved pairs record a PC that is a return address, so we
// rewind it into the CALL instruction.)
+// If gp.m.libcall{g,pc,sp} information is available, it uses that information in preference to
+// the pc/sp/lr passed in.
func tracebacktrap(pc, sp, lr uintptr, gp *g) {
+ if gp.m.libcallsp != 0 {
+ // We're in C code somewhere, traceback from the saved position.
+ traceback1(gp.m.libcallpc, gp.m.libcallsp, 0, gp.m.libcallg.ptr(), 0)
+ return
+ }
traceback1(pc, sp, lr, gp, _TraceTrap)
}