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;