aboutsummaryrefslogtreecommitdiff
path: root/src/com/android/tradefed/targetprep/CrashCollector.java
blob: 297b578a373aeba3fe407c45547a3aa5c1aa058d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/*
 * Copyright (C) 2016 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.tradefed.targetprep;

import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.build.IDeviceBuildInfo;
import com.android.tradefed.config.Option;
import com.android.tradefed.config.OptionClass;
import com.android.tradefed.device.BackgroundDeviceAction;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.device.LargeOutputReceiver;
import com.android.tradefed.log.ITestLogger;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.result.ITestLoggerReceiver;
import com.android.tradefed.result.InputStreamSource;
import com.android.tradefed.result.LogDataType;
import com.android.tradefed.util.StreamUtil;

/**
 * A {@link ITargetPreparer} that runs crash collector on device which suppresses and logs crashes
 * during test execution.
 * <p>
 * Note: this preparer requires N platform or newer.
 */
@OptionClass(alias = "crash-collector")
public class CrashCollector extends TestFilePushSetup
        implements ITestLoggerReceiver, ITargetCleaner {

    private static String LOG_NAME = "crash-collector-log";
    private ITestLogger mTestLogger;
    private BackgroundDeviceAction mCrashCollector;
    private LargeOutputReceiver mCrashReceiver;

    @Option(name = "crash-collector-path",
            description = "Path to crashcollector binary in test artifact bundle.")
    private String mCrashCollectorPath = "local/tmp/crashcollector";

    @Option(name = "crash-collector-binary",
            description = "The name of crashcollector binary in test artifact bundle.")
    private String mCrashCollectorBinary = "crashcollector";

    @Option(name = "disable", description = "If this preparer should be disabled.")
    private boolean mDisable = false;

    @Option(name = "max-crash-log-size", description = "Max size to retain for crash logs.")
    private long mMaxCrashLogSize = 10 * 1024 * 1024;

    boolean shouldDisable(ITestDevice device, IBuildInfo buildInfo)
            throws DeviceNotAvailableException {
        if (mDisable) {
            return true;
        }
        // first get pseudo API level to check for platform support
        String codeName = device.getProperty("ro.build.version.codename").trim();
        int apiLevel = device.getApiLevel();
        if (!"REL".equals(codeName)) {
            apiLevel++;
        }
        if (apiLevel < 24) {
            CLog.i("API Level too low: %s.", apiLevel);
            return true;
        }
        if (!(buildInfo instanceof IDeviceBuildInfo)) {
            CLog.w("Unsupported build info type: %s, cannot install crashcollector binary",
                    buildInfo.getClass().getSimpleName());
            return true;
        }
        return false;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setUp(ITestDevice device, IBuildInfo buildInfo)
            throws TargetSetupError, BuildError, DeviceNotAvailableException {
        mDisable = shouldDisable(device, buildInfo);
        if (mDisable) {
            CLog.i("Crash collector disabled.");
            return;
        }
        // clear all existing test file names, since we may receive that from the parameter defined
        // in parent class TestFilePushSetup when this class is used together with TestFilePushSetup
        // in a same config
        clearTestFileName();
        addTestFileName(mCrashCollectorPath);
        super.setUp(device, buildInfo);
        String crashCollectorPath = String.format("/data/%s/%s",
                mCrashCollectorPath, mCrashCollectorBinary);
        device.executeShellCommand("chmod 755 " + crashCollectorPath);
        mCrashReceiver = new LargeOutputReceiver("crash-collector",
                device.getSerialNumber(), mMaxCrashLogSize);
        mCrashCollector = new BackgroundDeviceAction(crashCollectorPath, "crash-collector",
                device, mCrashReceiver, 0);
        mCrashCollector.start();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void tearDown(ITestDevice device, IBuildInfo buildInfo, Throwable e)
            throws DeviceNotAvailableException {
        if (mCrashCollector != null) {
            mCrashCollector.cancel();
        }
        if (mCrashReceiver != null) {
            mCrashReceiver.cancel();
            InputStreamSource iss = mCrashReceiver.getData();
            try {
                mTestLogger.testLog(LOG_NAME, LogDataType.TEXT, iss);
            } finally {
                StreamUtil.cancel(iss);
            }
            mCrashReceiver.delete();
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setTestLogger(ITestLogger testLogger) {
        mTestLogger = testLogger;
    }
}