Fix thread names when absent or truncated.

When a large number of threads are active, the kernel may
start to report <...> as the name of a thread in the trace
buffer which is rather unhelpful.

This change uses information from 'ps -t' to fix thread names
when they are absent or truncated.  Use the '--no-fix-threads'
option to disable this feature.

Change-Id: I6566e557eeac33311ff5d4c6e95ff010cb83a0d6
diff --git a/systrace.py b/systrace.py
index 387f413..d89c35d 100755
--- a/systrace.py
+++ b/systrace.py
@@ -10,7 +10,7 @@
 the kernel.  It creates an HTML file for visualizing the trace.
 """
 
-import errno, optparse, os, select, subprocess, sys, time, zlib
+import errno, optparse, os, re, select, subprocess, sys, time, zlib
 
 flattened_css_file = 'style.css'
 flattened_js_file = 'script.js'
@@ -78,6 +78,8 @@
   parser.add_option('-a', '--app', dest='app_name', default=None, type='string',
                     action='store', help='enable application-level tracing for comma-separated ' +
                     'list of app cmdlines')
+  parser.add_option('--no-fix-threads', dest='fix_threads', default=True,
+                    action='store_false', help='don\'t fix missing or truncated thread names')
 
   parser.add_option('--link-assets', dest='link_assets', default=False,
                     action='store_true', help='link to original CSS or JS resources '
@@ -121,6 +123,9 @@
 
     atrace_args.extend(args)
 
+    if options.fix_threads:
+      atrace_args.extend([';', 'ps', '-t'])
+
   if atrace_args[0] == 'adb':
     add_adb_serial(atrace_args, options.device_serial)
 
@@ -219,20 +224,42 @@
         # Indicate to the user that the data download is complete.
         print " done\n"
 
+      # Extract the thread list dumped by ps.
+      threads = {}
+      if options.fix_threads:
+        parts = data.split('USER     PID   PPID  VSIZE  RSS     WCHAN    PC        NAME', 1)
+        if len(parts) == 2:
+          data = parts[0]
+          for line in parts[1].splitlines():
+            cols = line.split(None, 8)
+            if len(cols) == 9:
+              tid = int(cols[1])
+              name = cols[8]
+              threads[tid] = name
+
+      # Decompress and preprocess the data.
+      out = zlib.decompress(data)
+      if options.fix_threads:
+        def repl(m):
+          tid = int(m.group(2))
+          if tid > 0:
+            name = threads.get(tid)
+            if name is None:
+              name = m.group(1)
+              if name == '<...>':
+                name = '<' + str(tid) + '>'
+              threads[tid] = name
+            return name + '-' + m.group(2)
+          else:
+            return m.group(0)
+        out = re.sub(r'^\s*(\S+)-(\d+)', repl, out, flags=re.MULTILINE)
+
       html_prefix = read_asset(script_dir, 'prefix.html')
       html_suffix = read_asset(script_dir, 'suffix.html')
 
       html_file = open(html_filename, 'w')
       html_file.write(html_prefix % (css, js, templates))
-
-      size = 4096
-      dec = zlib.decompressobj()
-      for chunk in (data[i:i+size] for i in xrange(0, len(data), size)):
-        decoded_chunk = dec.decompress(chunk)
-        html_chunk = decoded_chunk.replace('\n', '\\n\\\n')
-        html_file.write(html_chunk)
-
-      html_out = dec.flush().replace('\n', '\\n\\\n')
+      html_out = out.replace('\n', '\\n\\\n')
       html_file.write(html_out)
       html_file.write(html_suffix)
       html_file.close()