blob: a06abe9a01ac3f86b19e9492de43ad92dcf0e36a [file] [log] [blame]
// Copyright 2024 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package osperf
import (
"context"
"encoding/json"
"os"
"strings"
"time"
"path/filepath"
"go.chromium.org/tast-tests/cros/common/chrome/extension"
"go.chromium.org/tast-tests/cros/common/perf"
"go.chromium.org/tast-tests/cros/remote/dutfs"
"go.chromium.org/tast-tests/cros/remote/osperf"
"go.chromium.org/tast-tests/cros/services/cros/ui"
"go.chromium.org/tast/core/rpc"
"go.chromium.org/tast/core/ssh/linuxssh"
"go.chromium.org/tast/core/testing"
"google.golang.org/protobuf/types/known/emptypb"
)
func init() {
testing.AddTest(&testing.Test{
Func: TabOpenLatencyPerfRaw,
LacrosStatus: testing.LacrosVariantUnneeded,
Desc: "Measures tab open latency remotely to get stable results quickly",
BugComponent: "b:167279", // ChromeOS > Platform > baseOS > Performance
Contacts: []string{"baseos-perf@google.com", "mtomoya@google.com"},
Attr: []string{"group:mainline", "informational"},
Data: []string{
"tab_open_latency_perf_raw/manifest.json",
"tab_open_latency_perf_raw/bench.js",
"tab_open_latency_perf_raw/background.js",
"tab_open_latency_perf_raw/bench.html",
},
SoftwareDeps: []string{"chrome"},
ServiceDeps: []string{
"tast.cros.browser.ChromeService",
"tast.cros.ui.ConnService",
"tast.cros.ui.TconnService",
},
})
}
func TabOpenLatencyPerfRaw(ctx context.Context, s *testing.State) {
d := s.DUT()
cl, err := rpc.Dial(ctx, s.DUT(), s.RPCHint())
if err != nil {
s.Fatal("Failed to dial to DUT for remote file system: ", err)
}
defer cl.Close(ctx)
const benchDir = "/tmp/bluebench" // Using /tmp dir to make it runnable with read-only rootfs.
fs := dutfs.NewClient(cl.Conn)
// (Re)create benchDir.
if dirExists, err := fs.Exists(ctx, benchDir); err != nil {
s.Fatal("Failed to check the existence of the USB device's original mount point: ", err)
} else if dirExists {
if err := fs.RemoveAll(ctx, benchDir); err != nil {
s.Fatalf("Failed to mkdir %s: %v", benchDir, err)
}
}
if err := fs.MkDir(ctx, benchDir, os.FileMode(0750)); err != nil {
s.Fatalf("Failed to mkdir %s: %v", benchDir, err)
}
dataPaths := s.DataPaths()
dataMap := make(map[string]string, len(dataPaths))
const dataDir = "tab_open_latency_perf_raw/"
for name, path := range dataPaths {
if !strings.HasPrefix(name, dataDir) {
s.Fatalf("The data path should start with %s but got %s", dataDir, name)
}
dataMap[path] = filepath.Join(benchDir, strings.TrimPrefix(name, dataDir))
}
if bytes, err := linuxssh.PutFiles(ctx, d.Conn(), dataMap, linuxssh.PreserveSymlinks); err != nil {
s.Fatal("Failed to copy bluebench files via ssh")
} else if bytes == 0 {
s.Fatal("zero bytes transferred while copying the bluebench files")
}
cr := run(ctx, s, cl, benchDir, filepath.Dir(dataPaths[dataDir+"manifest.json"]))
defer cr.Close(ctx, &emptypb.Empty{})
}
type tabOpenLatencyTestRawResult struct {
TabOpenLatencies []float64 `json:"tab_open_latencies"`
TotalDuration float64 `json:"total_duration"`
NumIterations int64 `json:"num_iterations"`
ResultLog string `json:"result_log"`
}
func run(ctx context.Context, s *testing.State, cl *rpc.Client, extDir, hostExtDir string) ui.ChromeServiceClient {
s.Log("Setting up bluebench extension")
ctx, cancel := context.WithTimeout(ctx, 5*time.Minute)
defer cancel()
extID, err := extension.ComputeExtensionID(hostExtDir)
if err != nil {
s.Fatalf("Failed to compute extension ID for %v: %v", extDir, err)
}
cr := ui.NewChromeServiceClient(cl.Conn)
req := ui.NewRequest{UnpackedExtensions: []string{extDir}}
if _, err := cr.New(ctx, &req); err != nil {
s.Fatal("Failed to start Chrome: ", err)
}
conn := ui.NewConnServiceClient(cl.Conn)
benchURL := "chrome-extension://" + extID + "/bench.html"
resTab, err := osperf.OpenBenchPage(ctx, benchURL, conn)
if err != nil {
s.Fatal("Failed to open new Tab: ", err)
}
s.Log("Running the benchmark")
resRun, err := conn.Call(ctx, &ui.ConnCallRequest{
Id: resTab.Id,
Fn: `
() => {
return window.startBench();
}
`,
})
if err != nil {
s.Fatal("startBench failed: ", err)
}
s.Log("Completed the benchmark")
resMap := resRun.AsInterface().(map[string]interface{})
jsonString, err := json.Marshal(resMap)
info := tabOpenLatencyTestRawResult{}
if err = json.Unmarshal(jsonString, &info); err != nil {
s.Fatal("Failed to parse tabOpenLatencyTestRawResult: ", err)
}
if err := os.WriteFile(filepath.Join(s.OutDir(), "bluebench_log.txt"),
[]byte(info.ResultLog), 0644); err != nil {
s.Error("Failed to write bluebench_log.txt: ", err)
}
dumpHardwareInformationWithVulnerabilityStatus(ctx, s)
// Save the result as results-chart.json.
pv := perf.NewValues()
pv.Set(perf.Metric{
Name: "TabOpenLatencies",
Unit: "milliseconds",
Direction: perf.SmallerIsBetter,
Multiple: true,
}, info.TabOpenLatencies...)
pv.Set(perf.Metric{
Name: "TotalDuration",
Unit: "milliseconds",
Direction: perf.SmallerIsBetter,
}, info.TotalDuration)
pv.Set(perf.Metric{
Name: "NumIterations",
Unit: "count",
Direction: perf.SmallerIsBetter,
}, float64(info.NumIterations))
pv.Save(s.OutDir())
return cr
}
func dumpHardwareInformationWithVulnerabilityStatus(ctx context.Context, s *testing.State) {
dumpCommandMap := map[string]string{
"crossystem.txt": "crossystem",
"vpd.txt": "vpd -l",
"uname.txt": "uname -a",
"meminfo.txt": "cat /proc/meminfo",
"lscpu.txt": "lscpu",
"cmdline.txt": "cat /proc/cmdline",
"kernel_version.txt": "cat /proc/version",
"cpuinfo.txt": "cat /proc/cpuinfo",
"ifconfig.txt": "ifconfig",
"mount.txt": "mount",
"current_kernel_part_hash.txt": "md5sum $(rootdev -s | sed -e 's/5$/4/' -e 's/3$/2/')",
"current_kernel_verification.txt": "vbutil_kernel --verify $(rootdev -s | sed -e 's/5$/4/' -e 's/3$/2/')",
"rootdev.txt": "rootdev -s",
"bootid.txt": "cat /proc/sys/kernel/random/boot_id",
"lsb_release.txt": "cat /etc/lsb-release",
"messages.txt": "cat /var/log/messages",
"hw_vuln.txt": "find /sys/devices/system/cpu/vulnerabilities/ -type f -print -exec cat {} \\;",
}
dut := s.DUT()
for name, command := range dumpCommandMap {
if err := osperf.DumpCommandOutput(ctx, dut, filepath.Join(s.OutDir(), name), command); err != nil {
s.Fatal("Failed to dump info: ", err)
}
}
}