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