Source file src/runtime/testdata/testprogcgo/tracebackctxt.go

     1  // Copyright 2016 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  // Test the context argument to SetCgoTraceback.
     8  // Use fake context, traceback, and symbolizer functions.
     9  
    10  /*
    11  // Defined in tracebackctxt_c.c.
    12  extern void C1(void);
    13  extern void C2(void);
    14  extern void tcContext(void*);
    15  extern void tcContextSimple(void*);
    16  extern void tcTraceback(void*);
    17  extern void tcSymbolizer(void*);
    18  extern int getContextCount(void);
    19  extern void TracebackContextPreemptionCallGo(int);
    20  extern void TracebackContextProfileCallGo(void);
    21  */
    22  import "C"
    23  
    24  import (
    25  	"fmt"
    26  	"io"
    27  	"runtime"
    28  	"runtime/pprof"
    29  	"sync"
    30  	"sync/atomic"
    31  	"unsafe"
    32  )
    33  
    34  func init() {
    35  	register("TracebackContext", TracebackContext)
    36  	register("TracebackContextPreemption", TracebackContextPreemption)
    37  	register("TracebackContextProfile", TracebackContextProfile)
    38  }
    39  
    40  var tracebackOK bool
    41  
    42  func TracebackContext() {
    43  	runtime.SetCgoTraceback(0, unsafe.Pointer(C.tcTraceback), unsafe.Pointer(C.tcContext), unsafe.Pointer(C.tcSymbolizer))
    44  	C.C1()
    45  	if got := C.getContextCount(); got != 0 {
    46  		fmt.Printf("at end contextCount == %d, expected 0\n", got)
    47  		tracebackOK = false
    48  	}
    49  	if tracebackOK {
    50  		fmt.Println("OK")
    51  	}
    52  }
    53  
    54  //export G1
    55  func G1() {
    56  	C.C2()
    57  }
    58  
    59  //export G2
    60  func G2() {
    61  	pc := make([]uintptr, 32)
    62  	n := runtime.Callers(0, pc)
    63  	cf := runtime.CallersFrames(pc[:n])
    64  	var frames []runtime.Frame
    65  	for {
    66  		frame, more := cf.Next()
    67  		frames = append(frames, frame)
    68  		if !more {
    69  			break
    70  		}
    71  	}
    72  
    73  	want := []struct {
    74  		function string
    75  		line     int
    76  	}{
    77  		{"main.G2", 0},
    78  		{"cFunction", 0x10200},
    79  		{"cFunction", 0x200},
    80  		{"cFunction", 0x10201},
    81  		{"cFunction", 0x201},
    82  		{"main.G1", 0},
    83  		{"cFunction", 0x10100},
    84  		{"cFunction", 0x100},
    85  		{"main.TracebackContext", 0},
    86  	}
    87  
    88  	ok := true
    89  	i := 0
    90  wantLoop:
    91  	for _, w := range want {
    92  		for ; i < len(frames); i++ {
    93  			if w.function == frames[i].Function {
    94  				if w.line != 0 && w.line != frames[i].Line {
    95  					fmt.Printf("found function %s at wrong line %#x (expected %#x)\n", w.function, frames[i].Line, w.line)
    96  					ok = false
    97  				}
    98  				i++
    99  				continue wantLoop
   100  			}
   101  		}
   102  		fmt.Printf("did not find function %s in\n", w.function)
   103  		for _, f := range frames {
   104  			fmt.Println(f)
   105  		}
   106  		ok = false
   107  		break
   108  	}
   109  	tracebackOK = ok
   110  	if got := C.getContextCount(); got != 2 {
   111  		fmt.Printf("at bottom contextCount == %d, expected 2\n", got)
   112  		tracebackOK = false
   113  	}
   114  }
   115  
   116  // Issue 47441.
   117  func TracebackContextPreemption() {
   118  	runtime.SetCgoTraceback(0, unsafe.Pointer(C.tcTraceback), unsafe.Pointer(C.tcContextSimple), unsafe.Pointer(C.tcSymbolizer))
   119  
   120  	const funcs = 10
   121  	const calls = 1e5
   122  	var wg sync.WaitGroup
   123  	for i := 0; i < funcs; i++ {
   124  		wg.Add(1)
   125  		go func(i int) {
   126  			defer wg.Done()
   127  			for j := 0; j < calls; j++ {
   128  				C.TracebackContextPreemptionCallGo(C.int(i*calls + j))
   129  			}
   130  		}(i)
   131  	}
   132  	wg.Wait()
   133  
   134  	fmt.Println("OK")
   135  }
   136  
   137  //export TracebackContextPreemptionGoFunction
   138  func TracebackContextPreemptionGoFunction(i C.int) {
   139  	// Do some busy work.
   140  	fmt.Sprintf("%d\n", i)
   141  }
   142  
   143  // Regression test for issue 71395.
   144  //
   145  // The SIGPROF handler can call the SetCgoTraceback traceback function if the
   146  // context function is also provided. Ensure that call is safe.
   147  func TracebackContextProfile() {
   148  	runtime.SetCgoTraceback(0, unsafe.Pointer(C.tcTraceback), unsafe.Pointer(C.tcContextSimple), unsafe.Pointer(C.tcSymbolizer))
   149  
   150  	if err := pprof.StartCPUProfile(io.Discard); err != nil {
   151  		panic(fmt.Sprintf("error starting CPU profile: %v", err))
   152  	}
   153  	defer pprof.StopCPUProfile()
   154  
   155  	const calls = 1e5
   156  	var wg sync.WaitGroup
   157  	for i := 0; i < runtime.GOMAXPROCS(0); i++ {
   158  		wg.Add(1)
   159  		go func() {
   160  			defer wg.Done()
   161  			for j := 0; j < calls; j++ {
   162  				C.TracebackContextProfileCallGo()
   163  			}
   164  		}()
   165  	}
   166  	wg.Wait()
   167  
   168  	fmt.Println("OK")
   169  }
   170  
   171  var sink atomic.Pointer[byte]
   172  
   173  //export TracebackContextProfileGoFunction
   174  func TracebackContextProfileGoFunction() {
   175  	// Issue 71395 occurs when SIGPROF lands on code running on the system
   176  	// stack in a cgo callback. The allocator uses the system stack.
   177  	b := make([]byte, 128)
   178  	sink.Store(&b[0])
   179  }
   180  

View as plain text