| // Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| package com.android.tools.r8.errors; | |
| import com.android.tools.r8.Diagnostic; | |
| import com.android.tools.r8.graph.DexEncodedMethod; | |
| import com.android.tools.r8.graph.DexProgramClass; | |
| import com.android.tools.r8.keepanno.annotations.KeepForApi; | |
| import com.android.tools.r8.origin.Origin; | |
| import com.android.tools.r8.position.Position; | |
| import com.android.tools.r8.profile.startup.profile.StartupProfile; | |
| import it.unimi.dsi.fastutil.ints.Int2IntMap; | |
| import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; | |
| import java.util.List; | |
| @KeepForApi | |
| public class StartupClassesNonStartupFractionDiagnostic implements Diagnostic { | |
| private final int numberOfStartupClasses; | |
| private final int numberOfStartupMethods; | |
| private final int numberOfNonStartupMethods; | |
| private final Int2IntMap numberOfStartupClassesByNumberOfStartupMethods; | |
| StartupClassesNonStartupFractionDiagnostic( | |
| int numberOfStartupClasses, | |
| int numberOfStartupMethods, | |
| int numberOfNonStartupMethods, | |
| Int2IntMap numberOfStartupClassesByNumberOfStartupMethods) { | |
| this.numberOfStartupClasses = numberOfStartupClasses; | |
| this.numberOfStartupMethods = numberOfStartupMethods; | |
| this.numberOfNonStartupMethods = numberOfNonStartupMethods; | |
| this.numberOfStartupClassesByNumberOfStartupMethods = | |
| numberOfStartupClassesByNumberOfStartupMethods; | |
| } | |
| @Override | |
| public Origin getOrigin() { | |
| return Origin.unknown(); | |
| } | |
| @Override | |
| public Position getPosition() { | |
| return Position.UNKNOWN; | |
| } | |
| @Override | |
| public String getDiagnosticMessage() { | |
| return String.format( | |
| "Startup DEX files contains %d classes and %d methods of which %d (%d%%) are non-startup " | |
| + "methods. Distribution of classes by their number of startup methods:\n" | |
| + "0: %d classes\n" | |
| + "1: %d classes\n" | |
| + "2-3: %d classes\n" | |
| + "4-5: %d classes\n" | |
| + "6-10: %d classes\n" | |
| + "11+: %d classes\n", | |
| numberOfStartupClasses, | |
| numberOfStartupMethods + numberOfNonStartupMethods, | |
| numberOfNonStartupMethods, | |
| Math.round( | |
| (((double) numberOfNonStartupMethods) | |
| / (numberOfStartupMethods + numberOfNonStartupMethods)) | |
| * 100.0), | |
| numberOfStartupClassesByNumberOfStartupMethods.get(0), | |
| numberOfStartupClassesByNumberOfStartupMethods.get(1), | |
| numberOfStartupClassesByNumberOfStartupMethods.get(2) | |
| + numberOfStartupClassesByNumberOfStartupMethods.get(3), | |
| numberOfStartupClassesByNumberOfStartupMethods.get(4) | |
| + numberOfStartupClassesByNumberOfStartupMethods.get(5), | |
| numberOfStartupClassesByNumberOfStartupMethods.get(6) | |
| + numberOfStartupClassesByNumberOfStartupMethods.get(7) | |
| + numberOfStartupClassesByNumberOfStartupMethods.get(8) | |
| + numberOfStartupClassesByNumberOfStartupMethods.get(9) | |
| + numberOfStartupClassesByNumberOfStartupMethods.get(10), | |
| numberOfStartupClassesByNumberOfStartupMethods.int2IntEntrySet().stream() | |
| .map(entry -> entry.getIntKey() > 10 ? entry.getIntValue() : 0) | |
| .reduce(0, Integer::sum)); | |
| } | |
| // Public factory to keep the constructor of the diagnostic out of the public API. | |
| public static class Factory { | |
| public static StartupClassesNonStartupFractionDiagnostic | |
| createStartupClassesNonStartupFractionDiagnostic( | |
| List<DexProgramClass> startupClasses, StartupProfile startupProfile) { | |
| assert !startupClasses.isEmpty(); | |
| assert !startupProfile.isEmpty(); | |
| int numberOfStartupClasses = startupClasses.size(); | |
| int numberOfStartupMethods = 0; | |
| int numberOfNonStartupMethods = 0; | |
| Int2IntMap numberOfStartupClassesByNumberOfStartupMethods = new Int2IntOpenHashMap(); | |
| numberOfStartupClassesByNumberOfStartupMethods.defaultReturnValue(0); | |
| for (DexProgramClass clazz : startupClasses) { | |
| assert startupProfile.isStartupClass(clazz.getType()); | |
| int numberOfStartupMethodsInClass = 0; | |
| for (DexEncodedMethod method : clazz.methods()) { | |
| if (startupProfile.containsMethodRule(method.getReference())) { | |
| numberOfStartupMethodsInClass++; | |
| } else { | |
| numberOfNonStartupMethods++; | |
| } | |
| } | |
| numberOfStartupMethods += numberOfStartupMethodsInClass; | |
| numberOfStartupClassesByNumberOfStartupMethods.put( | |
| numberOfStartupMethodsInClass, | |
| numberOfStartupClassesByNumberOfStartupMethods.get(numberOfStartupMethodsInClass) + 1); | |
| } | |
| return createStartupClassesNonStartupFractionDiagnostic( | |
| numberOfStartupClasses, | |
| numberOfStartupMethods, | |
| numberOfNonStartupMethods, | |
| numberOfStartupClassesByNumberOfStartupMethods); | |
| } | |
| public static StartupClassesNonStartupFractionDiagnostic | |
| createStartupClassesNonStartupFractionDiagnostic( | |
| int numberOfStartupClasses, | |
| int numberOfStartupMethods, | |
| int numberOfNonStartupMethods, | |
| Int2IntMap numberOfStartupClassesByNumberOfStartupMethods) { | |
| return new StartupClassesNonStartupFractionDiagnostic( | |
| numberOfStartupClasses, | |
| numberOfStartupMethods, | |
| numberOfNonStartupMethods, | |
| numberOfStartupClassesByNumberOfStartupMethods); | |
| } | |
| } | |
| } |