github.com/nya3jp/tast@v0.0.0-20230601000426-85c8e4d83a9b/src/go.chromium.org/tast/core/internal/command/signal.go (about) 1 // Copyright 2021 The ChromiumOS Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package command 6 7 import ( 8 "fmt" 9 "io" 10 "os" 11 "os/signal" 12 "path/filepath" 13 "runtime/pprof" 14 15 "github.com/shirou/gopsutil/v3/process" 16 "golang.org/x/sys/unix" 17 ) 18 19 var selfName = filepath.Base(os.Args[0]) 20 21 // InstallSignalHandler installs a signal handler that calls callback and 22 // runs common logic (e.g. dumping stack traces). out is the output stream to 23 // write messages to (typically stderr). 24 func InstallSignalHandler(out io.Writer, callback func(sig os.Signal)) { 25 ch := make(chan os.Signal, 1) 26 go func() { 27 sig := <-ch 28 fmt.Fprintf(out, "\n%s: Caught %v signal; exiting\n", selfName, sig) 29 callback(sig) 30 if sig == unix.SIGTERM { 31 handleSIGTERM(out) 32 } 33 os.Exit(1) 34 }() 35 signal.Notify(ch, unix.SIGINT, unix.SIGTERM) 36 } 37 38 func handleSIGTERM(out io.Writer) { 39 // SIGTERM is often sent by the parent process on timeout. In this 40 // case, print stack traces to help debugging. 41 fmt.Fprintf(out, "\n%s: Dumping all goroutines...\n\n", selfName) 42 if p := pprof.Lookup("goroutine"); p != nil { 43 p.WriteTo(out, 2) 44 } 45 fmt.Fprintf(out, "\n%s: Finished dumping goroutines\n", selfName) 46 47 // Also terminate all child processes with SIGTERM. This can recursively 48 // print stack traces. 49 procs, err := process.Processes() 50 if err != nil { 51 fmt.Fprintf(out, "Failed to terminate subprocesses: %v\n", err) 52 return 53 } 54 55 selfPid := int32(os.Getpid()) 56 57 for _, proc := range procs { 58 ppid, err := proc.Ppid() 59 if err != nil { 60 continue 61 } 62 if ppid == selfPid { 63 proc.Terminate() 64 } 65 } 66 }