Extend framework_test with cf_segments

This will allow us to benchmark cf_segments.

Change-Id: Id1956301e9feacbb5de10adbcd16052ab0971301
diff --git a/src/cf_segments/java/com/android/tools/r8/cf_segments/Metrics.java b/src/cf_segments/java/com/android/tools/r8/cf_segments/Metrics.java
index 1336c65..57786e1 100644
--- a/src/cf_segments/java/com/android/tools/r8/cf_segments/Metrics.java
+++ b/src/cf_segments/java/com/android/tools/r8/cf_segments/Metrics.java
@@ -65,8 +65,8 @@
   public final SegmentInfo methodInfo = new SegmentInfo("Method");
   public final SegmentInfo size = new SegmentInfo("Size").increment(1, 0);
   public final SegmentInfo stores = new SegmentInfo("Stores", false);
-  public final SegmentInfo stackMapTable = new SegmentInfo("StackmapTable");
-  public final SegmentInfo stackmapTableOtherEntries = new SegmentInfo("StackmapTableOtherEntries");
+  public final SegmentInfo stackMapTable = new SegmentInfo("StackMapTable");
+  public final SegmentInfo stackmapTableOtherEntries = new SegmentInfo("StackMapTableOtherEntries");
   public final SegmentInfo stackMapTableFullFrameEntries = new SegmentInfo("StackMapFullFrame");
 
   public List<SegmentInfo> asList() {
@@ -106,7 +106,7 @@
   @Override
   public String toString() {
     StringBuilder builder = new StringBuilder();
-    asList().forEach(s -> builder.append(s.name + "\t" + s.size + "\t" + s.items + "\n"));
+    asList().forEach(s -> builder.append("- " + s.name + ": " + s.size + " / " + s.items + "\n"));
     return builder.toString();
   }
 }
diff --git a/tools/golem_build.py b/tools/golem_build.py
index 30d46bc..5448589 100755
--- a/tools/golem_build.py
+++ b/tools/golem_build.py
@@ -10,7 +10,7 @@
 
 GRADLE_ARGS = ['--no-daemon']
 BUILD_TARGETS = ['R8', 'D8', 'R8LibApiOnly', 'buildExampleJars', 'CompatDx',
-                 'downloadAndroidCts', 'downloadDx']
+                 'downloadAndroidCts', 'downloadDx', 'buildCfSegments']
 
 def Main():
   gradle.RunGradle(GRADLE_ARGS + BUILD_TARGETS)
diff --git a/tools/run_bootstrap_benchmark.py b/tools/run_bootstrap_benchmark.py
index 9f2f7e4..90834f8 100755
--- a/tools/run_bootstrap_benchmark.py
+++ b/tools/run_bootstrap_benchmark.py
@@ -12,8 +12,6 @@
 import toolhelper
 import utils
 
-PINNED_R8_JAR = os.path.join(utils.REPO_ROOT, 'third_party/r8/r8.jar')
-PINNED_PGR8_JAR = os.path.join(utils.REPO_ROOT, 'third_party/r8/r8-pg6.0.1.jar')
 
 def parse_arguments(argv):
   parser = argparse.ArgumentParser(
@@ -50,7 +48,7 @@
     d8_pg_output = os.path.join(temp, 'd8pg.zip')
 
     return_code = minify_tool.minify_tool(
-      input_jar=PINNED_R8_JAR,
+      input_jar=utils.PINNED_R8_JAR,
       output_jar=r8_output,
       debug=False,
       build=False,
@@ -63,8 +61,9 @@
     print "BootstrapR8(CodeSize):", utils.uncompressed_size(r8_output)
     print "BootstrapR8Dex(CodeSize):", utils.uncompressed_size(d8_r8_output)
 
-    dex(PINNED_PGR8_JAR, d8_pg_output)
-    print "BootstrapR8PG(CodeSize):", utils.uncompressed_size(PINNED_PGR8_JAR)
+    dex(utils.PINNED_PGR8_JAR, d8_pg_output)
+    print "BootstrapR8PG(CodeSize):", utils.uncompressed_size(
+        utils.PINNED_PGR8_JAR)
     print "BootstrapR8PGDex(CodeSize):", utils.uncompressed_size(d8_pg_output)
 
   sys.exit(0)
diff --git a/tools/test_framework.py b/tools/test_framework.py
index 6bc2f90..7400541 100755
--- a/tools/test_framework.py
+++ b/tools/test_framework.py
@@ -54,13 +54,13 @@
           ' <NAME>-<segment>(CodeSize): <bytes>), the full size is reported'
           ' with <NAME>-Total(CodeSize)')
   parser.add_argument('--print-memoryuse',
-      help = 'Prints the line \'<NAME>-Total(MemoryUse):' +
-             ' <mem>\' at the end where <mem> is the peak' +
-             ' peak resident set size (VmHWM) in bytes.',
+      help = 'Prints the line \'<NAME>-Total(MemoryUse):' 
+          ' <mem>\' at the end where <mem> is the peak'
+          ' peak resident set size (VmHWM) in bytes.',
       default = False,
       action = 'store_true')
   parser.add_argument('--output',
-                      help = 'Output directory to keep the generated files')
+      help = 'Output directory to keep the generated files')
   return parser.parse_args()
 
 def Main():
