OSDN Git Service

4d850819a280ac3214ac5c00f13a87e6d824435e
[android-x86/system-bt.git] / tools / scripts / btsnooz.py
1 #!/usr/bin/env python
2 """
3 This script extracts btsnooz content from bugreports and generates
4 a valid btsnoop log file which can be viewed using standard tools
5 like Wireshark.
6
7 btsnooz is a custom format designed to be included in bugreports.
8 It can be described as:
9
10 base64 {
11   file_header
12   deflate {
13     repeated {
14       record_header
15       record_data
16     }
17   }
18 }
19
20 where the file_header and record_header are modified versions of
21 the btsnoop headers.
22 """
23
24
25 import base64
26 import fileinput
27 import struct
28 import sys
29 import zlib
30
31
32 # Enumeration of the values the 'type' field can take in a btsnooz
33 # header. These values come from the Bluetooth stack's internal
34 # representation of packet types.
35 TYPE_IN_EVT = 0x10
36 TYPE_IN_ACL = 0x11
37 TYPE_IN_SCO = 0x12
38 TYPE_OUT_CMD = 0x20
39 TYPE_OUT_ACL = 0x21
40 TYPE_OUT_SCO = 0x22
41
42
43 def type_to_direction(type):
44   """
45   Returns the inbound/outbound direction of a packet given its type.
46   0 = sent packet
47   1 = received packet
48   """
49   if type in [TYPE_IN_EVT, TYPE_IN_ACL, TYPE_IN_SCO]:
50     return 1
51   return 0
52
53
54 def type_to_hci(type):
55   """
56   Returns the HCI type of a packet given its btsnooz type.
57   """
58   if type == TYPE_OUT_CMD:
59     return '\x01'
60   if type == TYPE_IN_ACL or type == TYPE_OUT_ACL:
61     return '\x02'
62   if type == TYPE_IN_SCO or type == TYPE_OUT_SCO:
63     return '\x03'
64   if type == TYPE_IN_EVT:
65     return '\x04'
66
67
68 def decode_snooz(snooz):
69   """
70   Decodes all known versions of a btsnooz file into a btsnoop file.
71   """
72   version, last_timestamp_ms = struct.unpack_from('=bQ', snooz)
73
74   if version != 1 and version != 2:
75     sys.stderr.write('Unsupported btsnooz version: %s\n' % version)
76     exit(1)
77
78   # Oddly, the file header (9 bytes) is not compressed, but the rest is.
79   decompressed = zlib.decompress(snooz[9:])
80
81   sys.stdout.write('btsnoop\x00\x00\x00\x00\x01\x00\x00\x03\xea')
82
83   if version == 1:
84     decode_snooz_v1(decompressed, last_timestamp_ms)
85   elif version == 2:
86     decode_snooz_v2(decompressed, last_timestamp_ms)
87
88
89 def decode_snooz_v1(decompressed, last_timestamp_ms):
90   """
91   Decodes btsnooz v1 files into a btsnoop file.
92   """
93   # An unfortunate consequence of the file format design: we have to do a
94   # pass of the entire file to determine the timestamp of the first packet.
95   first_timestamp_ms = last_timestamp_ms + 0x00dcddb30f2f8000
96   offset = 0
97   while offset < len(decompressed):
98     length, delta_time_ms, type = struct.unpack_from('=HIb', decompressed, offset)
99     offset += 7 + length - 1
100     first_timestamp_ms -= delta_time_ms
101
102   # Second pass does the actual writing out to stdout.
103   offset = 0
104   while offset < len(decompressed):
105     length, delta_time_ms, type = struct.unpack_from('=HIb', decompressed, offset)
106     first_timestamp_ms += delta_time_ms
107     offset += 7
108     sys.stdout.write(struct.pack('>II', length, length))
109     sys.stdout.write(struct.pack('>II', type_to_direction(type), 0))
110     sys.stdout.write(struct.pack('>II', (first_timestamp_ms >> 32), (first_timestamp_ms & 0xFFFFFFFF)))
111     sys.stdout.write(type_to_hci(type))
112     sys.stdout.write(decompressed[offset : offset + length - 1])
113     offset += length - 1
114
115
116 def decode_snooz_v2(decompressed, last_timestamp_ms):
117   """
118   Decodes btsnooz v2 files into a btsnoop file.
119   """
120   # An unfortunate consequence of the file format design: we have to do a
121   # pass of the entire file to determine the timestamp of the first packet.
122   first_timestamp_ms = last_timestamp_ms + 0x00dcddb30f2f8000
123   offset = 0
124   while offset < len(decompressed):
125     length, packet_length, delta_time_ms, snooz_type = struct.unpack_from('=HHIb', decompressed, offset)
126     offset += 9 + length - 1
127     first_timestamp_ms -= delta_time_ms
128
129   # Second pass does the actual writing out to stdout.
130   offset = 0
131   while offset < len(decompressed):
132     length, packet_length, delta_time_ms, snooz_type = struct.unpack_from('=HHIb', decompressed, offset)
133     first_timestamp_ms += delta_time_ms
134     offset += 9
135     sys.stdout.write(struct.pack('>II', packet_length, length))
136     sys.stdout.write(struct.pack('>II', type_to_direction(snooz_type), 0))
137     sys.stdout.write(struct.pack('>II', (first_timestamp_ms >> 32), (first_timestamp_ms & 0xFFFFFFFF)))
138     sys.stdout.write(type_to_hci(snooz_type))
139     sys.stdout.write(decompressed[offset : offset + length - 1])
140     offset += length - 1
141
142
143 def main():
144   if len(sys.argv) > 2:
145     sys.stderr.write('Usage: %s [bugreport]\n' % sys.argv[0])
146     exit(1)
147
148   iterator = fileinput.input()
149   for line in iterator:
150     if line.find('--- BEGIN:BTSNOOP_LOG_SUMMARY') != -1:
151       decode_snooz(base64.standard_b64decode(iterator.next()))
152       sys.exit(0)
153   sys.stderr.write('No btsnooz section found in bugreport.\n');
154
155
156 if __name__ == '__main__':
157   main()