--- /dev/null
+// Copyright 2025 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
+
+/*
+// Defined in tracebackctxt_c.c.
+extern void C1(void);
+extern void C2(void);
+extern void tcContext(void*);
+extern void tcTraceback(void*);
+extern void tcSymbolizer(void*);
+*/
+import "C"
+
+import (
+ "fmt"
+ "runtime"
+ "sync"
+ "unsafe"
+)
+
+// Regression test for https://go.dev/issue/73949. TSAN should not report races
+// on writes to the argument passed to the symbolizer function.
+//
+// Triggering this race requires calls to the symbolizer function with the same
+// argument pointer on multiple threads. The runtime passes a stack variable to
+// this function, so that means we need to get a single goroutine to execute on
+// two threads, calling the symbolizer function on each.
+//
+// runtime.CallersFrames / Next will call the symbolizer function (if there are
+// C frames). So the approach here is, with GOMAXPROCS=2, have 2 goroutines
+// that use CallersFrames over and over, both frequently calling Gosched in an
+// attempt to get picked up by the other P.
+
+var tracebackOK bool
+
+func main() {
+ runtime.GOMAXPROCS(2)
+ runtime.SetCgoTraceback(0, unsafe.Pointer(C.tcTraceback), unsafe.Pointer(C.tcContext), unsafe.Pointer(C.tcSymbolizer))
+ C.C1()
+ if tracebackOK {
+ fmt.Println("OK")
+ }
+}
+
+//export G1
+func G1() {
+ C.C2()
+}
+
+//export G2
+func G2() {
+ pc := make([]uintptr, 32)
+ n := runtime.Callers(0, pc)
+
+ var wg sync.WaitGroup
+ for range 2 {
+ wg.Go(func() {
+ for range 1000 {
+ cf := runtime.CallersFrames(pc[:n])
+ var frames []runtime.Frame
+ for {
+ frame, more := cf.Next()
+ frames = append(frames, frame)
+ if !more {
+ break
+ }
+ }
+ runtime.Gosched()
+ }
+ })
+ }
+ wg.Wait()
+
+ tracebackOK = true
+}
--- /dev/null
+// Copyright 2025 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 tracebackctxt.go. That file uses //export so
+// it can't put function definitions in the "C" import comment.
+
+#include <stdint.h>
+#include <stdio.h>
+
+// Functions exported from Go.
+extern void G1(void);
+extern void G2(void);
+
+void C1() {
+ G1();
+}
+
+void C2() {
+ G2();
+}
+
+struct cgoContextArg {
+ uintptr_t context;
+};
+
+struct cgoTracebackArg {
+ uintptr_t context;
+ uintptr_t sigContext;
+ uintptr_t* buf;
+ uintptr_t max;
+};
+
+struct cgoSymbolizerArg {
+ uintptr_t pc;
+ const char* file;
+ uintptr_t lineno;
+ const char* func;
+ uintptr_t entry;
+ uintptr_t more;
+ uintptr_t data;
+};
+
+void tcContext(void* parg) {
+ struct cgoContextArg* arg = (struct cgoContextArg*)(parg);
+ if (arg->context == 0) {
+ arg->context = 1;
+ }
+}
+
+void tcTraceback(void* parg) {
+ int base, i;
+ struct cgoTracebackArg* arg = (struct cgoTracebackArg*)(parg);
+ if (arg->max < 1) {
+ return;
+ }
+ arg->buf[0] = 6; // Chosen by fair dice roll.
+}
+
+void tcSymbolizer(void *parg) {
+ struct cgoSymbolizerArg* arg = (struct cgoSymbolizerArg*)(parg);
+ if (arg->pc == 0) {
+ return;
+ }
+ // Report two lines per PC returned by traceback, to test more handling.
+ arg->more = arg->file == NULL;
+ arg->file = "tracebackctxt.go";
+ arg->func = "cFunction";
+ arg->lineno = arg->pc + (arg->more << 16);
+}
{src: "tsan13.go", needsRuntime: true},
{src: "tsan14.go", needsRuntime: true},
{src: "tsan15.go", needsRuntime: true},
+ {src: "tsan_tracebackctxt", needsRuntime: true}, // Subdirectory
}
for _, tc := range cases {
tc := tc
defer dir.RemoveAll(t)
outPath := dir.Join(name)
- mustRun(t, config.goCmd("build", "-o", outPath, srcPath(tc.src)))
+ mustRun(t, config.goCmd("build", "-o", outPath, "./"+srcPath(tc.src)))
cmdArgs := []string{outPath}
if goos == "linux" {
//go:linkname _cgo_sys_thread_create _cgo_sys_thread_create
//go:linkname _cgo_notify_runtime_init_done _cgo_notify_runtime_init_done
//go:linkname _cgo_callers _cgo_callers
-//go:linkname _cgo_set_context_function _cgo_set_context_function
+//go:linkname _cgo_set_traceback_functions _cgo_set_traceback_functions
+//go:linkname _cgo_call_traceback_function _cgo_call_traceback_function
+//go:linkname _cgo_call_symbolizer_function _cgo_call_symbolizer_function
//go:linkname _cgo_yield _cgo_yield
//go:linkname _cgo_pthread_key_created _cgo_pthread_key_created
//go:linkname _cgo_bindm _cgo_bindm
_cgo_sys_thread_create unsafe.Pointer
_cgo_notify_runtime_init_done unsafe.Pointer
_cgo_callers unsafe.Pointer
- _cgo_set_context_function unsafe.Pointer
+ _cgo_set_traceback_functions unsafe.Pointer
+ _cgo_call_traceback_function unsafe.Pointer
+ _cgo_call_symbolizer_function unsafe.Pointer
_cgo_yield unsafe.Pointer
_cgo_pthread_key_created unsafe.Pointer
_cgo_bindm unsafe.Pointer
var x_cgo_notify_runtime_init_done byte
var _cgo_notify_runtime_init_done = &x_cgo_notify_runtime_init_done
-// Sets the traceback context function. See runtime.SetCgoTraceback.
-
-//go:cgo_import_static x_cgo_set_context_function
-//go:linkname x_cgo_set_context_function x_cgo_set_context_function
-//go:linkname _cgo_set_context_function _cgo_set_context_function
-var x_cgo_set_context_function byte
-var _cgo_set_context_function = &x_cgo_set_context_function
+// Sets the traceback, context, and symbolizer functions. See
+// runtime.SetCgoTraceback.
+
+//go:cgo_import_static x_cgo_set_traceback_functions
+//go:linkname x_cgo_set_traceback_functions x_cgo_set_traceback_functions
+//go:linkname _cgo_set_traceback_functions _cgo_set_traceback_functions
+var x_cgo_set_traceback_functions byte
+var _cgo_set_traceback_functions = &x_cgo_set_traceback_functions
+
+// Call the traceback function registered with x_cgo_set_traceback_functions.
+
+//go:cgo_import_static x_cgo_call_traceback_function
+//go:linkname x_cgo_call_traceback_function x_cgo_call_traceback_function
+//go:linkname _cgo_call_traceback_function _cgo_call_traceback_function
+var x_cgo_call_traceback_function byte
+var _cgo_call_traceback_function = &x_cgo_call_traceback_function
+
+// Call the symbolizer function registered with x_cgo_set_symbolizer_functions.
+
+//go:cgo_import_static x_cgo_call_symbolizer_function
+//go:linkname x_cgo_call_symbolizer_function x_cgo_call_symbolizer_function
+//go:linkname _cgo_call_symbolizer_function _cgo_call_symbolizer_function
+var x_cgo_call_symbolizer_function byte
+var _cgo_call_symbolizer_function = &x_cgo_call_symbolizer_function
// Calls a libc function to execute background work injected via libc
// interceptors, such as processing pending signals under the thread
// Releases the cgo traceback context.
void _cgo_release_context(uintptr_t ctxt) {
- void (*pfn)(struct context_arg*);
+ void (*pfn)(struct cgoContextArg*);
pfn = _cgo_get_context_function();
if (ctxt != 0 && pfn != nil) {
- struct context_arg arg;
+ struct cgoContextArg arg;
arg.Context = ctxt;
(*pfn)(&arg);
uintptr_t x_cgo_pthread_key_created;
void (*x_crosscall2_ptr)(void (*fn)(void *), void *, int, size_t);
+// The traceback function, used when tracing C calls.
+static void (*cgo_traceback_function)(struct cgoTracebackArg*);
+
// The context function, used when tracing back C calls into Go.
-static void (*cgo_context_function)(struct context_arg*);
+static void (*cgo_context_function)(struct cgoContextArg*);
+
+// The symbolizer function, used when symbolizing C frames.
+static void (*cgo_symbolizer_function)(struct cgoSymbolizerArg*);
void
x_cgo_sys_thread_create(void* (*func)(void*), void* arg) {
uintptr_t
_cgo_wait_runtime_init_done(void) {
- void (*pfn)(struct context_arg*);
+ void (*pfn)(struct cgoContextArg*);
int done;
pfn = __atomic_load_n(&cgo_context_function, __ATOMIC_CONSUME);
x_cgo_pthread_key_created = 1;
}
-
// TODO(iant): For the case of a new C thread calling into Go, such
// as when using -buildmode=c-archive, we know that Go runtime
// initialization is complete but we do not know that all Go init
}
if (pfn != nil) {
- struct context_arg arg;
+ struct cgoContextArg arg;
arg.Context = 0;
(*pfn)(&arg);
pthread_mutex_unlock(&runtime_init_mu);
}
-// Sets the context function to call to record the traceback context
-// when calling a Go function from C code. Called from runtime.SetCgoTraceback.
-void x_cgo_set_context_function(void (*context)(struct context_arg*)) {
- __atomic_store_n(&cgo_context_function, context, __ATOMIC_RELEASE);
+// Sets the traceback, context, and symbolizer functions. Called from
+// runtime.SetCgoTraceback.
+void x_cgo_set_traceback_functions(struct cgoSetTracebackFunctionsArg* arg) {
+ __atomic_store_n(&cgo_traceback_function, arg->Traceback, __ATOMIC_RELEASE);
+ __atomic_store_n(&cgo_context_function, arg->Context, __ATOMIC_RELEASE);
+ __atomic_store_n(&cgo_symbolizer_function, arg->Symbolizer, __ATOMIC_RELEASE);
+}
+
+// Gets the traceback function to call to trace C calls.
+void (*(_cgo_get_traceback_function(void)))(struct cgoTracebackArg*) {
+ return __atomic_load_n(&cgo_traceback_function, __ATOMIC_CONSUME);
+}
+
+// Call the traceback function registered with x_cgo_set_traceback_functions.
+//
+// The traceback function is an arbitrary user C function which may be built
+// with TSAN, and thus must be wrapped with TSAN acquire/release calls. For
+// normal cgo calls, cmd/cgo automatically inserts TSAN acquire/release calls.
+// Since the traceback, context, and symbolizer functions are registered at
+// startup and called via the runtime, they do not get automatic TSAN
+// acquire/release calls.
+//
+// The only purpose of this wrapper is to perform TSAN acquire/release.
+// Alternatively, if the runtime arranged to safely call TSAN acquire/release,
+// it could perform the call directly.
+void x_cgo_call_traceback_function(struct cgoTracebackArg* arg) {
+ void (*pfn)(struct cgoTracebackArg*);
+
+ pfn = _cgo_get_traceback_function();
+ if (pfn == nil) {
+ return;
+ }
+
+ _cgo_tsan_acquire();
+ (*pfn)(arg);
+ _cgo_tsan_release();
}
-// Gets the context function.
-void (*(_cgo_get_context_function(void)))(struct context_arg*) {
+// Gets the context function to call to record the traceback context
+// when calling a Go function from C code.
+void (*(_cgo_get_context_function(void)))(struct cgoContextArg*) {
return __atomic_load_n(&cgo_context_function, __ATOMIC_CONSUME);
}
+// Gets the symbolizer function to call to symbolize C frames.
+void (*(_cgo_get_symbolizer_function(void)))(struct cgoSymbolizerArg*) {
+ return __atomic_load_n(&cgo_symbolizer_function, __ATOMIC_CONSUME);
+}
+
+// Call the symbolizer function registered with x_cgo_set_traceback_functions.
+//
+// See comment on x_cgo_call_traceback_function.
+void x_cgo_call_symbolizer_function(struct cgoSymbolizerArg* arg) {
+ void (*pfn)(struct cgoSymbolizerArg*);
+
+ pfn = _cgo_get_symbolizer_function();
+ if (pfn == nil) {
+ return;
+ }
+
+ _cgo_tsan_acquire();
+ (*pfn)(arg);
+ _cgo_tsan_release();
+}
+
// _cgo_try_pthread_create retries pthread_create if it fails with
// EAGAIN.
int
static HANDLE runtime_init_wait;
static int runtime_init_done;
+// No pthreads on Windows, these are always zero.
uintptr_t x_cgo_pthread_key_created;
void (*x_crosscall2_ptr)(void (*fn)(void *), void *, int, size_t);
uintptr_t
_cgo_wait_runtime_init_done(void) {
- void (*pfn)(struct context_arg*);
+ void (*pfn)(struct cgoContextArg*);
_cgo_maybe_run_preinit();
while (!_cgo_is_runtime_initialized()) {
}
pfn = _cgo_get_context_function();
if (pfn != nil) {
- struct context_arg arg;
+ struct cgoContextArg arg;
arg.Context = 0;
(*pfn)(&arg);
}
}
+// The traceback function, used when tracing C calls.
+static void (*cgo_traceback_function)(struct cgoTracebackArg*);
+
// The context function, used when tracing back C calls into Go.
-static void (*cgo_context_function)(struct context_arg*);
+static void (*cgo_context_function)(struct cgoContextArg*);
+
+// The symbolizer function, used when symbolizing C frames.
+static void (*cgo_symbolizer_function)(struct cgoSymbolizerArg*);
+
+// Sets the traceback, context, and symbolizer functions. Called from
+// runtime.SetCgoTraceback.
+void x_cgo_set_traceback_functions(struct cgoSetTracebackFunctionsArg* arg) {
+ EnterCriticalSection(&runtime_init_cs);
+ cgo_traceback_function = arg->Traceback;
+ cgo_context_function = arg->Context;
+ cgo_symbolizer_function = arg->Symbolizer;
+ LeaveCriticalSection(&runtime_init_cs);
+}
+
+// Gets the traceback function to call to trace C calls.
+void (*(_cgo_get_traceback_function(void)))(struct cgoTracebackArg*) {
+ void (*ret)(struct cgoTracebackArg*);
-// Sets the context function to call to record the traceback context
-// when calling a Go function from C code. Called from runtime.SetCgoTraceback.
-void x_cgo_set_context_function(void (*context)(struct context_arg*)) {
EnterCriticalSection(&runtime_init_cs);
- cgo_context_function = context;
+ ret = cgo_traceback_function;
LeaveCriticalSection(&runtime_init_cs);
+ return ret;
+}
+
+// Call the traceback function registered with x_cgo_set_traceback_functions.
+//
+// On other platforms, this coordinates with C/C++ TSAN. On Windows, there is
+// no C/C++ TSAN.
+void x_cgo_call_traceback_function(struct cgoTracebackArg* arg) {
+ void (*pfn)(struct cgoTracebackArg*);
+
+ pfn = _cgo_get_traceback_function();
+ if (pfn == nil) {
+ return;
+ }
+
+ (*pfn)(arg);
}
-// Gets the context function.
-void (*(_cgo_get_context_function(void)))(struct context_arg*) {
- void (*ret)(struct context_arg*);
+// Gets the context function to call to record the traceback context
+// when calling a Go function from C code.
+void (*(_cgo_get_context_function(void)))(struct cgoContextArg*) {
+ void (*ret)(struct cgoContextArg*);
EnterCriticalSection(&runtime_init_cs);
ret = cgo_context_function;
return ret;
}
+// Gets the symbolizer function to call to symbolize C frames.
+void (*(_cgo_get_symbolizer_function(void)))(struct cgoSymbolizerArg*) {
+ void (*ret)(struct cgoSymbolizerArg*);
+
+ EnterCriticalSection(&runtime_init_cs);
+ ret = cgo_symbolizer_function;
+ LeaveCriticalSection(&runtime_init_cs);
+ return ret;
+}
+
+// Call the symbolizer function registered with x_cgo_set_symbolizer_functions.
+//
+// On other platforms, this coordinates with C/C++ TSAN. On Windows, there is
+// no C/C++ TSAN.
+void x_cgo_call_symbolizer_function(struct cgoSymbolizerArg* arg) {
+ void (*pfn)(struct cgoSymbolizerArg*);
+
+ pfn = _cgo_get_symbolizer_function();
+ if (pfn == nil) {
+ return;
+ }
+
+ (*pfn)(arg);
+}
+
void _cgo_beginthread(unsigned long (__stdcall *func)(void*), void* arg) {
int tries;
HANDLE thandle;
void darwin_arm_init_mach_exception_handler(void);
/*
- * The cgo context function. See runtime.SetCgoTraceback.
- */
-struct context_arg {
- uintptr_t Context;
-};
-extern void (*(_cgo_get_context_function(void)))(struct context_arg*);
-
-/*
- * The argument for the cgo traceback callback. See runtime.SetCgoTraceback.
+ * The cgo traceback callback. See runtime.SetCgoTraceback.
*/
struct cgoTracebackArg {
uintptr_t Context;
uintptr_t* Buf;
uintptr_t Max;
};
+extern void (*(_cgo_get_traceback_function(void)))(struct cgoTracebackArg*);
+
+/*
+ * The cgo context callback. See runtime.SetCgoTraceback.
+ */
+struct cgoContextArg {
+ uintptr_t Context;
+};
+extern void (*(_cgo_get_context_function(void)))(struct cgoContextArg*);
+
+/*
+ * The argument for the cgo symbolizer callback. See runtime.SetCgoTraceback.
+ */
+struct cgoSymbolizerArg {
+ uintptr_t PC;
+ const char* File;
+ uintptr_t Lineno;
+ const char* Func;
+ uintptr_t Entry;
+ uintptr_t More;
+ uintptr_t Data;
+};
+extern void (*(_cgo_get_symbolizer_function(void)))(struct cgoSymbolizerArg*);
+
+/*
+ * The argument for x_cgo_set_traceback_functions. See runtime.SetCgoTraceback.
+ */
+struct cgoSetTracebackFunctionsArg {
+ void (*Traceback)(struct cgoTracebackArg*);
+ void (*Context)(struct cgoContextArg*);
+ void (*Symbolizer)(struct cgoSymbolizerArg*);
+};
/*
* TSAN support. This is only useful when building with
#ifdef CGO_TSAN
+// _cgo_tsan_acquire tells C/C++ TSAN that we are acquiring a dummy lock. We
+// call this when calling from Go to C. This is necessary because TSAN cannot
+// see the synchronization in Go. Note that C/C++ code built with TSAN is not
+// the same as the Go race detector.
+//
+// cmd/cgo generates calls to _cgo_tsan_acquire and _cgo_tsan_release. For
+// other cgo calls, manual calls are required.
+//
// These must match the definitions in yesTsanProlog in cmd/cgo/out.go.
// In general we should call _cgo_tsan_acquire when we enter C code,
// and call _cgo_tsan_release when we return to Go code.
+//
// This is only necessary when calling code that might be instrumented
// by TSAN, which mostly means system library calls that TSAN intercepts.
+//
// See the comment in cmd/cgo/out.go for more details.
long long _cgo_sync __attribute__ ((common));
}
funcInfo := findfunc(pc)
if !funcInfo.valid() {
- if cgoSymbolizer != nil {
+ if cgoSymbolizerAvailable() {
// Pre-expand cgo frames. We could do this
// incrementally, too, but there's no way to
// avoid allocation in this case anyway.
// expandCgoFrames expands frame information for pc, known to be
// a non-Go function, using the cgoSymbolizer hook. expandCgoFrames
// returns nil if pc could not be expanded.
+//
+// Preconditions: cgoSymbolizerAvailable returns true.
func expandCgoFrames(pc uintptr) []Frame {
arg := cgoSymbolizerArg{pc: pc}
callCgoSymbolizer(&arg)
--- /dev/null
+// Copyright 2025 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
+
+import (
+ "fmt"
+ "internal/abi"
+ "runtime"
+ "unsafe"
+)
+
+func init() {
+ register("SetCgoTracebackNoCgo", SetCgoTracebackNoCgo)
+}
+
+func cgoTraceback() {
+ panic("unexpectedly reached cgo traceback function")
+}
+
+func cgoContext() {
+ panic("unexpectedly reached cgo context function")
+}
+
+func cgoSymbolizer() {
+ panic("unexpectedly reached cgo symbolizer function")
+}
+
+// SetCgoTraceback is a no-op in non-cgo binaries.
+func SetCgoTracebackNoCgo() {
+ traceback := unsafe.Pointer(abi.FuncPCABIInternal(cgoTraceback))
+ context := unsafe.Pointer(abi.FuncPCABIInternal(cgoContext))
+ symbolizer := unsafe.Pointer(abi.FuncPCABIInternal(cgoSymbolizer))
+ runtime.SetCgoTraceback(0, traceback, context, symbolizer)
+
+ // In a cgo binary, runtime.(*Frames).Next calls the cgo symbolizer for
+ // any non-Go frames. Pass in a bogus frame to verify that Next does
+ // not attempt to call the cgo symbolizer, which would crash in a
+ // non-cgo binary like this one.
+ frames := runtime.CallersFrames([]uintptr{0x12345678})
+ frames.Next()
+
+ fmt.Println("OK")
+}
// If the current frame is not a cgo frame or if there's no registered cgo
// unwinder, it returns 0.
func (u *unwinder) cgoCallers(pcBuf []uintptr) int {
- if cgoTraceback == nil || u.frame.fn.funcID != abi.FuncID_cgocallback || u.cgoCtxt < 0 {
+ if !cgoTracebackAvailable() || u.frame.fn.funcID != abi.FuncID_cgocallback || u.cgoCtxt < 0 {
// We don't have a cgo unwinder (typical case), or we do but we're not
// in a cgo frame or we're out of cgo context.
return 0
anySymbolized := false
stop := false
for _, pc := range cgoBuf[:cgoN] {
- if cgoSymbolizer == nil {
+ if !cgoSymbolizerAvailable() {
if pr, stop := commitFrame(); stop {
break
} else if pr {
cgoContext = context
cgoSymbolizer = symbolizer
- // The context function is called when a C function calls a Go
- // function. As such it is only called by C code in runtime/cgo.
- if _cgo_set_context_function != nil {
- cgocall(_cgo_set_context_function, context)
+ if _cgo_set_traceback_functions != nil {
+ type cgoSetTracebackFunctionsArg struct {
+ traceback unsafe.Pointer
+ context unsafe.Pointer
+ symbolizer unsafe.Pointer
+ }
+ arg := cgoSetTracebackFunctionsArg{
+ traceback: traceback,
+ context: context,
+ symbolizer: symbolizer,
+ }
+ cgocall(_cgo_set_traceback_functions, noescape(unsafe.Pointer(&arg)))
}
}
var cgoContext unsafe.Pointer
var cgoSymbolizer unsafe.Pointer
+func cgoTracebackAvailable() bool {
+ // - The traceback function must be registered via SetCgoTraceback.
+ // - This must be a cgo binary (providing _cgo_call_traceback_function).
+ return cgoTraceback != nil && _cgo_call_traceback_function != nil
+}
+
+func cgoSymbolizerAvailable() bool {
+ // - The symbolizer function must be registered via SetCgoTraceback.
+ // - This must be a cgo binary (providing _cgo_call_symbolizer_function).
+ return cgoSymbolizer != nil && _cgo_call_symbolizer_function != nil
+}
+
// cgoTracebackArg is the type passed to cgoTraceback.
type cgoTracebackArg struct {
context uintptr
// printCgoTraceback prints a traceback of callers.
func printCgoTraceback(callers *cgoCallers) {
- if cgoSymbolizer == nil {
+ if !cgoSymbolizerAvailable() {
for _, c := range callers {
if c == 0 {
break
// printOneCgoTraceback prints the traceback of a single cgo caller.
// This can print more than one line because of inlining.
// It returns the "stop" result of commitFrame.
+//
+// Preconditions: cgoSymbolizerAvailable returns true.
func printOneCgoTraceback(pc uintptr, commitFrame func() (pr, stop bool), arg *cgoSymbolizerArg) bool {
arg.pc = pc
for {
}
// callCgoSymbolizer calls the cgoSymbolizer function.
+//
+// Preconditions: cgoSymbolizerAvailable returns true.
func callCgoSymbolizer(arg *cgoSymbolizerArg) {
call := cgocall
if panicking.Load() > 0 || getg().m.curg != getg() {
if asanenabled {
asanwrite(unsafe.Pointer(arg), unsafe.Sizeof(cgoSymbolizerArg{}))
}
- call(cgoSymbolizer, noescape(unsafe.Pointer(arg)))
+ call(_cgo_call_symbolizer_function, noescape(unsafe.Pointer(arg)))
}
// cgoContextPCs gets the PC values from a cgo traceback.
+//
+// Preconditions: cgoTracebackAvailable returns true.
func cgoContextPCs(ctxt uintptr, buf []uintptr) {
- if cgoTraceback == nil {
- return
- }
call := cgocall
if panicking.Load() > 0 || getg().m.curg != getg() {
// We do not want to call into the scheduler when panicking
if asanenabled {
asanwrite(unsafe.Pointer(&arg), unsafe.Sizeof(arg))
}
- call(cgoTraceback, noescape(unsafe.Pointer(&arg)))
+ call(_cgo_call_traceback_function, noescape(unsafe.Pointer(&arg)))
}
"bytes"
"fmt"
"internal/abi"
+ "internal/asan"
+ "internal/msan"
+ "internal/race"
"internal/testenv"
"regexp"
"runtime"
}
}
}
+
+func TestSetCgoTracebackNoCgo(t *testing.T) {
+ if asan.Enabled || msan.Enabled || race.Enabled {
+ t.Skip("skipped test: sanitizer builds use cgo")
+ }
+
+ output := runTestProg(t, "testprog", "SetCgoTracebackNoCgo")
+ want := "OK\n"
+ if output != want {
+ t.Fatalf("want %s, got %s\n", want, output)
+ }
+}