blob: f1b4c6fbca70e99424e839418aa41d00534ba8b2 [file] [log] [blame]
Lorenz Brun4025c9b2022-06-16 16:12:53 +00001// This uses the unstable overrideWrite interface to also emit all runtime
2// writes to a dedicated runtime file descriptor to catch and debug crash dumps.
3// See https://go-review.googlesource.com/c/go/+/278792 for details about the
4// interface. This interface is relatively special, refrain from using most Go
5// features in here as it might cause unexpected behavior. Especially yielding
6// is a bad idea as the scheduler might be in an inconsistent state. But using
7// this interface was judged to be vastly more maintenance-friendly than
8// attempting to parse out this information from a combined stderr.
9package main
10
11import (
Lorenz Brun4025c9b2022-06-16 16:12:53 +000012 "os"
13 "unsafe"
14
15 "golang.org/x/sys/unix"
16
17 "source.monogon.dev/metropolis/pkg/logtree"
18)
19
20// This hooks into a global variable which is checked by runtime.write and used
21// instead of runtime.write1 if populated.
Serge Bazanski3b098232023-03-16 17:57:02 +010022//
Lorenz Brun4025c9b2022-06-16 16:12:53 +000023//go:linkname overrideWrite runtime.overrideWrite
24var overrideWrite func(fd uintptr, p unsafe.Pointer, n int32) int32
25
Serge Bazanski5f8414d2022-06-24 13:02:11 +020026// Contains the files into which runtime logs and crashes are written.
27var runtimeFds []int
Lorenz Brun4025c9b2022-06-16 16:12:53 +000028
29// This is essentially a reimplementation of the assembly function
30// runtime.write1, just with a hardcoded file descriptor and using the assembly
31// function unix.RawSyscall to not get a dependency on Go's calling convention
32// and needing an implementation for every architecture.
Serge Bazanski3b098232023-03-16 17:57:02 +010033//
Lorenz Brun4025c9b2022-06-16 16:12:53 +000034//go:nosplit
35func runtimeWrite(fd uintptr, p unsafe.Pointer, n int32) int32 {
Serge Bazanski5f8414d2022-06-24 13:02:11 +020036 // Only redirect writes to stderr.
37 if fd != 2 {
38 a, _, err := unix.RawSyscall(unix.SYS_WRITE, fd, uintptr(p), uintptr(n))
39 if err == 0 {
40 return int32(a)
41 }
Lorenz Brun4025c9b2022-06-16 16:12:53 +000042 return int32(err)
43 }
Serge Bazanski5f8414d2022-06-24 13:02:11 +020044 // Write to the runtime panic FDs.
45 for _, f := range runtimeFds {
46 _, _, _ = unix.RawSyscall(unix.SYS_WRITE, uintptr(f), uintptr(p), uintptr(n))
47 }
48
49 // Finally, write to original FD
50 a, _, err := unix.RawSyscall(unix.SYS_WRITE, fd, uintptr(p), uintptr(n))
51 if err == 0 {
52 return int32(a)
53 }
Lorenz Brun4025c9b2022-06-16 16:12:53 +000054 return int32(err)
55}
56
Tim Windelschmidt13b83f72024-04-11 23:10:16 +020057func initPanicHandler(lt *logtree.LogTree, consoles []*console) {
Lorenz Brun4025c9b2022-06-16 16:12:53 +000058 l := lt.MustLeveledFor("panichandler")
Serge Bazanski5f8414d2022-06-24 13:02:11 +020059
Lorenz Brund1f82e92024-02-08 19:27:46 +010060 // Setup pstore userspace message buffer
61 fd, err := unix.Open("/dev/pmsg0", os.O_WRONLY, 0)
Lorenz Brun4025c9b2022-06-16 16:12:53 +000062 if err != nil {
Lorenz Brund1f82e92024-02-08 19:27:46 +010063 l.Errorf("Failed to open pstore userspace device (pstore probably unavailable): %v", err)
Lorenz Brun4025c9b2022-06-16 16:12:53 +000064 l.Warningf("Continuing without persistent panic storage.")
Serge Bazanski5f8414d2022-06-24 13:02:11 +020065 } else {
66 runtimeFds = append(runtimeFds, fd)
Lorenz Brun4025c9b2022-06-16 16:12:53 +000067 }
Serge Bazanski5f8414d2022-06-24 13:02:11 +020068
Tim Windelschmidt13b83f72024-04-11 23:10:16 +020069 for _, c := range consoles {
70 fd, err := unix.Open(c.path, os.O_WRONLY, 0)
Serge Bazanski5f8414d2022-06-24 13:02:11 +020071 if err == nil {
72 runtimeFds = append(runtimeFds, fd)
Tim Windelschmidt13b83f72024-04-11 23:10:16 +020073 l.Infof("Panic console: %s", c.path)
Serge Bazanski5f8414d2022-06-24 13:02:11 +020074 }
75 }
76
Lorenz Brun4025c9b2022-06-16 16:12:53 +000077 // This could cause a data race if the runtime crashed while we're
78 // initializing the crash handler, but there is no locking infrastructure
79 // for this so we have to take that risk.
80 overrideWrite = runtimeWrite
81 return
82}