// Parse writes a csv entry for each line in the kernel log file, and returns whether the format was valid. func Parse(f string) (bool, string, []error) { var errs []error var buf bytes.Buffer csvState := csv.NewState(&buf, false) activeMap := make(map[string]*entry) curTime := int64(0) startTime := int64(0) matched := false for _, l := range strings.Split(f, "\n") { if matches, result := historianutils.SubexpNames(WakeSourceRE, l); matches { timestamp, err := bugreportutils.TimeStampToMs(result["timeStamp"], result["remainder"], time.UTC) if err != nil { errs = append(errs, err) continue } matched = true if startTime == 0 { startTime = timestamp } curTime = timestamp t := result["transitionType"] v := result["value"] e, alreadyActive := activeMap[v] switch t { case PositiveTransition: if !alreadyActive { e = &entry{timestamp, v} activeMap[v] = e } else { // Double positive transition. Ignore the event. errs = append(errs, fmt.Errorf("two positive transitions for %q, wakesource %q", KernelWakeSource, v)) continue } case NegativeTransition: if !alreadyActive { errs = append(errs, fmt.Errorf("negative transition without positive transition for %q, wakesource %q", KernelWakeSource, v)) continue } delete(activeMap, v) default: errs = append(errs, fmt.Errorf("unknown transition for %q %q", KernelWakeSource, t)) continue } csvState.AddEntry(KernelWakeSource, e, curTime) } } csvState.PrintAllReset(curTime) return matched, buf.String(), errs }
// newParser creates a parser for the given bugreport. func newParser(br string) (*parser, []string, error) { loc, err := bugreportutils.TimeZone(br) if err != nil { return nil, []string{}, err } pm, warnings := bugreportutils.ExtractPIDMappings(br) // Extract the year and month from the bugreport dumpstate line. d, err := bugreportutils.DumpState(br) if err != nil { return nil, warnings, fmt.Errorf("could not find dumpstate information in the bugreport: %v", err) } buf := new(bytes.Buffer) return &parser{ referenceYear: d.Year(), referenceMonth: d.Month(), loc: loc, activeProcMap: make(map[string]*procEntry), buf: buf, csvState: csv.NewState(buf, false), pidMappings: pm, }, warnings, nil }
// Parse writes a CSV entry for each line in the powermonitor file, and returns whether the format was valid. func Parse(f string) (bool, string, []error) { var errs []error var buf bytes.Buffer csvState := csv.NewState(&buf, false) var e entry // The powermonitor file can have multiple readings per second. // Timestamps are in unix time (in seconds), so we count the number of readings per second, // and then later on convert them to ms. timestampCount := 0 var readings []float64 var timestamp, prevTimestamp time.Duration matched := false fractionalMode := false for _, l := range strings.Split(f, "\n") { // Lines should be in the format: unix_timestamp amps // Ignore non matching lines. matches, result := historianutils.SubexpNames(powermonitorRE, l) if !matches { errs = append(errs, fmt.Errorf("line did not match format: unix_timestamp amps : %q", l)) continue } hasFractional := result["fractional"] != "" // If we've matched a line previously and one line is fractional and the other isn't, flag an error. if matched && fractionalMode != hasFractional { errs = append(errs, fmt.Errorf("timestamp %q does not match fractional mode %v", l, fractionalMode)) continue } fractionalMode = hasFractional d := fmt.Sprintf("%s%ss", result["timeStamp"], result["fractional"]) var err error timestamp, err = time.ParseDuration(d) if err != nil { errs = append(errs, fmt.Errorf("could not parse timestamp %q", l)) continue } f64, err := strconv.ParseFloat(result["amps"], 64) if err != nil { errs = append(errs, fmt.Errorf("could not parse amps %q", l)) continue } matched = true // If the timestamps are in fractional seconds, we can set the CSV state immediately. // Otherwise for duplicated seconds timestamps we need to accumulate all entries for the current second. if fractionalMode { e.set(csvState, timestamp, f64) continue } if timestamp == 0 || timestamp == prevTimestamp { // The same timestamp appeared, increment the count. timestampCount++ } else { // New timestamp. // Emit entries for the previous time interval. if err := emitEntriesForInterval(prevTimestamp, timestamp, timestampCount, readings, csvState, &e); err != nil { errs = append(errs, err) } prevTimestamp = timestamp timestampCount = 1 readings = nil } readings = append(readings, f64) } if fractionalMode { csvState.PrintAllReset(int64(timestamp / time.Millisecond)) } else { if err := emitEntriesForInterval(prevTimestamp, prevTimestamp+time.Second, timestampCount, readings, csvState, &e); err != nil { errs = append(errs, err) } csvState.PrintAllReset(int64((prevTimestamp + time.Second) / time.Millisecond)) } return matched, buf.String(), errs }