Util for golem that sums up sizes of dex segments.
BUG=
Change-Id: I87aff4720102aa56966ea4c6058f1a6d95bd8f44
diff --git a/build.gradle b/build.gradle
index b8764b5..a3a6419 100644
--- a/build.gradle
+++ b/build.gradle
@@ -323,6 +323,20 @@
}
}
+task DexSegments(type: Jar) {
+ from sourceSets.main.output
+ baseName 'dexsegments'
+ manifest {
+ attributes 'Main-Class': 'com.android.tools.r8.DexSegments'
+ }
+ // In order to build without dependencies, pass the exclude_deps property using:
+ // gradle -Pexclude_deps DexSegments
+ if (!project.hasProperty('exclude_deps')) {
+ // Also include dependencies
+ from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
+ }
+}
+
task sourceJar(type: Jar, dependsOn: classes) {
classifier = 'src'
from sourceSets.main.allSource
diff --git a/src/main/java/com/android/tools/r8/DexSegments.java b/src/main/java/com/android/tools/r8/DexSegments.java
new file mode 100644
index 0000000..5f49ba7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/DexSegments.java
@@ -0,0 +1,127 @@
+// 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.google.common.collect.ImmutableList;
+import com.google.common.io.Closer;
+
+import com.android.tools.r8.dex.DexFileReader;
+import com.android.tools.r8.dex.Segment;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.OutputMode;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+
+public class DexSegments {
+ private static class Command extends BaseCommand {
+
+ public static class Builder
+ extends BaseCommand.Builder<Command, Builder> {
+
+ private Builder() {
+ super(CompilationMode.RELEASE);
+ }
+
+ @Override
+ Command.Builder self() {
+ return this;
+ }
+
+ @Override
+ public Command build() throws CompilationException, IOException {
+ // If printing versions ignore everything else.
+ if (isPrintHelp()) {
+ return new Command(isPrintHelp());
+ }
+ return new Command(
+ getAppBuilder().build(),
+ getOutputPath(),
+ getOutputMode(),
+ getMode(),
+ getMinApiLevel());
+ }
+ }
+
+ 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)
+ throws CompilationException, IOException {
+ Command.Builder builder = builder();
+ parse(args, builder);
+ return builder;
+ }
+
+ private static void parse(String[] args, Command.Builder builder)
+ throws CompilationException, IOException {
+ 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("--")) {
+ throw new CompilationException("Unknown option: " + arg);
+ }
+ builder.addProgramFiles(Paths.get(arg));
+ }
+ }
+ }
+
+ private Command(
+ AndroidApp inputApp,
+ Path outputPath,
+ OutputMode outputMode,
+ CompilationMode mode,
+ int minApiLevel) {
+ super(inputApp, outputPath, outputMode, mode, minApiLevel);
+ }
+
+ private Command(boolean printHelp) {
+ super(printHelp, false);
+ }
+
+ @Override
+ InternalOptions getInternalOptions() {
+ return new InternalOptions();
+ }
+ }
+
+ public static void main(String[] args)
+ throws IOException, ProguardRuleParserException, CompilationException, ExecutionException {
+ 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, Integer> result = new HashMap<>();
+ try (Closer closer = Closer.create()) {
+ for (Resource resource : app.getDexProgramResources()) {
+ for (Segment segment: DexFileReader.parseMapFrom(resource.getStream(closer))) {
+ int value = result.computeIfAbsent(segment.typeName(), (key) -> 0);
+ result.put(segment.typeName(), value + segment.getSize());
+ }
+ }
+ }
+ System.out.println("Segments in dex application (name: size):");
+ result.forEach( (key, value) -> System.out.println(" - " + key + ": " + value));
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/dex/DexFile.java b/src/main/java/com/android/tools/r8/dex/DexFile.java
index 28aff79..a2c65d3 100644
--- a/src/main/java/com/android/tools/r8/dex/DexFile.java
+++ b/src/main/java/com/android/tools/r8/dex/DexFile.java
@@ -29,7 +29,7 @@
version = parseMagic(buffer);
}
- DexFile(InputStream input) throws IOException {
+ public DexFile(InputStream input) throws IOException {
// TODO(zerny): Remove dependencies on file names.
name = "input-stream.dex";
buffer = ByteBuffer.wrap(ByteStreams.toByteArray(input));
diff --git a/src/main/java/com/android/tools/r8/dex/DexFileReader.java b/src/main/java/com/android/tools/r8/dex/DexFileReader.java
index 6aacd55..3342be6 100644
--- a/src/main/java/com/android/tools/r8/dex/DexFileReader.java
+++ b/src/main/java/com/android/tools/r8/dex/DexFileReader.java
@@ -54,6 +54,7 @@
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.nio.ShortBuffer;
import java.nio.file.Path;
import java.util.ArrayList;
@@ -72,10 +73,16 @@
private final ClassKind classKind;
public static Segment[] parseMapFrom(Path file) throws IOException {
- DexFileReader reader =
- new DexFileReader(
- new DexFile(file.toString()), ClassKind.PROGRAM, new DexItemFactory());
- return reader.parseMap();
+ return parseMapFrom(new DexFile(file.toString()));
+ }
+
+ public static Segment[] parseMapFrom(InputStream stream) throws IOException {
+ return parseMapFrom(new DexFile(stream));
+ }
+
+ private static Segment[] parseMapFrom(DexFile dex) throws IOException {
+ DexFileReader reader = new DexFileReader(dex, ClassKind.PROGRAM, new DexItemFactory());
+ return reader.segments;
}
public void close() {
diff --git a/src/main/java/com/android/tools/r8/dex/Segment.java b/src/main/java/com/android/tools/r8/dex/Segment.java
index 32b0c0b..1d891e0 100644
--- a/src/main/java/com/android/tools/r8/dex/Segment.java
+++ b/src/main/java/com/android/tools/r8/dex/Segment.java
@@ -30,7 +30,7 @@
this.end = end;
}
- String typeName() {
+ public String typeName() {
switch (type) {
case Constants.TYPE_HEADER_ITEM:
return "Header";
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index f57c022..1f21f52 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -236,7 +236,7 @@
// The declared target cannot be found so skip inlining.
continue;
}
- boolean forceInline = target.getOptimizationInfo().forceInline();
+ boolean forceInline = result.reason == Reason.FORCE;
if (!target.isProcessed() && !forceInline) {
// Do not inline code that was not processed unless we have to force inline.
continue;