-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
runtime/debug: add Example of SetCrashOutput in a crash monitor
Updates golang#42888 Change-Id: I72e408301e17b1579bbea189bed6b1a0154bd55f Reviewed-on: https://go-review.googlesource.com/c/go/+/548121 Reviewed-by: Michael Pratt <mpratt@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
- Loading branch information
Showing
1 changed file
with
99 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
// Copyright 2023 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. | ||
|
||
package debug_test | ||
|
||
import ( | ||
"io" | ||
"log" | ||
"os" | ||
"os/exec" | ||
"runtime/debug" | ||
) | ||
|
||
// ExampleSetCrashOutput_monitor shows an example of using | ||
// [debug.SetCrashOutput] to direct crashes to a "monitor" process, | ||
// for automated crash reporting. The monitor is the same executable, | ||
// invoked in a special mode indicated by an environment variable. | ||
func ExampleSetCrashOutput_monitor() { | ||
appmain() | ||
|
||
// This Example doesn't actually run as a test because its | ||
// purpose is to crash, so it has no "Output:" comment | ||
// within the function body. | ||
// | ||
// To observe the monitor in action, replace the entire text | ||
// of this comment with "Output:" and run this command: | ||
// | ||
// $ go test -run=ExampleSetCrashOutput_monitor runtime/debug | ||
// panic: oops | ||
// ...stack... | ||
// monitor: saved crash report at /tmp/10804884239807998216.crash | ||
} | ||
|
||
// appmain represents the 'main' function of your application. | ||
func appmain() { | ||
monitor() | ||
|
||
// Run the application. | ||
println("hello") | ||
panic("oops") | ||
} | ||
|
||
// monitor starts the monitor process, which performs automated | ||
// crash reporting. Call this function immediately within main. | ||
// | ||
// This function re-executes the same executable as a child process, | ||
// in a special mode. In that mode, the call to monitor will never | ||
// return. | ||
func monitor() { | ||
const monitorVar = "RUNTIME_DEBUG_MONITOR" | ||
if os.Getenv(monitorVar) != "" { | ||
// This is the monitor (child) process. | ||
log.SetFlags(0) | ||
log.SetPrefix("monitor: ") | ||
|
||
crash, err := io.ReadAll(os.Stdin) | ||
if err != nil { | ||
log.Fatalf("failed to read from input pipe: %v", err) | ||
} | ||
if len(crash) == 0 { | ||
// Parent process terminated without reporting a crash. | ||
os.Exit(0) | ||
} | ||
|
||
// Save the crash report securely in the file system. | ||
f, err := os.CreateTemp("", "*.crash") | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
if _, err := f.Write(crash); err != nil { | ||
log.Fatal(err) | ||
} | ||
if err := f.Close(); err != nil { | ||
log.Fatal(err) | ||
} | ||
log.Fatalf("saved crash report at %s", f.Name()) | ||
} | ||
|
||
// This is the application process. | ||
// Fork+exec the same executable in monitor mode. | ||
exe, err := os.Executable() | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
cmd := exec.Command(exe, "-test.run=ExampleSetCrashOutput_monitor") | ||
cmd.Env = append(os.Environ(), monitorVar+"=1") | ||
cmd.Stderr = os.Stderr | ||
cmd.Stdout = os.Stderr | ||
pipe, err := cmd.StdinPipe() | ||
if err != nil { | ||
log.Fatalf("StdinPipe: %v", err) | ||
} | ||
debug.SetCrashOutput(pipe.(*os.File)) // (this conversion is safe) | ||
if err := cmd.Start(); err != nil { | ||
log.Fatalf("can't start monitor: %v", err) | ||
} | ||
// Now return and start the application proper... | ||
} |