1 /* 2 * Copyright (C) 2020 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 17 package com.android.nn.crashtest.app; 18 19 20 import static com.android.nn.crashtest.app.CrashTestStatus.TestResult.HANG; 21 22 import static java.util.concurrent.TimeUnit.MILLISECONDS; 23 24 import android.annotation.SuppressLint; 25 import android.app.Activity; 26 import android.content.Intent; 27 import android.os.Bundle; 28 import android.os.RemoteException; 29 import android.util.Log; 30 import android.view.View; 31 import android.view.WindowManager; 32 import android.widget.Button; 33 import android.widget.TextView; 34 35 import com.android.nn.benchmark.app.R; 36 import com.android.nn.crashtest.core.CrashTestCoordinator; 37 import com.android.nn.crashtest.core.test.RunModelsInParallel; 38 39 import java.time.Duration; 40 41 42 public class NNParallelTestActivity extends Activity { 43 public static final int SHUTDOWN_TIMEOUT = 20000; 44 45 private static String TAG = "NNParallelTestActivity"; 46 47 public static final String EXTRA_TEST_DURATION_MILLIS = "duration"; 48 public static final String EXTRA_THREAD_COUNT = "thread_count"; 49 public static final String EXTRA_TEST_LIST = "test_list"; 50 public static final String EXTRA_RUN_IN_SEPARATE_PROCESS = "run_in_separate_process"; 51 public static final String EXTRA_TEST_NAME = "test_name"; 52 public static final String EXTRA_ACCELERATOR_NAME = "accelerator_name"; 53 public static final String EXTRA_IGNORE_UNSUPPORTED_MODELS = "ignore_unsupported_models"; 54 public static final String EXTRA_RUN_MODEL_COMPILATION_ONLY = "run_model_compilation_only"; 55 public static final String EXTRA_MEMORY_MAP_MODEL = "memory_map_model"; 56 57 // Not using AtomicBoolean to have the concept of unset status 58 private CrashTestCoordinator mCoordinator; 59 private TextView mTestResultView; 60 private Button mStopTestButton; 61 private String mTestName; 62 63 private final CrashTestStatus mTestStatus = new CrashTestStatus(this::showMessage); 64 65 @SuppressLint("SetTextI18n") 66 @Override onCreate(Bundle savedInstanceState)67 protected void onCreate(Bundle savedInstanceState) { 68 super.onCreate(savedInstanceState); 69 setContentView(R.layout.interruptable_test); 70 mTestResultView = findViewById(R.id.parallel_test_result); 71 mStopTestButton = findViewById(R.id.stop_test); 72 mStopTestButton.setEnabled(false); 73 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 74 } 75 showMessage(String msg)76 protected void showMessage(String msg) { 77 runOnUiThread(() -> mTestResultView.append(msg)); 78 } 79 80 81 @Override onResume()82 protected void onResume() { 83 super.onResume(); 84 85 if (mTestStatus.isTestCompleted()) { 86 // test was completed before resuming 87 return; 88 } 89 90 final Intent intent = getIntent(); 91 92 final int[] testList = intent.getIntArrayExtra(EXTRA_TEST_LIST); 93 94 final int threadCount = intent.getIntExtra(EXTRA_THREAD_COUNT, 10); 95 final long testDurationMillis = intent.getLongExtra(EXTRA_TEST_DURATION_MILLIS, 96 1000 * 60 * 10); 97 final boolean runInSeparateProcess = intent.getBooleanExtra(EXTRA_RUN_IN_SEPARATE_PROCESS, 98 true); 99 mTestName = intent.getStringExtra(EXTRA_TEST_NAME) != null 100 ? intent.getStringExtra(EXTRA_TEST_NAME) : "no-name"; 101 102 mCoordinator = new CrashTestCoordinator(getApplicationContext()); 103 104 String acceleratorName = intent.getStringExtra(EXTRA_ACCELERATOR_NAME); 105 boolean ignoreUnsupportedModels = intent.getBooleanExtra(EXTRA_IGNORE_UNSUPPORTED_MODELS, 106 false); 107 boolean mmapModel = intent.getBooleanExtra(EXTRA_MEMORY_MAP_MODEL, false); 108 109 final boolean runModelCompilationOnly = intent.getBooleanExtra( 110 EXTRA_RUN_MODEL_COMPILATION_ONLY, false); 111 112 mCoordinator.startTest(RunModelsInParallel.class, 113 RunModelsInParallel.intentInitializer(testList, threadCount, 114 Duration.ofMillis(testDurationMillis), mTestName, acceleratorName, 115 ignoreUnsupportedModels, runModelCompilationOnly, mmapModel), 116 mTestStatus, runInSeparateProcess, mTestName); 117 118 mStopTestButton.setEnabled(true); 119 } 120 121 @Override onPause()122 protected void onPause() { 123 super.onPause(); 124 if (mCoordinator != null) { 125 mCoordinator.shutdown(); 126 mCoordinator = null; 127 } 128 } 129 endTests()130 private void endTests() { 131 mCoordinator.shutdown(); 132 } 133 134 // This method blocks until the tests complete and returns true if all tests completed 135 // successfully 136 @SuppressLint("DefaultLocale") testResult()137 public CrashTestStatus.TestResult testResult() { 138 try { 139 final Intent intent = getIntent(); 140 final long testDurationMillis = intent.getLongExtra(EXTRA_TEST_DURATION_MILLIS, 141 60 * 10); 142 // Giving the test a bit of time to wrap up 143 final long testResultTimeout = testDurationMillis + SHUTDOWN_TIMEOUT; 144 boolean completed = mTestStatus.waitForCompletion(testResultTimeout, MILLISECONDS); 145 if (!completed) { 146 showMessage(String.format( 147 "Ending test '%s' since test result collection timeout of %d " 148 + "millis is expired", 149 mTestName, testResultTimeout)); 150 endTests(); 151 } 152 } catch (InterruptedException ignored) { 153 Thread.currentThread().interrupt(); 154 } 155 156 // If no result is available, assuming HANG 157 mTestStatus.compareAndSetResult(null, HANG); 158 return mTestStatus.result(); 159 } 160 onStopTestClicked(View view)161 public void onStopTestClicked(View view) { 162 showMessage("Stopping tests"); 163 endTests(); 164 } 165 166 /** 167 * Kills the process running the tests. 168 * 169 * @throws IllegalStateException if the method is called for an in-process test. 170 * @throws RemoteException if the test service is not reachable 171 */ killTestProcess()172 public void killTestProcess() throws RemoteException { 173 final Intent intent = getIntent(); 174 175 final boolean runInSeparateProcess = intent.getBooleanExtra(EXTRA_RUN_IN_SEPARATE_PROCESS, 176 true); 177 178 if (!runInSeparateProcess) { 179 throw new IllegalStateException("Cannot kill the test process in an in-process test!"); 180 } 181 182 Log.i(TAG, "Asking coordinator to kill test process"); 183 mCoordinator.killCrashTestService(); 184 } 185 } 186