func parse(raw []byte, cmd string) crash.Info { // This is "inefficient", but I'm going to parse each thing I'm interested // in out of the full output instead of doing a single pass. // this just prettifies the rest of the parsers slightly die := func() { explode(raw, cmd) } ci := crash.Info{} ci.Registers = parseRegisters(raw, die) ci.Stack = parseStack(raw, die) parseExploitable(raw, &ci, die) // easier for this one to modify ci directly ci.FaultingInsn, ci.Disassembly = parseDisasm(raw, die) return ci }
func parseStack(raw []byte, ci *crash.Info, die func()) { // Stack trace: // * thread #1: tid = 0x9f2fa1f, 0x00007fff97580282 libsystem_kernel.dylib`__pthread_kill + 10, queue = 'com.apple.main-thread', stop reason = signal SIGABRT // * frame #0: 0x00007fff97580282 libsystem_kernel.dylib`__pthread_kill + 10 // frame #1: 0x00007fff8e3d54c3 libsystem_pthread.dylib`pthread_kill + 90 // frame #2: 0x00007fff97a4cb73 libsystem_c.dylib`abort + 129 // frame #3: 0x00000001000b4dc4 pdftoppm`Catalog::getNumPages() [inlined] Object::dictIs(this=0x0000000000000007, dictType=<unavailable>) - 18446744069413843515 // frame #4: 0x00000001000029c0 pdftoppm`main(argc=2, argv=<unavailable>) - 18446744069414573631 // frame #5: 0x00007fff9a2f35c9 libdyld.dylib`start + 1 // frame #6: 0x00007fff9a2f35c9 libdyld.dylib`start + 1 scanner := bufio.NewScanner(bytes.NewReader(raw)) mustAdvanceTo("Stack trace:", scanner, die) scanner.Scan() ff := strings.Fields(scanner.Text()) if len(ff) < 8 || ff[1] != "thread" { die() } for scanner.Scan() { // scan until we run out of frames ff := strings.Fields(scanner.Text()) if len(ff) == 0 { break } // A leading asterisk adjusts all our offsets by 1 adjust := 0 if ff[0] == "*" { adjust++ } if ff[0+adjust] != "frame" { break } if len(ff) < 3 { die() } var address uint64 var found bool for _, s := range ff { if strings.HasPrefix(s, "0x") { address = mustParseHex(s, die) found = true } } if !found { die() } splits := []string{"???", "???"} if len(ff) > 3+adjust { // split on backtick (`) // libsystem_pthread.dylib`pthread_kill + 90 rest := strings.Join(ff[3+adjust:], " ") splits = strings.SplitN(rest, "`", 2) if len(splits) < 2 { die() } } entry := crash.StackEntry{ Address: address, Module: splits[0], Symbol: splits[1], } ci.Stack = append(ci.Stack, entry) } // Set the fault to the first frame that's not blacklisted walk: for _, frame := range ci.Stack { for _, r := range blacklist { if r.MatchString(frame.Module) { continue walk } } ci.FaultingFrame = frame break } // don't be too fussy about not finding a stack, here, some crashes set // those registers to values that are unreadable as addresses. return }