blob: 3f17ffc0049ed3f818fa1eba5d716504f52b64ee [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.
Serge Bazanski3b098232023-03-16 17:57:02 +010023//
Lorenz Brun4025c9b2022-06-16 16:12:53 +000024//go:linkname overrideWrite runtime.overrideWrite
25var overrideWrite func(fd uintptr, p unsafe.Pointer, n int32) int32
26
Serge Bazanski5f8414d2022-06-24 13:02:11 +020027// Contains the files into which runtime logs and crashes are written.
28var runtimeFds []int
Lorenz Brun4025c9b2022-06-16 16:12:53 +000029
30// This is essentially a reimplementation of the assembly function
31// runtime.write1, just with a hardcoded file descriptor and using the assembly
32// function unix.RawSyscall to not get a dependency on Go's calling convention
33// and needing an implementation for every architecture.
Serge Bazanski3b098232023-03-16 17:57:02 +010034//
Lorenz Brun4025c9b2022-06-16 16:12:53 +000035//go:nosplit
36func runtimeWrite(fd uintptr, p unsafe.Pointer, n int32) int32 {
Serge Bazanski5f8414d2022-06-24 13:02:11 +020037 // Only redirect writes to stderr.
38 if fd != 2 {
39 a, _, err := unix.RawSyscall(unix.SYS_WRITE, fd, uintptr(p), uintptr(n))
40 if err == 0 {
41 return int32(a)
42 }
Lorenz Brun4025c9b2022-06-16 16:12:53 +000043 return int32(err)
44 }
Serge Bazanski5f8414d2022-06-24 13:02:11 +020045 // Write to the runtime panic FDs.
46 for _, f := range runtimeFds {
47 _, _, _ = unix.RawSyscall(unix.SYS_WRITE, uintptr(f), uintptr(p), uintptr(n))
48 }
49
50 // Finally, write to original FD
51 a, _, err := unix.RawSyscall(unix.SYS_WRITE, fd, uintptr(p), uintptr(n))
52 if err == 0 {
53 return int32(a)
54 }
Lorenz Brun4025c9b2022-06-16 16:12:53 +000055 return int32(err)
56}
57
58const runtimeLogPath = "/esp/core_runtime.log"
59
Serge Bazanski3b098232023-03-16 17:57:02 +010060func initPanicHandler(lt *logtree.LogTree, consoles []console) {
Lorenz Brun4025c9b2022-06-16 16:12:53 +000061 rl := lt.MustRawFor("panichandler")
62 l := lt.MustLeveledFor("panichandler")
Serge Bazanski5f8414d2022-06-24 13:02:11 +020063
Lorenz Brun4025c9b2022-06-16 16:12:53 +000064 runtimeLogFile, err := os.Open(runtimeLogPath)
65 if err != nil && !os.IsNotExist(err) {
66 l.Errorf("Failed to open runtimeLogFile: %v", err)
67 }
68 if err == nil {
69 if _, err := io.Copy(rl, runtimeLogFile); err != nil {
70 l.Errorf("Failed to log old persistent crash: %v", err)
71 }
72 runtimeLogFile.Close()
73 if err := os.Remove(runtimeLogPath); err != nil {
74 l.Errorf("Failed to delete old persistent runtime crash log: %v", err)
75 }
76 }
77
Serge Bazanski5f8414d2022-06-24 13:02:11 +020078 // Setup ESP file.
79 fd, err := unix.Open(runtimeLogPath, os.O_CREATE|os.O_WRONLY, 0)
Lorenz Brun4025c9b2022-06-16 16:12:53 +000080 if err != nil {
Serge Bazanski5f8414d2022-06-24 13:02:11 +020081 l.Errorf("Failed to open core runtime log file: %v", err)
Lorenz Brun4025c9b2022-06-16 16:12:53 +000082 l.Warningf("Continuing without persistent panic storage.")
Serge Bazanski5f8414d2022-06-24 13:02:11 +020083 } else {
84 runtimeFds = append(runtimeFds, fd)
Lorenz Brun4025c9b2022-06-16 16:12:53 +000085 }
Serge Bazanski5f8414d2022-06-24 13:02:11 +020086
Serge Bazanski3b098232023-03-16 17:57:02 +010087 for _, console := range consoles {
88 fd, err := unix.Open(console.path, os.O_WRONLY, 0)
Serge Bazanski5f8414d2022-06-24 13:02:11 +020089 if err == nil {
90 runtimeFds = append(runtimeFds, fd)
Serge Bazanski3b098232023-03-16 17:57:02 +010091 l.Infof("Panic console: %s", console.path)
Serge Bazanski5f8414d2022-06-24 13:02:11 +020092 }
93 }
94
Lorenz Brun4025c9b2022-06-16 16:12:53 +000095 // This could cause a data race if the runtime crashed while we're
96 // initializing the crash handler, but there is no locking infrastructure
97 // for this so we have to take that risk.
98 overrideWrite = runtimeWrite
99 return
100}