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