1 /* 2 * Copyright (C) 2019 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 android.platform.test.rule; 18 19 import static java.util.stream.Collectors.joining; 20 21 import android.os.SystemClock; 22 import android.util.Log; 23 import androidx.annotation.VisibleForTesting; 24 25 import com.google.common.collect.ImmutableList; 26 27 import org.junit.runner.Description; 28 import org.junit.runners.model.InitializationError; 29 30 import java.util.HashSet; 31 import java.util.Set; 32 33 /** This rule compiles the applications with the specified filter, or skips if unspecified. */ 34 public class CompilationFilterRule extends TestWatcher { 35 // 36 private static final String LOG_TAG = CompilationFilterRule.class.getSimpleName(); 37 // Compilation constants 38 @VisibleForTesting static final String COMPILE_CMD_FORMAT = "cmd package compile -f -m %s %s"; 39 @VisibleForTesting static final String DUMP_PROFILE_CMD = "killall -s SIGUSR1 %s"; 40 private static final ImmutableList<String> COMPILE_FILTER_LIST = 41 ImmutableList.of("speed", "speed-profile", "quicken", "verify"); 42 @VisibleForTesting static final String SPEED_PROFILE_FILTER = "speed-profile"; 43 private static final String PROFILE_SAVE_TIMEOUT = "profile-save-timeout"; 44 @VisibleForTesting static final String COMPILE_FILTER_OPTION = "compilation-filter"; 45 @VisibleForTesting static final String COMPILE_SUCCESS = "Success"; 46 private static Set<String> mCompiledTests = new HashSet<>(); 47 48 private final String[] mApplications; 49 CompilationFilterRule()50 public CompilationFilterRule() throws InitializationError { 51 throw new InitializationError("Must supply an application to compile."); 52 } 53 CompilationFilterRule(String... applications)54 public CompilationFilterRule(String... applications) { 55 mApplications = applications; 56 } 57 58 @Override finished(Description description)59 protected void finished(Description description) { 60 // Identify the filter option to use. 61 String filter = getArguments().getString(COMPILE_FILTER_OPTION); 62 // Default speed profile save timeout set to 5 secs. 63 long profileSaveTimeout = Integer 64 .parseInt(getArguments().getString(PROFILE_SAVE_TIMEOUT, "5000")); 65 if (filter == null) { 66 // No option provided, default to a no-op. 67 Log.d(LOG_TAG, "Skipping complation because filter option is unset."); 68 return; 69 } else if (!COMPILE_FILTER_LIST.contains(filter)) { 70 String filterOptions = COMPILE_FILTER_LIST.stream().collect(joining(", ")); 71 throw new IllegalArgumentException( 72 String.format( 73 "Unknown compiler filter: %s, not part of %s", filter, filterOptions)); 74 } 75 // Profile varies based on the test even for the same app. Tracking the test id to make 76 // sure the test compiled once after the first iteration of the test. 77 String testId = description.getDisplayName(); 78 if (!mCompiledTests.contains(testId)) { 79 for (String app : mApplications) { 80 // For speed profile compilation, ART team recommended to wait for 5 secs when app 81 // is in the foreground, dump the profile, wait for another 5 secs before 82 // speed-profile compilation. 83 if (filter.equalsIgnoreCase(SPEED_PROFILE_FILTER)) { 84 SystemClock.sleep(profileSaveTimeout); 85 // Send SIGUSR1 to force dumping a profile. 86 String response = executeShellCommand(String.format(DUMP_PROFILE_CMD, app)); 87 if (!response.isEmpty()) { 88 Log.d(LOG_TAG, 89 String.format("Received dump profile cmd response: %s", response)); 90 throw new RuntimeException( 91 String.format("Failed to dump profile %s.", app)); 92 } 93 // killall is async, wait few seconds to let the app save the profile. 94 SystemClock.sleep(profileSaveTimeout); 95 } 96 String response = executeShellCommand( 97 String.format(COMPILE_CMD_FORMAT, filter, app)); 98 if (!response.contains(COMPILE_SUCCESS)) { 99 Log.d(LOG_TAG, String.format("Received compile cmd response: %s", response)); 100 throw new RuntimeException(String.format("Failed to compile %s.", app)); 101 } else { 102 mCompiledTests.add(testId); 103 } 104 } 105 } else { 106 Log.d(LOG_TAG, String.format("Test %s already compiled", testId)); 107 } 108 } 109 } 110