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