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