1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.multiuser; 17 18 import android.annotation.Nullable; 19 import android.os.Bundle; 20 import android.os.SystemClock; 21 import android.perftests.utils.ShellHelper; 22 23 import java.util.ArrayList; 24 25 // Based on //platform/frameworks/base/apct-tests/perftests/utils/BenchmarkState.java 26 public class BenchmarkRunner { 27 28 private static final long COOL_OFF_PERIOD_MS = 1000; 29 30 private static final int NUM_ITERATIONS = 4; 31 32 private static final int NOT_STARTED = 0; // The benchmark has not started yet. 33 private static final int RUNNING = 1; // The benchmark is running. 34 private static final int PAUSED = 2; // The benchmark is paused 35 private static final int FINISHED = 3; // The benchmark has stopped. 36 37 private final BenchmarkResults mResults = new BenchmarkResults(); 38 private int mState = NOT_STARTED; // Current benchmark state. 39 private int mIteration = 1; 40 41 public long mStartTimeNs; 42 public long mPausedDurationNs; 43 public long mPausedTimeNs; 44 45 private Throwable mFirstFailure = null; 46 47 /** 48 * Starts a new run. Also responsible for finalising the calculations from the previous run, 49 * if there was one; therefore, any previous run must not be {@link #pauseTiming() paused} when 50 * this is called. 51 */ keepRunning()52 public boolean keepRunning() { 53 switch (mState) { 54 case NOT_STARTED: 55 mState = RUNNING; 56 prepareForNextRun(); 57 return true; 58 case RUNNING: 59 mIteration++; 60 return startNextTestRun(); 61 case PAUSED: 62 throw new IllegalStateException("Benchmarking is in paused state"); 63 case FINISHED: 64 throw new IllegalStateException("Benchmarking is finished"); 65 default: 66 throw new IllegalStateException("BenchmarkRunner is in unknown state"); 67 } 68 } 69 startNextTestRun()70 private boolean startNextTestRun() { 71 mResults.addDuration(System.nanoTime() - mStartTimeNs - mPausedDurationNs); 72 if (mIteration == NUM_ITERATIONS + 1) { 73 mState = FINISHED; 74 return false; 75 } else { 76 prepareForNextRun(); 77 return true; 78 } 79 } 80 prepareForNextRun()81 private void prepareForNextRun() { 82 SystemClock.sleep(COOL_OFF_PERIOD_MS); 83 ShellHelper.runShellCommand("am wait-for-broadcast-idle --flush-broadcast-loopers"); 84 mStartTimeNs = System.nanoTime(); 85 mPausedDurationNs = 0; 86 } 87 pauseTiming()88 public void pauseTiming() { 89 if (mState != RUNNING) { 90 throw new IllegalStateException("Unable to pause the runner: not running currently"); 91 } 92 mPausedTimeNs = System.nanoTime(); 93 mState = PAUSED; 94 } 95 96 /** 97 * Resumes the timing after a previous {@link #pauseTiming()}. 98 * First waits for the system to be idle prior to resuming. 99 * 100 * If this is called at the end of the run (so that no further timing is actually desired before 101 * {@link #keepRunning()} is called anyway), use {@link #resumeTimingForNextIteration()} instead 102 * to avoid unnecessary waiting. 103 */ resumeTiming()104 public void resumeTiming() { 105 ShellHelper.runShellCommand("am wait-for-broadcast-idle --flush-broadcast-loopers"); 106 resumeTimer(); 107 } 108 109 /** 110 * Resume timing in preparation for a possible next run (rather than to continue timing the 111 * current run). 112 * 113 * It is equivalent to {@link #resumeTiming()} except that it skips steps that 114 * are unnecessary at the end of a trial (namely, waiting for the system to idle). 115 */ resumeTimingForNextIteration()116 public void resumeTimingForNextIteration() { 117 resumeTimer(); 118 } 119 resumeTimer()120 private void resumeTimer() { 121 if (mState != PAUSED) { 122 throw new IllegalStateException("Unable to resume the runner: already running"); 123 } 124 mPausedDurationNs += System.nanoTime() - mPausedTimeNs; 125 mState = RUNNING; 126 } 127 getStatsToReport()128 public Bundle getStatsToReport() { 129 return mResults.getStatsToReport(); 130 } 131 getStatsToLog()132 public Bundle getStatsToLog() { 133 return mResults.getStatsToLog(); 134 } 135 getAllDurations()136 public ArrayList<Long> getAllDurations() { 137 return mResults.getAllDurations(); 138 } 139 140 /** Returns which iteration (starting at 1) the Runner is currently on. */ getIteration()141 public int getIteration() { 142 return mIteration; 143 } 144 145 /** 146 * Marks the test run as failed, along with a message of why. 147 * Only the first fail message is retained. 148 */ markAsFailed(Throwable err)149 public void markAsFailed(Throwable err) { 150 if (mFirstFailure == null) { 151 mFirstFailure = err; 152 } 153 } 154 155 /** Gets the failure message if the test failed; otherwise {@code null}. */ getErrorOrNull()156 public @Nullable Throwable getErrorOrNull() { 157 if (mFirstFailure != null) { 158 return mFirstFailure; 159 } 160 if (mState != FINISHED) { 161 return new AssertionError("BenchmarkRunner state is not FINISHED."); 162 } 163 return null; 164 } 165 }