1 /* 2 * Copyright (C) 2016 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 package com.android.tradefed.targetprep; 17 18 import com.android.tradefed.build.IBuildInfo; 19 import com.android.tradefed.build.IDeviceBuildInfo; 20 import com.android.tradefed.config.Option; 21 import com.android.tradefed.config.OptionClass; 22 import com.android.tradefed.device.BackgroundDeviceAction; 23 import com.android.tradefed.device.DeviceNotAvailableException; 24 import com.android.tradefed.device.ITestDevice; 25 import com.android.tradefed.device.LargeOutputReceiver; 26 import com.android.tradefed.log.ITestLogger; 27 import com.android.tradefed.log.LogUtil.CLog; 28 import com.android.tradefed.result.ITestLoggerReceiver; 29 import com.android.tradefed.result.InputStreamSource; 30 import com.android.tradefed.result.LogDataType; 31 import com.android.tradefed.util.StreamUtil; 32 33 /** 34 * A {@link ITargetPreparer} that runs crash collector on device which suppresses and logs crashes 35 * during test execution. 36 * <p> 37 * Note: this preparer requires N platform or newer. 38 */ 39 @OptionClass(alias = "crash-collector") 40 public class CrashCollector extends TestFilePushSetup 41 implements ITestLoggerReceiver, ITargetCleaner { 42 43 private static final String LOG_NAME = "crash-collector-log"; 44 private ITestLogger mTestLogger; 45 private BackgroundDeviceAction mCrashCollector; 46 private LargeOutputReceiver mCrashReceiver; 47 48 @Option(name = "crash-collector-path", 49 description = "Path to crashcollector binary in test artifact bundle.") 50 private String mCrashCollectorPath = "local/tmp/crashcollector"; 51 52 @Option(name = "crash-collector-binary", 53 description = "The name of crashcollector binary in test artifact bundle.") 54 private String mCrashCollectorBinary = "crashcollector"; 55 56 @Option(name = "max-crash-log-size", description = "Max size to retain for crash logs.") 57 private long mMaxCrashLogSize = 10 * 1024 * 1024; 58 59 boolean shouldDisable(ITestDevice device, IBuildInfo buildInfo) 60 throws DeviceNotAvailableException { 61 if (isDisabled()) { 62 return true; 63 } 64 // first get pseudo API level to check for platform support 65 String codeName = device.getProperty("ro.build.version.codename").trim(); 66 int apiLevel = device.getApiLevel(); 67 if (!"REL".equals(codeName)) { 68 apiLevel++; 69 } 70 if (apiLevel < 24) { 71 CLog.i("API Level too low: %s.", apiLevel); 72 return true; 73 } 74 if (!(buildInfo instanceof IDeviceBuildInfo)) { 75 CLog.w("Unsupported build info type: %s, cannot install crashcollector binary", 76 buildInfo.getClass().getSimpleName()); 77 return true; 78 } 79 return false; 80 } 81 82 /** 83 * {@inheritDoc} 84 */ 85 @Override 86 public void setUp(ITestDevice device, IBuildInfo buildInfo) 87 throws TargetSetupError, BuildError, DeviceNotAvailableException { 88 boolean shouldDisable = shouldDisable(device, buildInfo); 89 if (shouldDisable) { 90 CLog.i("Crash collector disabled."); 91 return; 92 } 93 // for backwards compatibility, don't throw if the crash collector does not exist in 94 // test zip bundle 95 setThrowIfNoFile(false); 96 // clear all existing test file names, since we may receive that from the parameter defined 97 // in parent class TestFilePushSetup when this class is used together with TestFilePushSetup 98 // in a same config 99 clearTestFileName(); 100 addTestFileName(mCrashCollectorPath); 101 super.setUp(device, buildInfo); 102 String crashCollectorPath = String.format("/data/%s/%s", 103 mCrashCollectorPath, mCrashCollectorBinary); 104 device.executeShellCommand("chmod 755 " + crashCollectorPath); 105 mCrashReceiver = new LargeOutputReceiver("crash-collector", 106 device.getSerialNumber(), mMaxCrashLogSize); 107 mCrashCollector = new BackgroundDeviceAction(crashCollectorPath, "crash-collector", 108 device, mCrashReceiver, 0); 109 mCrashCollector.start(); 110 } 111 112 /** 113 * {@inheritDoc} 114 */ 115 @Override 116 public void tearDown(ITestDevice device, IBuildInfo buildInfo, Throwable e) 117 throws DeviceNotAvailableException { 118 if (mCrashCollector != null) { 119 mCrashCollector.cancel(); 120 } 121 if (mCrashReceiver != null) { 122 mCrashReceiver.cancel(); 123 InputStreamSource iss = mCrashReceiver.getData(); 124 try { 125 mTestLogger.testLog(LOG_NAME, LogDataType.TEXT, iss); 126 } finally { 127 StreamUtil.cancel(iss); 128 } 129 mCrashReceiver.delete(); 130 } 131 } 132 133 /** 134 * {@inheritDoc} 135 */ 136 @Override 137 public void setTestLogger(ITestLogger testLogger) { 138 mTestLogger = testLogger; 139 } 140 } 141