1 /* 2 * Copyright (C) 2021 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.internal.car.test; 18 19 import static com.google.common.truth.Truth.assertWithMessage; 20 21 import com.android.compatibility.common.util.CommonTestUtils; 22 import com.android.compatibility.common.util.PollingCheck; 23 import com.android.tradefed.log.LogUtil.CLog; 24 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; 25 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; 26 27 import static org.junit.Assume.assumeTrue; 28 29 import org.junit.After; 30 import org.junit.Before; 31 import org.junit.Test; 32 import org.junit.runner.RunWith; 33 34 import java.util.ArrayList; 35 import java.util.Arrays; 36 import java.util.List; 37 import java.util.concurrent.atomic.AtomicReference; 38 39 @RunWith(DeviceJUnit4ClassRunner.class) 40 public final class CarServiceCrashDumpTest extends BaseHostJUnit4Test { 41 private static final int DEFAULT_TIMEOUT_SEC = 20; 42 private static final long POLL_TIMEOUT_MS = 20000; 43 private static final String BUILD_TYPE_PROPERTY = "ro.build.type"; 44 45 // This must be in sync with WatchDog lib. 46 private static final List<String> HAL_INTERFACES_OF_INTEREST = Arrays.asList( 47 "android.hardware.audio@4.0::IDevicesFactory", 48 "android.hardware.audio@5.0::IDevicesFactory", 49 "android.hardware.audio@6.0::IDevicesFactory", 50 "android.hardware.audio@7.0::IDevicesFactory", 51 "android.hardware.biometrics.face@1.0::IBiometricsFace", 52 "android.hardware.biometrics.fingerprint@2.1::IBiometricsFingerprint", 53 "android.hardware.bluetooth@1.0::IBluetoothHci", 54 "android.hardware.camera.provider@2.4::ICameraProvider", 55 "android.hardware.gnss@1.0::IGnss", 56 "android.hardware.graphics.allocator@2.0::IAllocator", 57 "android.hardware.graphics.composer@2.1::IComposer", 58 "android.hardware.health@2.0::IHealth", 59 "android.hardware.light@2.0::ILight", 60 "android.hardware.media.c2@1.0::IComponentStore", 61 "android.hardware.media.omx@1.0::IOmx", 62 "android.hardware.media.omx@1.0::IOmxStore", 63 "android.hardware.neuralnetworks@1.0::IDevice", 64 "android.hardware.power.stats@1.0::IPowerStats", 65 "android.hardware.sensors@1.0::ISensors", 66 "android.hardware.sensors@2.0::ISensors", 67 "android.hardware.sensors@2.1::ISensors", 68 "android.hardware.vr@1.0::IVr", 69 "android.system.suspend@1.0::ISystemSuspend" 70 ); 71 72 // Which native processes to dump into dropbox's stack traces, must be in sync with Watchdog 73 // lib. 74 private static final String[] NATIVE_STACKS_OF_INTEREST = new String[] { 75 "/system/bin/audioserver", 76 "/system/bin/cameraserver", 77 "/system/bin/drmserver", 78 "/system/bin/keystore2", 79 "/system/bin/mediadrmserver", 80 "/system/bin/mediaserver", 81 "/system/bin/netd", 82 "/system/bin/sdcard", 83 "/system/bin/surfaceflinger", 84 "/system/bin/vold", 85 "media.extractor", // system/bin/mediaextractor 86 "media.metrics", // system/bin/mediametrics 87 "media.codec", // vendor/bin/hw/android.hardware.media.omx@1.0-service 88 "media.swcodec", // /apex/com.android.media.swcodec/bin/mediaswcodec 89 "media.transcoding", // Media transcoding service 90 "com.android.bluetooth", // Bluetooth service 91 "/apex/com.android.os.statsd/bin/statsd", // Stats daemon 92 }; 93 94 /** 95 * Executes the shell command and returns the output. 96 */ executeCommand(String command, Object... args)97 private String executeCommand(String command, Object... args) throws Exception { 98 String fullCommand = String.format(command, args); 99 return getDevice().executeShellCommand(fullCommand); 100 } 101 102 /** 103 * Waits until the car service is ready. 104 */ waitForCarServiceReady()105 private void waitForCarServiceReady() throws Exception { 106 CommonTestUtils.waitUntil("timed out waiting for car service ", 107 DEFAULT_TIMEOUT_SEC, () -> isCarServiceReady()); 108 } 109 isCarServiceReady()110 private boolean isCarServiceReady() { 111 String cmd = "service check car_service"; 112 try { 113 String output = getDevice().executeShellCommand(cmd).strip(); 114 return !output.endsWith("not found"); 115 } catch (Exception e) { 116 CLog.w("%s failed: %s", cmd, e.getMessage()); 117 } 118 return false; 119 } 120 121 @Before setUp()122 public void setUp() throws Exception { 123 executeCommand("logcat -c"); 124 } 125 126 /** 127 * Read the content of the dumped file. 128 */ getDumpFile()129 private String getDumpFile() throws Exception { 130 AtomicReference<String> log = new AtomicReference<>(); 131 String dumpString = "ActivityManager: Dumping to "; 132 String doneDumpingString = "ActivityManager: Done dumping"; 133 PollingCheck.check("dumpStackTrace not found in log", POLL_TIMEOUT_MS, () -> { 134 String logString = executeCommand("logcat -d"); 135 if (logString.contains("ActivityManager: dumpStackTraces") && logString.contains( 136 dumpString) && logString.contains(doneDumpingString)) { 137 log.set(logString); 138 return true; 139 } 140 return false; 141 }); 142 String logString = log.get(); 143 int start = logString.indexOf(dumpString) + dumpString.length(); 144 int end = logString.indexOf("\n", start); 145 if (end == -1) { 146 end = logString.length(); 147 } 148 return logString.substring(start, end); 149 } 150 151 /** 152 * Get a list of PIDs for the interesting HALs that would be dumped. 153 */ getHalPids()154 private List<String> getHalPids() throws Exception { 155 String lshalResult = executeCommand("lshal -i -p"); 156 List<String> pids = new ArrayList<String>(); 157 int i = 0; 158 for (String line: lshalResult.split("\n")) { 159 line = line.strip(); 160 if (line.equals("")) { 161 // When we see an empty line, we stops the parsing. 162 break; 163 } 164 if (i < 2) { 165 // Skip the first two lines 166 i++; 167 continue; 168 } 169 String[] fields = line.split("\\s+"); 170 for (String interestHal: HAL_INTERFACES_OF_INTEREST) { 171 if (fields[0].contains(interestHal)) { 172 pids.add(fields[1]); 173 break; 174 } 175 } 176 i++; 177 } 178 return pids; 179 } 180 181 /** 182 * Get a list of PIDs for the native services that would be dumped. 183 */ getNativePids()184 private List<String> getNativePids() throws Exception { 185 List<String> pids = new ArrayList<String>(); 186 for (String name: NATIVE_STACKS_OF_INTEREST) { 187 String pid = executeCommand(String.format("pidof %s", name)).strip(); 188 if (!pid.equals("")) { 189 pids.add(pid); 190 } 191 } 192 return pids; 193 } 194 195 @Test testCarServiceCrashDump()196 public void testCarServiceCrashDump() throws Exception { 197 String buildType = getDevice().getProperty("ro.build.type"); 198 // Only run on userdebug devices. 199 assumeTrue(buildType.equals("userdebug") || buildType.equals("eng")); 200 201 List<String> pids = new ArrayList<String>(); 202 203 getDevice().enableAdbRoot(); 204 205 String systemServerPid = executeCommand(String.format("pidof %s", "system_server")).strip(); 206 assertWithMessage("system_service pid not empty").that(systemServerPid).isNotEmpty(); 207 pids.add(systemServerPid); 208 209 List<String> halPids = getHalPids(); 210 pids.addAll(halPids); 211 assertWithMessage("hal pids").that(halPids.size() > 0).isTrue(); 212 213 List<String> nativePids = getNativePids(); 214 pids.addAll(nativePids); 215 assertWithMessage("native pids").that(nativePids.size() > 0).isTrue(); 216 217 executeCommand("am crash --user 0 com.android.car"); 218 219 String dumpFile = getDumpFile(); 220 assertWithMessage("dump file").that(dumpFile).isNotEmpty(); 221 222 String grepResult = executeCommand("cat %s", dumpFile); 223 224 assertWithMessage("dumped content not empty").that(grepResult) 225 .isNotEmpty(); 226 227 for (String pid : pids) { 228 assertWithMessage("dumped content contains interesting pid").that(grepResult) 229 .contains(String.format("----- pid %s at", pid)); 230 } 231 } 232 233 @After tearDown()234 public void tearDown() throws Exception { 235 waitForCarServiceReady(); 236 } 237 } 238