diff --git a/_fixtures/cgosigsegvstack.go b/_fixtures/cgosigsegvstack.go new file mode 100644 index 00000000..84e455d6 --- /dev/null +++ b/_fixtures/cgosigsegvstack.go @@ -0,0 +1,18 @@ +package main + +// #cgo CFLAGS: -g -Wall -O0 + +/* +void sigsegv(int x) { + int *p = NULL; + *p = x; +} +void testfn(int x) { + sigsegv(x); +} +*/ +import "C" + +func main() { + C.testfn(C.int(10)) +} diff --git a/cmd/dlv/main.go b/cmd/dlv/main.go index 9a89eb8e..34df91db 100644 --- a/cmd/dlv/main.go +++ b/cmd/dlv/main.go @@ -17,7 +17,7 @@ func main() { } const cgoCflagsEnv = "CGO_CFLAGS" if os.Getenv(cgoCflagsEnv) == "" { - os.Setenv(cgoCflagsEnv, "-O -g") + os.Setenv(cgoCflagsEnv, "-O0 -g") } else { logrus.WithFields(logrus.Fields{"layer": "dlv"}).Warnln("CGO_CFLAGS already set, Cgo code could be optimized.") } diff --git a/pkg/proc/proc_test.go b/pkg/proc/proc_test.go index 78efa4cd..0510f1f0 100644 --- a/pkg/proc/proc_test.go +++ b/pkg/proc/proc_test.go @@ -4464,3 +4464,18 @@ func TestIssue1615(t *testing.T) { assertLineNumber(p, t, 19, "") }) } + +func TestCgoStacktrace2(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("fixture crashes go runtime on windows") + } + // If a panic happens during cgo execution the stacktrace should show the C + // function that caused the problem. + withTestProcess("cgosigsegvstack", t, func(p proc.Process, fixture protest.Fixture) { + proc.Continue(p) + frames, err := proc.ThreadStacktrace(p.CurrentThread(), 100) + assertNoError(err, t, "Stacktrace()") + logStacktrace(t, p.BinInfo(), frames) + stacktraceCheck(t, []string{"C.sigsegv", "C.testfn", "main.main"}, frames) + }) +} diff --git a/pkg/proc/stack.go b/pkg/proc/stack.go index a5279ecf..c28af74d 100644 --- a/pkg/proc/stack.go +++ b/pkg/proc/stack.go @@ -316,8 +316,28 @@ func (it *stackIterator) switchStack() bool { it.atend = true return true + case "runtime.mstart": + // Calls to runtime.systemstack will switch to the systemstack then: + // 1. alter the goroutine stack so that it looks like systemstack_switch + // was called + // 2. alter the system stack so that it looks like the bottom-most frame + // belongs to runtime.mstart + // If we find a runtime.mstart frame on the system stack of a goroutine + // parked on runtime.systemstack_switch we assume runtime.systemstack was + // called and continue tracing from the parked position. + + if it.top || !it.systemstack || it.g == nil { + return false + } + if fn := it.bi.PCToFunc(it.g.PC); fn == nil || fn.Name != "runtime.systemstack_switch" { + return false + } + + it.switchToGoroutineStack() + return true + default: - if it.systemstack && it.top && it.g != nil && strings.HasPrefix(it.frame.Current.Fn.Name, "runtime.") { + if it.systemstack && it.top && it.g != nil && strings.HasPrefix(it.frame.Current.Fn.Name, "runtime.") && it.frame.Current.Fn.Name != "runtime.fatalthrow" { // The runtime switches to the system stack in multiple places. // This usually happens through a call to runtime.systemstack but there // are functions that switch to the system stack manually (for example @@ -325,11 +345,12 @@ func (it *stackIterator) switchStack() bool { // Since we are only interested in printing the system stack for cgo // calls we switch directly to the goroutine stack if we detect that the // function at the top of the stack is a runtime function. - it.systemstack = false - it.top = false - it.pc = it.g.PC - it.regs.Reg(it.regs.SPRegNum).Uint64Val = it.g.SP - it.regs.Reg(it.regs.BPRegNum).Uint64Val = it.g.BP + // + // The function "runtime.fatalthrow" is deliberately excluded from this + // because it can end up in the stack during a cgo call and switching to + // the goroutine stack will exclude all the C functions from the stack + // trace. + it.switchToGoroutineStack() return true } @@ -337,6 +358,14 @@ func (it *stackIterator) switchStack() bool { } } +func (it *stackIterator) switchToGoroutineStack() { + it.systemstack = false + it.top = false + it.pc = it.g.PC + it.regs.Reg(it.regs.SPRegNum).Uint64Val = it.g.SP + it.regs.Reg(it.regs.BPRegNum).Uint64Val = it.g.BP +} + // Frame returns the frame the iterator is pointing at. func (it *stackIterator) Frame() Stackframe { it.frame.Bottom = it.atend