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 
keepRunning()47     public boolean keepRunning() {
48         switch (mState) {
49             case NOT_STARTED:
50                 mState = RUNNING;
51                 prepareForNextRun();
52                 return true;
53             case RUNNING:
54                 mIteration++;
55                 return startNextTestRun();
56             case PAUSED:
57                 throw new IllegalStateException("Benchmarking is in paused state");
58             case FINISHED:
59                 throw new IllegalStateException("Benchmarking is finished");
60             default:
61                 throw new IllegalStateException("BenchmarkRunner is in unknown state");
62         }
63     }
64 
startNextTestRun()65     private boolean startNextTestRun() {
66         mResults.addDuration(System.nanoTime() - mStartTimeNs - mPausedDurationNs);
67         if (mIteration == NUM_ITERATIONS + 1) {
68             mState = FINISHED;
69             return false;
70         } else {
71             prepareForNextRun();
72             return true;
73         }
74     }
75 
prepareForNextRun()76     private void prepareForNextRun() {
77         SystemClock.sleep(COOL_OFF_PERIOD_MS);
78         ShellHelper.runShellCommand("am wait-for-broadcast-idle");
79         mStartTimeNs = System.nanoTime();
80         mPausedDurationNs = 0;
81     }
82 
pauseTiming()83     public void pauseTiming() {
84         if (mState != RUNNING) {
85             throw new IllegalStateException("Unable to pause the runner: not running currently");
86         }
87         mPausedTimeNs = System.nanoTime();
88         mState = PAUSED;
89     }
90 
resumeTiming()91     public void resumeTiming() {
92         if (mState != PAUSED) {
93             throw new IllegalStateException("Unable to resume the runner: already running");
94         }
95         mPausedDurationNs += System.nanoTime() - mPausedTimeNs;
96         mState = RUNNING;
97     }
98 
getStatsToReport()99     public Bundle getStatsToReport() {
100         return mResults.getStatsToReport();
101     }
102 
getStatsToLog()103     public Bundle getStatsToLog() {
104         return mResults.getStatsToLog();
105     }
106 
getAllDurations()107     public ArrayList<Long> getAllDurations() {
108         return mResults.getAllDurations();
109     }
110 
111     /** Returns which iteration (starting at 1) the Runner is currently on. */
getIteration()112     public int getIteration() {
113         return mIteration;
114     }
115 
116     /**
117      * Marks the test run as failed, along with a message of why.
118      * Only the first fail message is retained.
119      */
markAsFailed(Throwable err)120     public void markAsFailed(Throwable err) {
121         if (mFirstFailure == null) {
122             mFirstFailure = err;
123         }
124     }
125 
126     /** Gets the failure message if the test failed; otherwise {@code null}. */
getErrorOrNull()127     public @Nullable Throwable getErrorOrNull() {
128         if (mFirstFailure != null) {
129             return mFirstFailure;
130         }
131         if (mState != FINISHED) {
132             return new AssertionError("BenchmarkRunner state is not FINISHED.");
133         }
134         return null;
135     }
136 }