func (p *appleParser) parseBinaryImages(startIndex int) error { p.modules = make(map[string]binaryImage) for _, line := range p.lines[startIndex:] { // Stop at the first line which is blank or starts with "Sample analysis of // process [<process ID> written]", which indicates the end of the section. if line == "" || strings.HasPrefix(line, kSampleAnalysisWritten) { break } matches := kBinaryImage.FindAllStringSubmatch(line, -1) if matches == nil || len(matches) != 1 { return fmt.Errorf("invalid binary image: %s", line) } image := binaryImage{ name: matches[0][2], ident: matches[0][3], path: matches[0][4], } var err error image.baseAddress, err = breakpad.ParseAddress(matches[0][1]) if err != nil { return fmt.Errorf("parse binary image: %v", err) } p.modules[image.name] = image } return nil }
func (p *appleParser) Symbolize(tables []breakpad.SymbolTable) string { if p.lineParser == nil { panic(fmt.Sprintf("Cannot handle report version %d", p.reportVersion)) } tableMap := p.mapTables(tables) // The p.modules is mapped by bundle ID, so re-map it to be done by breakpad // name. var modules map[string]binaryImage if p.tableMapType == kModuleTypeBreakpad { modules = make(map[string]binaryImage, len(p.modules)) for _, module := range p.modules { modules[module.breakpadName()] = module } } for i, line := range p.lines { frag := p.lineParser(line) if frag == nil { continue } address, err := breakpad.ParseAddress(line[frag.address[0]:frag.address[1]]) if err != nil { continue } var binaryImage binaryImage moduleName := line[frag.module[0]:frag.module[1]] if p.tableMapType == kModuleTypeBreakpad { var ok bool binaryImage, ok = modules[moduleName] if !ok { continue } } else { binaryImage = p.modules[moduleName] } table, ok := tableMap[binaryImage.breakpadName()] if !ok { continue } symbol := table.SymbolForAddress(address - binaryImage.baseAddress) rl := replacementList{ {loc: frag.functionName, value: symbol.Function}, {loc: frag.fileNameLocation, value: symbol.FileLine()}, } sort.Sort(sort.Reverse(rl)) for _, r := range rl { start, end := r.loc[0], r.loc[1] p.lines[i] = p.lines[i][:start] + r.value + p.lines[i][end:] } } return strings.Join(p.lines, "\n") }
func (p *fragmentParser) parseAddresses(gip *GeneratorParser, input string) error { addresses := strings.Fields(input) for _, address := range addresses { absAddress, err := breakpad.ParseAddress(address) if err != nil { gip.EmitStackFrame(0, GIPStackFrame{Placeholder: address}) } else { gip.EmitStackFrame(0, GIPStackFrame{ RawAddress: absAddress, Address: absAddress - p.baseAddress, Module: p.module, }) } } return nil }
// handleFragment extracts fragment-specific input from the HTTP request and // returns a FragmentParser if successful. func (h *Handler) handleFragment(ctx context.Context, rw http.ResponseWriter, req *http.Request) parser.Parser { module := req.FormValue("module") ident := req.FormValue("ident") if module == "" || ident == "" { replyError(req, rw, http.StatusBadRequest, "Missing module or ident") return nil } loadAddress, err := breakpad.ParseAddress(req.FormValue("load_address")) if err != nil { replyError(req, rw, http.StatusBadRequest, fmt.Sprintf("Load address: %s", err)) return nil } return parser.NewFragmentParser(module, ident, loadAddress) }
func main() { flag.Parse() if *symbolFile == "" { fatal("Need to specify a symbol file") } offset, err := breakpad.ParseAddress(*baseAddress) if err != nil { fatal(err) } fd, err := os.Open(*symbolFile) if err != nil { fatal(err) } defer fd.Close() data, err := ioutil.ReadAll(fd) if err != nil { fatal(err) } table, err := breakpad.NewBreakpadSymbolTable(string(data)) if err != nil { fatal(err) } input := strings.Join(flag.Args(), " ") parser := parser.NewFragmentParser(table.ModuleName(), table.Identifier(), offset) if err = parser.ParseInput(input); err != nil { fatal(err) } fmt.Println(parser.Symbolize([]breakpad.SymbolTable{table})) }
// buildGenParser performs two steps: 1) parse stack frames from the given input; // 2) parse out the build version number, which we use to locate a module n the crash // server. The parser is derived from clank/tools/stack_core.py. Once these two steps // have been completed, this function returns a GeneratorParser, which encapsultes // the infor parsed in these two steps and help to format the output in Symbolize. func (p *androidParser) buildGenParser(lines []string) (*GeneratorParser, error) { // An example of a line of logcat frame: // "0I/DEBUG ( 2636): #23 pc 0002b5ec /system/lib/libdvm.so (dvmInterpret(Thread*, Method const*, JValue*)+184)" frameLine := regexp.MustCompile("(.*)\\#([0-9]+)[ \t]+(..)[ \t]+([0-9a-f]{8})[ \t]+([^\r\n \t]*)( \\((.*)\\))?") // An example of the version number (format 0): // "W/google-breakpad(27887): 27.0.1453.105". version0Line := regexp.MustCompile("google\\-breakpad(?:\\([0-9]+\\))*: (([0-9]+\\.)+[0-9]+)$") // An example of the version number (format 1): // "W/google-breakpad(27887): 1453106". version1Line := regexp.MustCompile("google\\-breakpad(?:\\([0-9]+\\))*: (([0-9]+\\.)*[0-9]+)$") // Keep track of the android chrome version for crash server look-up. var version string // Keep track of the frames we read in the input. frames := make([]androidFrame, 0, len(lines)) for _, line := range lines { // Parse out the version number of this android chrome build. if version0Line.MatchString(line) { match := version0Line.FindStringSubmatch(line) version = match[1] } else if version1Line.MatchString(line) && version == "" { match := version1Line.FindStringSubmatch(line) version = match[1] } else if frameLine.MatchString(line) { // Parse out a single frame. match := frameLine.FindStringSubmatch(line) if fnum, err := strconv.ParseUint(match[2], 10, 0); err == nil { // ParseAddress cannot fail if the regular expression passes addr, _ := breakpad.ParseAddress(match[4]) frames = append(frames, androidFrame{ module: match[5], address: addr, frameNumber: uint(fnum), symbol: match[7], }) } else { return nil, fmt.Errorf("Failed to parse the frame number %s in line: %s", match[2], line) } } } // If a version was given as manual input. The manual version number supersedes the version in the log. if p.version != "" { version = p.version } // Check here to see we found the version number in the log. if version == "" { return nil, errors.New("Version number of Chrome was not found.") } // Use the version number to retrieve the chrome module (libchromeview.so). if chromeViewModule, err := p.retrieveChromeModule(version); err == nil { // Create a GeneratorParser. For every libchromeview symbol, we emit a proper stack frame. // For other frames, we store the given module and symbol name as the place holder; they will // show up in the final output. retparser := NewGeneratorParser(func(parser *GeneratorParser, input string) error { for _, frame := range frames { if strings.HasSuffix(frame.module, "libchromeview.so") { parser.EmitStackFrame(0, GIPStackFrame{ RawAddress: frame.address, Address: frame.address, Module: chromeViewModule, }) } else { parser.EmitStackFrame(0, GIPStackFrame{ RawAddress: frame.address, Address: frame.address, Placeholder: "[" + frame.module + "] " + frame.symbol, }) } } return nil }) return retparser, nil } else { return nil, err } }
func (p *stackwalkParser) ParseInput(data string) error { buf := bytes.NewBufferString(data) parsingThreads := false for { // Read the input string a line at a time. line, err := buf.ReadString('\n') if err != nil { if err == io.EOF { return nil } else { return err } } line = line[0 : len(line)-1] // Remove \n. // There is only one blank line in the input: the separator between the // metadata and the thread list. if line == "" { if !parsingThreads { parsingThreads = true continue } else { return errors.New("unexpected blank line: already encountered thread list") } } fields := strings.Split(line, "|") if parsingThreads { if len(fields) < kStackwalkFrame_Len { return fieldError("stack frame", kStackwalkFrame_Len, len(fields), line) } // Extract the thread ID from the frame and create a new thread // slice if it is a new thread. threadId, err := strconv.Atoi(fields[kStackwalkFrameThread]) if err != nil { return err } // Create the frame information. address, err := breakpad.ParseAddress(fields[kStackwalkFrameAddress]) if err != nil { return err } module := fields[kStackwalkFrameModule] p.threads[threadId] = append(p.threads[threadId], stackwalkFrame{ module: module, address: address, }) if module != "" { p.usedModules[module] = true } } else { switch fields[0] { case kStackwalkCrash: if len(fields) < kStackwalkCrash_Len { return fieldError("crash line", kStackwalkCrash_Len, len(fields), line) } p.crashInfo = fields[1] + " @ " + fields[2] crashedThread, err := strconv.Atoi(fields[3]) if err != nil { return err } p.crashedThread = crashedThread case kStackwalkModule: if len(fields) < kStackwalkModule_Len { return fieldError("module", kStackwalkFrame_Len, len(fields), line) } name := fields[kStackwalkModuleName] p.modules[name] = fields[kStackwalkModuleIdentifier] } } } }