1 /* 2 * Copyright (C) 2018 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.device.metric; 17 18 import com.android.annotations.Nullable; 19 import com.android.annotations.VisibleForTesting; 20 import com.android.tradefed.build.IBuildInfo; 21 import com.android.tradefed.config.Option; 22 import com.android.tradefed.config.OptionClass; 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.LogUtil.CLog; 27 import com.android.tradefed.metrics.proto.MetricMeasurement.DataType; 28 import com.android.tradefed.metrics.proto.MetricMeasurement.Metric; 29 import com.android.tradefed.result.FileInputStreamSource; 30 import com.android.tradefed.result.InputStreamSource; 31 import com.android.tradefed.result.LogDataType; 32 import com.android.tradefed.util.CommandResult; 33 import com.android.tradefed.util.CommandStatus; 34 import com.android.tradefed.util.FileUtil; 35 import com.android.tradefed.util.Pair; 36 import com.android.tradefed.util.RunUtil; 37 import com.android.tradefed.util.StreamUtil; 38 import com.android.tradefed.util.ZipUtil; 39 40 import java.io.ByteArrayOutputStream; 41 import java.io.File; 42 import java.io.FileNotFoundException; 43 import java.io.FileOutputStream; 44 import java.io.IOException; 45 import java.io.InputStream; 46 import java.io.OutputStream; 47 import java.util.ArrayList; 48 import java.util.Collection; 49 import java.util.List; 50 import java.util.concurrent.TimeUnit; 51 52 /** 53 * Base implementation of {@link FilePullerDeviceMetricCollector} that allows 54 * pulling the perfetto files from the device and collect the metrics from it. 55 * Also used for converting the raw trace file into perfetto metric file. 56 */ 57 @OptionClass(alias = "perfetto-metric-collector") 58 public class PerfettoPullerMetricCollector extends FilePullerDeviceMetricCollector { 59 60 private static final String LINE_SEPARATOR = "\\r?\\n"; 61 private static final char KEY_VALUE_SEPARATOR = ':'; 62 private static final String EXTRACTOR_STATUS = "trace_extractor_status"; 63 private static final String PROCESSOR_STATUS = "trace_processor_status"; 64 private static final String STATUS_SUCCESS = "1"; 65 private static final String STATUS_FAILURE = "0"; 66 private static final String EXTRACTOR_RUNTIME = "trace_extractor_runtime"; 67 private static final String PROCESSOR_RUNTIME = "trace_processor_runtime"; 68 private static final String RAW_TRACE_FILE_SIZE = "perfetto_trace_file_size_bytes"; 69 private static final String NSS_CACHE_ERROR = "base/nsscache-inl.h failed to lookup"; 70 71 public enum METRIC_FILE_FORMAT { 72 text, 73 binary, 74 json, 75 } 76 77 @Option(name = "compress-perfetto", 78 description = "If enabled retrieves the perfetto compressed content," 79 + "decompress for processing and upload the compressed file. If" 80 + "this flag is not enabled uncompressed version of perfetto file is" 81 + "pulled, processed and uploaded.") 82 private boolean mCompressPerfetto = false; 83 84 @Option(name = "max-compressed-file-size", description = "Max size of the compressed" 85 + " perfetto file. If the compressed file size exceeds the max then" 86 + " post processing and uploading the compressed file will not happen.") 87 private long mMaxCompressedFileSize = 10000L * 1024 * 1024; 88 89 @Option( 90 name = "compressed-trace-shell-timeout", 91 description = "Timeout for retrieving compressed trace content through shell", 92 isTimeVal = true) 93 private long mCompressedTimeoutMs = TimeUnit.MINUTES.toMillis(20); 94 95 @Option( 96 name = "compress-response-timeout", 97 description = "Timeout to receive the shell response when running the gzip command.", 98 isTimeVal = true) 99 private long mCompressResponseTimeoutMs = TimeUnit.SECONDS.toMillis(30); 100 101 @Option( 102 name = "decompress-perfetto-timeout", 103 description = "Timeout to decompress perfetto compressed file.", 104 isTimeVal = true) 105 private long mDecompressTimeoutMs = TimeUnit.MINUTES.toMillis(20); 106 107 @Option( 108 name = "perfetto-binary-path", 109 description = "Path to the script files used to analyze the trace files." 110 + "Used for collecting the key value metrics from the perfetto" 111 + "trace file.") 112 @Deprecated 113 private List<File> mScriptFiles = new ArrayList<>(); 114 115 @Option( 116 name = "perfetto-binary-args", 117 description = "Extra arguments to be passed to the binaries.") 118 @Deprecated 119 private List<String> mPerfettoBinaryArgs = new ArrayList<>(); 120 121 @Option( 122 name = "perfetto-metric-prefix", 123 description = "Prefix to be used with the metrics collected from perfetto.") 124 @Deprecated 125 private String mMetricPrefix = "perfetto"; 126 127 // List of process names passed to perfetto binary. 128 @Option( 129 name = "process-name", 130 description = 131 "Process names to be passed in perfetto script.") 132 @Deprecated 133 private Collection<String> mProcessNames = new ArrayList<String>(); 134 135 // Timeout for the script to process the trace files. 136 // The default is arbitarily chosen to be 5 mins to prevent the test spending more time in 137 // processing the files. 138 @Option( 139 name = "perfetto-script-timeout", 140 description = "Timeout for the perfetto script.", 141 isTimeVal = true) 142 @Deprecated 143 private long mScriptTimeoutMs = TimeUnit.MINUTES.toMillis(5); 144 145 @Option(name = "convert-metric-file", 146 description = "Convert the raw trace file to perfetto metric file.") 147 private boolean mConvertToMetricFile = true; 148 149 @Option(name = "collect-perfetto-file-size", 150 description = "Set it to true to collect the perfetto file size as part" 151 + " of the metrics.") 152 private boolean mCollectPerfettoFileSize = false; 153 154 @Option( 155 name = "trace-processor-binary", 156 description = "Path to the trace processor shell. This will" 157 + " override the trace-processor-name which is used to " 158 + " lookup in build artifacts. Used for converting the raw" 159 + " trace into perfetto metric file.") 160 private File mTraceProcessorBinary = null; 161 162 @Option( 163 name = "trace-processor-name", 164 description = "Trace processor name to look up from the test artifacts" 165 + " or module artifacts.") 166 private String mTraceProcessorName = "trace_processor_shell"; 167 168 @Option( 169 name = "trace-processor-run-metrics", 170 description = "Comma separated list of metrics to extract from raw trace file." 171 + " For example android_mem.") 172 private String mTraceProcessorMetrics = "android_mem"; 173 174 @Option( 175 name = "trace-processor-output-format", 176 description = "Trace processor output format. [binary|text|json]") 177 private METRIC_FILE_FORMAT mTraceProcessorOutputFormat = METRIC_FILE_FORMAT.text; 178 179 @Option( 180 name = "trace-processor-timeout", 181 description = "Timeout to convert the raw trace file to metric proto file.", 182 isTimeVal = true) 183 private long mTraceConversionTimeout = TimeUnit.MINUTES.toMillis(20); 184 185 186 /** 187 * Process the perfetto trace file for the additional metrics and add it to final metrics. 188 * Decompress the perfetto file for processing if the compression was enabled. 189 * 190 * @param key the option key associated to the file that was pulled from the device. 191 * @param metricFile the {@link File} pulled from the device matching the option key. 192 * @param data where metrics will be stored. 193 */ 194 @Override processMetricFile(String key, File metricFile, DeviceMetricData data)195 public void processMetricFile(String key, File metricFile, 196 DeviceMetricData data) { 197 File processSrcFile = metricFile; 198 if (mCompressPerfetto) { 199 processSrcFile = decompressFile(metricFile); 200 } 201 202 // Update the file size metrics. 203 if (processSrcFile != null && mCollectPerfettoFileSize) { 204 double perfettoFileSizeInBytes = processSrcFile.length(); 205 Metric.Builder metricDurationBuilder = Metric.newBuilder(); 206 metricDurationBuilder.getMeasurementsBuilder().setSingleDouble( 207 perfettoFileSizeInBytes); 208 data.addMetric(RAW_TRACE_FILE_SIZE, metricDurationBuilder.setType(DataType.RAW)); 209 } 210 211 // Convert to perfetto metric format. 212 if (mConvertToMetricFile) { 213 TraceProcessorResult result = convertToMetricProto(processSrcFile); 214 File convertedMetricFile = result.file; 215 if (convertedMetricFile != null) { 216 try (InputStreamSource source = new FileInputStreamSource(convertedMetricFile, 217 true)) { 218 testLog(convertedMetricFile.getName(), getLogDataType(), source); 219 } 220 } 221 222 Metric.Builder processorRuntimeBuilder = Metric.newBuilder(); 223 processorRuntimeBuilder 224 .getMeasurementsBuilder() 225 .setSingleDouble((double) result.runtime); 226 data.addMetric( 227 String.format("%s_%s", mMetricPrefix, PROCESSOR_RUNTIME), 228 processorRuntimeBuilder.setType(DataType.RAW)); 229 230 Metric.Builder processorStatusBuilder = Metric.newBuilder(); 231 processorStatusBuilder.getMeasurementsBuilder().setSingleString(result.status); 232 data.addMetric( 233 String.format("%s_%s", mMetricPrefix, PROCESSOR_STATUS), 234 processorStatusBuilder.setType(DataType.RAW)); 235 } 236 237 if (processSrcFile != null) { 238 // Extract the metrics from the trace file. 239 for (File scriptFile : mScriptFiles) { 240 // Apply necessary execute permissions to the script. 241 FileUtil.chmodGroupRWX(scriptFile); 242 243 List<String> commandArgsList = new ArrayList<String>(); 244 commandArgsList.add(scriptFile.getAbsolutePath()); 245 commandArgsList.addAll(mPerfettoBinaryArgs); 246 commandArgsList.add("-trace_file"); 247 commandArgsList.add(processSrcFile.getAbsolutePath()); 248 249 if (!mProcessNames.isEmpty()) { 250 commandArgsList.add("-process_names"); 251 commandArgsList.add(String.join(",", mProcessNames)); 252 } 253 254 String traceExtractorStatus = STATUS_SUCCESS; 255 256 double scriptDuration = 0; 257 double scriptStartTime = System.currentTimeMillis(); 258 CommandResult cr = runHostCommand(mScriptTimeoutMs, 259 commandArgsList.toArray(new String[commandArgsList.size()]), null, 260 null); 261 scriptDuration = System.currentTimeMillis() - scriptStartTime; 262 263 // Update the script duration metrics. 264 Metric.Builder metricDurationBuilder = Metric.newBuilder(); 265 metricDurationBuilder.getMeasurementsBuilder().setSingleDouble(scriptDuration); 266 data.addMetric( 267 String.format("%s_%s", mMetricPrefix, EXTRACTOR_RUNTIME), 268 metricDurationBuilder.setType(DataType.RAW)); 269 270 // Adding temporary workaround to handle the NSS cache error. 271 // TODO: Revert the NSS cache error handling after b/156924255 is fixed. 272 if (CommandStatus.SUCCESS.equals(cr.getStatus()) || 273 (CommandStatus.FAILED.equals(cr.getStatus()) && 274 cr.getStdout().contains(NSS_CACHE_ERROR))) { 275 String[] metrics = cr.getStdout().split(LINE_SEPARATOR); 276 277 boolean isMetricStarted = false; 278 for (String metric : metrics) { 279 // Skip until the first metric line is parsed. 280 // Usually "trace-durations-ms" is the first metric from the output. 281 if(isMetricStarted || metric.contains("trace-duration-ms")) { 282 isMetricStarted = true; 283 } else { 284 continue; 285 } 286 287 Pair<String, String> kv = splitKeyValue(metric); 288 289 if (kv != null) { 290 Metric.Builder metricBuilder = Metric.newBuilder(); 291 metricBuilder.getMeasurementsBuilder().setSingleString(kv.second); 292 data.addMetric( 293 String.format("%s_%s", mMetricPrefix, kv.first), 294 metricBuilder.setType(DataType.RAW)); 295 } else { 296 CLog.e("Output %s not in the expected format.", metric); 297 } 298 } 299 CLog.i(cr.getStdout()); 300 } else { 301 traceExtractorStatus = STATUS_FAILURE; 302 CLog.e("Unable to parse the trace file %s due to %s - Status - %s ", 303 processSrcFile.getName(), cr.getStderr(), cr.getStatus()); 304 } 305 306 if (mCompressPerfetto) { 307 processSrcFile.delete(); 308 } 309 Metric.Builder metricStatusBuilder = Metric.newBuilder(); 310 metricStatusBuilder.getMeasurementsBuilder() 311 .setSingleString(traceExtractorStatus); 312 data.addMetric( 313 String.format("%s_%s", mMetricPrefix, EXTRACTOR_STATUS), 314 metricStatusBuilder.setType(DataType.RAW)); 315 } 316 } 317 318 // Upload and delete the host trace file. 319 try (InputStreamSource source = new FileInputStreamSource(metricFile, true)) { 320 if (mCompressPerfetto) { 321 if (processSrcFile != null) { 322 testLog(metricFile.getName(), LogDataType.GZIP, source); 323 } else { 324 metricFile.delete(); 325 } 326 327 } else { 328 testLog(metricFile.getName(), LogDataType.PERFETTO, source); 329 } 330 } 331 332 } 333 334 private static class TraceProcessorResult { 335 public final File file; 336 // runtime is set to -1 if the trace processor was never run. 337 public final long runtime; 338 public final String status; 339 TraceProcessorResult(File file, long runtime, String status)340 public TraceProcessorResult(File file, long runtime, String status) { 341 this.file = file; 342 this.runtime = runtime; 343 this.status = status; 344 } 345 } 346 347 /** 348 * Converts the raw trace file into perfetto metric file. 349 * 350 * @param perfettoRawTraceFile Raw perfetto trace file that needs to be converted. 351 * @return The result of the conversion. 352 */ convertToMetricProto(File perfettoRawTraceFile)353 private TraceProcessorResult convertToMetricProto(File perfettoRawTraceFile) { 354 355 // Use absolute path to the trace file if it is available otherwise 356 // resolve the trace processor name from the test or module artifacts. 357 if (mTraceProcessorBinary == null || !mTraceProcessorBinary.exists()) { 358 mTraceProcessorBinary = getFileFromTestArtifacts(mTraceProcessorName); 359 } 360 361 File metricOutputFile = null; 362 long runtime = -1L; 363 364 if (mTraceProcessorBinary == null) { 365 CLog.e("Failed to locate the trace processor shell binary file."); 366 return new TraceProcessorResult(metricOutputFile, runtime, STATUS_FAILURE); 367 } 368 369 FileUtil.chmodGroupRWX(mTraceProcessorBinary); 370 List<String> commandArgsList = new ArrayList<String>(); 371 commandArgsList.add(mTraceProcessorBinary.getAbsolutePath()); 372 373 // Comma separated list of metrics to extract. 374 if (!mTraceProcessorMetrics.isEmpty()) { 375 commandArgsList.add("--run-metrics"); 376 commandArgsList.add(mTraceProcessorMetrics); 377 } 378 // Metric file output format. 379 commandArgsList.add("--metrics-output=" + mTraceProcessorOutputFormat); 380 commandArgsList.add(perfettoRawTraceFile.getAbsolutePath()); 381 382 try { 383 metricOutputFile = FileUtil.createTempFile( 384 "metric_" + getRawTraceFileName(perfettoRawTraceFile.getName()), ""); 385 } catch (IOException e) { 386 CLog.e("Not able to create metric perfetto output file."); 387 CLog.e(e); 388 return new TraceProcessorResult(null, runtime, STATUS_FAILURE); 389 } 390 391 // Running the trace conversion. 392 CLog.i("Run the trace convertor."); 393 boolean isConversionSuccess = true; 394 try (FileOutputStream outStream = new FileOutputStream(metricOutputFile); 395 ByteArrayOutputStream errStream = new ByteArrayOutputStream()) { 396 long startTime = System.currentTimeMillis(); 397 CommandResult conversionResult = runHostCommand(mTraceConversionTimeout, 398 commandArgsList.toArray(new String[commandArgsList 399 .size()]), 400 outStream, errStream); 401 runtime = System.currentTimeMillis() - startTime; 402 if (!CommandStatus.SUCCESS.equals(conversionResult.getStatus())) { 403 CLog.e("Unable to convert the raw trace - %s to metric file due to" 404 + " %s - Status - %s ", perfettoRawTraceFile.getName(), 405 errStream.toString(), conversionResult.getStatus()); 406 isConversionSuccess = false; 407 } else if (mTraceProcessorOutputFormat.equals(METRIC_FILE_FORMAT.text) || 408 mTraceProcessorOutputFormat.equals(METRIC_FILE_FORMAT.json)) { 409 File compressedFile = getCompressedFile(metricOutputFile); 410 metricOutputFile.delete(); 411 return new TraceProcessorResult(compressedFile, runtime, STATUS_SUCCESS); 412 } 413 } catch (FileNotFoundException e) { 414 CLog.e("Not able to find the result metric file to write the " 415 + "metric output."); 416 CLog.e(e); 417 isConversionSuccess = false; 418 } catch (IOException e1) { 419 CLog.e("Unable to close the streams."); 420 CLog.e(e1); 421 isConversionSuccess = false; 422 } finally { 423 if (!isConversionSuccess) { 424 metricOutputFile.delete(); 425 return new TraceProcessorResult(null, runtime, STATUS_FAILURE); 426 } 427 } 428 return new TraceProcessorResult(metricOutputFile, runtime, STATUS_SUCCESS); 429 } 430 431 /** 432 * Pull the file from the specified path in the device. Pull the compressed content of the 433 * perfetto file if the compress perfetto option is enabled. 434 * 435 * @param device which has the file. 436 * @param remoteFilePath location in the device. 437 * @return compressed or decompressed version of perfetto file based on mCompressPerfetto option 438 * is set or not. 439 * @throws DeviceNotAvailableException 440 */ 441 @Override retrieveFile(ITestDevice device, String remoteFilePath, int userId)442 protected File retrieveFile(ITestDevice device, String remoteFilePath, int userId) 443 throws DeviceNotAvailableException { 444 if (!mCompressPerfetto) { 445 return super.retrieveFile(device, remoteFilePath, userId); 446 } 447 File perfettoCompressedFile = null; 448 String filePathInDevice = remoteFilePath; 449 CLog.i("Retrieving the compressed perfetto trace content from device."); 450 LargeOutputReceiver compressedOutputReceiver = 451 new LargeOutputReceiver( 452 "perfetto_compressed_temp", 453 device.getSerialNumber(), 454 mMaxCompressedFileSize); 455 device.executeShellCommand( 456 String.format("gzip -c %s", filePathInDevice), 457 compressedOutputReceiver, 458 mCompressedTimeoutMs, 459 mCompressResponseTimeoutMs, 460 TimeUnit.MILLISECONDS, 461 1); 462 compressedOutputReceiver.flush(); 463 compressedOutputReceiver.cancel(); 464 465 // Copy to temp file which will be used for decompression, perfetto 466 // metrics extraction and uploading the file later. 467 try (InputStreamSource largeStreamSrc = compressedOutputReceiver.getData(); 468 InputStream inputStream = largeStreamSrc.createInputStream()) { 469 perfettoCompressedFile = FileUtil.createTempFile("perfetto_compressed", ".gz"); 470 FileOutputStream outStream = new FileOutputStream(perfettoCompressedFile); 471 byte[] buffer = new byte[4096]; 472 int bytesRead = -1; 473 while ((bytesRead = inputStream.read(buffer)) > -1) { 474 outStream.write(buffer, 0, bytesRead); 475 } 476 StreamUtil.close(outStream); 477 CLog.i("Successfully copied the compressed content from device to" + " host."); 478 } catch (IOException e) { 479 if (perfettoCompressedFile != null) { 480 perfettoCompressedFile.delete(); 481 } 482 CLog.e("Failed to copy compressed perfetto to temporary file."); 483 CLog.e(e); 484 } finally { 485 compressedOutputReceiver.delete(); 486 } 487 return perfettoCompressedFile; 488 } 489 490 /** 491 * Decompress the file to a temporary file in the host. 492 * 493 * @param compressedFile file to be decompressed. 494 * @return decompressed file used for postprocessing. 495 */ decompressFile(File compressedFile)496 private File decompressFile(File compressedFile) { 497 File decompressedFile = null; 498 try { 499 decompressedFile = FileUtil.createTempFile("perfetto_decompressed", ".pb"); 500 } catch (IOException e) { 501 CLog.e("Not able to create decompressed perfetto file."); 502 CLog.e(e); 503 return null; 504 } 505 // Keep the original file for uploading. 506 List<String> decompressArgsList = new ArrayList<String>(); 507 decompressArgsList.add("gzip"); 508 decompressArgsList.add("-k"); 509 decompressArgsList.add("-c"); 510 decompressArgsList.add("-d"); 511 decompressArgsList.add(compressedFile.getAbsolutePath()); 512 513 // Decompress perfetto trace file. 514 CLog.i("Start decompressing the perfetto trace file."); 515 try (FileOutputStream outStream = new FileOutputStream(decompressedFile); 516 ByteArrayOutputStream errStream = new ByteArrayOutputStream()) { 517 CommandResult decompressResult = runHostCommand(mDecompressTimeoutMs, 518 decompressArgsList.toArray(new String[decompressArgsList 519 .size()]), outStream, errStream); 520 521 if (!CommandStatus.SUCCESS.equals(decompressResult.getStatus())) { 522 CLog.e("Unable decompress the metric file %s due to %s - Status - %s ", 523 compressedFile.getName(), errStream.toString(), 524 decompressResult.getStatus()); 525 decompressedFile.delete(); 526 return null; 527 } 528 } catch (FileNotFoundException e) { 529 CLog.e("Not able to find the decompressed file to copy the" 530 + " decompressed contents."); 531 CLog.e(e); 532 return null; 533 } catch (IOException e1) { 534 CLog.e("Unable to close the streams."); 535 CLog.e(e1); 536 } 537 CLog.i("Successfully decompressed the perfetto trace file."); 538 return decompressedFile; 539 } 540 541 @Override processMetricDirectory(String key, File metricDirectory, DeviceMetricData runData)542 public void processMetricDirectory(String key, File metricDirectory, DeviceMetricData runData) { 543 // Implement if all the files under specific directory have to be post processed. 544 } 545 546 /** 547 * Run a host command with the given array of command args. 548 * 549 * @param commandArgs args to be used to construct the host command. 550 * @param stdout output of the command. 551 * @param stderr error message if any from the command. 552 * @return return the command results. 553 */ 554 @VisibleForTesting runHostCommand(long timeOut, String[] commandArgs, OutputStream stdout, OutputStream stderr)555 CommandResult runHostCommand(long timeOut, String[] commandArgs, OutputStream stdout, 556 OutputStream stderr) { 557 if (stdout != null && stderr != null) { 558 return RunUtil.getDefault().runTimedCmd(timeOut, stdout, stderr, commandArgs); 559 } 560 return RunUtil.getDefault().runTimedCmd(timeOut, commandArgs); 561 } 562 563 @VisibleForTesting 564 @Nullable splitKeyValue(String s)565 static Pair<String, String> splitKeyValue(String s) { 566 // Expected script test output format. 567 // Key1:Value1 568 // Key2:Value2 569 int separatorIdx = s.lastIndexOf(KEY_VALUE_SEPARATOR); 570 if (separatorIdx > 0 && separatorIdx + 1 < s.length()) { 571 return new Pair<>(s.substring(0, separatorIdx), s.substring(separatorIdx + 1)); 572 } 573 return null; 574 } 575 576 /** 577 * Get the log data type based on the output metric perfetto file. 578 * 579 * @return LogDataType type of the file used for uploading the artifacts. 580 */ getLogDataType()581 private LogDataType getLogDataType() { 582 // text option in perfetto trace processor means text proto. 583 if(mTraceProcessorOutputFormat.equals(METRIC_FILE_FORMAT.text)) { 584 return LogDataType.ZIP; 585 } else if(mTraceProcessorOutputFormat.equals(METRIC_FILE_FORMAT.binary)) { 586 return LogDataType.PB; 587 } else { 588 return LogDataType.TEXT; 589 } 590 } 591 592 /** 593 * Extract the raw trace file name used for constructing the output 594 * perfetto metric file name 595 * 596 * @param rawTraceFileName 597 * @return String name of the raw trace file name excluding the UUID. 598 */ getRawTraceFileName(String rawTraceFileName)599 private String getRawTraceFileName(String rawTraceFileName) { 600 // For example return perfetto_<test_name>-1_ from 601 // perfetto_<test_name>-1_13388308985625987330.pb excluding the UID. 602 int lastIndex = rawTraceFileName.lastIndexOf("_"); 603 if (lastIndex != -1) { 604 return rawTraceFileName.substring(0, lastIndex + 1); 605 } 606 return rawTraceFileName; 607 } 608 609 /** 610 * Retrieve the current build info. 611 * 612 * @return BuildInfo which has access to test artifacts directory. 613 */ 614 @VisibleForTesting getCurrentBuildInfo()615 IBuildInfo getCurrentBuildInfo() { 616 return getBuildInfos().get(0); 617 } 618 619 /** 620 * Compress the given file. 621 * 622 * @return File compressed version of the file. 623 */ 624 @VisibleForTesting getCompressedFile(File metricOutputFile)625 File getCompressedFile(File metricOutputFile) throws IOException { 626 return ZipUtil.createZip(metricOutputFile, 627 metricOutputFile.getName()); 628 } 629 } 630