Merge "Enable Systrace to be used during boot."
diff --git a/agents/atrace_agent.py b/agents/atrace_agent.py
index 5ac96d9..c8288c8 100644
--- a/agents/atrace_agent.py
+++ b/agents/atrace_agent.py
@@ -30,6 +30,10 @@
 TRACE_START_REGEXP = r'TRACE\:'
 # Plain-text trace data should always start with this string.
 TRACE_TEXT_HEADER = '# tracer'
+# The property name for switching on and off tracing during boot.
+BOOTTRACE_PROP = 'persist.debug.atrace.boottrace'
+# The file path for specifying categories to be traced during boot.
+BOOTTRACE_CATEGORIES = '/data/misc/boottrace/categories'
 
 # This list is based on the tags in frameworks/native/include/utils/Trace.h for
 # legacy platform.
@@ -53,7 +57,17 @@
 
   device_sdk_version = util.get_device_sdk_version()
   if device_sdk_version >= 18:
-    return AtraceAgent(options, categories)
+    if options.boot:
+      # atrace --async_stop, which is used by BootAgent, does not work properly
+      # on the device SDK version 22 or before.
+      if device_sdk_version <= 22:
+        print >> sys.stderr, ('--boot option does not work on the device SDK '
+                              'version 22 or before.\nYour device SDK version '
+                              'is %d.' % device_sdk_version)
+        sys.exit(1)
+      return BootAgent(options, categories)
+    else:
+      return AtraceAgent(options, categories)
   elif device_sdk_version >= 16:
     return AtraceLegacyAgent(options, categories)
 
@@ -350,6 +364,50 @@
     return extra_args
 
 
+class BootAgent(AtraceAgent):
+  """AtraceAgent that specializes in tracing the boot sequence."""
+
+  def __init__(self, options, categories):
+    super(BootAgent, self).__init__(options, categories)
+
+  def start(self):
+    try:
+      setup_args = self._construct_setup_command()
+      try:
+        subprocess.check_call(setup_args)
+        print 'Hit Ctrl+C once the device has booted up.'
+        while True:
+          time.sleep(1)
+      except KeyboardInterrupt:
+        pass
+      tracer_args = self._construct_trace_command()
+      self._adb = subprocess.Popen(tracer_args, stdout=subprocess.PIPE,
+                                   stderr=subprocess.PIPE)
+    except OSError as error:
+      print >> sys.stderr, (
+          'The command "%s" failed with the following error:' %
+          ' '.join(tracer_args))
+      print >> sys.stderr, '    ', error
+      sys.exit(1)
+
+  def _construct_setup_command(self):
+    echo_args = ['echo'] + self._categories + ['>', BOOTTRACE_CATEGORIES]
+    setprop_args = ['setprop', BOOTTRACE_PROP, '1']
+    reboot_args = ['reboot']
+    return util.construct_adb_shell_command(
+        echo_args + ['&&'] + setprop_args + ['&&'] + reboot_args,
+        self._options.device_serial)
+
+  def _construct_trace_command(self):
+    self._expect_trace = True
+    atrace_args = ['atrace', '--async_stop']
+    setprop_args = ['setprop', BOOTTRACE_PROP, '0']
+    rm_args = ['rm', BOOTTRACE_CATEGORIES]
+    return util.construct_adb_shell_command(
+          atrace_args + ['&&'] + setprop_args + ['&&'] + rm_args,
+          self._options.device_serial)
+
+
 class FileReaderThread(threading.Thread):
   """Reads data from a file/pipe on a worker thread.
 
diff --git a/run_unittest.py b/run_unittest.py
index 66496ba..5c43322 100755
--- a/run_unittest.py
+++ b/run_unittest.py
@@ -28,6 +28,13 @@
 LEGACY_TRACE_CMD = (ADB_SHELL + LEGACY_ATRACE_ARGS +
              [';', 'ps', '-t'])
 
+SYSTRACE_BOOT_CMD = (['./systrace.py', '--boot', '-e', DEVICE_SERIAL] +
+                     CATEGORIES)
+TRACE_BOOT_CMD = (ADB_SHELL +
+                  ['atrace', '--async_stop', '&&', 'setprop',
+                   'persist.debug.atrace.boottrace', '0', '&&',
+                   'rm', '/data/misc/boottrace/categories'])
+
 TEST_DIR = 'test_data/'
 ATRACE_DATA = TEST_DIR + 'atrace_data'
 ATRACE_DATA_RAW = TEST_DIR + 'atrace_data_raw'
@@ -131,5 +138,15 @@
     self.assertEqual(' '.join(LEGACY_TRACE_CMD), ' '.join(tracer_args))
     self.assertEqual(True, agent.expect_trace())
 
+
+class BootAgentUnitTest(unittest.TestCase):
+  def test_boot(self):
+    options, categories = systrace.parse_options(SYSTRACE_BOOT_CMD)
+    agent = atrace_agent.BootAgent(options, categories)
+    tracer_args = agent._construct_trace_command()
+    self.assertEqual(' '.join(TRACE_BOOT_CMD), ' '.join(tracer_args))
+    self.assertEqual(True, agent.expect_trace())
+
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/systrace.py b/systrace.py
index 38443fe..fdf895a 100755
--- a/systrace.py
+++ b/systrace.py
@@ -69,6 +69,10 @@
   parser.add_option('--link-assets', dest='link_assets', default=False,
                     action='store_true',
                     help='(deprecated)')
+  parser.add_option('--boot', dest='boot', default=False, action='store_true',
+                    help='reboot the device with tracing during boot enabled. '
+                    'The report is created by hitting Ctrl+C after the device '
+                    'has booted up.')
   parser.add_option('--from-file', dest='from_file', action='store',
                     help='read the trace from a file (compressed) rather than '
                     'running a live trace')