add support for legacy (pre-4.3) platform

It seems systrace-legacy.py had not maintained for a while and has some
bugs and missing features.
So implement atrace_legacy_agent to use legacy platforms.

Change-Id: I9a60c4d9899cff5d088d00c8be643493fc163272
Signed-off-by: Young-Ho Cha <ganadist@gmail.com>
diff --git a/agents/atrace_agent.py b/agents/atrace_agent.py
index f01ce51..5ac96d9 100644
--- a/agents/atrace_agent.py
+++ b/agents/atrace_agent.py
@@ -31,9 +31,31 @@
 # Plain-text trace data should always start with this string.
 TRACE_TEXT_HEADER = '# tracer'
 
+# This list is based on the tags in frameworks/native/include/utils/Trace.h for
+# legacy platform.
+LEGACY_TRACE_TAG_BITS = (
+  ('gfx',       1<<1),
+  ('input',     1<<2),
+  ('view',      1<<3),
+  ('webview',   1<<4),
+  ('wm',        1<<5),
+  ('am',        1<<6),
+  ('sm',        1<<7),
+  ('audio',     1<<8),
+  ('video',     1<<9),
+  ('camera',    1<<10),
+)
+
 
 def try_create_agent(options, categories):
-  return AtraceAgent(options, categories)
+  if options.from_file is not None:
+    return AtraceAgent(options, categories)
+
+  device_sdk_version = util.get_device_sdk_version()
+  if device_sdk_version >= 18:
+    return AtraceAgent(options, categories)
+  elif device_sdk_version >= 16:
+    return AtraceLegacyAgent(options, categories)
 
 
 class AtraceAgent(systrace_agent.SystraceAgent):
@@ -69,6 +91,23 @@
   def get_class_name(self):
     return 'trace-data'
 
+  def _construct_list_categories_command(self):
+    return util.construct_adb_shell_command(
+          LIST_CATEGORIES_ARGS, self._options.device_serial)
+
+  def _construct_extra_trace_command(self):
+    extra_args = []
+    if self._options.app_name is not None:
+      extra_args.extend(['-a', self._options.app_name])
+
+    if self._options.kfuncs is not None:
+      extra_args.extend(['-k', self._options.kfuncs])
+
+    if not self._categories:
+      self._categories = get_default_categories(self._options.device_serial)
+    extra_args.extend(self._categories)
+    return extra_args
+
   def _construct_trace_command(self):
     """Builds a command-line used to invoke a trace process.
 
@@ -78,14 +117,13 @@
       stream trace data.
     """
     if self._options.list_categories:
-      tracer_args = util.construct_adb_shell_command(
-          LIST_CATEGORIES_ARGS, self._options.device_serial)
+      tracer_args = self._construct_list_categories_command()
       self._expect_trace = False
     elif self._options.from_file is not None:
       tracer_args = ['cat', self._options.from_file]
       self._expect_trace = True
     else:
-      atrace_args = ATRACE_BASE_ARGS
+      atrace_args = ATRACE_BASE_ARGS[:]
       self._expect_trace = True
       if self._options.compress_trace_data:
         atrace_args.extend(['-z'])
@@ -97,16 +135,8 @@
       if ((self._options.trace_buf_size is not None)
           and (self._options.trace_buf_size > 0)):
         atrace_args.extend(['-b', str(self._options.trace_buf_size)])
-
-      if self._options.app_name is not None:
-        atrace_args.extend(['-a', self._options.app_name])
-
-      if self._options.kfuncs is not None:
-        atrace_args.extend(['-k', self._options.kfuncs])
-
-      if not self._categories:
-        self._categories = get_default_categories(self._options.device_serial)
-      atrace_args.extend(self._categories)
+      extra_args = self._construct_extra_trace_command()
+      atrace_args.extend(extra_args)
 
       if self._options.fix_threads:
         atrace_args.extend([';', 'ps', '-t'])
@@ -262,6 +292,64 @@
     return trace_data
 
 
