11c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati#!/usr/bin/env python
21c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati"""
31c50abb87294b033d1604820d34ba5853d5bf253Sharvil NanavatiThis script extracts btsnooz content from bugreports and generates
41c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavatia valid btsnoop log file which can be viewed using standard tools
51c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavatilike Wireshark.
61c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati
71c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavatibtsnooz is a custom format designed to be included in bugreports.
81c50abb87294b033d1604820d34ba5853d5bf253Sharvil NanavatiIt can be described as:
91c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati
101c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavatibase64 {
111c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  file_header
121c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  deflate {
131c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    repeated {
141c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati      record_header
151c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati      record_data
161c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    }
171c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  }
181c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati}
191c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati
201c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavatiwhere the file_header and record_header are modified versions of
211c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavatithe btsnoop headers.
221c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati"""
231c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati
241c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati
251c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavatiimport base64
26700b163a3e5673a47df8de9509e971bb10f0580eSharvil Nanavatiimport fileinput
271c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavatiimport struct
281c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavatiimport sys
291c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavatiimport zlib
301c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati
311c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati
321c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati# Enumeration of the values the 'type' field can take in a btsnooz
331c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati# header. These values come from the Bluetooth stack's internal
341c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati# representation of packet types.
351c50abb87294b033d1604820d34ba5853d5bf253Sharvil NanavatiTYPE_IN_EVT = 0x10
361c50abb87294b033d1604820d34ba5853d5bf253Sharvil NanavatiTYPE_IN_ACL = 0x11
371c50abb87294b033d1604820d34ba5853d5bf253Sharvil NanavatiTYPE_IN_SCO = 0x12
381c50abb87294b033d1604820d34ba5853d5bf253Sharvil NanavatiTYPE_OUT_CMD = 0x20
391c50abb87294b033d1604820d34ba5853d5bf253Sharvil NanavatiTYPE_OUT_ACL = 0x21
401c50abb87294b033d1604820d34ba5853d5bf253Sharvil NanavatiTYPE_OUT_SCO = 0x22
411c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati
421c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati
431c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavatidef type_to_direction(type):
441c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  """
451c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  Returns the inbound/outbound direction of a packet given its type.
461c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  0 = sent packet
471c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  1 = received packet
481c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  """
491c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  if type in [TYPE_IN_EVT, TYPE_IN_ACL, TYPE_IN_SCO]:
501c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    return 1
511c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  return 0
521c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati
531c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati
541c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavatidef type_to_hci(type):
551c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  """
561c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  Returns the HCI type of a packet given its btsnooz type.
571c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  """
581c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  if type == TYPE_OUT_CMD:
591c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    return '\x01'
601c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  if type == TYPE_IN_ACL or type == TYPE_OUT_ACL:
611c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    return '\x02'
621c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  if type == TYPE_IN_SCO or type == TYPE_OUT_SCO:
631c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    return '\x03'
641c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  if type == TYPE_IN_EVT:
651c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    return '\x04'
661c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati
671c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati
681c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavatidef decode_snooz(snooz):
691c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  """
701c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  Decodes all known versions of a btsnooz file into a btsnoop file.
711c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  """
721c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  version, last_timestamp_ms = struct.unpack_from('=bQ', snooz)
731c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati
741c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  if version != 1 and version != 2:
751c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    sys.stderr.write('Unsupported btsnooz version: %s\n' % version)
761c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    exit(1)
771c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati
781c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  # Oddly, the file header (9 bytes) is not compressed, but the rest is.
791c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  decompressed = zlib.decompress(snooz[9:])
801c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati
811c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  sys.stdout.write('btsnoop\x00\x00\x00\x00\x01\x00\x00\x03\xea')
821c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati
831c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  if version == 1:
841c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    decode_snooz_v1(decompressed, last_timestamp_ms)
851c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  elif version == 2:
861c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    decode_snooz_v2(decompressed, last_timestamp_ms)
871c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati
881c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati
891c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavatidef decode_snooz_v1(decompressed, last_timestamp_ms):
901c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  """
911c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  Decodes btsnooz v1 files into a btsnoop file.
921c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  """
931c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  # An unfortunate consequence of the file format design: we have to do a
941c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  # pass of the entire file to determine the timestamp of the first packet.
951c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  first_timestamp_ms = last_timestamp_ms + 0x00dcddb30f2f8000
961c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  offset = 0
971c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  while offset < len(decompressed):
981c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    length, delta_time_ms, type = struct.unpack_from('=HIb', decompressed, offset)
991c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    offset += 7 + length - 1
1001c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    first_timestamp_ms -= delta_time_ms
1011c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati
1021c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  # Second pass does the actual writing out to stdout.
1031c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  offset = 0
1041c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  while offset < len(decompressed):
1051c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    length, delta_time_ms, type = struct.unpack_from('=HIb', decompressed, offset)
1061c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    first_timestamp_ms += delta_time_ms
1071c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    offset += 7
1081c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    sys.stdout.write(struct.pack('>II', length, length))
1091c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    sys.stdout.write(struct.pack('>II', type_to_direction(type), 0))
1101c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    sys.stdout.write(struct.pack('>II', (first_timestamp_ms >> 32), (first_timestamp_ms & 0xFFFFFFFF)))
1111c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    sys.stdout.write(type_to_hci(type))
1121c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    sys.stdout.write(decompressed[offset : offset + length - 1])
1131c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    offset += length - 1
1141c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati
1151c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati
1161c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavatidef decode_snooz_v2(decompressed, last_timestamp_ms):
1171c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  """
1181c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  Decodes btsnooz v2 files into a btsnoop file.
1191c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  """
1201c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  # An unfortunate consequence of the file format design: we have to do a
1211c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  # pass of the entire file to determine the timestamp of the first packet.
1221c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  first_timestamp_ms = last_timestamp_ms + 0x00dcddb30f2f8000
1231c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  offset = 0
1241c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  while offset < len(decompressed):
1251c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    length, packet_length, delta_time_ms, snooz_type = struct.unpack_from('=HHIb', decompressed, offset)
1261c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    offset += 9 + length - 1
1271c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    first_timestamp_ms -= delta_time_ms
1281c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati
1291c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  # Second pass does the actual writing out to stdout.
1301c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  offset = 0
1311c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  while offset < len(decompressed):
1321c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    length, packet_length, delta_time_ms, snooz_type = struct.unpack_from('=HHIb', decompressed, offset)
1331c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    first_timestamp_ms += delta_time_ms
1341c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    offset += 9
1351c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    sys.stdout.write(struct.pack('>II', packet_length, length))
1361c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    sys.stdout.write(struct.pack('>II', type_to_direction(snooz_type), 0))
1371c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    sys.stdout.write(struct.pack('>II', (first_timestamp_ms >> 32), (first_timestamp_ms & 0xFFFFFFFF)))
1381c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    sys.stdout.write(type_to_hci(snooz_type))
1391c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    sys.stdout.write(decompressed[offset : offset + length - 1])
1401c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    offset += length - 1
1411c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati
1421c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati
1431c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavatidef main():
144700b163a3e5673a47df8de9509e971bb10f0580eSharvil Nanavati  if len(sys.argv) > 2:
145700b163a3e5673a47df8de9509e971bb10f0580eSharvil Nanavati    sys.stderr.write('Usage: %s [bugreport]\n' % sys.argv[0])
1461c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati    exit(1)
1471c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati
148700b163a3e5673a47df8de9509e971bb10f0580eSharvil Nanavati  iterator = fileinput.input()
1492b70aa4406d8b3fcc26c191896a30db37b0846bbAndre Eisenbach  found = False
1502b70aa4406d8b3fcc26c191896a30db37b0846bbAndre Eisenbach  base64_string = ""
151700b163a3e5673a47df8de9509e971bb10f0580eSharvil Nanavati  for line in iterator:
1522b70aa4406d8b3fcc26c191896a30db37b0846bbAndre Eisenbach    if found:
1532b70aa4406d8b3fcc26c191896a30db37b0846bbAndre Eisenbach      if line.find('--- END:BTSNOOP_LOG_SUMMARY') != -1:
1542b70aa4406d8b3fcc26c191896a30db37b0846bbAndre Eisenbach        decode_snooz(base64.standard_b64decode(base64_string))
1552b70aa4406d8b3fcc26c191896a30db37b0846bbAndre Eisenbach        sys.exit(0)
1562b70aa4406d8b3fcc26c191896a30db37b0846bbAndre Eisenbach      base64_string += line.strip()
1572b70aa4406d8b3fcc26c191896a30db37b0846bbAndre Eisenbach
1587b6db95593e40e36a013b665334f391e62f19d1cAjay Panicker    if line.find('--- BEGIN:BTSNOOP_LOG_SUMMARY') != -1:
1592b70aa4406d8b3fcc26c191896a30db37b0846bbAndre Eisenbach      found = True
1602b70aa4406d8b3fcc26c191896a30db37b0846bbAndre Eisenbach
1612b70aa4406d8b3fcc26c191896a30db37b0846bbAndre Eisenbach  if not found:
1622b70aa4406d8b3fcc26c191896a30db37b0846bbAndre Eisenbach    sys.stderr.write('No btsnooz section found in bugreport.\n')
1632b70aa4406d8b3fcc26c191896a30db37b0846bbAndre Eisenbach    sys.exit(1)
1641c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati
1651c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati
1661c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavatiif __name__ == '__main__':
1671c50abb87294b033d1604820d34ba5853d5bf253Sharvil Nanavati  main()
168