| // Copyright 2020 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 arc |
| |
| import ( |
| "bufio" |
| "context" |
| "regexp" |
| "strconv" |
| "strings" |
| "time" |
| |
| "chromiumos/tast/common/perf" |
| "chromiumos/tast/ctxutil" |
| "chromiumos/tast/local/arc" |
| "chromiumos/tast/local/chrome" |
| "chromiumos/tast/local/power" |
| "chromiumos/tast/local/power/setup" |
| "chromiumos/tast/testing" |
| "chromiumos/tast/testing/hwdep" |
| ) |
| |
| type testArgsForPowerIdlePerf struct { |
| setupOption setup.BatteryDischargeMode |
| } |
| |
| func init() { |
| testing.AddTest(&testing.Test{ |
| Func: PowerIdlePerf, |
| Desc: "Measures the battery drain of an idle system", |
| Contacts: []string{ |
| "cwd@chromium.org", |
| "arcvm-eng@google.com", |
| }, |
| SoftwareDeps: []string{"chrome"}, |
| Attr: []string{"group:crosbolt", "crosbolt_nightly"}, |
| Timeout: 15 * time.Minute, |
| Params: []testing.Param{ |
| // Parameters generated by power_idle_perf_test.go. DO NOT EDIT. |
| { |
| Name: "noarc", |
| ExtraSoftwareDeps: []string{"arc"}, |
| ExtraHardwareDeps: hwdep.D(hwdep.ForceDischarge()), |
| Val: testArgsForPowerIdlePerf{ |
| setupOption: setup.ForceBatteryDischarge, |
| }, |
| Fixture: "chromeLoggedIn", |
| }, |
| { |
| ExtraSoftwareDeps: []string{"android_p"}, |
| ExtraHardwareDeps: hwdep.D(hwdep.ForceDischarge()), |
| Val: testArgsForPowerIdlePerf{ |
| setupOption: setup.ForceBatteryDischarge, |
| }, |
| Fixture: "arcBooted", |
| }, |
| { |
| Name: "vm", |
| ExtraSoftwareDeps: []string{"android_vm"}, |
| ExtraHardwareDeps: hwdep.D(hwdep.ForceDischarge()), |
| Val: testArgsForPowerIdlePerf{ |
| setupOption: setup.ForceBatteryDischarge, |
| }, |
| Fixture: "arcBooted", |
| }, |
| { |
| Name: "noarc_nobatterymetrics", |
| ExtraSoftwareDeps: []string{"arc"}, |
| ExtraHardwareDeps: hwdep.D(hwdep.NoForceDischarge()), |
| Val: testArgsForPowerIdlePerf{ |
| setupOption: setup.NoBatteryDischarge, |
| }, |
| Fixture: "chromeLoggedIn", |
| }, |
| { |
| Name: "nobatterymetrics", |
| ExtraSoftwareDeps: []string{"android_p"}, |
| ExtraHardwareDeps: hwdep.D(hwdep.NoForceDischarge()), |
| Val: testArgsForPowerIdlePerf{ |
| setupOption: setup.NoBatteryDischarge, |
| }, |
| Fixture: "arcBooted", |
| }, |
| { |
| Name: "vm_nobatterymetrics", |
| ExtraSoftwareDeps: []string{"android_vm"}, |
| ExtraHardwareDeps: hwdep.D(hwdep.NoForceDischarge()), |
| Val: testArgsForPowerIdlePerf{ |
| setupOption: setup.NoBatteryDischarge, |
| }, |
| Fixture: "arcBooted", |
| }, |
| }, |
| }) |
| } |
| |
| func PowerIdlePerf(ctx context.Context, s *testing.State) { |
| const ( |
| iterationCount = 30 |
| iterationDuration = 10 * time.Second |
| ) |
| args := s.Param().(testArgsForPowerIdlePerf) |
| |
| // Give cleanup actions a minute to run, even if we fail by exceeding our |
| // deadline. |
| cleanupCtx := ctx |
| ctx, cancel := ctxutil.Shorten(ctx, time.Minute) |
| defer cancel() |
| |
| cr, ok := s.FixtValue().(*chrome.Chrome) |
| if !ok { |
| cr = s.FixtValue().(*arc.PreData).Chrome |
| } |
| tconn, err := cr.TestAPIConn(ctx) |
| if err != nil { |
| s.Fatal("Failed to connect to test API: ", err) |
| } |
| |
| sup, cleanup := setup.New("power idle perf") |
| defer func() { |
| if err := cleanup(cleanupCtx); err != nil { |
| s.Fatal("Cleanup failed: ", err) |
| } |
| }() |
| |
| sup.Add(setup.PowerTest(ctx, tconn, setup.PowerTestOptions{ |
| Wifi: setup.DisableWifiInterfaces, Battery: args.setupOption, NightLight: setup.DisableNightLight})) |
| if err := sup.Check(ctx); err != nil { |
| s.Fatal("Setup failed: ", err) |
| } |
| |
| metrics, err := perf.NewTimeline(ctx, power.TestMetrics(), perf.Interval(iterationDuration)) |
| if err != nil { |
| s.Fatal("Failed to build metrics: ", err) |
| } |
| s.Log("Finished setup") |
| |
| // Wait until CPU is cooled down. |
| cooldownTime, err := power.WaitUntilCPUCoolDown(ctx, power.DefaultCoolDownConfig(power.CoolDownPreserveUI)) |
| if err != nil { |
| s.Fatal("CPU failed to cool down: ", err) |
| } |
| |
| if err := metrics.Start(ctx); err != nil { |
| s.Fatal("Failed to start metrics: ", err) |
| } |
| |
| if err := metrics.StartRecording(ctx); err != nil { |
| s.Fatal("Failed to start recording: ", err) |
| } |
| |
| if err := testing.Sleep(ctx, iterationCount*iterationDuration); err != nil { |
| s.Fatal("Failed to sleep: ", err) |
| } |
| |
| p, err := metrics.StopRecording(ctx) |
| if err != nil { |
| s.Fatal("Error while recording power metrics: ", err) |
| } |
| |
| cooldownTimeMetric := perf.Metric{Name: "cooldown_time", Unit: "ms", Direction: perf.SmallerIsBetter} |
| p.Set(cooldownTimeMetric, float64(cooldownTime.Milliseconds())) |
| |
| // Report memory usage. |
| arcPre, ok := s.FixtValue().(*arc.PreData) |
| metricRegexp := regexp.MustCompile("[^a-zA-Z0-9]") |
| if ok { |
| memOut, err := arcPre.ARC.Command(ctx, "cat", "/proc/meminfo").Output() |
| var valMemFree int64 |
| var valMemTotal int64 |
| if err != nil { |
| s.Fatal("Failed to read from /proc/meminfo: ", err) |
| } else { |
| scanner := bufio.NewScanner(strings.NewReader(string(memOut))) |
| for scanner.Scan() { |
| // Line format example: MemTotal: 14863104 kB |
| line := scanner.Text() |
| tokens := strings.Fields(line) |
| memName := tokens[0][:len(tokens[0])-1] |
| // No special characters allowed in metric names. |
| metricName := metricRegexp.ReplaceAllString(memName, "_") |
| metricValue := tokens[1] |
| metricUnit := tokens[2] |
| |
| if metricUnit != "kB" { |
| s.Fatalf("Invalid metric unit. Found %s, expected kB", metricUnit) |
| } |
| |
| valueInt, err := strconv.ParseInt(metricValue, 10, 64) |
| if err != nil { |
| s.Fatal("Invalid metric value: ", err) |
| } |
| |
| // Convert KB to bytes. |
| valueBytes := valueInt * 1000 |
| |
| if memName == "MemFree" { |
| valMemFree = valueBytes |
| } else if memName == "MemTotal" { |
| valMemTotal = valueBytes |
| } |
| |
| memMetric := perf.Metric{Name: "arc_mem_" + metricName, Unit: "bytes", Direction: perf.SmallerIsBetter} |
| p.Set(memMetric, float64(valueBytes)) |
| } |
| |
| // TotalUsage = MemTotal - MemFree |
| memMetric := perf.Metric{Name: "arc_mem_TotalUsage", Unit: "bytes", Direction: perf.SmallerIsBetter} |
| valMemUsage := valMemTotal - valMemFree |
| if valMemUsage <= 0 { |
| s.Fatalf("Invalid total memory usage: total = %d, free = %d", valMemTotal, valMemFree) |
| } |
| p.Set(memMetric, float64(valMemUsage)) |
| } |
| } else { |
| testing.ContextLog(ctx, "ARC not running, skipping memory metrics") |
| } |
| |
| if err := p.Save(s.OutDir()); err != nil { |
| s.Error("Failed saving perf data: ", err) |
| } |
| } |