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>