1 /* 2 * Copyright (C) 2022 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.tradefed.device.metric; 18 19 import com.android.tradefed.device.DeviceNotAvailableException; 20 import com.android.tradefed.device.ITestDevice; 21 import com.android.tradefed.invoker.IInvocationContext; 22 import com.android.tradefed.log.LogUtil.CLog; 23 import com.android.tradefed.metrics.proto.MetricMeasurement; 24 import com.android.tradefed.result.FileInputStreamSource; 25 import com.android.tradefed.result.ITestInvocationListener; 26 import com.android.tradefed.result.LogDataType; 27 import com.android.tradefed.util.PerfettoTraceRecorder; 28 29 import java.io.File; 30 import java.io.IOException; 31 import java.util.LinkedHashMap; 32 import java.util.Map; 33 /** 34 * Collector that will start perfetto trace when a test run starts and log trace file at the end. 35 */ 36 public class DeviceTraceCollector extends BaseDeviceMetricCollector { 37 // Format of the trace name should be: device-trace_<device-serial>_<trace-count>_<event-name>. 38 private static final String NAME_FORMAT = "device-trace_%s_%d_%s"; 39 private PerfettoTraceRecorder mPerfettoTraceRecorder = new PerfettoTraceRecorder(); 40 // package name for an instrumentation test, null otherwise. 41 private String mInstrumentationPkgName; 42 43 private Map<ITestDevice, Integer> mTraceCountMap = new LinkedHashMap<>(); 44 // Map of trace files and the proper name it should be logged with 45 private Map<File, String> mTraceFilesMap = new LinkedHashMap(); 46 DeviceTraceCollector()47 public DeviceTraceCollector() { 48 setDisableReceiver(false); 49 } 50 51 @Override extraInit(IInvocationContext context, ITestInvocationListener listener)52 public void extraInit(IInvocationContext context, ITestInvocationListener listener) 53 throws DeviceNotAvailableException { 54 super.extraInit(context, listener); 55 for (ITestDevice device : getRealDevices()) { 56 startTraceOnDevice(device); 57 } 58 } 59 60 @Override onTestRunEnd( DeviceMetricData runData, Map<String, MetricMeasurement.Metric> currentRunMetrics)61 public void onTestRunEnd( 62 DeviceMetricData runData, Map<String, MetricMeasurement.Metric> currentRunMetrics) 63 throws DeviceNotAvailableException { 64 for (ITestDevice device : getRealDevices()) { 65 collectTraceFileFromDevice(device, "testRunEnded"); 66 } 67 logTraceFiles(); 68 } 69 startTraceOnDevice(ITestDevice device)70 private void startTraceOnDevice(ITestDevice device) { 71 // count should be increased even if no trace file collected to make missing traces visible. 72 mTraceCountMap.put(device, mTraceCountMap.getOrDefault(device, 0) + 1); 73 try { 74 Map<String, String> extraConfigs = new LinkedHashMap<>(); 75 if (mInstrumentationPkgName != null) { 76 extraConfigs.put("atrace_apps", String.format("\"%s\"", mInstrumentationPkgName)); 77 } 78 mPerfettoTraceRecorder.startTrace(device, extraConfigs); 79 } catch (IOException e) { 80 CLog.d( 81 "Failed to start perfetto trace on %s trace-count:%d with error: %s", 82 device.getSerialNumber(), mTraceCountMap.get(device), e.getMessage()); 83 } 84 } 85 collectTraceFileFromDevice(ITestDevice device, String eventName)86 private void collectTraceFileFromDevice(ITestDevice device, String eventName) { 87 File traceFile = mPerfettoTraceRecorder.stopTrace(device); 88 if (traceFile == null) { 89 CLog.d( 90 "Failed to collect device trace from %s on event:%s trace-count:%d.", 91 device.getSerialNumber(), eventName, mTraceCountMap.get(device)); 92 return; 93 } 94 CLog.d( 95 "Collected device trace from %s on event:%s. trace-count:%d. size:%d", 96 device.getSerialNumber(), 97 eventName, 98 mTraceCountMap.get(device), 99 traceFile.length()); 100 String name = 101 String.format( 102 NAME_FORMAT, 103 device.getSerialNumber(), 104 mTraceCountMap.get(device), 105 eventName); 106 mTraceFilesMap.put(traceFile, name); 107 } 108 logTraceFiles()109 private void logTraceFiles() { 110 for (Map.Entry<File, String> entry : mTraceFilesMap.entrySet()) { 111 try (FileInputStreamSource source = new FileInputStreamSource(entry.getKey(), true)) { 112 super.testLog(entry.getValue(), LogDataType.PERFETTO, source); 113 } 114 } 115 } 116 setInstrumentationPkgName(String packageName)117 public void setInstrumentationPkgName(String packageName) { 118 mInstrumentationPkgName = packageName; 119 } 120 121 @Override rebootStarted(ITestDevice device)122 public void rebootStarted(ITestDevice device) throws DeviceNotAvailableException { 123 super.rebootStarted(device); 124 // save previous trace running on this device. 125 collectTraceFileFromDevice(device, "rebootStarted"); 126 } 127 128 @Override rebootEnded(ITestDevice device)129 public void rebootEnded(ITestDevice device) throws DeviceNotAvailableException { 130 super.rebootEnded(device); 131 // start new trace running on this device 132 startTraceOnDevice(device); 133 } 134 } 135