func (p *parser) parseANR(pkgs []*usagepb.PackageInfo, timestamp int64, v string) (string, error) { // Expected format of v is: User,pid,Package Name,Flags,reason. parts := strings.Split(v, ",") if len(parts) < 5 { return "", fmt.Errorf("%s: got %d parts, want 5", ANREvent, len(parts)) } warning := "" if len(parts) > 5 { warning = fmt.Sprintf("%s: got %d parts, expected 5", ANREvent, len(parts)) } var uid string // ANR event should still be displayed even if uid could not be matched. // Any error is returned at end of function. pkg, err := packageutils.GuessPackage(parts[2], "", pkgs) if pkg != nil { uid = fmt.Sprintf("%d", pkg.GetUid()) } // We store the UID as part of the ANR value rather than in the Opt field. // Usually the Opt field is used to populate a service mapper in the JS, however a less roundabout way is to just have the UID as part of the event itself, which will be specially parsed in the JS code. parts = append(parts[1:5], uid) p.csvState.PrintInstantEvent(csv.Entry{ Desc: "ANR", Start: timestamp, Type: "service", Value: strings.Join(parts, "~"), }) return warning, err }
// Parse writes a CSV entry for each line matching activity manager proc start and died, ANR and low memory events. // Package info is used to match crash events to UIDs. Errors encountered during parsing will be collected into an errors slice and will continue parsing remaining events. func Parse(pkgs []*usagepb.PackageInfo, f string) (string, []string, []error) { p, warnings, err := newParser(f) if err != nil { return "", nil, []error{err} } var errs []error crashSource := "" for _, line := range strings.Split(f, "\n") { m, result := historianutils.SubexpNames(logEntryRE, line) if !m { continue } timestamp, err := p.fullTimestamp(result["month"], result["day"], result["timeStamp"], result["remainder"]) if err != nil { errs = append(errs, err) continue } details := result["details"] pid := result["pid"] if m, _ = historianutils.SubexpNames(bluetoothScanRE, details); m { p.parseBluetoothScan(timestamp, pid) continue } if m, result = historianutils.SubexpNames(crashStartRE, details); m { crashSource = result["source"] continue } if m, result = historianutils.SubexpNames(crashProcessRE, details); m && crashSource != "" { var uid string pkg, err := packageutils.GuessPackage(result["process"], "", pkgs) if err != nil { errs = append(errs, err) // Still want to show the crash event even if there was an error matching a package. } else if pkg != nil { uid = fmt.Sprintf("%d", pkg.GetUid()) } p.csvState.PrintInstantEvent(csv.Entry{ Desc: "Crashes", Start: timestamp, Type: "service", Value: fmt.Sprintf("%s: %s", result["process"], crashSource), Opt: uid, }) crashSource = "" continue } m, result = historianutils.SubexpNames(activityManagerRE, details) if !m { // Non matching lines are ignored but not considered errors. continue } t := result["transitionType"] // Format of the value is defined at frameworks/base/services/core/java/com/android/server/am/EventLogTags.logtags. v := result["value"] switch t { case LowMemoryEvent: p.parseLowMemory(timestamp, v) case ANREvent: warning, err := p.parseANR(pkgs, timestamp, v) if err != nil { errs = append(errs, err) } if warning != "" { warnings = append(warnings, warning) } case ProcStartEvent, ProcDiedEvent: warning, err := p.parseProc(timestamp, v, t) if err != nil { errs = append(errs, err) } if warning != "" { warnings = append(warnings, warning) } default: errs = append(errs, fmt.Errorf("unknown transition for %q: %q", AMProc, t)) } } // If there was no corresponding am_proc_died event, set the end time to 0. p.csvState.PrintAllReset(0) return p.buf.String(), warnings, errs }