]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: support capturing C backtrace from signal handler on darwin/amd64
authorNikhil Benesch <nikhil.benesch@gmail.com>
Sat, 24 Mar 2018 22:51:01 +0000 (18:51 -0400)
committerIan Lance Taylor <iant@golang.org>
Wed, 4 Jul 2018 00:18:48 +0000 (00:18 +0000)
The implementation is mostly copied from the commit that added
linux/amd64 support for this feature (https://golang.org/cl/17761).

Change-Id: I3f482167620a7a3daf50a48087f8849a30d713bd
Reviewed-on: https://go-review.googlesource.com/102438
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/runtime/cgo/callbacks_traceback.go
src/runtime/cgo/gcc_traceback.c
src/runtime/cgo/libcgo.h
src/runtime/crash_cgo_test.go
src/runtime/os_darwin.go
src/runtime/sys_darwin_386.s
src/runtime/sys_darwin_amd64.s
src/runtime/sys_darwin_arm.s
src/runtime/sys_darwin_arm64.s

index f754846722647d6e7a334c497ea36df99b8ba061..cdadf9e66f62a4071fc23f3a0f1c1252cf7adac8 100644 (file)
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build linux
+// +build darwin linux
 
 package cgo
 
index 667ea4c0cfd70fc00229cd04e24f9cbe8252ab9b..d86331c583a674cc6d2fa89bbb582ba439999c0c 100644 (file)
@@ -2,17 +2,10 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build cgo
-// +build linux
+// +build cgo,darwin cgo,linux
 
 #include <stdint.h>
-
-struct cgoTracebackArg {
-       uintptr_t  Context;
-       uintptr_t  SigContext;
-       uintptr_t* Buf;
-       uintptr_t  Max;
-};
+#include "libcgo.h"
 
 // Call the user's traceback function and then call sigtramp.
 // The runtime signal handler will jump to this code.
index c38fb643ff7d5e36254cd2f0646d7d675a8501be..60326720a77f135b69965a79e1e307164a273776 100644 (file)
@@ -96,6 +96,16 @@ struct context_arg {
 };
 extern void (*(_cgo_get_context_function(void)))(struct context_arg*);
 
+/*
+ * The argument for the cgo traceback callback. See runtime.SetCgoTraceback.
+ */
+struct cgoTracebackArg {
+       uintptr_t  Context;
+       uintptr_t  SigContext;
+       uintptr_t* Buf;
+       uintptr_t  Max;
+};
+
 /*
  * TSAN support.  This is only useful when building with
  *   CGO_CFLAGS="-fsanitize=thread" CGO_LDFLAGS="-fsanitize=thread" go install
index b2ee8df1f0adc54a86eb3d8e5a408690dbb61b7c..6da8341e8419877fb188b184518606e50bfa88c8 100644 (file)
@@ -239,8 +239,12 @@ func TestCgoCCodeSIGPROF(t *testing.T) {
 
 func TestCgoCrashTraceback(t *testing.T) {
        t.Parallel()
-       if runtime.GOOS != "linux" || (runtime.GOARCH != "amd64" && runtime.GOARCH != "ppc64le") {
-               t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH)
+       switch platform := runtime.GOOS + "/" + runtime.GOARCH; platform {
+       case "darwin/amd64":
+       case "linux/amd64":
+       case "linux/ppc64le":
+       default:
+               t.Skipf("not yet supported on %s", platform)
        }
        got := runTestProg(t, "testprogcgo", "CrashTraceback")
        for i := 1; i <= 3; i++ {
index ff375004a38052612fea675cb86565006f231250..d2144edf2ebdf48debc4c1dfc18693169e181ff1 100644 (file)
@@ -274,7 +274,11 @@ func setsig(i uint32, fn uintptr) {
        sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTART
        sa.sa_mask = ^uint32(0)
        if fn == funcPC(sighandler) {
-               fn = funcPC(sigtramp)
+               if iscgo {
+                       fn = funcPC(cgoSigtramp)
+               } else {
+                       fn = funcPC(sigtramp)
+               }
        }
        *(*uintptr)(unsafe.Pointer(&sa.__sigaction_u)) = fn
        sigaction(i, &sa, nil)
@@ -283,6 +287,7 @@ func setsig(i uint32, fn uintptr) {
 // sigtramp is the callback from libc when a signal is received.
 // It is called with the C calling convention.
 func sigtramp()
+func cgoSigtramp()
 
 //go:nosplit
 //go:nowritebarrierrec
index 09f12283a18749315be5be0c953e6fc65098a698..4bfb9b83623b6cc392c6baa796709e6e1de370c5 100644 (file)
@@ -326,6 +326,9 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$0
        ADDL    $28, SP
        RET
 
+TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0
+       JMP     runtime·sigtramp(SB)
+
 TEXT runtime·usleep_trampoline(SB),NOSPLIT,$0
        PUSHL   BP
        MOVL    SP, BP
index 142933585db8f97f03557bbda74f14b957b5c4e3..2a2e7379ca90b3977a9da69c3648f11d4f4b16d0 100644 (file)
@@ -215,6 +215,82 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$0
        POPQ    BP
        RET
 
+// Used instead of sigtramp in programs that use cgo.
+// Arguments from kernel are in DI, SI, DX.
+TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0
+       // If no traceback function, do usual sigtramp.
+       MOVQ    runtime·cgoTraceback(SB), AX
+       TESTQ   AX, AX
+       JZ      sigtramp
+
+       // If no traceback support function, which means that
+       // runtime/cgo was not linked in, do usual sigtramp.
+       MOVQ    _cgo_callers(SB), AX
+       TESTQ   AX, AX
+       JZ      sigtramp
+
+       // Figure out if we are currently in a cgo call.
+       // If not, just do usual sigtramp.
+       get_tls(CX)
+       MOVQ    g(CX),AX
+       TESTQ   AX, AX
+       JZ      sigtrampnog     // g == nil
+       MOVQ    g_m(AX), AX
+       TESTQ   AX, AX
+       JZ      sigtramp        // g.m == nil
+       MOVL    m_ncgo(AX), CX
+       TESTL   CX, CX
+       JZ      sigtramp        // g.m.ncgo == 0
+       MOVQ    m_curg(AX), CX
+       TESTQ   CX, CX
+       JZ      sigtramp        // g.m.curg == nil
+       MOVQ    g_syscallsp(CX), CX
+       TESTQ   CX, CX
+       JZ      sigtramp        // g.m.curg.syscallsp == 0
+       MOVQ    m_cgoCallers(AX), R8
+       TESTQ   R8, R8
+       JZ      sigtramp        // g.m.cgoCallers == nil
+       MOVL    m_cgoCallersUse(AX), CX
+       TESTL   CX, CX
+       JNZ     sigtramp        // g.m.cgoCallersUse != 0
+
+       // Jump to a function in runtime/cgo.
+       // That function, written in C, will call the user's traceback
+       // function with proper unwind info, and will then call back here.
+       // The first three arguments, and the fifth, are already in registers.
+       // Set the two remaining arguments now.
+       MOVQ    runtime·cgoTraceback(SB), CX
+       MOVQ    $runtime·sigtramp(SB), R9
+       MOVQ    _cgo_callers(SB), AX
+       JMP     AX
+
+sigtramp:
+       JMP     runtime·sigtramp(SB)
+
+sigtrampnog:
+       // Signal arrived on a non-Go thread. If this is SIGPROF, get a
+       // stack trace.
+       CMPL    DI, $27 // 27 == SIGPROF
+       JNZ     sigtramp
+
+       // Lock sigprofCallersUse.
+       MOVL    $0, AX
+       MOVL    $1, CX
+       MOVQ    $runtime·sigprofCallersUse(SB), R11
+       LOCK
+       CMPXCHGL        CX, 0(R11)
+       JNZ     sigtramp  // Skip stack trace if already locked.
+
+       // Jump to the traceback function in runtime/cgo.
+       // It will call back to sigprofNonGo, which will ignore the
+       // arguments passed in registers.
+       // First three arguments to traceback function are in registers already.
+       MOVQ    runtime·cgoTraceback(SB), CX
+       MOVQ    $runtime·sigprofCallers(SB), R8
+       MOVQ    $runtime·sigprofNonGo(SB), R9
+       MOVQ    _cgo_callers(SB), AX
+       JMP     AX
+
 TEXT runtime·mmap_trampoline(SB),NOSPLIT,$0
        PUSHQ   BP                      // make a frame; keep stack aligned
        MOVQ    SP, BP
index 9b5c667f45198aa03522676e3b08f80970ddc216..7a269cf576335e5ded4c63881a8b52cdac7e0451 100644 (file)
@@ -227,6 +227,9 @@ nog:
 
        RET
 
+TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0
+       JMP     runtime·sigtramp(SB)
+
 TEXT runtime·sigprocmask_trampoline(SB),NOSPLIT,$0
        MOVW    4(R0), R1       // arg 2 new
        MOVW    8(R0), R2       // arg 3 old
index c324994d26f5aaf496ae476bd030843fb255a5ca..4f9d0b8d58c5c7e8f7215328d4ce07e8aa12d9ab 100644 (file)
@@ -223,6 +223,9 @@ nog:
 
        RET
 
+TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0
+       JMP     runtime·sigtramp(SB)
+
 TEXT runtime·sigprocmask_trampoline(SB),NOSPLIT,$0
        MOVD    8(R0), R1       // arg 2 new
        MOVD    16(R0), R2      // arg 3 old