1#!/usr/bin/env python 2# 3# Copyright (C) 2014 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17"""Script that parses a trace filed produced in streaming mode. The file is broken up into 18 a header and body part, which, when concatenated, make up a non-streaming trace file that 19 can be used with traceview.""" 20 21import sys 22 23class MyException(Exception): 24 pass 25 26class BufferUnderrun(Exception): 27 pass 28 29def ReadShortLE(f): 30 byte1 = f.read(1) 31 if not byte1: 32 raise BufferUnderrun() 33 byte2 = f.read(1) 34 if not byte2: 35 raise BufferUnderrun() 36 return ord(byte1) + (ord(byte2) << 8); 37 38def WriteShortLE(f, val): 39 bytes = [ (val & 0xFF), ((val >> 8) & 0xFF) ] 40 asbytearray = bytearray(bytes) 41 f.write(asbytearray) 42 43def ReadIntLE(f): 44 byte1 = f.read(1) 45 if not byte1: 46 raise BufferUnderrun() 47 byte2 = f.read(1) 48 if not byte2: 49 raise BufferUnderrun() 50 byte3 = f.read(1) 51 if not byte3: 52 raise BufferUnderrun() 53 byte4 = f.read(1) 54 if not byte4: 55 raise BufferUnderrun() 56 return ord(byte1) + (ord(byte2) << 8) + (ord(byte3) << 16) + (ord(byte4) << 24); 57 58def WriteIntLE(f, val): 59 bytes = [ (val & 0xFF), ((val >> 8) & 0xFF), ((val >> 16) & 0xFF), ((val >> 24) & 0xFF) ] 60 asbytearray = bytearray(bytes) 61 f.write(asbytearray) 62 63def Copy(input, output, length): 64 buf = input.read(length) 65 if len(buf) != length: 66 raise BufferUnderrun() 67 output.write(buf) 68 69class Rewriter: 70 71 def PrintHeader(self, header): 72 header.write('*version\n'); 73 header.write('3\n'); 74 header.write('data-file-overflow=false\n'); 75 header.write('clock=dual\n'); 76 header.write('vm=art\n'); 77 78 def ProcessDataHeader(self, input, body): 79 magic = ReadIntLE(input) 80 if magic != 0x574f4c53: 81 raise MyException("Magic wrong") 82 83 WriteIntLE(body, magic) 84 85 version = ReadShortLE(input) 86 if (version & 0xf0) != 0xf0: 87 raise MyException("Does not seem to be a streaming trace: %d." % version) 88 version = version ^ 0xf0 89 90 if version != 3: 91 raise MyException("Only support version 3") 92 93 WriteShortLE(body, version) 94 95 # read offset 96 offsetToData = ReadShortLE(input) - 16 97 WriteShortLE(body, offsetToData + 16) 98 99 # copy startWhen 100 Copy(input, body, 8) 101 102 if version == 1: 103 self._mRecordSize = 9; 104 elif version == 2: 105 self._mRecordSize = 10; 106 else: 107 self._mRecordSize = ReadShortLE(input) 108 WriteShortLE(body, self._mRecordSize) 109 offsetToData -= 2; 110 111 # Skip over offsetToData bytes 112 Copy(input, body, offsetToData) 113 114 def ProcessMethod(self, input): 115 stringLength = ReadShortLE(input) 116 str = input.read(stringLength) 117 self._methods.append(str) 118 print 'New method: %s' % str 119 120 def ProcessThread(self, input): 121 tid = ReadShortLE(input) 122 stringLength = ReadShortLE(input) 123 str = input.read(stringLength) 124 self._threads.append('%d\t%s\n' % (tid, str)) 125 print 'New thread: %d/%s' % (tid, str) 126 127 def ProcessSpecial(self, input): 128 code = ord(input.read(1)) 129 if code == 1: 130 self.ProcessMethod(input) 131 elif code == 2: 132 self.ProcessThread(input) 133 else: 134 raise MyException("Unknown special!") 135 136 def Process(self, input, body): 137 try: 138 while True: 139 threadId = ReadShortLE(input) 140 if threadId == 0: 141 self.ProcessSpecial(input) 142 else: 143 # Regular package, just copy 144 WriteShortLE(body, threadId) 145 Copy(input, body, self._mRecordSize - 2) 146 except BufferUnderrun: 147 print 'Buffer underrun, file was probably truncated. Results should still be usable.' 148 149 def Finalize(self, header): 150 header.write('*threads\n') 151 for t in self._threads: 152 header.write(t) 153 header.write('*methods\n') 154 for m in self._methods: 155 header.write(m) 156 header.write('*end\n') 157 158 def ProcessFile(self, filename): 159 input = open(filename, 'rb') # Input file 160 header = open(filename + '.header', 'w') # Header part 161 body = open(filename + '.body', 'wb') # Body part 162 163 self.PrintHeader(header) 164 165 self.ProcessDataHeader(input, body) 166 167 self._methods = [] 168 self._threads = [] 169 self.Process(input, body) 170 171 self.Finalize(header) 172 173 input.close() 174 header.close() 175 body.close() 176 177def main(): 178 Rewriter().ProcessFile(sys.argv[1]) 179 header_name = sys.argv[1] + '.header' 180 body_name = sys.argv[1] + '.body' 181 print 'Results have been written to %s and %s.' % (header_name, body_name) 182 print 'Concatenate the files to get a result usable with traceview.' 183 sys.exit(0) 184 185if __name__ == '__main__': 186 main()