+class AtraceLegacyAgent(AtraceAgent):
+  def _construct_list_categories_command(self):
+    LEGACY_CATEGORIES = """       sched - CPU Scheduling
+        freq - CPU Frequency
+        idle - CPU Idle
+        load - CPU Load
+        disk - Disk I/O (requires root)
+         bus - Bus utilization (requires root)
+   workqueue - Kernel workqueues (requires root)"""
+    return ["echo", LEGACY_CATEGORIES]
+
+  def start(self):
+    super(AtraceLegacyAgent, self).start()
+    if self.expect_trace():
+      SHELL_ARGS = ['getprop', 'debug.atrace.tags.enableflags']
+      output, return_code = util.run_adb_shell(SHELL_ARGS, self._options.device_serial)
+      flags = 0
+      if return_code == 0:
+        try:
+          if output.startswith('0x'):
+            flags = int(output, 16)
+          elif output.startswith('0'):
+            flags = int(output, 8)
+          else:
+            flags = int(output)
+        except ValueError, e:
+          pass
+
+      if flags:
+        tags = []
+        for desc, bit in LEGACY_TRACE_TAG_BITS:
+          if bit & flags:
+            tags.append(desc)
+        categories = tags + self._categories
+        print 'Collecting data with following categories:', ' '.join(categories)
+
+  def _construct_extra_trace_command(self):
+    extra_args = []
+    if not self._categories:
+      self._categories = ['sched', ]
+    if 'sched' in self._categories:
+      extra_args.append('-s')
+    if 'freq' in self._categories:
+      extra_args.append('-f')
+    if 'idle' in self._categories:
+      extra_args.append('-i')
+    if 'load' in self._categories:
+      extra_args.append('-l')
+    if 'disk' in self._categories:
+      extra_args.append('-d')
+    if 'bus' in self._categories:
+      extra_args.append('-u')
+    if 'workqueue' in self._categories:
+      extra_args.append('-w')
+
+    return extra_args
+
+
 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 db1b95e..98f5628 100755
--- a/run_unittest.py
+++ b/run_unittest.py
@@ -50,7 +50,7 @@
 class AtraceAgentUnitTest(unittest.TestCase):
   def test_construct_trace_command(self):
     options, categories = systrace.parse_options(SYSTRACE_CMD)
-    agent = atrace_agent.try_create_agent(options, categories)
+    agent = atrace_agent.AtraceAgent(options, categories)
     tracer_args = agent._construct_trace_command()
     self.assertEqual(' '.join(TRACE_CMD), ' '.join(tracer_args))
     self.assertEqual(True, agent.expect_trace())
@@ -98,13 +98,13 @@
       atrace_data = f2.read()
 
       options, categories = systrace.parse_options([])
-      agent = atrace_agent.try_create_agent(options, categories)
+      agent = atrace_agent.AtraceAgent(options, categories)
       trace_data = agent._preprocess_trace_data(atrace_data_with_thread_list)
       self.assertEqual(atrace_data, trace_data)
 
   def test_list_categories(self):
     options, categories = systrace.parse_options(SYSTRACE_LIST_CATEGORIES_CMD)
-    agent = atrace_agent.try_create_agent(options, categories)
+    agent = atrace_agent.AtraceAgent(options, categories)
     tracer_args = agent._construct_trace_command()
     self.assertEqual(' '.join(TRACE_LIST_CATEGORIES_CMD), ' '.join(tracer_args))
     self.assertEqual(False, agent.expect_trace())
diff --git a/systrace.py b/systrace.py
index e25ef77..38443fe 100755
--- a/systrace.py
+++ b/systrace.py
@@ -167,16 +167,9 @@
 
 
 def main():
-  device_sdk_version = util.get_device_sdk_version()
-  if device_sdk_version < 18:
-    legacy_script = os.path.join(os.path.dirname(sys.argv[0]),
-                                 'systrace-legacy.py')
-    # execv() does not return.
-    os.execv(legacy_script, sys.argv)
-
   options, categories = parse_options(sys.argv)
-
   agents = create_agents(options, categories)
+
   if not agents:
     dirs = DEFAULT_AGENT_DIR
     if options.agent_dirs: