| // Copyright 2022 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. |
| |
| //go:build !plan9 && !windows |
| |
| package main |
| |
| // Make many C-to-Go callback while collecting a CPU profile. |
| // |
| // This is a regression test for issue 50936. |
| |
| /* |
| #include <unistd.h> |
| |
| void goCallbackPprof(); |
| |
| static void callGo() { |
| // Spent >20us in C so this thread is eligible for sysmon to retake its |
| // P. |
| usleep(50); |
| goCallbackPprof(); |
| } |
| */ |
| import "C" |
| |
| import ( |
| "fmt" |
| "os" |
| "runtime" |
| "runtime/pprof" |
| "time" |
| ) |
| |
| func init() { |
| register("CgoPprofCallback", CgoPprofCallback) |
| } |
| |
| //export goCallbackPprof |
| func goCallbackPprof() { |
| // No-op. We want to stress the cgocall and cgocallback internals, |
| // landing as many pprof signals there as possible. |
| } |
| |
| func CgoPprofCallback() { |
| // Issue 50936 was a crash in the SIGPROF handler when the signal |
| // arrived during the exitsyscall following a cgocall(back) in dropg or |
| // execute, when updating mp.curg. |
| // |
| // These are reachable only when exitsyscall finds no P available. Thus |
| // we make C calls from significantly more Gs than there are available |
| // Ps. Lots of runnable work combined with >20us spent in callGo makes |
| // it possible for sysmon to retake Ps, forcing C calls to go down the |
| // desired exitsyscall path. |
| // |
| // High GOMAXPROCS is used to increase opportunities for failure on |
| // high CPU machines. |
| const ( |
| P = 16 |
| G = 64 |
| ) |
| runtime.GOMAXPROCS(P) |
| |
| f, err := os.CreateTemp("", "prof") |
| if err != nil { |
| fmt.Fprintln(os.Stderr, err) |
| os.Exit(2) |
| } |
| defer f.Close() |
| |
| if err := pprof.StartCPUProfile(f); err != nil { |
| fmt.Fprintln(os.Stderr, err) |
| os.Exit(2) |
| } |
| |
| for i := 0; i < G; i++ { |
| go func() { |
| for { |
| C.callGo() |
| } |
| }() |
| } |
| |
| time.Sleep(time.Second) |
| |
| pprof.StopCPUProfile() |
| |
| fmt.Println("OK") |
| } |