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 if (mCoordinator != null) { 90 // test is already running. 91 return; 92 } 93 94 final Intent intent = getIntent(); 95 96 final int[] testList = intent.getIntArrayExtra(EXTRA_TEST_LIST); 97 98 final int threadCount = intent.getIntExtra(EXTRA_THREAD_COUNT, 10); 99 final long testDurationMillis = intent.getLongExtra(EXTRA_TEST_DURATION_MILLIS, 100 1000 * 60 * 10); 101 final boolean runInSeparateProcess = intent.getBooleanExtra(EXTRA_RUN_IN_SEPARATE_PROCESS, 102 true); 103 mTestName = intent.getStringExtra(EXTRA_TEST_NAME) != null 104 ? intent.getStringExtra(EXTRA_TEST_NAME) : "no-name"; 105 106 mCoordinator = new CrashTestCoordinator(getApplicationContext()); 107 108 String acceleratorName = intent.getStringExtra(EXTRA_ACCELERATOR_NAME); 109 boolean ignoreUnsupportedModels = intent.getBooleanExtra(EXTRA_IGNORE_UNSUPPORTED_MODELS, 110 false); 111 boolean mmapModel = intent.getBooleanExtra(EXTRA_MEMORY_MAP_MODEL, false); 112 113 final boolean runModelCompilationOnly = intent.getBooleanExtra( 114 EXTRA_RUN_MODEL_COMPILATION_ONLY, false); 115 116 mCoordinator.startTest(RunModelsInParallel.class, 117 RunModelsInParallel.intentInitializer(testList, threadCount, 118 Duration.ofMillis(testDurationMillis), mTestName, acceleratorName, 119 ignoreUnsupportedModels, runModelCompilationOnly, mmapModel), 120 mTestStatus, runInSeparateProcess, mTestName); 121 122 mStopTestButton.setEnabled(true); 123 } 124 endTests()125 private void endTests() { 126 mCoordinator.shutdown(); 127 mCoordinator = null; 128 } 129 130 // This method blocks until the tests complete and returns true if all tests completed 131 // successfully 132 @SuppressLint("DefaultLocale") testResult()133 public CrashTestStatus.TestResult testResult() { 134 try { 135 final Intent intent = getIntent(); 136 final long testDurationMillis = intent.getLongExtra(EXTRA_TEST_DURATION_MILLIS, 137 60 * 10); 138 // Giving the test a bit of time to wrap up 139 final long testResultTimeout = testDurationMillis + SHUTDOWN_TIMEOUT; 140 boolean completed = mTestStatus.waitForCompletion(testResultTimeout, MILLISECONDS); 141 if (!completed) { 142 showMessage(String.format( 143 "Ending test '%s' since test result collection timeout of %d " 144 + "millis is expired", 145 mTestName, testResultTimeout)); 146 endTests(); 147 } 148 } catch (InterruptedException ignored) { 149 Thread.currentThread().interrupt(); 150 } 151 152 // If no result is available, assuming HANG 153 mTestStatus.compareAndSetResult(null, HANG); 154 return mTestStatus.result(); 155 } 156 onStopTestClicked(View view)157 public void onStopTestClicked(View view) { 158 showMessage("Stopping tests"); 159 endTests(); 160 } 161 162 /** 163 * Kills the process running the tests. 164 * 165 * @throws IllegalStateException if the method is called for an in-process test. 166 * @throws RemoteException if the test service is not reachable 167 */ killTestProcess()168 public void killTestProcess() throws RemoteException { 169 final Intent intent = getIntent(); 170 171 final boolean runInSeparateProcess = intent.getBooleanExtra(EXTRA_RUN_IN_SEPARATE_PROCESS, 172 true); 173 174 if (!runInSeparateProcess) { 175 throw new IllegalStateException("Cannot kill the test process in an in-process test!"); 176 } 177 178 Log.i(TAG, "Asking coordinator to kill test process"); 179 mCoordinator.killCrashTestService(); 180 } 181 } 182