Add memory usage tracking to test scripts.

Bug:
Change-Id: Ifb0742fb47f4031c973ad39ec09627270bc357ea
diff --git a/tools/proguard.py b/tools/proguard.py
index 58c049f..efcf560 100755
--- a/tools/proguard.py
+++ b/tools/proguard.py
@@ -15,10 +15,13 @@
 PROGUARD_JAR = os.path.join(utils.REPO_ROOT, 'third_party', 'proguard',
     'proguard_internal_159423826', 'ProGuard_deploy.jar')
 
-def run(args):
-  cmd = ['java', '-jar', PROGUARD_JAR]
+def run(args, track_memory_file = None):
+  cmd = []
+  if track_memory_file:
+    cmd.extend(['tools/track_memory.sh', track_memory_file])
+  cmd.extend(['java', '-jar', PROGUARD_JAR])
   cmd.extend(args)
-  print('-- ' + ' '.join(cmd))
+  utils.PrintCmd(cmd)
   subprocess.check_call(cmd)
 
 def Main():
diff --git a/tools/run_on_app.py b/tools/run_on_app.py
index f3cc9b5..d7db7fb 100755
--- a/tools/run_on_app.py
+++ b/tools/run_on_app.py
@@ -3,6 +3,7 @@
 # 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.
 
+from __future__ import print_function
 import optparse
 import os
 import sys
@@ -55,8 +56,11 @@
                          'If passing several options use a quoted string.')
   result.add_option('--r8-flags',
                     help='Additional option(s) for the compiler. ' +
-                         'Same as --compiler-flags, keeping it for backward compatibility. ' +
+                         'Same as --compiler-flags, keeping it for backward'
+                         ' compatibility. ' +
                          'If passing several options use a quoted string.')
+  # TODO(tamaskenez) remove track-memory-to-file as soon as we updated golem
+  # to use --print-memoryuse instead
   result.add_option('--track-memory-to-file',
                     help='Track how much memory the jvm is using while ' +
                     ' compiling. Output to the specified file.')
@@ -73,6 +77,11 @@
                     help='Prints the line \'<BENCHMARKNAME>(RunTimeRaw):' +
                          ' <elapsed> ms\' at the end where <elapsed> is' +
                          ' the elapsed time in milliseconds.')
+  result.add_option('--print-memoryuse',
+                    metavar='BENCHMARKNAME',
+                    help='Prints the line \'<BENCHMARKNAME>(MemoryUse):' +
+                         ' <mem>\' at the end where <mem> is the peak' +
+                         ' peak resident set size (VmHWM) in bytes.')
   return result.parse_args()
 
 # Most apps have the -printmapping and -printseeds in the Proguard
@@ -81,10 +90,10 @@
 # placing these two output files together with the dex output.
 def GenerateAdditionalProguardConfiguration(temp, outdir):
   name = "output.config"
-  with open(os.path.join(temp, name), 'w') as file:
-    file.write('-printmapping ' + os.path.join(outdir, 'proguard.map') + "\n")
-    file.write('-printseeds ' + os.path.join(outdir, 'proguard.seeds') + "\n")
-    return os.path.abspath(file.name)
+  with open(os.path.join(temp, name), 'w') as f:
+    f.write('-printmapping ' + os.path.join(outdir, 'proguard.map') + "\n")
+    f.write('-printseeds ' + os.path.join(outdir, 'proguard.seeds') + "\n")
+    return os.path.abspath(f.name)
 
 def main():
   app_provided_pg_conf = False;
@@ -104,8 +113,9 @@
     raise 'Unexpected'
 
   if not options.version in data.VERSIONS.keys():
-    print 'No version %s for application %s' % (options.version, options.app)
-    print 'Valid versions are %s' % data.VERSIONS.keys()
+    print('No version {} for application {}'
+        .format(options.version, options.app))
+    print('Valid versions are {}'.format(data.VERSIONS.keys()))
     return 1
 
   version = data.VERSIONS[options.version]
@@ -115,13 +125,15 @@
         else 'proguarded'
 
   if options.type not in version:
-    print 'No type %s for version %s' % (options.type, options.version)
-    print 'Valid types are %s' % version.keys()
+    print('No type {} for version {}'.format(options.type, options.version))
+    print('Valid types are {}'.format(version.keys()))
     return 1
   values = version[options.type]
   inputs = None
