Source file src/runtime/debug/example_monitor_test.go

     1  // Copyright 2023 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  package debug_test
     6  
     7  import (
     8  	"io"
     9  	"log"
    10  	"os"
    11  	"os/exec"
    12  	"runtime/debug"
    13  )
    14  
    15  // ExampleSetCrashOutput_monitor shows an example of using
    16  // [debug.SetCrashOutput] to direct crashes to a "monitor" process,
    17  // for automated crash reporting. The monitor is the same executable,
    18  // invoked in a special mode indicated by an environment variable.
    19  func ExampleSetCrashOutput_monitor() {
    20  	appmain()
    21  
    22  	// This Example doesn't actually run as a test because its
    23  	// purpose is to crash, so it has no "Output:" comment
    24  	// within the function body.
    25  	//
    26  	// To observe the monitor in action, replace the entire text
    27  	// of this comment with "Output:" and run this command:
    28  	//
    29  	//    $ go test -run=ExampleSetCrashOutput_monitor runtime/debug
    30  	//    panic: oops
    31  	//    ...stack...
    32  	//    monitor: saved crash report at /tmp/10804884239807998216.crash
    33  }
    34  
    35  // appmain represents the 'main' function of your application.
    36  func appmain() {
    37  	monitor()
    38  
    39  	// Run the application.
    40  	println("hello")
    41  	panic("oops")
    42  }
    43  
    44  // monitor starts the monitor process, which performs automated
    45  // crash reporting. Call this function immediately within main.
    46  //
    47  // This function re-executes the same executable as a child process,
    48  // in a special mode. In that mode, the call to monitor will never
    49  // return.
    50  func monitor() {
    51  	const monitorVar = "RUNTIME_DEBUG_MONITOR"
    52  	if os.Getenv(monitorVar) != "" {
    53  		// This is the monitor (child) process.
    54  		log.SetFlags(0)
    55  		log.SetPrefix("monitor: ")
    56  
    57  		crash, err := io.ReadAll(os.Stdin)
    58  		if err != nil {
    59  			log.Fatalf("failed to read from input pipe: %v", err)
    60  		}
    61  		if len(crash) == 0 {
    62  			// Parent process terminated without reporting a crash.
    63  			os.Exit(0)
    64  		}
    65  
    66  		// Save the crash report securely in the file system.
    67  		f, err := os.CreateTemp("", "*.crash")
    68  		if err != nil {
    69  			log.Fatal(err)
    70  		}
    71  		if _, err := f.Write(crash); err != nil {
    72  			log.Fatal(err)
    73  		}
    74  		if err := f.Close(); err != nil {
    75  			log.Fatal(err)
    76  		}
    77  		log.Fatalf("saved crash report at %s", f.Name())
    78  	}
    79  
    80  	// This is the application process.
    81  	// Fork+exec the same executable in monitor mode.
    82  	exe, err := os.Executable()
    83  	if err != nil {
    84  		log.Fatal(err)
    85  	}
    86  	cmd := exec.Command(exe, "-test.run=ExampleSetCrashOutput_monitor")
    87  	cmd.Env = append(os.Environ(), monitorVar+"=1")
    88  	cmd.Stderr = os.Stderr
    89  	cmd.Stdout = os.Stderr
    90  	pipe, err := cmd.StdinPipe()
    91  	if err != nil {
    92  		log.Fatalf("StdinPipe: %v", err)
    93  	}
    94  	debug.SetCrashOutput(pipe.(*os.File), debug.CrashOptions{}) // (this conversion is safe)
    95  	if err := cmd.Start(); err != nil {
    96  		log.Fatalf("can't start monitor: %v", err)
    97  	}
    98  	// Now return and start the application proper...
    99  }
   100  

View as plain text