diff --git a/tools/test_r8cfsegments.py b/tools/test_r8cfsegments.py
new file mode 100755
index 0000000..d160b03
--- /dev/null
+++ b/tools/test_r8cfsegments.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python
+# Copyright (c) 2019, 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.
+
+# Run R8 or PG on 'third_party/r8/r8.jar'.
+# Report Golem-compatible CodeSize and RunTimeRaw values:
+#
+#     <NAME>-Total(CodeSize): <size>
+#     <NAME>-Total(RunTimeRaw>: <time> ms
+#
+# and also detailed segment sizes for each classfile segment:
+#
+#    <NAME>-Code(CodeSize): <size>
+#    <NAME>-AnnotationSets(CodeSize): <size>
+#    ...
+#
+# Uses the R8CfSegments Java tool (Gradle target).
+
+from __future__ import print_function
+import argparse
+import golem
+import minify_tool
+import os
+import sys
+import utils
+
+def parse_arguments():
+  parser = argparse.ArgumentParser(
+      description = 'Run R8 or PG on'
+          ' third_party/r8/r8.jar.'
+          ' Report Golem-compatible CodeSize and RunTimeRaw values.')
+  parser.add_argument('--tool',
+      choices = ['pg', 'r8'],
+      required = True,
+      help = 'Compiler tool to use.')
+  parser.add_argument('--golem',
+      help = 'Running on golem, link in third_party resources.',
+      default = False,
+      action = 'store_true')
+  parser.add_argument('--name',
+      required = True,
+      help = 'Results will be printed using the specified benchmark name (e.g.'
+          ' <NAME>-<segment>(CodeSize): <bytes>), the full size is reported'
+          ' with <NAME>-Total(CodeSize)')
+  parser.add_argument('--print-memoryuse',
+      help = 'Prints the line \'<NAME>-Total(MemoryUse):'
+             ' <mem>\' at the end where <mem> is the peak'
+             ' peak resident set size (VmHWM) in bytes.',
+      default = False,
+      action = 'store_true')
+  parser.add_argument('--output',
+                      help = 'Output directory to keep the generated files')
+  return parser.parse_args()
+
+
+def Main():
+  utils.check_java_version()
+  args = parse_arguments()
+  output_dir = args.output
+  if args.golem:
+    golem.link_third_party()
+  with utils.TempDir() as temp_dir:
+    if not output_dir:
+      output_dir = temp_dir
+    track_memory_file = None
+    if args.print_memoryuse:
+      track_memory_file = os.path.join(output_dir, utils.MEMORY_USE_TMP_FILE)
+    if args.tool == 'pg':
+      utils.print_cfsegments(args.name, [utils.PINNED_PGR8_JAR])
+    else:
+      out_file = os.path.join(output_dir, 'out.jar')
+      return_code = minify_tool.minify_tool(
+          input_jar=utils.PINNED_R8_JAR,
+          output_jar=out_file,
+          debug=False,
+          build=False,
+          track_memory_file=track_memory_file,
+          benchmark_name=args.name + "-Total")
+      if return_code != 0:
+        sys.exit(return_code)
+
+      utils.print_cfsegments(args.name, [out_file])
+
+
+if __name__ == '__main__':
+  sys.exit(Main())
diff --git a/tools/utils.py b/tools/utils.py
index ad85089..0f7aa96 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -53,7 +53,16 @@
 GENERATED_LICENSE = os.path.join(GENERATED_LICENSE_DIR, 'LICENSE')
 RT_JAR = os.path.join(REPO_ROOT, 'third_party/openjdk/openjdk-rt-1.8/rt.jar')
 R8LIB_KEEP_RULES = os.path.join(REPO_ROOT, 'src/main/keep.txt')
-RETRACE_JAR = os.path.join(THIRD_PARTY, 'proguard', 'proguard6.0.1', 'lib', 'retrace.jar')
+RETRACE_JAR = os.path.join(
+    THIRD_PARTY,
+    'proguard',
+    'proguard6.0.1',
+    'lib',
+    'retrace.jar')
+CF_SEGMENTS_JAR = os.path.join(LIBS, 'cf_segments.jar')
+PINNED_R8_JAR = os.path.join(REPO_ROOT, 'third_party/r8/r8.jar')
+PINNED_PGR8_JAR = os.path.join(REPO_ROOT, 'third_party/r8/r8-pg6.0.1.jar')
+
 
 # Common environment setup.
 USER_HOME = os.path.expanduser('~')
@@ -372,9 +381,39 @@
 
   return result
 
+# Return a dictionary: {segment_name -> segments_size}
+def getCfSegmentSizes(cfFile):
+  cmd = ['java',
+         '-cp',
+         CF_SEGMENTS_JAR,
+         'com.android.tools.r8.cf_segments.MeasureLib',
+         cfFile]
+  PrintCmd(cmd)
+  output = subprocess.check_output(cmd)
+
+  matches = DEX_SEGMENTS_RESULT_PATTERN.findall(output)
+
+  if matches is None or len(matches) == 0:
+    raise Exception('CfSegments failed to return any output for' \
+                    ' the file: ' + cfFile)
+
+  result = {}
+
+  for match in matches:
+    result[match[0]] = int(match[1])
+
+  return result
+
+
 def get_maven_path(version):
   return os.path.join('com', 'android', 'tools', 'r8', version)
 
+def print_cfsegments(prefix, cf_files):
+  for cf_file in cf_files:
+    for segment_name, size in getCfSegmentSizes(cf_file).items():
+      print('{}-{}(CodeSize): {}'
+            .format(prefix, segment_name, size))
+
 def print_dexsegments(prefix, dex_files):
   for segment_name, size in getDexSegmentSizes(dex_files).items():
     print('{}-{}(CodeSize): {}'