blob: 7e341f2bcf381aa00c94a0c8c29e955bb4be7544 [file] [log] [blame]
Andreas Gampe40da2862015-02-27 12:49:04 -08001#!/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
Shukang Zhou8a5ab912017-01-20 11:40:16 -0800127 def ProcessTraceSummary(self, input):
128 summaryLength = ReadIntLE(input)
129 str = input.read(summaryLength)
130 self._summary = str
131 print 'Summary: \"%s\"' % str
132
Andreas Gampe40da2862015-02-27 12:49:04 -0800133 def ProcessSpecial(self, input):
134 code = ord(input.read(1))
135 if code == 1:
136 self.ProcessMethod(input)
137 elif code == 2:
138 self.ProcessThread(input)
Shukang Zhou8a5ab912017-01-20 11:40:16 -0800139 elif code == 3:
140 self.ProcessTraceSummary(input)
Andreas Gampe40da2862015-02-27 12:49:04 -0800141 else:
142 raise MyException("Unknown special!")
143
144 def Process(self, input, body):
145 try:
146 while True:
147 threadId = ReadShortLE(input)
148 if threadId == 0:
149 self.ProcessSpecial(input)
150 else:
151 # Regular package, just copy
152 WriteShortLE(body, threadId)
153 Copy(input, body, self._mRecordSize - 2)
154 except BufferUnderrun:
155 print 'Buffer underrun, file was probably truncated. Results should still be usable.'
156
157 def Finalize(self, header):
Shukang Zhou8a5ab912017-01-20 11:40:16 -0800158 # If the summary is present in the input file, use it as the header except
159 # for the methods section which is emtpy in the input file. If not present,
160 # apppend header with the threads that are recorded in the input stream.
161 if (self._summary):
162 # Erase the contents that's already written earlier by PrintHeader.
163 header.seek(0)
164 header.truncate()
165 # Copy the lines from the input summary to the output header until
166 # the methods section is seen.
167 for line in self._summary.splitlines(True):
168 if line == "*methods\n":
169 break
170 else:
171 header.write(line)
172 else:
173 header.write('*threads\n')
174 for t in self._threads:
175 header.write(t)
Andreas Gampe40da2862015-02-27 12:49:04 -0800176 header.write('*methods\n')
177 for m in self._methods:
178 header.write(m)
179 header.write('*end\n')
180
181 def ProcessFile(self, filename):
182 input = open(filename, 'rb') # Input file
183 header = open(filename + '.header', 'w') # Header part
184 body = open(filename + '.body', 'wb') # Body part
185
186 self.PrintHeader(header)
187
188 self.ProcessDataHeader(input, body)
189
190 self._methods = []
191 self._threads = []
Shukang Zhou8a5ab912017-01-20 11:40:16 -0800192 self._summary = None
Andreas Gampe40da2862015-02-27 12:49:04 -0800193 self.Process(input, body)
194
195 self.Finalize(header)
196
197 input.close()
198 header.close()
199 body.close()
200
201def main():
202 Rewriter().ProcessFile(sys.argv[1])
203 header_name = sys.argv[1] + '.header'
204 body_name = sys.argv[1] + '.body'
205 print 'Results have been written to %s and %s.' % (header_name, body_name)
206 print 'Concatenate the files to get a result usable with traceview.'
207 sys.exit(0)
208
209if __name__ == '__main__':
210 main()