Example #1
0
// 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
}
Example #2
0
// 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
}