-  # For R8 'deploy' the JAR is located using the Proguard configuration -injars option.
-  if 'inputs' in values and (options.compiler != 'r8' or options.type != 'deploy'):
+  # For R8 'deploy' the JAR is located using the Proguard configuration
+  # -injars option.
+  if 'inputs' in values and (options.compiler != 'r8'
+      or options.type != 'deploy'):
     inputs = values['inputs']
 
   args.extend(['--output', outdir])
@@ -145,7 +157,8 @@
     for lib in values['libraries']:
       args.extend(['--lib', lib])
 
-  if not outdir.endswith('.zip') and not outdir.endswith('.jar') and not os.path.exists(outdir):
+  if not outdir.endswith('.zip') and not outdir.endswith('.jar') \
+      and not os.path.exists(outdir):
     os.makedirs(outdir)
 
   if options.compiler == 'r8':
@@ -161,16 +174,18 @@
     args.extend(inputs)
 
   t0 = time.time()
-
   if options.dump_args_file:
     with open(options.dump_args_file, 'w') as args_file:
       args_file.writelines([arg + os.linesep for arg in args])
   else:
-    if options.compiler == 'd8':
-      d8.run(args, not options.no_build, not options.no_debug, options.profile,
-             options.track_memory_to_file)
-    else:
-      with utils.TempDir() as temp:
+    with utils.TempDir() as temp:
+      if options.print_memoryuse and not options.track_memory_to_file:
+        options.track_memory_to_file = os.path.join(temp,
+            utils.MEMORY_USE_TMP_FILE)
+      if options.compiler == 'd8':
+        d8.run(args, not options.no_build, not options.no_debug,
+            options.profile, options.track_memory_to_file)
+      else:
         if app_provided_pg_conf:
           # Ensure that output of -printmapping and -printseeds go to the output
           # location and not where the app Proguard configuration places them.
@@ -181,8 +196,12 @@
           additional_pg_conf = GenerateAdditionalProguardConfiguration(
               temp, os.path.abspath(pg_outdir))
           args.extend(['--pg-conf', additional_pg_conf])
-        r8.run(args, not options.no_build, not options.no_debug, options.profile,
-               options.track_memory_to_file)
+        r8.run(args, not options.no_build, not options.no_debug,
+            options.profile, options.track_memory_to_file)
+      if options.print_memoryuse:
+        print('{}(MemoryUse): {}'
+            .format(options.print_memoryuse,
+                utils.grep_memoryuse(options.track_memory_to_file)))
 
   if options.print_runtimeraw:
     print('{}(RunTimeRaw): {} ms'
diff --git a/tools/run_proguard_dx_on_gmscore.py b/tools/run_proguard_dx_on_gmscore.py
index ca0b5e9..de716b0 100755
--- a/tools/run_proguard_dx_on_gmscore.py
+++ b/tools/run_proguard_dx_on_gmscore.py
@@ -40,6 +40,11 @@
       help = 'Prints the line \'<BENCHMARKNAME>(RunTimeRaw): <elapsed>' +
              ' ms\' at the end where <elapsed> is the elapsed time in' +
              ' milliseconds.')
