package crashlog import ( "bufio" "fmt" "regexp" "strings" "github.com/apex/log" "github.com/spf13/cast" ) type Field struct { Title string Value any } func pad(in string, ammount int) string { if ammount >= len(in) { return in + strings.Repeat(" ", ammount-len(in)) } return in } func (f *Field) String() string { if f == nil { return "" } switch v := f.Value.(type) { case string: return fmt.Sprintf("%s %s\n", pad(colorField(f.Title)+":", 36), v) case uint64: return fmt.Sprintf("%s %#x\n", pad(colorField(f.Title)+":", 36), v) case bool: return fmt.Sprintf("%s %t\n", pad(colorField(f.Title)+":", 36), v) default: return fmt.Sprintf("%s %v\n", pad(colorField(f.Title)+":", 36), f.Value) } } type EpochTimeSec struct { Sec uint64 Usec uint64 } func (e EpochTimeSec) String() string { return fmt.Sprintf("%#08x %#08x", e.Sec, e.Usec) } type EpochTime struct { Boot EpochTimeSec Sleep EpochTimeSec Wake EpochTimeSec Calendar EpochTimeSec } func (e EpochTime) String() string { return fmt.Sprintf( colorField("Epoch Time sec usec\n")+ colorImage(" Boot ")+": %s\n"+ colorImage(" Sleep ")+": %s\n"+ colorImage(" Wake ")+": %s\n"+ colorImage(" Calendar")+": %s\n", e.Boot, e.Sleep, e.Wake, e.Calendar) } type Range struct { Start uint64 End uint64 } func (r Range) String() string { return fmt.Sprintf("%#016x - %#016x", r.Start, r.End) } type ZoneInfo struct { found bool ZoneMap Range VM Range RO Range Gen0 Range Gen1 Range Gen2 Range Gen3 Range Data Range Metadata Range Bitmaps Range Extra Range } func (z ZoneInfo) String() string { if !z.found { return "" } return fmt.Sprintf( colorField("Zone Info\n")+ colorImage(" Zone Map")+": %s\n"+ colorImage(" . VM ")+": %s\n"+ colorImage(" . RO ")+": %s\n"+ colorImage(" . GEN0 ")+": %s\n"+ colorImage(" . GEN1 ")+": %s\n"+ colorImage(" . GEN2 ")+": %s\n"+ colorImage(" . GEN3 ")+": %s\n"+ colorImage(" . DATA ")+": %s\n"+ colorImage(" Metadata")+": %s\n"+ colorImage(" Bitmaps ")+": %s\n"+ colorImage(" Extra ")+": %s\n\n", z.ZoneMap, z.VM, z.RO, z.Gen0, z.Gen1, z.Gen2, z.Gen3, z.Data, z.Metadata, z.Bitmaps, z.Extra) } type TPIDRx_ELy map[string]uint64 func (t TPIDRx_ELy) String() string { if len(t) == 0 { return "" } var tpidrx strings.Builder for key, val := range t { tpidrx.WriteString(fmt.Sprintf(colorImage(" %-3s", key)+": %#016x\n", val)) } return fmt.Sprintf(colorField("TPIDRx_ELy")+"\n%s\n", tpidrx.String()) } type Core struct { Num int Msg string PC uint64 LR uint64 FP uint64 } func (c Core) String() string { if len(c.Msg) > 0 { return fmt.Sprintf(colorField("CORE")+" %d %s", c.Num, c.Msg) } return fmt.Sprintf(colorField("CORE")+" %d"+colorField(": PC")+"=%#016x, "+colorField("LR")+"=%#016x, "+colorField("FP")+"=%#016x", c.Num, c.PC, c.LR, c.FP) } type PanickedTask struct { PID int Name string Address uint64 Pages int Threads int } func (p PanickedTask) String() string { return fmt.Sprintf("%s: %#016x, %d %s, %d %s, %s %d: %s", colorField("Panicked Task"), p.Address, p.Pages, colorField("pages"), p.Threads, colorField("threads"), colorField("pid"), p.PID, colorImage(p.Name)) } type State struct { LR uint64 FP uint64 } func (s State) String() string { return fmt.Sprintf(colorImage("lr")+": %#016x - "+colorImage("fp")+": %#016x", s.LR, s.FP) } type BackTraceKext struct { Name string Version string UUID string Range Range } type PanickedThread struct { TID int Backtrace uint64 Address uint64 CallStack []State Kexts []BackTraceKext } func (p PanickedThread) String() string { var callstack strings.Builder callstack.WriteString("\n") for _, state := range p.CallStack { callstack.WriteString(fmt.Sprintf(" %s\n", state)) } kexts := "" if len(p.Kexts) > 0 { kexts = colorField("Kernel Extensions In Backtrace") + ":\n" } for _, kext := range p.Kexts { kexts += fmt.Sprintf(" %s (%v) %s @ %s\n", kext.Name, kext.Version, kext.UUID, kext.Range) } return fmt.Sprintf(colorField("Panicked Thread")+": %#016x, "+colorField("backtrace")+": %#016x, "+colorField("tid")+": %d%s%s", p.Address, p.Backtrace, p.TID, callstack.String(), kexts) } type LastStartedKext struct { StartedAt uint64 Name string Version string Address uint64 Size uint64 } func (l *LastStartedKext) String() string { if l == nil { return "" } return fmt.Sprintf( colorField("Last Started Kext at")+": %#x: %s %s (%s %#x, %s %d)\n", l.StartedAt, colorImage(l.Name), l.Version, colorField("addr"), l.Address, colorField("size"), l.Size) } type LoadedKexts []LoadedKext func (l LoadedKexts) String() string { if len(l) == 0 { return "" } var loadedKexts strings.Builder for _, kext := range l { loadedKexts.WriteString(fmt.Sprintf(" %s\n", kext)) } return fmt.Sprintf(colorField("Loaded Kexts:")+"\n%s\n", loadedKexts.String()) } type LoadedKext struct { Name string Version string } func (l LoadedKext) String() string { return fmt.Sprintf("%s %s", colorImage(l.Name), l.Version) } type Panic210 struct { Panic string DebuggerMessage *Field // string MemoryID *Field // uint64 OsReleaseType *Field // string OsVersion *Field // string KernelVersion *Field // string FilesetKernelCacheUUID *Field // string KernelCacheUUID *Field // string KernelUUID *Field // string BootSessionUUID *Field // string IBootVersion *Field // string SecureBoot *Field // bool RootsInstalled *Field // bool PaniclogVersion *Field // string KernelCacheSlide *Field // uint64 KernelCacheBase *Field // uint64 KernelSlide *Field // uint64 KernelTextBase *Field // uint64 KernelTextExecSlide *Field // uint64 KernelTextExecBase *Field // uint64 SptmLoadAddress *Field // uint64 SptmUUID *Field // string TxmLoadAddress *Field // uint64 TxmUUID *Field // string DebugHdrAddress *Field // uint64 DebugHdrEntryCount *Field // uint64 DebugHdrKernelCacheLoadAddress *Field // uint64 DebugHdrKernelCacheUUID *Field // string MachAbsoluteTime *Field // uint64 EpochTime EpochTime ZoneInfo ZoneInfo TPIDRx_ELy TPIDRx_ELy Cores []Core CompressorInfo *Field // string PanickedTask PanickedTask PanickedThread PanickedThread LastStartedKext *LastStartedKext LoadedKexts LoadedKexts lines []string } func parsePanicString210(in string) (*Panic210, error) { var err error crash := &Panic210{} scanner := bufio.NewScanner(strings.NewReader(in)) for scanner.Scan() { crash.lines = append(crash.lines, scanner.Text()) } if err := scanner.Err(); err != nil { return nil, fmt.Errorf("failed to scan panic string: %v", err) } crash.Panic = crash.getPanicString() crash.DebuggerMessage, err = crash.getStrField("Debugger Message", "Debugger message: ") if err != nil { log.WithError(err).Error("failed to get debugger message") } crash.MemoryID, err = crash.getIntField("Memory ID", "Memory ID:") if err != nil { log.WithError(err).Error("failed to get memory ID") } crash.OsReleaseType, err = crash.getStrField("OS Release Type", "OS release type: ") if err != nil { log.WithError(err).Error("failed to get OS release type") } crash.OsVersion, err = crash.getStrField("OS Version", "OS version: ") if err != nil { log.WithError(err).Error("failed to get OS version") } crash.KernelVersion, err = crash.getStrField("Kernel Version", "Kernel version: ") if err != nil { log.WithError(err).Error("failed to get kernel version") } crash.KernelCacheUUID, err = crash.getStrField("KernelCache UUID", "KernelCache UUID: ") if err != nil { log.WithError(err).Debug("failed to get kernelcache UUID") } crash.FilesetKernelCacheUUID, err = crash.getStrField("Fileset KernelCache UUID", "Fileset KernelCache UUID: ") if err != nil { log.WithError(err).Debug("failed to get fileset kernelcache UUID") } crash.KernelUUID, err = crash.getStrField("Kernel UUID", "Kernel UUID: ") if err != nil { log.WithError(err).Debug("failed to get kernel UUID") } crash.BootSessionUUID, err = crash.getStrField("Boot Session UUID", "Boot session UUID: ") if err != nil { log.WithError(err).Debug("failed to get boot session UUID") } crash.IBootVersion, err = crash.getStrField("iBoot Version", "iBoot version: ") if err != nil { log.WithError(err).Error("failed to get iBoot version") } crash.SecureBoot, err = crash.getBoolField("Secure Boot", "secure boot?: ") if err != nil { log.WithError(err).Debug("failed to get secure boot") } crash.RootsInstalled, err = crash.getBoolField("Roots Installed", "roots installed: ") if err != nil { log.WithError(err).Debug("failed to get roots installed") } crash.PaniclogVersion, err = crash.getStrField("Paniclog Version", "Paniclog version: ") if err != nil { log.WithError(err).Error("failed to get paniclog version") } crash.KernelCacheSlide, err = crash.getIntField("KernelCache Slide", "KernelCache slide:") if err != nil { log.WithError(err).Debug("failed to get kernelcache slide") } crash.KernelCacheBase, err = crash.getIntField("KernelCache Base", "KernelCache base:") if err != nil { log.WithError(err).Debug("failed to get kernelcache base") } crash.KernelSlide, err = crash.getIntField("Kernel Slide", "Kernel slide:") if err != nil { log.WithError(err).Error("failed to get kernel slide") } crash.KernelTextBase, err = crash.getIntField("Kernel Text Base", "Kernel text base:") if err != nil { log.WithError(err).Error("failed to get kernel text base") } crash.KernelTextExecSlide, err = crash.getIntField("Kernel Text Exec Slide", "Kernel text exec slide:") if err != nil { log.WithError(err).Debug("failed to get kernel text exec slide") } crash.KernelTextExecBase, err = crash.getIntField("Kernel Text Exec Base", "Kernel text exec base:") if err != nil { log.WithError(err).Debug("failed to get kernel text exec base") } crash.SptmLoadAddress, err = crash.getIntField("SPTM Load Address", "SPTM load address:") if err != nil { log.WithError(err).Debug("failed to get SPTM load address") } crash.SptmUUID, err = crash.getStrField("SPTM UUID", "SPTM UUID: ") if err != nil { log.WithError(err).Debug("failed to get SPTM UUID") } crash.TxmLoadAddress, err = crash.getIntField("TXM Load Address", "TXM load address:") if err != nil { log.WithError(err).Debug("failed to get TXM load address") } crash.TxmUUID, err = crash.getStrField("TXM UUID", "TXM UUID: ") if err != nil { log.WithError(err).Debug("failed to get TXM UUID") } crash.DebugHdrAddress, err = crash.getIntField("Debug Header Address", "Debug Header address:") if err != nil { log.WithError(err).Debug("failed to get Debug Header address") } crash.DebugHdrEntryCount, err = crash.getStrField("Debug Header Entry Count", "Debug Header entry count:") if err != nil { log.WithError(err).Debug("failed to get Debug Header entry count") } crash.DebugHdrKernelCacheLoadAddress, err = crash.getIntField("Debug Header KernelCache Load Address", "Debug Header kernelcache load address:") if err != nil { log.WithError(err).Debug("failed to get TXM load address") } crash.DebugHdrKernelCacheUUID, err = crash.getStrField("Debug Header KernelCache UUID", "Debug Header kernelcache UUID: ") if err != nil { log.WithError(err).Debug("failed to get TXM UUID") } crash.MachAbsoluteTime, err = crash.getIntField("Mach Absolute Time", "mach_absolute_time:") if err != nil { log.WithError(err).Error("failed to get mach absolute time") } if err := crash.getEpochTime(); err != nil { log.WithError(err).Error("failed to get epoch time") } if err := crash.getZoneInfo(); err != nil { log.WithError(err).Error("failed to get zone info") } if err := crash.getTPIDRx_ELy(); err != nil { log.WithError(err).Debug("failed to get TPIDRx_ELy") } if err := crash.getCores(); err != nil { log.WithError(err).Error("failed to get cores") } crash.CompressorInfo, err = crash.getStrField("Compressor Info", "Compressor Info: ") if err != nil { log.WithError(err).Debug("failed to get compressor info") } if err := crash.getPanickedTask(); err != nil { log.WithError(err).Error("failed to get panicked task") } if err := crash.getPanickedThread(); err != nil { log.WithError(err).Error("failed to get panicked thread") } if err := crash.getLastStartedKext(); err != nil { log.WithError(err).Debug(err.Error()) } if err := crash.getLoadedKexts(); err != nil { log.WithError(err).Debug(err.Error()) } return crash, nil } // hasPrefixFold checks if s has the given prefix, ignoring case func hasPrefixFold(s, prefix string) bool { return len(s) >= len(prefix) && strings.EqualFold(s[:len(prefix)], prefix) } func (p *Panic210) getBoolField(title, key string) (*Field, error) { for _, line := range p.lines { if hasPrefixFold(line, key) { switch strings.ToLower(line[len(key):]) { case "yes", "1", "true": return &Field{Title: title, Value: true}, nil case "no", "0", "false": return &Field{Title: title, Value: false}, nil default: return nil, fmt.Errorf("failed to parse bool: %s", line) } } } return nil, fmt.Errorf("failed to find '%s'", key) } func (p *Panic210) getStrField(title, key string) (*Field, error) { for _, line := range p.lines { if hasPrefixFold(line, key) { // Use the actual prefix length to trim, preserving the remainder return &Field{Title: title, Value: line[len(key):]}, nil } } return nil, fmt.Errorf("failed to find '%s'", key) } func (p *Panic210) getIntField(title, key string) (*Field, error) { for _, line := range p.lines { if hasPrefixFold(line, key) { return &Field{Title: title, Value: cast.ToUint64(strings.TrimSpace(line[len(key):]))}, nil } } return nil, fmt.Errorf("failed to find '%s'", key) } func (p *Panic210) getPanicString() string { var lines []string for _, line := range p.lines { if strings.HasPrefix(line, "Debugger message:") { break } lines = append(lines, line) } return strings.Join(lines, "\n") } func (p *Panic210) getEpochTime() (err error) { found := false parse := func(line string) (uint64, uint64, error) { timeRE := regexp.MustCompile(`: (?P0x\w+) (?P0x\w+)`) matches := timeRE.FindStringSubmatch(line) if len(matches) < 3 { return 0, 0, fmt.Errorf("failed to parse time: %s", line) } return cast.ToUint64(matches[1]), cast.ToUint64(matches[2]), nil } for _, line := range p.lines { if strings.HasPrefix(line, "Epoch Time: sec usec") { found = true continue } if found { if strings.HasPrefix(line, " Boot : ") { p.EpochTime.Boot.Sec, p.EpochTime.Boot.Usec, err = parse(line) if err != nil { return err } continue } if strings.HasPrefix(line, " Sleep : ") { p.EpochTime.Sleep.Sec, p.EpochTime.Sleep.Usec, err = parse(line) if err != nil { return err } continue } if strings.HasPrefix(line, " Wake : ") { p.EpochTime.Wake.Sec, p.EpochTime.Wake.Usec, err = parse(line) if err != nil { return err } continue } if strings.HasPrefix(line, " Calendar: ") { p.EpochTime.Calendar.Sec, p.EpochTime.Calendar.Usec, err = parse(line) if err != nil { return err } continue } } } if !found { return fmt.Errorf("failed to find Epoch Time") } return nil } func (p *Panic210) getZoneInfo() (err error) { parse := func(line string) (uint64, uint64, error) { timeRE := regexp.MustCompile(`: (?P\w+) - (?P\w+)`) matches := timeRE.FindStringSubmatch(line) if len(matches) < 3 { return 0, 0, fmt.Errorf("failed to parse time: %s", line) } return cast.ToUint64(matches[1]), cast.ToUint64(matches[2]), nil } for _, line := range p.lines { if strings.HasPrefix(line, "Zone info:") { p.ZoneInfo.found = true continue } if p.ZoneInfo.found { if strings.HasPrefix(line, " Zone map: ") { p.ZoneInfo.ZoneMap.Start, p.ZoneInfo.ZoneMap.End, err = parse(line) if err != nil { return err } continue } if strings.HasPrefix(line, " . VM : ") { p.ZoneInfo.VM.Start, p.ZoneInfo.VM.End, err = parse(line) if err != nil { return err } continue } if strings.HasPrefix(line, " . RO : ") { p.ZoneInfo.RO.Start, p.ZoneInfo.RO.End, err = parse(line) if err != nil { return err } continue } if strings.HasPrefix(line, " . GEN0 : ") { p.ZoneInfo.Gen0.Start, p.ZoneInfo.Gen0.End, err = parse(line) if err != nil { return err } continue } if strings.HasPrefix(line, " . GEN1 : ") { p.ZoneInfo.Gen1.Start, p.ZoneInfo.Gen1.End, err = parse(line) if err != nil { return err } continue } if strings.HasPrefix(line, " . GEN2 : ") { p.ZoneInfo.Gen2.Start, p.ZoneInfo.Gen2.End, err = parse(line) if err != nil { return err } continue } if strings.HasPrefix(line, " . GEN3 : ") { p.ZoneInfo.Gen3.Start, p.ZoneInfo.Gen3.End, err = parse(line) if err != nil { return err } continue } if strings.HasPrefix(line, " . DATA : ") { p.ZoneInfo.Data.Start, p.ZoneInfo.Data.End, err = parse(line) if err != nil { return err } continue } if strings.HasPrefix(line, " Metadata: ") { p.ZoneInfo.Metadata.Start, p.ZoneInfo.Metadata.End, err = parse(line) if err != nil { return err } continue } if strings.HasPrefix(line, " Bitmaps : ") { p.ZoneInfo.Bitmaps.Start, p.ZoneInfo.Bitmaps.End, err = parse(line) if err != nil { return err } continue } if strings.HasPrefix(line, " Extra : ") { p.ZoneInfo.Extra.Start, p.ZoneInfo.Extra.End, err = parse(line) if err != nil { return err } continue } } } if !p.ZoneInfo.found { return fmt.Errorf("failed to find Zone info") } return nil } func (p *Panic210) getTPIDRx_ELy() (err error) { re := regexp.MustCompile(`^TPIDRx_ELy`) p.TPIDRx_ELy = make(map[string]uint64) for _, line := range p.lines { if re.MatchString(line) { tpidRE := regexp.MustCompile(`(?:(?P\w+): (?P\w+)\s)+`) matches := tpidRE.FindAllStringSubmatch(line, -1) if len(matches) < 3 { continue } for _, match := range matches { p.TPIDRx_ELy[match[1]] = cast.ToUint64(match[2]) } return nil } } return fmt.Errorf("failed to find TPIDRx_ELy") } func (p *Panic210) getCores() (err error) { re := regexp.MustCompile(`^CORE \d+`) for _, line := range p.lines { if re.MatchString(line) { coreRE := regexp.MustCompile(`^CORE (?P\d+): PC=(?P0x\w+), LR=(?P0x\w+), FP=(?P0x\w+)`) matches := coreRE.FindStringSubmatch(line) if len(matches) < 5 { coreRE = regexp.MustCompile(`^CORE (?P\d+) (?P.*)`) if matches = coreRE.FindStringSubmatch(line); len(matches) == 3 { core := Core{ Num: cast.ToInt(matches[1]), Msg: matches[2], } p.Cores = append(p.Cores, core) } continue } core := Core{ Num: cast.ToInt(matches[1]), PC: cast.ToUint64(matches[2]), LR: cast.ToUint64(matches[3]), FP: cast.ToUint64(matches[4]), } p.Cores = append(p.Cores, core) } } return nil } func (p *Panic210) getPanickedTask() (err error) { re := regexp.MustCompile(`^Panicked task `) for _, line := range p.lines { if re.MatchString(line) { ptRE := regexp.MustCompile(`^Panicked task (?P\w+): (?P\d+) pages, (?P\d+) threads: pid (?P\d+): (?P.*)`) matches := ptRE.FindStringSubmatch(line) if len(matches) < 6 { continue } p.PanickedTask = PanickedTask{ PID: cast.ToInt(matches[4]), Name: matches[5], Address: cast.ToUint64(matches[1]), Pages: cast.ToInt(matches[2]), Threads: cast.ToInt(matches[3]), } return nil } } return fmt.Errorf("failed to find panicked task") } func (p *Panic210) getPanickedThread() (err error) { re := regexp.MustCompile(`^Panicked thread:`) found := false kextsFound := false for _, line := range p.lines { if re.MatchString(line) { ptRE := regexp.MustCompile(`^Panicked thread: (?P\w+), backtrace: (?P\w+), tid: (?P\d+)`) matches := ptRE.FindStringSubmatch(line) if len(matches) < 4 { continue } p.PanickedThread = PanickedThread{ Address: cast.ToUint64(matches[1]), Backtrace: cast.ToUint64(matches[2]), TID: cast.ToInt(matches[3]), } found = true continue } if found { csRE := regexp.MustCompile(`^\s+lr: (?P\w+) fp: (?P\w+)`) matches := csRE.FindStringSubmatch(line) if len(matches) < 3 { continue } p.PanickedThread.CallStack = append(p.PanickedThread.CallStack, State{ LR: cast.ToUint64(matches[1]), FP: cast.ToUint64(matches[2]), }) kextInBtRE := regexp.MustCompile(`^\s+Kernel Extensions in backtrace:`) if kextInBtRE.MatchString(line) { kextsFound = true continue } if kextsFound { kextRE := regexp.MustCompile(`^\s+(?P\w+)\((?P[0-9.]+)\) \[(?P.*)\]@(?P\w+)->(?P\w+)`) matches := kextRE.FindStringSubmatch(line) if len(matches) < 6 { continue } p.PanickedThread.Kexts = append(p.PanickedThread.Kexts, BackTraceKext{ Name: matches[1], Version: matches[2], UUID: matches[3], Range: Range{Start: cast.ToUint64(matches[4]), End: cast.ToUint64(matches[5])}, }) } } } if found { return nil } return fmt.Errorf("failed to find panicked thread") } func (p *Panic210) getLastStartedKext() (err error) { re := regexp.MustCompile(`^last started kext at`) found := false for _, line := range p.lines { if re.MatchString(line) { ptRE := regexp.MustCompile(`^last started kext at (?P.+): (?P.*)\s+(?P[0-9.]+) \(addr (?P\w+), size (?P\w+)\)`) matches := ptRE.FindStringSubmatch(line) if len(matches) < 4 { continue } p.LastStartedKext = &LastStartedKext{ StartedAt: cast.ToUint64(matches[1]), Name: matches[2], Version: matches[3], Address: cast.ToUint64(matches[4]), Size: cast.ToUint64(matches[5]), } found = true continue } } if found { return nil } return fmt.Errorf("failed to find 'last started kext at'") } func (p *Panic210) getLoadedKexts() (err error) { re := regexp.MustCompile(`^loaded kexts:`) found := false for _, line := range p.lines { if re.MatchString(line) { found = true continue } if found { lkRE := regexp.MustCompile(`^(?P\S+)\s+(?P[0-9.]+)`) matches := lkRE.FindStringSubmatch(line) if len(matches) < 3 { break } p.LoadedKexts = append(p.LoadedKexts, LoadedKext{ Name: matches[1], Version: matches[2], }) } } if found { return nil } return fmt.Errorf("failed to find 'loaded kexts:'") } func (p *Panic210) String() string { panic := p.Panic start, rest, ok := strings.Cut(p.Panic, ":") if ok { panic = colorField("Panic") + fmt.Sprintf("\n%s\n %s", start, rest) } var cores strings.Builder for _, core := range p.Cores { cores.WriteString(fmt.Sprintf("%s\n", core)) } return fmt.Sprintf( "%s\n\n"+ // panic "%s"+ "%s"+ "%s"+ "%s"+ "%s"+ "%s"+ "%s"+ "%s"+ "%s"+ "%s"+ "%s"+ "%s"+ "%s"+ "%s"+ "%s"+ "%s"+ "%s"+ "%s"+ "%s"+ "%s"+ "%s"+ "%s"+ "%s"+ "%s"+ "%s"+ "%s"+ "%s"+ "\n%s\n"+ // EpochTime "%s"+ // ZoneInfo "%s"+ // TPIDRx_ELy "%s\n"+ // cores "%s"+ // CompressorInfo "%s\n"+ // PanickedTask "%s"+ // PanickedThread "%s"+ // LastStartedKext "%s", // LoadedKexts panic, p.DebuggerMessage, p.MemoryID, p.OsReleaseType, p.OsVersion, p.KernelVersion, p.KernelCacheUUID, p.KernelUUID, p.BootSessionUUID, p.IBootVersion, p.SecureBoot, p.RootsInstalled, p.PaniclogVersion, p.KernelCacheSlide, p.KernelCacheBase, p.KernelSlide, p.KernelTextBase, p.KernelTextExecSlide, p.KernelTextExecBase, p.SptmLoadAddress, p.SptmUUID, p.TxmLoadAddress, p.TxmUUID, p.DebugHdrAddress, p.DebugHdrEntryCount, p.DebugHdrKernelCacheLoadAddress, p.DebugHdrKernelCacheUUID, p.MachAbsoluteTime, p.EpochTime, p.ZoneInfo, p.TPIDRx_ELy, cores.String(), p.CompressorInfo, p.PanickedTask, p.PanickedThread, p.LastStartedKext, p.LoadedKexts, ) }