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