+  parser.add_argument('--print-memoryuse',
+      metavar='BENCHMARKNAME',
+      help='Prints the line \'<BENCHMARKNAME>(MemoryUse):' +
+           ' <mem>\' at the end where <mem> is the peak' +
+           ' peak resident set size (VmHWM) in bytes.')
   parser.add_argument('--compatdx',
       help = 'Use CompatDx (D8) instead of DX.',
       default = False,
@@ -72,7 +77,15 @@
 
   t0 = time.time()
 
-  proguard.run(args)
+  proguard_memoryuse = None
+
+  with utils.TempDir() as temp:
+    track_memory_file = None
+    if options.print_memoryuse:
+      track_memory_file = join(temp, utils.MEMORY_USE_TMP_FILE)
+    proguard.run(args, track_memory_file = track_memory_file)
+    if options.print_memoryuse:
+      proguard_memoryuse = utils.grep_memoryuse(track_memory_file)
 
   # run dex on the result
   if options.compatdx:
@@ -80,10 +93,21 @@
   else:
     jar = DX_JAR
 
-  cmd = ['java', '-jar', jar, '--min-sdk-version=26', '--multi-dex',
-      '--output=' + outdir, '--dex', PROGUARDED_OUTPUT];
-  utils.PrintCmd(cmd);
-  check_call(cmd)
+  with utils.TempDir() as temp:
+    track_memory_file = None
+    cmd = []
+    if options.print_memoryuse:
+      track_memory_file = join(temp, utils.MEMORY_USE_TMP_FILE)
+      cmd.extend(['tools/track_memory.sh', track_memory_file])
+    cmd.extend(['java', '-jar', jar, '--min-sdk-version=26', '--multi-dex',
+        '--output=' + outdir, '--dex', PROGUARDED_OUTPUT]);
+    utils.PrintCmd(cmd);
+    check_call(cmd)
+    if options.print_memoryuse:
+      dx_memoryuse = utils.grep_memoryuse(track_memory_file)
+      print('{}(MemoryUse): {}'
+          .format(options.print_memoryuse,
+              max(proguard_memoryuse, dx_memoryuse)))
 
   if options.print_runtimeraw:
     print('{}(RunTimeRaw): {} ms'
diff --git a/tools/test_framework.py b/tools/test_framework.py
index 810935d..57279bf 100755
--- a/tools/test_framework.py
+++ b/tools/test_framework.py
@@ -53,6 +53,12 @@
       required = True,
       help = 'Results will be printed using the specified benchmark name (e.g.'
           ' <NAME>-<segment>(CodeSize): <bytes>)')
+  parser.add_argument('--print-memoryuse',
+      help = 'Prints the line \'<NAME>(MemoryUse):' +
+             ' <mem>\' at the end where <mem> is the peak' +
+             ' peak resident set size (VmHWM) in bytes.',
+      default = False,
+      action = 'store_true')
   return parser.parse_args()
 
 # Return a dictionary: {segment_name -> segments_size}
@@ -96,10 +102,16 @@
       if args.tool == 'd8-release':
         tool_args.append('--release')
 
+
+    cmd = []
+
+    track_memory_file = None
+    if args.print_memoryuse:
+      track_memory_file = os.path.join(temp_dir, utils.MEMORY_USE_TMP_FILE)
+      cmd.extend(['tools/track_memory.sh', track_memory_file])
+
     if tool_file.endswith('.jar'):
-      cmd = ['java', '-jar']
-    else:
-      cmd = []
+      cmd.extend(['java', '-jar'])
 
     cmd.extend([tool_file] + tool_args + [FRAMEWORK_JAR])
 
@@ -109,6 +121,10 @@
     subprocess.check_call(cmd)
     dt = time.time() - t0
 
+    if args.print_memoryuse:
+      print('{}(MemoryUse): {}'
+          .format(args.name, utils.grep_memoryuse(track_memory_file)))
+
     dex_files = [f for f in glob(os.path.join(temp_dir, '*.dex'))]
     code_size = 0
     for dex_file in dex_files:
diff --git a/tools/utils.py b/tools/utils.py
index fd7212b..2564adc 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -14,6 +14,7 @@
 
 TOOLS_DIR = os.path.abspath(os.path.normpath(os.path.join(__file__, '..')))
 REPO_ROOT = os.path.realpath(os.path.join(TOOLS_DIR, '..'))
+MEMORY_USE_TMP_FILE = 'memory_use.tmp'
 
 def PrintCmd(s):
   if type(s) is list:
@@ -118,3 +119,25 @@
         outcome = m.groups()[0]
         assert outcome in ['fail', 'pass']
         yield CtsTest(m.groups()[1], outcome == 'pass')
+
+def grep_memoryuse(logfile):
+  re_vmhwm = re.compile('^VmHWM:[ \t]*([0-9]+)[ \t]*([a-zA-Z]*)')
+  result = None
+  with open(logfile) as f:
+    for line in f:
+      m = re_vmhwm.search(line)
+      if m:
+        groups = m.groups()
+        s = len(groups)
+        if s >= 1:
+          result = int(groups[0])
+          if s >= 2:
+            unit = groups[1]
+            if unit == 'kB':
+              result *= 1024
+            elif unit != '':
+              raise Exception('Unrecognized unit in memory usage log: {}'
+                  .format(unit))
+  if result is None:
+    raise Exception('No memory usage found in log: {}'.format(logfile))
+  return result
\ No newline at end of file