[go: nahoru, domu]

android: Support lightweight instrumentation in orderfile_generator_backend.py.

This adds a --lightweight-instrumentation flag to orderfile_generator.py
to use the new pipeline. The new step is using process_profiles.py to
output a list of reached offsets symbols, matching the output format
from the current instrumentation.

This allows the rest of the pipeline to remain unchanged.

Bug: 776702
Change-Id: Iddf8d42112f1d44a7ef36d26328f51d0b3b894cb
Reviewed-on: https://chromium-review.googlesource.com/746665
Commit-Queue: Benoit L <lizeb@chromium.org>
Reviewed-by: Egor Pasko <pasko@chromium.org>
Reviewed-by: Matthew Cary <mattcary@chromium.org>
Cr-Commit-Position: refs/heads/master@{#514470}
diff --git a/tools/cygprofile/orderfile_generator_backend.py b/tools/cygprofile/orderfile_generator_backend.py
index 37cb1da1..23ddbdc 100755
--- a/tools/cygprofile/orderfile_generator_backend.py
+++ b/tools/cygprofile/orderfile_generator_backend.py
@@ -23,9 +23,11 @@
 import shutil
 import subprocess
 import sys
+import tempfile
 import time
 
 import cygprofile_utils
+import process_profiles
 import profile_android_startup
 
 
@@ -137,6 +139,31 @@
   shutil.move(stashpath, buildpath)
 
 
+def _EnsureOrderfileStartsWithAnchorSection(filename):
+  """Ensures that the orderfile starts with the right anchor symbol.
+
+  This changes the orderfile, if required.
+
+  Args:
+    filename: (str) Path to the orderfile.
+  """
+  anchor_section = '.text.dummy_function_to_anchor_text'
+  with open(filename, 'r') as f:
+    if f.readline().strip() == anchor_section:
+      return
+  try:
+    f = tempfile.NamedTemporaryFile(dir=os.path.dirname(filename), delete=False)
+    f.write(anchor_section + '\n')
+    with open(filename, 'r') as orderfile_file:
+      for line in orderfile_file:
+        f.write(line + '\n')
+    f.close()
+    os.rename(f.name, filename)
+  finally:
+    if os.path.exists(f.name):
+      os.remove(f.name)
+
+
 class StepRecorder(object):
   """Records steps and timings."""
 
@@ -221,15 +248,15 @@
   """Handles compilation of clank."""
 
   def __init__(self, out_dir, step_recorder, arch, jobs, max_load, use_goma,
-               goma_dir):
-    self._arch = arch
-    self._goma_dir = goma_dir
-    self._jobs = jobs
-    self._max_load = max_load
+               goma_dir, lightweight_instrumentation):
     self._out_dir = out_dir
     self._step_recorder = step_recorder
+    self._arch = arch
+    self._jobs = jobs
+    self._max_load = max_load
     self._use_goma = use_goma
-
+    self._goma_dir = goma_dir
+    self._lightweight_instrumentation = lightweight_instrumentation
     lib_chrome_so_dir = 'lib.unstripped'
     self.lib_chrome_so = os.path.join(
         self._out_dir, 'Release', lib_chrome_so_dir, 'libchrome.so')
@@ -240,8 +267,8 @@
     """Builds the provided ninja target with or without order_profiling on.
 
     Args:
-      instrumented: Whether we want to build an instrumented binary.
-      target: The name of the ninja target to build.
+      instrumented: (bool) Whether we want to build an instrumented binary.
+      target: (str) The name of the ninja target to build.
     """
     self._step_recorder.BeginStep('Compile %s' % target)
 
@@ -259,8 +286,11 @@
         'use_goma=' + str(self._use_goma).lower(),
         'use_order_profiling=' + str(instrumented).lower(),
     ]
+    if instrumented and self._lightweight_instrumentation:
+      args.append('use_lightweight_order_profiling=true')
     if self._goma_dir:
       args += ['goma_dir="%s"' % self._goma_dir]
+
     self._step_recorder.RunCommand(
         ['gn', 'gen', os.path.join(self._out_dir, 'Release'),
          '--args=' + ' '.join(args)])
@@ -273,7 +303,7 @@
     """Builds a Chrome.apk either with or without order_profiling on.
 
     Args:
-      instrumented: Whether we want to build an instrumented apk.
+      instrumented: (bool) Whether to build an instrumented apk.
       force_relink: Whether libchromeview.so should be re-created.
     """
     if force_relink:
@@ -284,8 +314,8 @@
     """Builds a libchrome.so either with or without order_profiling on.
 
     Args:
-      instrumented: Whether we want to build an instrumented apk.
-      force_relink: Whether libchrome.so should be re-created.
+      instrumented: (bool) Whether to build an instrumented apk.
+      force_relink: (bool) Whether libchrome.so should be re-created.
     """
     if force_relink:
       self._step_recorder.RunCommand(['rm', '-rf', self.lib_chrome_so])
