diff options
author | Andreas Gampe <agampe@google.com> | 2015-02-27 12:49:04 -0800 |
---|---|---|
committer | Andreas Gampe <agampe@google.com> | 2015-04-15 20:45:35 -0700 |
commit | 40da286d3207d88ed8ff3f5caac4873874603428 (patch) | |
tree | 3f9720425b2a024a5a54a0a71447dcea107229a8 /tools | |
parent | 6508158f8388847f4cc3693e2cc1dbee6c2c7d18 (diff) | |
download | art-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-x | tools/stream-trace-converter.py | 186 |
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 |