| // Copyright (c) 2017, 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; |
| |
| import com.android.tools.r8.ProgramResource.Kind; |
| import com.android.tools.r8.dex.Constants; |
| import com.android.tools.r8.dex.DexParser; |
| import com.android.tools.r8.dex.DexSection; |
| import com.android.tools.r8.origin.CommandLineOrigin; |
| import com.android.tools.r8.utils.AndroidApp; |
| import com.android.tools.r8.utils.InternalOptions; |
| import com.android.tools.r8.utils.StringDiagnostic; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.io.Closer; |
| import java.io.IOException; |
| import java.nio.file.Paths; |
| import java.util.LinkedHashMap; |
| import java.util.Map; |
| |
| public class DexSegments { |
| private static class Command extends BaseCommand { |
| |
| public static class Builder |
| extends BaseCommand.Builder<Command, Builder> { |
| |
| @Override |
| Command.Builder self() { |
| return this; |
| } |
| |
| @Override |
| protected Command makeCommand() { |
| // If printing versions ignore everything else. |
| if (isPrintHelp()) { |
| return new Command(isPrintHelp()); |
| } |
| return new Command(getAppBuilder().build()); |
| } |
| } |
| |
| static final String USAGE_MESSAGE = String.join("\n", ImmutableList.of( |
| "Usage: dexsegments [options] <input-files>", |
| " where <input-files> are dex files", |
| " --version # Print the version of r8.", |
| " --help # Print this message.")); |
| |
| public static Command.Builder builder() { |
| return new Command.Builder(); |
| } |
| |
| public static Command.Builder parse(String[] args) { |
| Command.Builder builder = builder(); |
| parse(args, builder); |
| return builder; |
| } |
| |
| private static void parse(String[] args, Command.Builder builder) { |
| for (int i = 0; i < args.length; i++) { |
| String arg = args[i].trim(); |
| if (arg.length() == 0) { |
| continue; |
| } else if (arg.equals("--help")) { |
| builder.setPrintHelp(true); |
| } else { |
| if (arg.startsWith("--")) { |
| builder.getReporter().error(new StringDiagnostic("Unknown option: " + arg, |
| CommandLineOrigin.INSTANCE)); |
| } |
| builder.addProgramFiles(Paths.get(arg)); |
| } |
| } |
| } |
| |
| private Command(AndroidApp inputApp) { |
| super(inputApp); |
| } |
| |
| private Command(boolean printHelp) { |
| super(printHelp, false); |
| } |
| |
| @Override |
| InternalOptions getInternalOptions() { |
| return new InternalOptions(); |
| } |
| } |
| |
| public static void main(String[] args) |
| throws IOException, CompilationFailedException, ResourceException { |
| Command.Builder builder = Command.parse(args); |
| Command command = builder.build(); |
| if (command.isPrintHelp()) { |
| System.out.println(Command.USAGE_MESSAGE); |
| return; |
| } |
| AndroidApp app = command.getInputApp(); |
| |
| Map<String, SegmentInfo> result = new LinkedHashMap<>(); |
| // Fill the results with all benchmark items otherwise golem may report missing benchmarks. |
| int[] benchmarks = |
| new int[] { |
| Constants.TYPE_ENCODED_ARRAY_ITEM, |
| Constants.TYPE_HEADER_ITEM, |
| Constants.TYPE_DEBUG_INFO_ITEM, |
| Constants.TYPE_FIELD_ID_ITEM, |
| Constants.TYPE_ANNOTATION_SET_REF_LIST, |
| Constants.TYPE_STRING_ID_ITEM, |
| Constants.TYPE_MAP_LIST, |
| Constants.TYPE_PROTO_ID_ITEM, |
| Constants.TYPE_METHOD_ID_ITEM, |
| Constants.TYPE_TYPE_ID_ITEM, |
| Constants.TYPE_STRING_DATA_ITEM, |
| Constants.TYPE_CLASS_DATA_ITEM, |
| Constants.TYPE_TYPE_LIST, |
| Constants.TYPE_ANNOTATIONS_DIRECTORY_ITEM, |
| Constants.TYPE_ANNOTATION_ITEM, |
| Constants.TYPE_ANNOTATION_SET_ITEM, |
| Constants.TYPE_CLASS_DEF_ITEM |
| }; |
| for (int benchmark : benchmarks) { |
| result.computeIfAbsent(DexSection.typeName(benchmark), (key) -> new SegmentInfo()); |
| } |
| try (Closer closer = Closer.create()) { |
| for (ProgramResource resource : app.computeAllProgramResources()) { |
| if (resource.getKind() == Kind.DEX) { |
| for (DexSection dexSection : |
| DexParser.parseMapFrom( |
| closer.register(resource.getByteStream()), resource.getOrigin())) { |
| SegmentInfo info = |
| result.computeIfAbsent(dexSection.typeName(), (key) -> new SegmentInfo()); |
| info.increment(dexSection.length, dexSection.size()); |
| } |
| } |
| } |
| } |
| System.out.println("Segments in dex application (name: size / items):"); |
| // This output is parsed by tools/test_framework.py. Check the parsing there when updating. |
| result.forEach( |
| (key, value) -> System.out.println(" - " + key + ": " + value.size + " / " + value.items)); |
| } |
| |
| private static class SegmentInfo { |
| private int items; |
| private int size; |
| |
| SegmentInfo() { |
| this.items = 0; |
| this.size = 0; |
| } |
| |
| void increment(int items, int size) { |
| this.items += items; |
| this.size += size; |
| } |
| } |
| } |