| /* |
| * Copyright (C) 2020 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.nn.crashtest; |
| |
| import android.annotation.SuppressLint; |
| import android.os.Bundle; |
| import android.util.Log; |
| import android.view.View; |
| import android.widget.AdapterView; |
| import android.widget.ArrayAdapter; |
| import android.widget.Button; |
| import android.widget.CheckBox; |
| import android.widget.NumberPicker; |
| import android.widget.Spinner; |
| import android.widget.TextView; |
| |
| import androidx.appcompat.app.AppCompatActivity; |
| |
| import com.android.nn.benchmark.core.NNTestBase; |
| import com.android.nn.benchmark.core.NnApiDelegationFailure; |
| import com.android.nn.benchmark.core.Processor; |
| import com.android.nn.benchmark.core.TestModels; |
| import com.android.nn.benchmark.core.TestModelsListLoader; |
| import com.android.nn.crashtest.core.CrashTestCoordinator; |
| import com.android.nn.crashtest.core.test.RunModelsInParallel; |
| import com.android.nn.benchmark.util.TestExternalStorageActivity; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.io.PrintStream; |
| import java.time.Duration; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Optional; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| import java.util.concurrent.atomic.AtomicInteger; |
| import java.util.concurrent.atomic.AtomicReference; |
| import java.util.stream.Collectors; |
| |
| public class MainActivity extends AppCompatActivity { |
| |
| private static final String ALL_AVAILABLE_ACCELERATORS = "All available"; |
| private static final String TAG = "NN_STRESS_TEST"; |
| private static final int JOB_FREQUENCY_MILLIS = 15 * 60 * 1000; // 15 minutes |
| private final AtomicInteger mSelectedModelIndex = new AtomicInteger(-1); |
| private final AtomicBoolean mUseSeparateProcess = new AtomicBoolean(true); |
| private final AtomicReference<String> mAcceleratorName = new AtomicReference<>(null); |
| AtomicBoolean mTestRunning = new AtomicBoolean(false); |
| private Button mStartTestButton; |
| private TextView mMessage; |
| private NumberPicker mThreadCount; |
| private NumberPicker mTestDurationMinutes; |
| private ArrayAdapter<String> mModelsAdapter; |
| private List<String> mAllTestModels; |
| private CheckBox mMmapModel; |
| private CheckBox mCompileModelsOnly; |
| |
| @Override |
| protected void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| setContentView(R.layout.activity_main); |
| TestExternalStorageActivity.testWriteExternalStorage(this, true); |
| |
| mStartTestButton = (Button) findViewById(R.id.start_button); |
| mMessage = (TextView) findViewById(R.id.message); |
| |
| try { |
| TestModelsListLoader.parseFromAssets(getAssets()); |
| } catch (IOException e) { |
| Log.e(TAG, "Could not load models", e); |
| } |
| |
| mAllTestModels = TestModels.modelsList().stream().map( |
| TestModels.TestModelEntry::getTestName).collect( |
| Collectors.toList()); |
| |
| final List<String> modelNames = new ArrayList<>(); |
| modelNames.add("All Models"); |
| modelNames.addAll(modelsForAccelerator(ALL_AVAILABLE_ACCELERATORS)); |
| mModelsAdapter = new ArrayAdapter<String>(this, |
| android.R.layout.simple_spinner_item, |
| modelNames); |
| mModelsAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); |
| final Spinner testModelSpinner = (Spinner) findViewById(R.id.test_model); |
| testModelSpinner.setAdapter(mModelsAdapter); |
| testModelSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { |
| @Override |
| public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { |
| mSelectedModelIndex.set((int) id - 1); |
| } |
| |
| @Override |
| public void onNothingSelected(AdapterView<?> parent) { |
| mSelectedModelIndex.set(-1); |
| } |
| }); |
| |
| final List<String> acceleratorNames = new ArrayList<>(); |
| acceleratorNames.add(ALL_AVAILABLE_ACCELERATORS); |
| acceleratorNames.addAll(NNTestBase.availableAcceleratorNames()); |
| final ArrayAdapter<String> acceleratorNamesAdapter = new ArrayAdapter<String>( |
| this, |
| android.R.layout.simple_spinner_item, acceleratorNames); |
| final Spinner acceleratorNameSpinner = (Spinner) findViewById(R.id.accelerator_name); |
| acceleratorNameSpinner.setAdapter(acceleratorNamesAdapter); |
| acceleratorNameSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { |
| @Override |
| public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { |
| mAcceleratorName.set(position == 0 ? null : acceleratorNames.get(position)); |
| mModelsAdapter.clear(); |
| mModelsAdapter.addAll(modelsForAccelerator(mAcceleratorName.get())); |
| mModelsAdapter.notifyDataSetChanged(); |
| } |
| |
| @Override |
| public void onNothingSelected(AdapterView<?> parent) { |
| onItemSelected(parent, null, 0, 0); |
| } |
| }); |
| |
| final ArrayAdapter<String> testTypeAdapter = new ArrayAdapter<String>(this, |
| android.R.layout.simple_spinner_item, |
| new String[]{"Separate process", "In process"}); |
| testTypeAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); |
| final Spinner testTypeSpinner = (Spinner) findViewById(R.id.test_type); |
| testTypeSpinner.setAdapter(testTypeAdapter); |
| testTypeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { |
| @Override |
| public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { |
| mUseSeparateProcess.set(position == 0); |
| } |
| |
| @Override |
| public void onNothingSelected(AdapterView<?> parent) { |
| mUseSeparateProcess.set(true); |
| } |
| }); |
| |
| |
| mThreadCount = (NumberPicker) findViewById(R.id.thread_count); |
| mTestDurationMinutes = (NumberPicker) findViewById(R.id.duration_minutes); |
| |
| mThreadCount.setMinValue(1); |
| mThreadCount.setMaxValue(20); |
| |
| mTestDurationMinutes.setMinValue(1); |
| mTestDurationMinutes.setMaxValue(60); |
| |
| mMmapModel = (CheckBox) findViewById(R.id.mmap_model); |
| mCompileModelsOnly = (CheckBox) findViewById(R.id.compile_only); |
| } |
| |
| private List<String> modelsForAccelerator(String acceleratorName) { |
| List<String> result = new ArrayList<>(); |
| if (acceleratorName == null || acceleratorName.equals(ALL_AVAILABLE_ACCELERATORS)) { |
| result.add("All models"); |
| result.addAll(mAllTestModels); |
| } else { |
| result.add("All supported models"); |
| result.addAll(TestModels.modelsList().stream() |
| .map(TestModels.TestModelEntry::withDisabledEvaluation).filter( |
| model -> { |
| try { |
| return Processor.isTestModelSupportedByAccelerator( |
| this, |
| model, acceleratorName); |
| } catch (NnApiDelegationFailure nnApiDelegationFailure) { |
| runOnUiThread(() -> { |
| mMessage.append(String.format( |
| "Driver %s failed when trying to check support " |
| + "for model %s!!\n", |
| acceleratorName, model.mModelName)); |
| ByteArrayOutputStream stsackTraceByteOS = |
| new ByteArrayOutputStream(); |
| try (PrintStream stackTracePrintStream = new PrintStream( |
| stsackTraceByteOS)) { |
| nnApiDelegationFailure.printStackTrace( |
| stackTracePrintStream); |
| mMessage.append( |
| stackTracePrintStream.toString() + "\n"); |
| } |
| mStartTestButton.setEnabled(true); |
| }); |
| return false; |
| } |
| }).map( |
| TestModels.TestModelEntry::getTestName).collect( |
| Collectors.toList())); |
| } |
| |
| return result; |
| } |
| |
| void testStopped(String msg) { |
| Log.i(TAG, "Test stopped " + msg); |
| mTestRunning.set(false); |
| runOnUiThread(() -> { |
| mMessage.append(msg + "\n"); |
| mStartTestButton.setEnabled(true); |
| }); |
| } |
| |
| public void startTestClicked(View v) { |
| Log.i(TAG, "Starting test"); |
| |
| if (mTestRunning.getAndSet(true)) { |
| return; |
| } |
| |
| mStartTestButton.setEnabled(false); |
| |
| startInferenceTest(); |
| } |
| |
| @SuppressLint("DefaultLocale") |
| private void startInferenceTest() { |
| CrashTestCoordinator coordinator = new CrashTestCoordinator(this); |
| |
| int threadCount = mThreadCount.getValue(); |
| int testDurationMinutes = mTestDurationMinutes.getValue(); |
| |
| int[] testList; |
| if (mSelectedModelIndex.get() < 0) { |
| testList = new int[mModelsAdapter.getCount()]; |
| // The first item is the 'all models' or 'all supported models' entry |
| for (int i = 1; i < mModelsAdapter.getCount(); i++) { |
| String modelName = mModelsAdapter.getItem(i); |
| testList[i] = mAllTestModels.indexOf(modelName); |
| } |
| } else { |
| testList = new int[]{mSelectedModelIndex.get()}; |
| } |
| |
| CrashTestCoordinator.CrashTestCompletionListener testCompletionListener = |
| new CrashTestCoordinator.CrashTestCompletionListener() { |
| @Override |
| public void testCrashed() { |
| testStopped("Test crashed"); |
| } |
| |
| @Override |
| public void testSucceeded() { |
| testStopped("Test succeeded"); |
| } |
| |
| @Override |
| public void testFailed(String reason) { |
| testStopped("Test failed with reason " + reason); |
| } |
| |
| @Override |
| public void testProgressing(Optional<String> description) { |
| Log.i(TAG, "> " + description.orElse("Test progressing..")); |
| // Ignoring message to avoid cluttering the test Text Area |
| runOnUiThread(() -> mMessage.append(".")); |
| } |
| }; |
| |
| final int testTimeoutMillis = testDurationMinutes * 1500; |
| final String testName = "in-app-test@" + System.currentTimeMillis(); |
| final String acceleratorName = mAcceleratorName.get(); |
| final boolean mmapModel = mMmapModel.isChecked(); |
| final boolean runModelCompilationOnly = mCompileModelsOnly.isChecked(); |
| coordinator.startTest(RunModelsInParallel.class, |
| RunModelsInParallel.intentInitializer(testList, threadCount, |
| Duration.ofMinutes(testDurationMinutes), |
| testName, acceleratorName, false, runModelCompilationOnly, mmapModel), |
| testCompletionListener, |
| mUseSeparateProcess.get(), testName); |
| |
| mMessage.setText( |
| String.format( |
| "%s test started with %d threads for %d minutes on %s\n", |
| runModelCompilationOnly ? "Compilation" : "Inference", |
| threadCount, |
| testDurationMinutes, |
| acceleratorName != null ? "accelerator " + acceleratorName |
| : "NNAPI-selected accelerator")); |
| |
| } |
| |
| } |