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 = "disable", description = "If this preparer should be disabled.") 57 private boolean mDisable = false; 58 59 @Option(name = "max-crash-log-size", description = "Max size to retain for crash logs.") 60 private long mMaxCrashLogSize = 10 * 1024 * 1024; 61 62 boolean shouldDisable(ITestDevice device, IBuildInfo buildInfo) 63 throws DeviceNotAvailableException { 64 if (mDisable) { 65 return true; 66 } 67 // first get pseudo API level to check for platform support 68 String codeName = device.getProperty("ro.build.version.codename").trim(); 69 int apiLevel = device.getApiLevel(); 70 if (!"REL".equals(codeName)) { 71 apiLevel++; 72 } 73 if (apiLevel < 24) { 74 CLog.i("API Level too low: %s.", apiLevel); 75 return true; 76 } 77 if (!(buildInfo instanceof IDeviceBuildInfo)) { 78 CLog.w("Unsupported build info type: %s, cannot install crashcollector binary", 79 buildInfo.getClass().getSimpleName()); 80 return true; 81 } 82 return false; 83 } 84 85 /** 86 * {@inheritDoc} 87 */ 88 @Override 89 public void setUp(ITestDevice device, IBuildInfo buildInfo) 90 throws TargetSetupError, BuildError, DeviceNotAvailableException { 91 mDisable = shouldDisable(device, buildInfo); 92 if (mDisable) { 93 CLog.i("Crash collector disabled."); 94 return; 95 } 96 // for backwards compatibility, don't throw if the crash collector does not exist in 97 // test zip bundle 98 setThrowIfNoFile(false); 99 // clear all existing test file names, since we may receive that from the parameter defined 100 // in parent class TestFilePushSetup when this class is used together with TestFilePushSetup 101 // in a same config 102 clearTestFileName(); 103 addTestFileName(mCrashCollectorPath); 104 super.setUp(device, buildInfo); 105 String crashCollectorPath = String.format("/data/%s/%s", 106 mCrashCollectorPath, mCrashCollectorBinary); 107 device.executeShellCommand("chmod 755 " + crashCollectorPath); 108 mCrashReceiver = new LargeOutputReceiver("crash-collector", 109 device.getSerialNumber(), mMaxCrashLogSize); 110 mCrashCollector = new BackgroundDeviceAction(crashCollectorPath, "crash-collector", 111 device, mCrashReceiver, 0); 112 mCrashCollector.start(); 113 } 114 115 /** 116 * {@inheritDoc} 117 */ 118 @Override 119 public void tearDown(ITestDevice device, IBuildInfo buildInfo, Throwable e) 120 throws DeviceNotAvailableException { 121 if (mCrashCollector != null) { 122 mCrashCollector.cancel(); 123 } 124 if (mCrashReceiver != null) { 125 mCrashReceiver.cancel(); 126 InputStreamSource iss = mCrashReceiver.getData(); 127 try { 128 mTestLogger.testLog(LOG_NAME, LogDataType.TEXT, iss); 129 } finally { 130 StreamUtil.cancel(iss); 131 } 132 mCrashReceiver.delete(); 133 } 134 } 135 136 /** 137 * {@inheritDoc} 138 */ 139 @Override 140 public void setTestLogger(ITestLogger testLogger) { 141 mTestLogger = testLogger; 142 } 143 } 144