140da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe#!/usr/bin/env python
240da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe#
340da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe# Copyright (C) 2014 The Android Open Source Project
440da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe#
540da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe# Licensed under the Apache License, Version 2.0 (the "License");
640da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe# you may not use this file except in compliance with the License.
740da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe# You may obtain a copy of the License at
840da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe#
940da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe#      http://www.apache.org/licenses/LICENSE-2.0
1040da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe#
1140da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe# Unless required by applicable law or agreed to in writing, software
1240da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe# distributed under the License is distributed on an "AS IS" BASIS,
1340da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1440da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe# See the License for the specific language governing permissions and
1540da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe# limitations under the License.
1640da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
1740da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe"""Script that parses a trace filed produced in streaming mode. The file is broken up into
1840da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe   a header and body part, which, when concatenated, make up a non-streaming trace file that
1940da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe   can be used with traceview."""
2040da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
2140da286d3207d88ed8ff3f5caac4873874603428Andreas Gampeimport sys
2240da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
2340da286d3207d88ed8ff3f5caac4873874603428Andreas Gampeclass MyException(Exception):
2440da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  pass
2540da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
2640da286d3207d88ed8ff3f5caac4873874603428Andreas Gampeclass BufferUnderrun(Exception):
2740da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  pass
2840da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
2940da286d3207d88ed8ff3f5caac4873874603428Andreas Gampedef ReadShortLE(f):
3040da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  byte1 = f.read(1)
3140da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  if not byte1:
3240da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    raise BufferUnderrun()
3340da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  byte2 = f.read(1)
3440da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  if not byte2:
3540da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    raise BufferUnderrun()
3640da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  return ord(byte1) + (ord(byte2) << 8);
3740da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
3840da286d3207d88ed8ff3f5caac4873874603428Andreas Gampedef WriteShortLE(f, val):
3940da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  bytes = [ (val & 0xFF), ((val >> 8) & 0xFF) ]
4040da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  asbytearray = bytearray(bytes)
4140da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  f.write(asbytearray)
4240da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
4340da286d3207d88ed8ff3f5caac4873874603428Andreas Gampedef ReadIntLE(f):
4440da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  byte1 = f.read(1)
4540da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  if not byte1:
4640da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    raise BufferUnderrun()
4740da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  byte2 = f.read(1)
4840da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  if not byte2:
4940da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    raise BufferUnderrun()
5040da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  byte3 = f.read(1)
5140da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  if not byte3:
5240da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    raise BufferUnderrun()
5340da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  byte4 = f.read(1)
5440da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  if not byte4:
5540da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    raise BufferUnderrun()
5640da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  return ord(byte1) + (ord(byte2) << 8) + (ord(byte3) << 16) + (ord(byte4) << 24);
5740da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
5840da286d3207d88ed8ff3f5caac4873874603428Andreas Gampedef WriteIntLE(f, val):
5940da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  bytes = [ (val & 0xFF), ((val >> 8) & 0xFF), ((val >> 16) & 0xFF), ((val >> 24) & 0xFF) ]
6040da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  asbytearray = bytearray(bytes)
6140da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  f.write(asbytearray)
6240da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
6340da286d3207d88ed8ff3f5caac4873874603428Andreas Gampedef Copy(input, output, length):
6440da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  buf = input.read(length)
6540da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  if len(buf) != length:
6640da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    raise BufferUnderrun()
6740da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  output.write(buf)
6840da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
6940da286d3207d88ed8ff3f5caac4873874603428Andreas Gampeclass Rewriter:
7040da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
7140da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  def PrintHeader(self, header):
7240da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    header.write('*version\n');
7340da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    header.write('3\n');
7440da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    header.write('data-file-overflow=false\n');
7540da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    header.write('clock=dual\n');
7640da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    header.write('vm=art\n');
7740da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
7840da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  def ProcessDataHeader(self, input, body):
7940da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    magic = ReadIntLE(input)
8040da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    if magic != 0x574f4c53:
8140da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe      raise MyException("Magic wrong")
8240da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
8340da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    WriteIntLE(body, magic)
8440da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
8540da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    version = ReadShortLE(input)
8640da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    if (version & 0xf0) != 0xf0:
8740da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe      raise MyException("Does not seem to be a streaming trace: %d." % version)
8840da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    version = version ^ 0xf0
8940da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
9040da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    if version != 3:
9140da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe      raise MyException("Only support version 3")
9240da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
9340da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    WriteShortLE(body, version)
9440da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
9540da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    # read offset
9640da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    offsetToData = ReadShortLE(input) - 16
9740da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    WriteShortLE(body, offsetToData + 16)
9840da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
9940da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    # copy startWhen
10040da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    Copy(input, body, 8)
10140da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
10240da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    if version == 1:
10340da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe      self._mRecordSize = 9;
10440da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    elif version == 2:
10540da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe      self._mRecordSize = 10;
10640da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    else:
10740da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe      self._mRecordSize = ReadShortLE(input)
10840da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe      WriteShortLE(body, self._mRecordSize)
10940da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe      offsetToData -= 2;
11040da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
11140da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    # Skip over offsetToData bytes
11240da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    Copy(input, body, offsetToData)
11340da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
11440da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  def ProcessMethod(self, input):
11540da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    stringLength = ReadShortLE(input)
11640da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    str = input.read(stringLength)
11740da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    self._methods.append(str)
11840da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    print 'New method: %s' % str
11940da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
12040da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  def ProcessThread(self, input):
12140da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    tid = ReadShortLE(input)
12240da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    stringLength = ReadShortLE(input)
12340da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    str = input.read(stringLength)
12440da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    self._threads.append('%d\t%s\n' % (tid, str))
12540da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    print 'New thread: %d/%s' % (tid, str)
12640da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
12740da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  def ProcessSpecial(self, input):
12840da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    code = ord(input.read(1))
12940da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    if code == 1:
13040da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe      self.ProcessMethod(input)
13140da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    elif code == 2:
13240da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe      self.ProcessThread(input)
13340da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    else:
13440da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe      raise MyException("Unknown special!")
13540da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
13640da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  def Process(self, input, body):
13740da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    try:
13840da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe      while True:
13940da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe        threadId = ReadShortLE(input)
14040da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe        if threadId == 0:
14140da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe          self.ProcessSpecial(input)
14240da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe        else:
14340da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe          # Regular package, just copy
14440da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe          WriteShortLE(body, threadId)
14540da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe          Copy(input, body, self._mRecordSize - 2)
14640da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    except BufferUnderrun:
14740da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe      print 'Buffer underrun, file was probably truncated. Results should still be usable.'
14840da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
14940da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  def Finalize(self, header):
15040da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    header.write('*threads\n')
15140da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    for t in self._threads:
15240da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe      header.write(t)
15340da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    header.write('*methods\n')
15440da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    for m in self._methods:
15540da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe      header.write(m)
15640da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    header.write('*end\n')
15740da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
15840da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  def ProcessFile(self, filename):
15940da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    input = open(filename, 'rb')                     # Input file
16040da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    header = open(filename + '.header', 'w')         # Header part
16140da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    body = open(filename + '.body', 'wb')            # Body part
16240da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
16340da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    self.PrintHeader(header)
16440da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
16540da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    self.ProcessDataHeader(input, body)
16640da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
16740da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    self._methods = []
16840da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    self._threads = []
16940da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    self.Process(input, body)
17040da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
17140da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    self.Finalize(header)
17240da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
17340da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    input.close()
17440da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    header.close()
17540da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    body.close()
17640da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
17740da286d3207d88ed8ff3f5caac4873874603428Andreas Gampedef main():
17840da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  Rewriter().ProcessFile(sys.argv[1])
17940da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  header_name = sys.argv[1] + '.header'
18040da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  body_name = sys.argv[1] + '.body'
18140da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  print 'Results have been written to %s and %s.' % (header_name, body_name)
18240da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  print 'Concatenate the files to get a result usable with traceview.'
18340da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  sys.exit(0)
18440da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
18540da286d3207d88ed8ff3f5caac4873874603428Andreas Gampeif __name__ == '__main__':
18640da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  main()