11ca3442bSMatthias Ringwald#!/usr/bin/env python 21ca3442bSMatthias Ringwald# BlueKitchen GmbH (c) 2014 31ca3442bSMatthias Ringwald 41ca3442bSMatthias Ringwald# convert log output to PacketLogger format 51ca3442bSMatthias Ringwald# can be viewed with Wireshark 61ca3442bSMatthias Ringwald 71ca3442bSMatthias Ringwald# APPLE PacketLogger 81ca3442bSMatthias Ringwald# typedef struct { 91ca3442bSMatthias Ringwald# uint32_t len; 101ca3442bSMatthias Ringwald# uint32_t ts_sec; 111ca3442bSMatthias Ringwald# uint32_t ts_usec; 121ca3442bSMatthias Ringwald# uint8_t type; // 0xfc for note 131ca3442bSMatthias Ringwald# } 141ca3442bSMatthias Ringwald 151ca3442bSMatthias Ringwaldimport re 161ca3442bSMatthias Ringwaldimport sys 171ca3442bSMatthias Ringwaldimport time 181ca3442bSMatthias Ringwald 191ca3442bSMatthias Ringwaldpacket_counter = 0 201ca3442bSMatthias Ringwaldlast_time = None 211ca3442bSMatthias Ringwald 221ca3442bSMatthias Ringwalddef chop(line, prefix): 231ca3442bSMatthias Ringwald if line.startswith(prefix): 241ca3442bSMatthias Ringwald return line[len(prefix):] 251ca3442bSMatthias Ringwald return None 261ca3442bSMatthias Ringwald 271ca3442bSMatthias Ringwalddef str2hex(value): 281ca3442bSMatthias Ringwald if value: 291ca3442bSMatthias Ringwald return int(value, 16) 301ca3442bSMatthias Ringwald return None 311ca3442bSMatthias Ringwald 321ca3442bSMatthias Ringwalddef arrayForNet32(value): 331ca3442bSMatthias Ringwald return bytearray([value >> 24, (value >> 16) & 0xff, (value >> 8) & 0xff, value & 0xff]) 341ca3442bSMatthias Ringwald 351ca3442bSMatthias Ringwalddef generateTimestamp(t): 361ca3442bSMatthias Ringwald global last_time 371ca3442bSMatthias Ringwald global packet_counter 381ca3442bSMatthias Ringwald 391ca3442bSMatthias Ringwald # use last_time if time missing for this entry 401ca3442bSMatthias Ringwald if not t: 411ca3442bSMatthias Ringwald t = last_time 421ca3442bSMatthias Ringwald if t: 431ca3442bSMatthias Ringwald last_time = t 441ca3442bSMatthias Ringwald # handle ms 451ca3442bSMatthias Ringwald try: 461ca3442bSMatthias Ringwald (t1, t2) = t.split('.') 471ca3442bSMatthias Ringwald if t1 and t2: 481ca3442bSMatthias Ringwald t_obj = time.strptime(t1, "%Y-%m-%d %H:%M:%S") 491ca3442bSMatthias Ringwald tv_sec = int(time.mktime(t_obj)) 501ca3442bSMatthias Ringwald tv_usec = int(t2) * 1000 511ca3442bSMatthias Ringwald return (tv_sec, tv_usec) 521ca3442bSMatthias Ringwald except ValueError: 531ca3442bSMatthias Ringwald # print 'Cannot parse time', t 541ca3442bSMatthias Ringwald pass 551ca3442bSMatthias Ringwald packet_counter += 1 561ca3442bSMatthias Ringwald return (packet_counter, 0) 571ca3442bSMatthias Ringwald 581ca3442bSMatthias Ringwalddef dumpPacket(fout, timestamp, type, data): 591ca3442bSMatthias Ringwald length = 9 + len(data) 601ca3442bSMatthias Ringwald (tv_sec, tv_usec) = generateTimestamp(timestamp) 611ca3442bSMatthias Ringwald fout.write(arrayForNet32(length)) 621ca3442bSMatthias Ringwald fout.write(arrayForNet32(tv_sec)) 631ca3442bSMatthias Ringwald fout.write(arrayForNet32(tv_usec)) 641ca3442bSMatthias Ringwald fout.write(bytearray([type])) 651ca3442bSMatthias Ringwald fout.write(data) 661ca3442bSMatthias Ringwald 671ca3442bSMatthias Ringwalddef handleHexPacket(fout, timestamp, type, text): 681ca3442bSMatthias Ringwald try: 69*f55dd2adSMatthias Ringwald data = bytearray(list(map(str2hex, text.strip().split()))) 701ca3442bSMatthias Ringwald dumpPacket(fout, timestamp, type, data) 711ca3442bSMatthias Ringwald except TypeError: 72*f55dd2adSMatthias Ringwald print('Cannot parse hexdump', text.strip()) 731ca3442bSMatthias Ringwald 741ca3442bSMatthias Ringwaldif len(sys.argv) == 1: 75*f55dd2adSMatthias Ringwald print('BTstack Console to PacketLogger converter') 76*f55dd2adSMatthias Ringwald print('Copyright 2014, BlueKitchen GmbH') 77*f55dd2adSMatthias Ringwald print('') 78*f55dd2adSMatthias Ringwald print('Usage: ', sys.argv[0], 'asci-log-file.txt [hci_dump.pkgl]') 79*f55dd2adSMatthias Ringwald print('Converted hci_dump.pklg can be viewed with Wireshark and OS X PacketLogger') 801ca3442bSMatthias Ringwald exit(0) 811ca3442bSMatthias Ringwald 821ca3442bSMatthias Ringwaldinfile = sys.argv[1] 831ca3442bSMatthias Ringwaldoutfile = 'hci_dump.pklg' 841ca3442bSMatthias Ringwaldif len(sys.argv) > 2: 851ca3442bSMatthias Ringwald outfile = sys.argv[2] 861ca3442bSMatthias Ringwald 871ca3442bSMatthias Ringwald# with open(outfile, 'w') as fout: 881ca3442bSMatthias Ringwaldwith open (outfile, 'wb') as fout: 89*f55dd2adSMatthias Ringwald with open (infile, 'rt') as fin: 901ca3442bSMatthias Ringwald packet_counter = 0 911ca3442bSMatthias Ringwald for line in fin: 921ca3442bSMatthias Ringwald timestamp = None 931ca3442bSMatthias Ringwald parts = parts = re.match('\[(.*)\] (.*)', line) 941ca3442bSMatthias Ringwald if parts and len(parts.groups()) == 2: 951ca3442bSMatthias Ringwald (timestamp, line) = parts.groups() 961ca3442bSMatthias Ringwald rest = chop(line,'CMD => ') 971ca3442bSMatthias Ringwald if rest: 981ca3442bSMatthias Ringwald handleHexPacket(fout, timestamp, 0, rest) 991ca3442bSMatthias Ringwald continue 1001ca3442bSMatthias Ringwald rest = chop(line,'EVT <= ') 1011ca3442bSMatthias Ringwald if rest: 1021ca3442bSMatthias Ringwald handleHexPacket(fout, timestamp, 1, rest) 1031ca3442bSMatthias Ringwald continue 1041ca3442bSMatthias Ringwald rest = chop(line,'ACL => ') 1051ca3442bSMatthias Ringwald if rest: 1061ca3442bSMatthias Ringwald handleHexPacket(fout, timestamp, 2, rest) 1071ca3442bSMatthias Ringwald continue 1081ca3442bSMatthias Ringwald rest = chop(line,'ACL <= ') 1091ca3442bSMatthias Ringwald if rest: 1101ca3442bSMatthias Ringwald handleHexPacket(fout, timestamp, 3, rest) 1111ca3442bSMatthias Ringwald continue 1121ca3442bSMatthias Ringwald rest = chop(line,'LOG -- ') 1131ca3442bSMatthias Ringwald if rest: 1141ca3442bSMatthias Ringwald line = rest 115*f55dd2adSMatthias Ringwald dumpPacket(fout, timestamp, 0xfc, line.encode('ascii')) 116