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

     1  // Copyright 2022 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  //go:build !plan9 && !windows
     6  
     7  package main
     8  
     9  // Make many C-to-Go callback while collecting a CPU profile.
    10  //
    11  // This is a regression test for issue 50936.
    12  
    13  /*
    14  #include <unistd.h>
    15  
    16  void goCallbackPprof();
    17  
    18  static void callGo() {
    19  	// Spent >20us in C so this thread is eligible for sysmon to retake its
    20  	// P.
    21  	usleep(50);
    22  	goCallbackPprof();
    23  }
    24  */
    25  import "C"
    26  
    27  import (
    28  	"fmt"
    29  	"os"
    30  	"runtime"
    31  	"runtime/pprof"
    32  	"time"
    33  )
    34  
    35  func init() {
    36  	register("CgoPprofCallback", CgoPprofCallback)
    37  }
    38  
    39  //export goCallbackPprof
    40  func goCallbackPprof() {
    41  	// No-op. We want to stress the cgocall and cgocallback internals,
    42  	// landing as many pprof signals there as possible.
    43  }
    44  
    45  func CgoPprofCallback() {
    46  	// Issue 50936 was a crash in the SIGPROF handler when the signal
    47  	// arrived during the exitsyscall following a cgocall(back) in dropg or
    48  	// execute, when updating mp.curg.
    49  	//
    50  	// These are reachable only when exitsyscall finds no P available. Thus
    51  	// we make C calls from significantly more Gs than there are available
    52  	// Ps. Lots of runnable work combined with >20us spent in callGo makes
    53  	// it possible for sysmon to retake Ps, forcing C calls to go down the
    54  	// desired exitsyscall path.
    55  	//
    56  	// High GOMAXPROCS is used to increase opportunities for failure on
    57  	// high CPU machines.
    58  	const (
    59  		P = 16
    60  		G = 64
    61  	)
    62  	runtime.GOMAXPROCS(P)
    63  
    64  	f, err := os.CreateTemp("", "prof")
    65  	if err != nil {
    66  		fmt.Fprintln(os.Stderr, err)
    67  		os.Exit(2)
    68  	}
    69  	defer f.Close()
    70  
    71  	if err := pprof.StartCPUProfile(f); err != nil {
    72  		fmt.Fprintln(os.Stderr, err)
    73  		os.Exit(2)
    74  	}
    75  
    76  	for i := 0; i < G; i++ {
    77  		go func() {
    78  			for {
    79  				C.callGo()
    80  			}
    81  		}()
    82  	}
    83  
    84  	time.Sleep(time.Second)
    85  
    86  	pprof.StopCPUProfile()
    87  
    88  	fmt.Println("OK")
    89  }
    90  

View as plain text