1*1ca3442bSMatthias Ringwald#!/usr/bin/env python 2*1ca3442bSMatthias Ringwald# BlueKitchen GmbH (c) 2014 3*1ca3442bSMatthias Ringwald 4*1ca3442bSMatthias Ringwald# convert log output to PacketLogger format 5*1ca3442bSMatthias Ringwald# can be viewed with Wireshark 6*1ca3442bSMatthias Ringwald 7*1ca3442bSMatthias Ringwald# APPLE PacketLogger 8*1ca3442bSMatthias Ringwald# typedef struct { 9*1ca3442bSMatthias Ringwald# uint32_t len; 10*1ca3442bSMatthias Ringwald# uint32_t ts_sec; 11*1ca3442bSMatthias Ringwald# uint32_t ts_usec; 12*1ca3442bSMatthias Ringwald# uint8_t type; // 0xfc for note 13*1ca3442bSMatthias Ringwald# } 14*1ca3442bSMatthias Ringwald 15*1ca3442bSMatthias Ringwaldimport re 16*1ca3442bSMatthias Ringwaldimport sys 17*1ca3442bSMatthias Ringwaldimport time 18*1ca3442bSMatthias Ringwald 19*1ca3442bSMatthias Ringwaldpacket_counter = 0 20*1ca3442bSMatthias Ringwaldlast_time = None 21*1ca3442bSMatthias Ringwald 22*1ca3442bSMatthias Ringwalddef chop(line, prefix): 23*1ca3442bSMatthias Ringwald if line.startswith(prefix): 24*1ca3442bSMatthias Ringwald return line[len(prefix):] 25*1ca3442bSMatthias Ringwald return None 26*1ca3442bSMatthias Ringwald 27*1ca3442bSMatthias Ringwalddef str2hex(value): 28*1ca3442bSMatthias Ringwald if value: 29*1ca3442bSMatthias Ringwald return int(value, 16) 30*1ca3442bSMatthias Ringwald return None 31*1ca3442bSMatthias Ringwald 32*1ca3442bSMatthias Ringwalddef arrayForNet32(value): 33*1ca3442bSMatthias Ringwald return bytearray([value >> 24, (value >> 16) & 0xff, (value >> 8) & 0xff, value & 0xff]) 34*1ca3442bSMatthias Ringwald 35*1ca3442bSMatthias Ringwalddef generateTimestamp(t): 36*1ca3442bSMatthias Ringwald global last_time 37*1ca3442bSMatthias Ringwald global packet_counter 38*1ca3442bSMatthias Ringwald 39*1ca3442bSMatthias Ringwald # use last_time if time missing for this entry 40*1ca3442bSMatthias Ringwald if not t: 41*1ca3442bSMatthias Ringwald t = last_time 42*1ca3442bSMatthias Ringwald if t: 43*1ca3442bSMatthias Ringwald last_time = t 44*1ca3442bSMatthias Ringwald # handle ms 45*1ca3442bSMatthias Ringwald try: 46*1ca3442bSMatthias Ringwald (t1, t2) = t.split('.') 47*1ca3442bSMatthias Ringwald if t1 and t2: 48*1ca3442bSMatthias Ringwald t_obj = time.strptime(t1, "%Y-%m-%d %H:%M:%S") 49*1ca3442bSMatthias Ringwald tv_sec = int(time.mktime(t_obj)) 50*1ca3442bSMatthias Ringwald tv_usec = int(t2) * 1000 51*1ca3442bSMatthias Ringwald return (tv_sec, tv_usec) 52*1ca3442bSMatthias Ringwald except ValueError: 53*1ca3442bSMatthias Ringwald # print 'Cannot parse time', t 54*1ca3442bSMatthias Ringwald pass 55*1ca3442bSMatthias Ringwald packet_counter += 1 56*1ca3442bSMatthias Ringwald return (packet_counter, 0) 57*1ca3442bSMatthias Ringwald 58*1ca3442bSMatthias Ringwalddef dumpPacket(fout, timestamp, type, data): 59*1ca3442bSMatthias Ringwald length = 9 + len(data) 60*1ca3442bSMatthias Ringwald (tv_sec, tv_usec) = generateTimestamp(timestamp) 61*1ca3442bSMatthias Ringwald fout.write(arrayForNet32(length)) 62*1ca3442bSMatthias Ringwald fout.write(arrayForNet32(tv_sec)) 63*1ca3442bSMatthias Ringwald fout.write(arrayForNet32(tv_usec)) 64*1ca3442bSMatthias Ringwald fout.write(bytearray([type])) 65*1ca3442bSMatthias Ringwald fout.write(data) 66*1ca3442bSMatthias Ringwald 67*1ca3442bSMatthias Ringwalddef handleHexPacket(fout, timestamp, type, text): 68*1ca3442bSMatthias Ringwald try: 69*1ca3442bSMatthias Ringwald data = bytearray(map(str2hex, text.strip().split())) 70*1ca3442bSMatthias Ringwald dumpPacket(fout, timestamp, type, data) 71*1ca3442bSMatthias Ringwald except TypeError: 72*1ca3442bSMatthias Ringwald print 'Cannot parse hexdump', text.strip() 73*1ca3442bSMatthias Ringwald 74*1ca3442bSMatthias Ringwaldif len(sys.argv) == 1: 75*1ca3442bSMatthias Ringwald print 'BTstack Console to PacketLogger converter' 76*1ca3442bSMatthias Ringwald print 'Copyright 2014, BlueKitchen GmbH' 77*1ca3442bSMatthias Ringwald print '' 78*1ca3442bSMatthias Ringwald print 'Usage: ', sys.argv[0], 'asci-log-file.txt [hci_dump.pkgl]' 79*1ca3442bSMatthias Ringwald print 'Converted hci_dump.pklg can be viewed with Wireshark and OS X PacketLogger' 80*1ca3442bSMatthias Ringwald exit(0) 81*1ca3442bSMatthias Ringwald 82*1ca3442bSMatthias Ringwaldinfile = sys.argv[1] 83*1ca3442bSMatthias Ringwaldoutfile = 'hci_dump.pklg' 84*1ca3442bSMatthias Ringwaldif len(sys.argv) > 2: 85*1ca3442bSMatthias Ringwald outfile = sys.argv[2] 86*1ca3442bSMatthias Ringwald 87*1ca3442bSMatthias Ringwald# with open(outfile, 'w') as fout: 88*1ca3442bSMatthias Ringwaldwith open (outfile, 'wb') as fout: 89*1ca3442bSMatthias Ringwald with open (infile, 'rb') as fin: 90*1ca3442bSMatthias Ringwald packet_counter = 0 91*1ca3442bSMatthias Ringwald for line in fin: 92*1ca3442bSMatthias Ringwald # print line 93*1ca3442bSMatthias Ringwald timestamp = None 94*1ca3442bSMatthias Ringwald parts = parts = re.match('\[(.*)\] (.*)', line) 95*1ca3442bSMatthias Ringwald if parts and len(parts.groups()) == 2: 96*1ca3442bSMatthias Ringwald (timestamp, line) = parts.groups() 97*1ca3442bSMatthias Ringwald rest = chop(line,'CMD => ') 98*1ca3442bSMatthias Ringwald if rest: 99*1ca3442bSMatthias Ringwald handleHexPacket(fout, timestamp, 0, rest) 100*1ca3442bSMatthias Ringwald continue 101*1ca3442bSMatthias Ringwald rest = chop(line,'EVT <= ') 102*1ca3442bSMatthias Ringwald if rest: 103*1ca3442bSMatthias Ringwald handleHexPacket(fout, timestamp, 1, rest) 104*1ca3442bSMatthias Ringwald continue 105*1ca3442bSMatthias Ringwald rest = chop(line,'ACL => ') 106*1ca3442bSMatthias Ringwald if rest: 107*1ca3442bSMatthias Ringwald handleHexPacket(fout, timestamp, 2, rest) 108*1ca3442bSMatthias Ringwald continue 109*1ca3442bSMatthias Ringwald rest = chop(line,'ACL <= ') 110*1ca3442bSMatthias Ringwald if rest: 111*1ca3442bSMatthias Ringwald handleHexPacket(fout, timestamp, 3, rest) 112*1ca3442bSMatthias Ringwald continue 113*1ca3442bSMatthias Ringwald rest = chop(line,'LOG -- ') 114*1ca3442bSMatthias Ringwald if rest: 115*1ca3442bSMatthias Ringwald line = rest 116*1ca3442bSMatthias Ringwald dumpPacket(fout, timestamp, 0xfc, line) 117