blob: 89cae683f8a16504ac9c22830d2ad9429638a764 [file] [log] [blame]
Tim Windelschmidt83da4222024-12-16 02:49:50 +01001package bringup
2
3// This uses the unstable overrideWrite interface to also emit all runtime
4// writes to a dedicated runtime file descriptor to catch and debug crash dumps.
5// See https://go-review.googlesource.com/c/go/+/278792 for details about the
6// interface. This interface is relatively special, refrain from using most Go
7// features in here as it might cause unexpected behavior. Especially yielding
8// is a bad idea as the scheduler might be in an inconsistent state. But using
9// this interface was judged to be vastly more maintenance-friendly than
10// attempting to parse out this information from a combined stderr.
11
12import (
13 "os"
14 "syscall"
15 "unsafe"
16
17 "golang.org/x/sys/unix"
18
19 "source.monogon.dev/osbase/logtree"
20)
21
22// This hooks into a global variable which is checked by runtime.write and used
23// instead of runtime.write1 if populated.
24//
25//go:linkname overrideWrite runtime.overrideWrite
26var overrideWrite func(fd uintptr, p unsafe.Pointer, n int32) int32
27
28// Contains the files into which runtime logs and crashes are written.
29var runtimeFds []uintptr
30
31// This is essentially a reimplementation of the assembly function
32// runtime.write1, just with a hardcoded file descriptor and using the assembly
33// function syscall.RawSyscall to not get a dependency on Go's calling
34// convention and needing an implementation for every architecture.
35//
36// This has to use the syscall package as the x/sys/unix implementation
37// has a broken trampoline that calls the abi0 variant which is instrumented
38// for some unknown reason.
39//
40//go:norace
41//go:nosplit
42func runtimeWrite(fd uintptr, p unsafe.Pointer, n int32) int32 {
43 // Only redirect writes to stderr.
44 if fd != 2 {
45 a, _, err := syscall.RawSyscall(syscall.SYS_WRITE, fd, uintptr(p), uintptr(n))
46 if err == 0 {
47 return int32(a)
48 }
49 return int32(err)
50 }
51
52 // Write to the runtime panic FDs.
53 for _, f := range runtimeFds {
54 _, _, _ = syscall.RawSyscall(syscall.SYS_WRITE, f, uintptr(p), uintptr(n))
55 }
56
57 // Finally, write to original FD
58 a, _, err := syscall.RawSyscall(syscall.SYS_WRITE, fd, uintptr(p), uintptr(n))
59 if err == 0 {
60 return int32(a)
61 }
62 return int32(err)
63}
64
65func initPanicHandler(lt *logtree.LogTree, consoles []*console) {
66 l := lt.MustLeveledFor("panichandler")
67
68 // Setup pstore userspace message buffer
69 fd, err := unix.Open("/dev/pmsg0", os.O_WRONLY|os.O_SYNC, 0)
70 if err != nil {
71 l.Errorf("Failed to open pstore userspace device (pstore probably unavailable): %v", err)
72 l.Warningf("Continuing without persistent panic storage.")
73 } else {
74 runtimeFds = append(runtimeFds, uintptr(fd))
75 }
76
77 for _, c := range consoles {
78 fd, err := unix.Open(c.path, os.O_WRONLY, 0)
79 if err != nil {
80 l.Errorf("Failed opening panic console: %s", err)
81 continue
82 }
83
84 runtimeFds = append(runtimeFds, uintptr(fd))
85 l.Infof("Panic console: %s", c.path)
86 }
87
88 // This could cause a data race if the runtime crashed while we're
89 // initializing the crash handler, but there is no locking infrastructure
90 // for this so we have to take that risk.
91 overrideWrite = runtimeWrite
92}