summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorAndreas Gampe <agampe@google.com>2015-02-27 12:49:04 -0800
committerAndreas Gampe <agampe@google.com>2015-04-15 20:45:35 -0700
commit40da286d3207d88ed8ff3f5caac4873874603428 (patch)
tree3f9720425b2a024a5a54a0a71447dcea107229a8 /tools
parent6508158f8388847f4cc3693e2cc1dbee6c2c7d18 (diff)
downloadart-40da286d3207d88ed8ff3f5caac4873874603428.zip
art-40da286d3207d88ed8ff3f5caac4873874603428.tar.gz
art-40da286d3207d88ed8ff3f5caac4873874603428.tar.bz2
ART: Streaming trace mode
Add a streaming mode for tracing. Streaming uses a buffer of 16KB and writes to the output when that buffer gets full. Streaming mode can be enabled with -Xmethod-trace-stream and is currently not exposed otherwise. Add a python script that can parse the streaming format, which simply contains strings for newly encountered threads and methods inline, and create output that can be used with traceview. Add Trace::Pause and Trace::Abort, which can pause and abort tracing. Abort is different from Stop in that it does not write the data. Add code to the zygote hooks JNI implementation that pauses tracing before the fork, making sure that a child cannot clobber the parent's data. Add code to the zygote hooks JNI implementation that aborts old tracing and starts new tracing in the child after the fork. Currently base the output on the pid. This will not work on an unmodified device, as the profiles directory is not generally writable, but we do not have enough information at that point. Consider a scheme that restarts tracing later. Change-Id: I93c7bf87e35af582bdfdd3ecc7c52454514220dd
Diffstat (limited to 'tools')
-rwxr-xr-xtools/stream-trace-converter.py186
1 files changed, 186 insertions, 0 deletions
diff --git a/tools/stream-trace-converter.py b/tools/stream-trace-converter.py
new file mode 100755
index 0000000..951b05b
--- /dev/null
+++ b/tools/stream-trace-converter.py
@@ -0,0 +1,186 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Script that parses a trace filed produced in streaming mode. The file is broken up into
+ a header and body part, which, when concatenated, make up a non-streaming trace file that
+ can be used with traceview."""
+
+import sys
+
+class MyException(Exception):
+ pass
+
+class BufferUnderrun(Exception):
+ pass
+
+def ReadShortLE(f):
+ byte1 = f.read(1)
+ if not byte1:
+ raise BufferUnderrun()
+ byte2 = f.read(1)
+ if not byte2:
+ raise BufferUnderrun()
+ return ord(byte1) + (ord(byte2) << 8);
+
+def WriteShortLE(f, val):
+ bytes = [ (val & 0xFF), ((val >> 8) & 0xFF) ]
+ asbytearray = bytearray(bytes)
+ f.write(asbytearray)
+
+def ReadIntLE(f):
+ byte1 = f.read(1)
+ if not byte1:
+ raise BufferUnderrun()
+ byte2 = f.read(1)
+ if not byte2:
+ raise BufferUnderrun()
+ byte3 = f.read(1)
+ if not byte3:
+ raise BufferUnderrun()
+ byte4 = f.read(1)
+ if not byte4:
+ raise BufferUnderrun()
+ return ord(byte1) + (ord(byte2) << 8) + (ord(byte3) << 16) + (ord(byte4) << 24);
+
+def WriteIntLE(f, val):
+ bytes = [ (val & 0xFF), ((val >> 8) & 0xFF), ((val >> 16) & 0xFF), ((val >> 24) & 0xFF) ]
+ asbytearray = bytearray(bytes)
+ f.write(asbytearray)
+
+def Copy(input, output, length):
+ buf = input.read(length)
+ if len(buf) != length:
+ raise BufferUnderrun()
+ output.write(buf)
+
+class Rewriter:
+
+ def PrintHeader(self, header):
+ header.write('*version\n');
+ header.write('3\n');
+ header.write('data-file-overflow=false\n');
+ header.write('clock=dual\n');
+ header.write('vm=art\n');
+
+ def ProcessDataHeader(self, input, body):
+ magic = ReadIntLE(input)
+ if magic != 0x574f4c53:
+ raise MyException("Magic wrong")
+
+ WriteIntLE(body, magic)
+
+ version = ReadShortLE(input)
+ if (version & 0xf0) != 0xf0:
+ raise MyException("Does not seem to be a streaming trace: %d." % version)
+ version = version ^ 0xf0
+
+ if version != 3:
+ raise MyException("Only support version 3")
+
+ WriteShortLE(body, version)
+
+ # read offset
+ offsetToData = ReadShortLE(input) - 16
+ WriteShortLE(body, offsetToData + 16)
+
+ # copy startWhen
+ Copy(input, body, 8)
+
+ if version == 1:
+ self._mRecordSize = 9;
+ elif version == 2:
+ self._mRecordSize = 10;
+ else:
+ self._mRecordSize = ReadShortLE(input)
+ WriteShortLE(body, self._mRecordSize)
+ offsetToData -= 2;
+
+ # Skip over offsetToData bytes
+ Copy(input, body, offsetToData)
+
+ def ProcessMethod(self, input):
+ stringLength = ReadShortLE(input)
+ str = input.read(stringLength)
+ self._methods.append(str)
+ print 'New method: %s' % str
+
+ def ProcessThread(self, input):
+ tid = ReadShortLE(input)
+ stringLength = ReadShortLE(input)
+ str = input.read(stringLength)
+ self._threads.append('%d\t%s\n' % (tid, str))
+ print 'New thread: %d/%s' % (tid, str)
+
+ def ProcessSpecial(self, input):
+ code = ord(input.read(1))
+ if code == 1:
+ self.ProcessMethod(input)
+ elif code == 2:
+ self.ProcessThread(input)
+ else:
+ raise MyException("Unknown special!")
+
+ def Process(self, input, body):
+ try:
+ while True:
+ threadId = ReadShortLE(input)
+ if threadId == 0:
+ self.ProcessSpecial(input)
+ else:
+ # Regular package, just copy
+ WriteShortLE(body, threadId)
+ Copy(input, body, self._mRecordSize - 2)
+ except BufferUnderrun:
+ print 'Buffer underrun, file was probably truncated. Results should still be usable.'
+
+ def Finalize(self, header):
+ header.write('*threads\n')
+ for t in self._threads:
+ header.write(t)
+ header.write('*methods\n')
+ for m in self._methods:
+ header.write(m)
+ header.write('*end\n')
+
+ def ProcessFile(self, filename):
+ input = open(filename, 'rb') # Input file
+ header = open(filename + '.header', 'w') # Header part
+ body = open(filename + '.body', 'wb') # Body part
+
+ self.PrintHeader(header)
+
+ self.ProcessDataHeader(input, body)
+
+ self._methods = []
+ self._threads = []
+ self.Process(input, body)
+
+ self.Finalize(header)
+
+ input.close()
+ header.close()
+ body.close()
+
+def main():
+ Rewriter().ProcessFile(sys.argv[1])
+ header_name = sys.argv[1] + '.header'
+ body_name = sys.argv[1] + '.body'
+ print 'Results have been written to %s and %s.' % (header_name, body_name)
+ print 'Concatenate the files to get a result usable with traceview.'
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main() \ No newline at end of file