Create helper script to parse systrace files

Create a script that can parse systrace files and calculates
metrics. The example metrics are average DrawFrame and average
time to record a View.

Test: ran the script and compared outputs with numbers visible,
when systrace file is opened in a browser.

Change-Id: If37322e7838e177efb3e2e4a00cb6e97755aa453
diff --git a/libs/hwui/tests/scripts/process_systrace.py b/libs/hwui/tests/scripts/process_systrace.py
new file mode 100755
index 0000000..f497bf5
--- /dev/null
+++ b/libs/hwui/tests/scripts/process_systrace.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+
+import codecs, httplib, json, os, urllib, shutil, subprocess, sys, argparse
+
+upstream_git = 'https://github.com/catapult-project/catapult.git'
+
+script_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
+catapult_src_dir = os.path.join(script_dir, 'catapult-upstream')
+
+parser = argparse.ArgumentParser()
+parser.add_argument('trace_file_or_dir',
+      help='Path to trace file or directory of trace files.')
+parser.add_argument('--output_file', dest='outfile', default=os.path.join(os.getcwd(), 'mapper_output.json'),
+      help='Path to output file to store results.')
+parser.add_argument('--mapper_func', dest='func', default='AvgDrawFrame',
+      help='Name of javascript mapper function in systrace_parser.html.')
+args = parser.parse_args()
+
+# Update the source if needed.
+if not os.path.exists(catapult_src_dir):
+  # Pull the latest source from the upstream git.
+  git_args = ['git', 'clone', upstream_git, catapult_src_dir]
+  p = subprocess.Popen(git_args, stdout=subprocess.PIPE, cwd=script_dir)
+  p.communicate()
+  if p.wait() != 0:
+    print 'Failed to checkout source from upstream git.'
+    sys.exit(1)
+
+mapper_func_file = os.path.join(script_dir, 'systrace_parser.html')
+path_to_process_traces = os.path.join(catapult_src_dir, 'trace_processor/bin/process_traces')
+run_command = path_to_process_traces + " --mapper_handle " + mapper_func_file + ":" + args.func + " --output_file " + args.outfile + " " + args.trace_file_or_dir
+print run_command
+sys.exit(os.system(run_command))
+
diff --git a/libs/hwui/tests/scripts/systrace_parser.html b/libs/hwui/tests/scripts/systrace_parser.html
new file mode 100644
index 0000000..4c66ae2
--- /dev/null
+++ b/libs/hwui/tests/scripts/systrace_parser.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+
+<script>
+'use strict';
+
+const RENDER_THREAD_NAME = "RenderThread";
+const UI_THREAD_NAME = "UI Thread";
+const DRAW_FRAME_SLICE_TITLE = "DrawFrame";
+const BINDER_SLICE_TITLE = "binder transaction";
+const RECORD_SLICE_TITLE = "Record View#draw()";
+const DEQUEUE_BUFFER_SLICE_TITLE = "dequeueBuffer";
+
+function getTimeInBinder(slice) {
+    if (slice.title === BINDER_SLICE_TITLE) {
+        return slice.duration;
+    }
+    let result = 0.0;
+    for (let subslice of slice.subSlices) {
+        result += getTimeInBinder(subslice);
+    }
+    return result;
+}
+
+function getTimeInDequeueBuffer(slice) {
+    if (slice.title === DEQUEUE_BUFFER_SLICE_TITLE) {
+        return slice.duration;
+    }
+    let result = 0.0;
+    for (let subslice of slice.subSlices) {
+        result += getTimeInDequeueBuffer(subslice);
+    }
+    return result;
+}
+
+tr.mre.FunctionRegistry.register(
+    function AvgDrawFrame(result, model) {
+        let canonicalUrl = model.canonicalUrl;
+
+        for (let p of model.getAllProcesses()) {
+            //calc stats for processes that have UI and render threads and at least 10 frames
+            let renderThread = p.findAtMostOneThreadNamed(RENDER_THREAD_NAME);
+            let UIThread = p.findAtMostOneThreadNamed(UI_THREAD_NAME);
+            if (renderThread && UIThread)
+            {
+                let numDrawFrames = 0;
+                let drawFramesWallDuration = 0.0;
+                let binderDuration = 0.0;
+                let dequeueBufferDuration = 0.0;
+
+                let numRecordViewDraw = 0;
+                let recordViewDrawWallDuration = 0.0;
+
+                renderThread.sliceGroup.slices.forEach(function(slice) {
+                    if (slice.title === DRAW_FRAME_SLICE_TITLE) {
+                        drawFramesWallDuration += slice.duration;
+                        numDrawFrames++;
+                        binderDuration += getTimeInBinder(slice);
+                        dequeueBufferDuration += getTimeInDequeueBuffer(slice);
+                    }
+                });
+                if (numDrawFrames < 10) continue;
+                UIThread.sliceGroup.slices.forEach(function(slice) {
+                    if (slice.title === RECORD_SLICE_TITLE) {
+                        recordViewDrawWallDuration += slice.duration;
+                        numRecordViewDraw++;
+                    }
+                });
+
+                let avgDrawFrameDuration = undefined;
+                if (numDrawFrames > 0) {
+                    avgDrawFrameDuration = (drawFramesWallDuration-dequeueBufferDuration)/numDrawFrames;
+                }
+                let avgRecordViewDrawDuration = undefined;
+                if (numRecordViewDraw > 0) {
+                    avgRecordViewDrawDuration = recordViewDrawWallDuration/numRecordViewDraw;
+                }
+
+                result.addPair('result', {
+                    canonicalUrl: canonicalUrl,
+                    processName: p.name,
+                    avgDrawFrameDuration: Number(avgDrawFrameDuration).toFixed(3),
+                    avgRecordViewDrawDuration: Number(avgRecordViewDrawDuration).toFixed(3),
+                    avgRecordAndPlay: Number(avgDrawFrameDuration+avgRecordViewDrawDuration).toFixed(3)
+                });
+            }
+        }
+    });
+
+</script>