blob: ca792886396138633186304a578201423799986d [file]
// Copyright 2022 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Package hpsutil contains functionality used by the HPS tast tests.
package hpsutil
import (
"bufio"
"context"
"encoding/csv"
"os"
"strconv"
"strings"
"chromiumos/tast/common/testexec"
"chromiumos/tast/errors"
"chromiumos/tast/testing"
)
// PowerlogResult returns all the power samples for all the columns returned by running powerlog.
type PowerlogResult struct {
Value map[string][]float64
Err error
}
func parsePowerlogCsv(ctx context.Context, csvFilename string) PowerlogResult {
f, err := os.Open(csvFilename)
if err != nil {
return PowerlogResult{Err: errors.Wrapf(err, "unable to read input file %s", csvFilename)}
}
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
if strings.Contains(scanner.Text(), "IndexError: list index out of range") {
return PowerlogResult{Err: errors.New("Sweetberry not found (replug the USB)")}
}
}
_, err = f.Seek(0, 0)
if err != nil {
return PowerlogResult{Err: errors.Wrapf(err, "unable to seek %s to 0", csvFilename)}
}
csvReader := csv.NewReader(f)
records, err := csvReader.ReadAll()
if err != nil {
return PowerlogResult{Err: errors.Wrapf(err, "unable to parse %s as CSV", csvFilename)}
}
if len(records) == 0 {
return PowerlogResult{Err: errors.New("CSV is empty, replug the Sweetberry")}
}
// column number to column name.
names := make(map[int]string)
// column name to slice of data.
data := make(map[string][]float64)
for i, name := range records[0] {
names[i] = strings.TrimSpace(name)
}
// TODO: calculate SPI by subtracting everything from TOTAL_POWER
for _, row := range records[1:] {
for i, strValue := range row {
column := names[i]
v, err := strconv.ParseFloat(strings.TrimSpace(strValue), 64)
if err != nil {
return PowerlogResult{Err: errors.Wrap(err, "unable to parse float")}
}
data[column] = append(data[column], v)
}
}
return PowerlogResult{Value: data}
}
// Powerlog runs sweetberry power measurement for specified boardFilename and scenarioFilename, saves the resulting csvFilename, and parses it to the PowerlogResult.
func Powerlog(ctx context.Context, csvFilename, boardFilename, scenarioFilename string, quit chan struct{}, result chan PowerlogResult) {
{
cmd := testexec.CommandContext(ctx,
"powerlog",
"-b", boardFilename,
"-c", scenarioFilename)
testing.ContextLog(ctx, cmd)
csvFile, err := os.OpenFile(csvFilename,
os.O_WRONLY|os.O_CREATE|os.O_APPEND,
0644)
if err != nil {
result <- PowerlogResult{Err: errors.Wrapf(err, "cannot open file %s", csvFilename)}
return
}
defer csvFile.Close()
cmd.Stderr = csvFile
cmd.Stdout = csvFile
if err := cmd.Start(); err != nil {
result <- PowerlogResult{Err: errors.Wrapf(err, "see %s for more details", csvFilename)}
return
}
done := false
for !done {
select {
case <-quit:
testing.ContextLog(ctx, "quitting powerlog")
done = true
}
}
testing.ContextLog(ctx, "quit powerlog")
if err := cmd.Kill(); err != nil {
result <- PowerlogResult{Err: errors.Wrap(err, "failed to kill powerlog")}
return
}
}
result <- parsePowerlogCsv(ctx, csvFilename)
}