blob: fe0e2e1dcf2ab0340f1733450fa6ce1e5ff351d1 [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 (
12 "io"
13 "os"
14 "unsafe"
15
16 "golang.org/x/sys/unix"
17
18 "source.monogon.dev/metropolis/pkg/logtree"
19)
20
21// This hooks into a global variable which is checked by runtime.write and used
22// instead of runtime.write1 if populated.
23//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.
33//go:nosplit
34func runtimeWrite(fd uintptr, p unsafe.Pointer, n int32) int32 {
Serge Bazanski5f8414d2022-06-24 13:02:11 +020035 // Only redirect writes to stderr.
36 if fd != 2 {
37 a, _, err := unix.RawSyscall(unix.SYS_WRITE, fd, uintptr(p), uintptr(n))
38 if err == 0 {
39 return int32(a)
40 }
Lorenz Brun4025c9b2022-06-16 16:12:53 +000041 return int32(err)
42 }
Serge Bazanski5f8414d2022-06-24 13:02:11 +020043 // Write to the runtime panic FDs.
44 for _, f := range runtimeFds {
45 _, _, _ = unix.RawSyscall(unix.SYS_WRITE, uintptr(f), uintptr(p), uintptr(n))
46 }
47
48 // Finally, write to original FD
49 a, _, err := unix.RawSyscall(unix.SYS_WRITE, fd, uintptr(p), uintptr(n))
50 if err == 0 {
51 return int32(a)
52 }
Lorenz Brun4025c9b2022-06-16 16:12:53 +000053 return int32(err)
54}
55
56const runtimeLogPath = "/esp/core_runtime.log"
57
Serge Bazanski5f8414d2022-06-24 13:02:11 +020058func initPanicHandler(lt *logtree.LogTree, consoles []string) {
Lorenz Brun4025c9b2022-06-16 16:12:53 +000059 rl := lt.MustRawFor("panichandler")
60 l := lt.MustLeveledFor("panichandler")
Serge Bazanski5f8414d2022-06-24 13:02:11 +020061
Lorenz Brun4025c9b2022-06-16 16:12:53 +000062 runtimeLogFile, err := os.Open(runtimeLogPath)
63 if err != nil && !os.IsNotExist(err) {
64 l.Errorf("Failed to open runtimeLogFile: %v", err)
65 }
66 if err == nil {
67 if _, err := io.Copy(rl, runtimeLogFile); err != nil {
68 l.Errorf("Failed to log old persistent crash: %v", err)
69 }
70 runtimeLogFile.Close()
71 if err := os.Remove(runtimeLogPath); err != nil {
72 l.Errorf("Failed to delete old persistent runtime crash log: %v", err)
73 }
74 }
75
Serge Bazanski5f8414d2022-06-24 13:02:11 +020076 // Setup ESP file.
77 fd, err := unix.Open(runtimeLogPath, os.O_CREATE|os.O_WRONLY, 0)
Lorenz Brun4025c9b2022-06-16 16:12:53 +000078 if err != nil {
Serge Bazanski5f8414d2022-06-24 13:02:11 +020079 l.Errorf("Failed to open core runtime log file: %v", err)
Lorenz Brun4025c9b2022-06-16 16:12:53 +000080 l.Warningf("Continuing without persistent panic storage.")
Serge Bazanski5f8414d2022-06-24 13:02:11 +020081 } else {
82 runtimeFds = append(runtimeFds, fd)
Lorenz Brun4025c9b2022-06-16 16:12:53 +000083 }
Serge Bazanski5f8414d2022-06-24 13:02:11 +020084
85 for _, s := range consoles {
86 fd, err := unix.Open(s, os.O_WRONLY, 0)
87 if err == nil {
88 runtimeFds = append(runtimeFds, fd)
89 l.Infof("Panic console: %s", s)
90 }
91 }
92
Lorenz Brun4025c9b2022-06-16 16:12:53 +000093 // This could cause a data race if the runtime crashed while we're
94 // initializing the crash handler, but there is no locking infrastructure
95 // for this so we have to take that risk.
96 overrideWrite = runtimeWrite
97 return
98}