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  }