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
1278a5ab9102fe705b63eda6e6bcfe98ee1c03e5a6cShukang Zhou  def ProcessTraceSummary(self, input):
1288a5ab9102fe705b63eda6e6bcfe98ee1c03e5a6cShukang Zhou    summaryLength = ReadIntLE(input)
1298a5ab9102fe705b63eda6e6bcfe98ee1c03e5a6cShukang Zhou    str = input.read(summaryLength)
1308a5ab9102fe705b63eda6e6bcfe98ee1c03e5a6cShukang Zhou    self._summary = str
1318a5ab9102fe705b63eda6e6bcfe98ee1c03e5a6cShukang Zhou    print 'Summary: \"%s\"' % str
1328a5ab9102fe705b63eda6e6bcfe98ee1c03e5a6cShukang Zhou
13340da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  def ProcessSpecial(self, input):
13440da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    code = ord(input.read(1))
13540da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    if code == 1:
13640da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe      self.ProcessMethod(input)
13740da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    elif code == 2:
13840da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe      self.ProcessThread(input)
1398a5ab9102fe705b63eda6e6bcfe98ee1c03e5a6cShukang Zhou    elif code == 3:
1408a5ab9102fe705b63eda6e6bcfe98ee1c03e5a6cShukang Zhou      self.ProcessTraceSummary(input)
14140da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    else:
14240da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe      raise MyException("Unknown special!")
14340da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
14440da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  def Process(self, input, body):
14540da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    try:
14640da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe      while True:
14740da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe        threadId = ReadShortLE(input)
14840da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe        if threadId == 0:
14940da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe          self.ProcessSpecial(input)
15040da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe        else:
15140da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe          # Regular package, just copy
15240da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe          WriteShortLE(body, threadId)
15340da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe          Copy(input, body, self._mRecordSize - 2)
15440da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    except BufferUnderrun:
15540da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe      print 'Buffer underrun, file was probably truncated. Results should still be usable.'
15640da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
15740da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  def Finalize(self, header):
1588a5ab9102fe705b63eda6e6bcfe98ee1c03e5a6cShukang Zhou    # If the summary is present in the input file, use it as the header except
1598a5ab9102fe705b63eda6e6bcfe98ee1c03e5a6cShukang Zhou    # for the methods section which is emtpy in the input file. If not present,
1608a5ab9102fe705b63eda6e6bcfe98ee1c03e5a6cShukang Zhou    # apppend header with the threads that are recorded in the input stream.
1618a5ab9102fe705b63eda6e6bcfe98ee1c03e5a6cShukang Zhou    if (self._summary):
1628a5ab9102fe705b63eda6e6bcfe98ee1c03e5a6cShukang Zhou      # Erase the contents that's already written earlier by PrintHeader.
1638a5ab9102fe705b63eda6e6bcfe98ee1c03e5a6cShukang Zhou      header.seek(0)
1648a5ab9102fe705b63eda6e6bcfe98ee1c03e5a6cShukang Zhou      header.truncate()
1658a5ab9102fe705b63eda6e6bcfe98ee1c03e5a6cShukang Zhou      # Copy the lines from the input summary to the output header until
1668a5ab9102fe705b63eda6e6bcfe98ee1c03e5a6cShukang Zhou      # the methods section is seen.
1678a5ab9102fe705b63eda6e6bcfe98ee1c03e5a6cShukang Zhou      for line in self._summary.splitlines(True):
1688a5ab9102fe705b63eda6e6bcfe98ee1c03e5a6cShukang Zhou        if line == "*methods\n":
1698a5ab9102fe705b63eda6e6bcfe98ee1c03e5a6cShukang Zhou          break
1708a5ab9102fe705b63eda6e6bcfe98ee1c03e5a6cShukang Zhou        else:
1718a5ab9102fe705b63eda6e6bcfe98ee1c03e5a6cShukang Zhou          header.write(line)
1728a5ab9102fe705b63eda6e6bcfe98ee1c03e5a6cShukang Zhou    else:
1738a5ab9102fe705b63eda6e6bcfe98ee1c03e5a6cShukang Zhou      header.write('*threads\n')
1748a5ab9102fe705b63eda6e6bcfe98ee1c03e5a6cShukang Zhou      for t in self._threads:
1758a5ab9102fe705b63eda6e6bcfe98ee1c03e5a6cShukang Zhou        header.write(t)
17640da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    header.write('*methods\n')
17740da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    for m in self._methods:
17840da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe      header.write(m)
17940da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    header.write('*end\n')
18040da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
18140da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  def ProcessFile(self, filename):
18240da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    input = open(filename, 'rb')                     # Input file
18340da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    header = open(filename + '.header', 'w')         # Header part
18440da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    body = open(filename + '.body', 'wb')            # Body part
18540da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
18640da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    self.PrintHeader(header)
18740da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
18840da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    self.ProcessDataHeader(input, body)
18940da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
19040da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    self._methods = []
19140da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    self._threads = []
1928a5ab9102fe705b63eda6e6bcfe98ee1c03e5a6cShukang Zhou    self._summary = None
19340da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    self.Process(input, body)
19440da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
19540da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    self.Finalize(header)
19640da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
19740da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    input.close()
19840da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    header.close()
19940da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe    body.close()
20040da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
20140da286d3207d88ed8ff3f5caac4873874603428Andreas Gampedef main():
20240da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  Rewriter().ProcessFile(sys.argv[1])
20340da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  header_name = sys.argv[1] + '.header'
20440da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  body_name = sys.argv[1] + '.body'
20540da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  print 'Results have been written to %s and %s.' % (header_name, body_name)
20640da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  print 'Concatenate the files to get a result usable with traceview.'
20740da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  sys.exit(0)
20840da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe
20940da286d3207d88ed8ff3f5caac4873874603428Andreas Gampeif __name__ == '__main__':
21040da286d3207d88ed8ff3f5caac4873874603428Andreas Gampe  main()