Source file src/cmd/cgo/internal/testsanitizers/testdata/tsan_tracebackctxt/main.go
1 // Copyright 2025 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package main 6 7 /* 8 // Defined in tracebackctxt_c.c. 9 extern void C1(void); 10 extern void C2(void); 11 extern void tcContext(void*); 12 extern void tcTraceback(void*); 13 extern void tcSymbolizer(void*); 14 */ 15 import "C" 16 17 import ( 18 "fmt" 19 "runtime" 20 "sync" 21 "unsafe" 22 ) 23 24 // Regression test for https://go.dev/issue/73949. TSAN should not report races 25 // on writes to the argument passed to the symbolizer function. 26 // 27 // Triggering this race requires calls to the symbolizer function with the same 28 // argument pointer on multiple threads. The runtime passes a stack variable to 29 // this function, so that means we need to get a single goroutine to execute on 30 // two threads, calling the symbolizer function on each. 31 // 32 // runtime.CallersFrames / Next will call the symbolizer function (if there are 33 // C frames). So the approach here is, with GOMAXPROCS=2, have 2 goroutines 34 // that use CallersFrames over and over, both frequently calling Gosched in an 35 // attempt to get picked up by the other P. 36 37 var tracebackOK bool 38 39 func main() { 40 runtime.GOMAXPROCS(2) 41 runtime.SetCgoTraceback(0, unsafe.Pointer(C.tcTraceback), unsafe.Pointer(C.tcContext), unsafe.Pointer(C.tcSymbolizer)) 42 C.C1() 43 if tracebackOK { 44 fmt.Println("OK") 45 } 46 } 47 48 //export G1 49 func G1() { 50 C.C2() 51 } 52 53 //export G2 54 func G2() { 55 pc := make([]uintptr, 32) 56 n := runtime.Callers(0, pc) 57 58 var wg sync.WaitGroup 59 for range 2 { 60 wg.Go(func() { 61 for range 1000 { 62 cf := runtime.CallersFrames(pc[:n]) 63 var frames []runtime.Frame 64 for { 65 frame, more := cf.Next() 66 frames = append(frames, frame) 67 if !more { 68 break 69 } 70 } 71 runtime.Gosched() 72 } 73 }) 74 } 75 wg.Wait() 76 77 tracebackOK = true 78 } 79