@@ -445,10 +475,15 @@
 
   def _RunCygprofileUnitTests(self):
     """Builds, deploys and runs cygprofile_unittests."""
+    # There an no unittests (yet) for the lightweight instrumentation.
+    # TODO(lizeb): Fix this.
+    if self._options.lightweight_instrumentation:
+      return
     tools_compiler = ClankCompiler(
         os.path.dirname(constants.GetOutDirectory()),
         self._step_recorder, self._options.arch, self._options.jobs,
-        self._options.max_load, self._options.use_goma, self._options.goma_dir)
+        self._options.max_load, self._options.use_goma, self._options.goma_dir,
+        self._options.lightweight_instrumentation)
     tools_compiler.Build(instrumented=False, target='android_tools')
     self._compiler.Build(instrumented=True, target='cygprofile_unittests')
 
@@ -489,10 +524,15 @@
           self._compiler.chrome_apk,
           constants.PACKAGE_INFO['chrome'])
       self._step_recorder.BeginStep('Process cyglog')
-      with open(self._MERGED_CYGLOG_FILENAME, 'w') as merged_cyglog:
-        self._step_recorder.RunCommand([self._MERGE_TRACES_SCRIPT] + files,
-                                       constants.DIR_SOURCE_ROOT,
-                                       stdout=merged_cyglog)
+      if self._options.lightweight_instrumentation:
+        assert os.path.exists(self._compiler.lib_chrome_so)
+        process_profiles.GetReachedSymbolsFromDumpsAndMaybeWriteOffsets(
+            files, self._compiler.lib_chrome_so, self._MERGED_CYGLOG_FILENAME)
+      else:
+        with open(self._MERGED_CYGLOG_FILENAME, 'w') as merged_cyglog:
+          self._step_recorder.RunCommand([self._MERGE_TRACES_SCRIPT] + files,
+                                         constants.DIR_SOURCE_ROOT,
+                                         stdout=merged_cyglog)
     except CommandError:
       for f in files:
         self._SaveForDebugging(f)
@@ -502,12 +542,16 @@
       logging.getLogger().setLevel(logging.INFO)
 
     try:
-      self._step_recorder.RunCommand([self._CYGLOG_TO_ORDERFILE_SCRIPT,
-                                      self._MERGED_CYGLOG_FILENAME,
-                                      self._compiler.lib_chrome_so,
-                                      self._GetUnpatchedOrderfileFilename(),
-                                      '--target-arch=' + self._options.arch],
-                                     constants.DIR_SOURCE_ROOT)
+      command_args = [
+          '--target-arch=' + self._options.arch,
+          '--native-library=' + self._compiler.lib_chrome_so,
+          '--output=' + self._GetUnpatchedOrderfileFilename()]
+      if self._options.lightweight_instrumentation:
+        command_args.append('--reached-offsets=' + self._MERGED_CYGLOG_FILENAME)
+      else:
+        command_args.append('--merged-cyglog=' + self._MERGED_CYGLOG_FILENAME)
+      self._step_recorder.RunCommand(
+          [self._CYGLOG_TO_ORDERFILE_SCRIPT] + command_args)
     except CommandError:
       self._SaveForDebugging(self._MERGED_CYGLOG_FILENAME)
       self._SaveForDebuggingWithOverwrite(self._compiler.lib_chrome_so)
@@ -642,9 +686,13 @@
             self._instrumented_out_dir,
             self._step_recorder, self._options.arch, self._options.jobs,
             self._options.max_load, self._options.use_goma,
-            self._options.goma_dir)
+            self._options.goma_dir,
+            self._options.lightweight_instrumentation)
         self._RunCygprofileUnitTests()
-        self._compiler.CompileChromeApk(True)
+        if self._options.lightweight_instrumentation:
+          _EnsureOrderfileStartsWithAnchorSection(self._GetPathToOrderfile())
+        self._compiler.CompileChromeApk(
+            True, self._options.lightweight_instrumentation)
         self._GenerateAndProcessProfile()
         self._MaybeArchiveOrderfile(self._GetUnpatchedOrderfileFilename())
         profile_uploaded = True
@@ -660,7 +708,8 @@
         self._compiler = ClankCompiler(
             self._uninstrumented_out_dir, self._step_recorder,
             self._options.arch, self._options.jobs, self._options.max_load,
-            self._options.use_goma, self._options.goma_dir)
+            self._options.use_goma, self._options.goma_dir,
+            self._options.lightweight_instrumentation)
         self._compiler.CompileLibchrome(False)
         self._PatchOrderfile()
         # Because identical code folding is a bit different with and without
@@ -696,6 +745,9 @@
 def CreateOptionParser():
   parser = optparse.OptionParser()
   parser.add_option(
+      '--lightweight-instrumentation', action='store_true', default=False,
+      help='Use the lightweight instrumentation path')
+  parser.add_option(
       '--buildbot', action='store_true',
       help='If true, the script expects to be run on a buildbot')
   parser.add_option(