1 /* 2 * Copyright (C) 2023 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.server.art; 18 19 import static com.android.server.art.model.ArtFlags.BatchDexoptPass; 20 21 import android.annotation.NonNull; 22 import android.app.job.JobParameters; 23 import android.os.Build; 24 25 import androidx.annotation.RequiresApi; 26 27 import com.android.server.art.model.ArtFlags; 28 import com.android.server.art.model.DexoptResult; 29 30 import dalvik.system.DexFile; 31 32 import java.util.List; 33 import java.util.Optional; 34 35 /** 36 * This is a helper class to report the background DexOpt job metrics to StatsD. 37 * 38 * @hide 39 */ 40 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 41 public class BackgroundDexoptJobStatsReporter { reportFailure()42 public static void reportFailure() { 43 // The fatal error can occur during any pass, but we attribute it to the main pass for 44 // simplicity. 45 ArtStatsLog.write(ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED, 46 ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED__STATUS__STATUS_FATAL_ERROR, 47 JobParameters.STOP_REASON_UNDEFINED, 0L /* durationMs */, 0L /* deprecated */, 48 0 /* optimizedPackagesCount */, 0 /* packagesDependingOnBootClasspathCount */, 49 0 /* totalPackagesCount */, 50 ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED__PASS__PASS_MAIN); 51 } 52 reportSuccess(@onNull BackgroundDexoptJob.CompletedResult completedResult, Optional<Integer> stopReason)53 public static void reportSuccess(@NonNull BackgroundDexoptJob.CompletedResult completedResult, 54 Optional<Integer> stopReason) { 55 for (var entry : completedResult.dexoptResultByPass().entrySet()) { 56 reportPass(entry.getKey(), entry.getValue(), 57 completedResult.durationMsByPass().getOrDefault(entry.getKey(), 0l), 58 stopReason); 59 } 60 } 61 reportPass(@atchDexoptPass int pass, @NonNull DexoptResult dexoptResult, long durationMs, Optional<Integer> stopReason)62 public static void reportPass(@BatchDexoptPass int pass, @NonNull DexoptResult dexoptResult, 63 long durationMs, Optional<Integer> stopReason) { 64 // The job contains multiple passes, so the stop reason may not be for the current pass. We 65 // shouldn't report the stop reason if the current pass finished before the job was 66 // cancelled. 67 int reportedStopReason = dexoptResult.getFinalStatus() == DexoptResult.DEXOPT_CANCELLED 68 ? stopReason.orElse(JobParameters.STOP_REASON_UNDEFINED) 69 : JobParameters.STOP_REASON_UNDEFINED; 70 71 List<DexoptResult.PackageDexoptResult> packageDexoptResults = 72 getFilteredPackageResults(dexoptResult); 73 74 ArtStatsLog.write(ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED, 75 getStatusForStats(dexoptResult, stopReason), reportedStopReason, durationMs, 76 0L /* deprecated */, getDexoptedPackagesCount(packageDexoptResults), 77 getPackagesDependingOnBootClasspathCount(packageDexoptResults), 78 packageDexoptResults.size(), toStatsdPassEnum(pass)); 79 } 80 81 @NonNull getFilteredPackageResults( @onNull DexoptResult dexoptResult)82 private static List<DexoptResult.PackageDexoptResult> getFilteredPackageResults( 83 @NonNull DexoptResult dexoptResult) { 84 return dexoptResult.getPackageDexoptResults() 85 .stream() 86 .filter(packageResult 87 -> packageResult.getDexContainerFileDexoptResults().stream().anyMatch( 88 fileResult 89 -> (fileResult.getExtendedStatusFlags() 90 & DexoptResult.EXTENDED_SKIPPED_NO_DEX_CODE) 91 == 0)) 92 .toList(); 93 } 94 getStatusForStats( @onNull DexoptResult dexoptResult, Optional<Integer> stopReason)95 private static int getStatusForStats( 96 @NonNull DexoptResult dexoptResult, Optional<Integer> stopReason) { 97 if (dexoptResult.getFinalStatus() == DexoptResult.DEXOPT_CANCELLED) { 98 if (stopReason.isPresent()) { 99 return ArtStatsLog 100 .BACKGROUND_DEXOPT_JOB_ENDED__STATUS__STATUS_ABORT_BY_CANCELLATION; 101 } else { 102 return ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED__STATUS__STATUS_ABORT_BY_API; 103 } 104 } 105 106 boolean isSkippedDueToStorageLow = 107 dexoptResult.getPackageDexoptResults() 108 .stream() 109 .flatMap(packageResult 110 -> packageResult.getDexContainerFileDexoptResults().stream()) 111 .anyMatch(fileResult 112 -> (fileResult.getExtendedStatusFlags() 113 & DexoptResult.EXTENDED_SKIPPED_STORAGE_LOW) 114 != 0); 115 if (isSkippedDueToStorageLow) { 116 return ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED__STATUS__STATUS_ABORT_NO_SPACE_LEFT; 117 } 118 119 return ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED__STATUS__STATUS_JOB_FINISHED; 120 } 121 getDexoptedPackagesCount( @onNull List<DexoptResult.PackageDexoptResult> packageResults)122 private static int getDexoptedPackagesCount( 123 @NonNull List<DexoptResult.PackageDexoptResult> packageResults) { 124 return (int) packageResults.stream() 125 .filter(result -> result.getStatus() == DexoptResult.DEXOPT_PERFORMED) 126 .count(); 127 } 128 getPackagesDependingOnBootClasspathCount( @onNull List<DexoptResult.PackageDexoptResult> packageResults)129 private static int getPackagesDependingOnBootClasspathCount( 130 @NonNull List<DexoptResult.PackageDexoptResult> packageResults) { 131 return (int) packageResults.stream() 132 .map(DexoptResult.PackageDexoptResult::getDexContainerFileDexoptResults) 133 .filter(BackgroundDexoptJobStatsReporter::isDependentOnBootClasspath) 134 .count(); 135 } 136 isDependentOnBootClasspath( @onNull List<DexoptResult.DexContainerFileDexoptResult> filesResults)137 private static boolean isDependentOnBootClasspath( 138 @NonNull List<DexoptResult.DexContainerFileDexoptResult> filesResults) { 139 return filesResults.stream() 140 .map(DexoptResult.DexContainerFileDexoptResult::getActualCompilerFilter) 141 .anyMatch(DexFile::isOptimizedCompilerFilter); 142 } 143 toStatsdPassEnum(@atchDexoptPass int pass)144 private static int toStatsdPassEnum(@BatchDexoptPass int pass) { 145 switch (pass) { 146 case ArtFlags.PASS_DOWNGRADE: 147 return ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED__PASS__PASS_DOWNGRADE; 148 case ArtFlags.PASS_MAIN: 149 return ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED__PASS__PASS_MAIN; 150 case ArtFlags.PASS_SUPPLEMENTARY: 151 return ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED__PASS__PASS_SUPPLEMENTARY; 152 } 153 throw new IllegalArgumentException("Unknown batch dexopt pass " + pass); 154 } 155 } 156