15c544019SMatthias Ringwald#!/usr/bin/env python3 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 18d398ec02SMatthias Ringwaldimport os 191ca3442bSMatthias Ringwald 20ad6274a7SMatthias Ringwalddefault_date="2001-01-01" 21ad6274a7SMatthias Ringwalddefault_hours = 12 221ca3442bSMatthias Ringwaldpacket_counter = 0 23ad6274a7SMatthias Ringwaldlast_time = default_date + " " + str(default_hours) + ":00:00.000" 241ca3442bSMatthias Ringwald 251ca3442bSMatthias Ringwalddef chop(line, prefix): 261ca3442bSMatthias Ringwald if line.startswith(prefix): 271ca3442bSMatthias Ringwald return line[len(prefix):] 281ca3442bSMatthias Ringwald return None 291ca3442bSMatthias Ringwald 301ca3442bSMatthias Ringwalddef str2hex(value): 311ca3442bSMatthias Ringwald if value: 321ca3442bSMatthias Ringwald return int(value, 16) 331ca3442bSMatthias Ringwald return None 341ca3442bSMatthias Ringwald 351ca3442bSMatthias Ringwalddef arrayForNet32(value): 361ca3442bSMatthias Ringwald return bytearray([value >> 24, (value >> 16) & 0xff, (value >> 8) & 0xff, value & 0xff]) 371ca3442bSMatthias Ringwald 381ca3442bSMatthias Ringwalddef generateTimestamp(t): 391ca3442bSMatthias Ringwald global last_time 401ca3442bSMatthias Ringwald global packet_counter 411ca3442bSMatthias Ringwald 421ca3442bSMatthias Ringwald # use last_time if time missing for this entry 431ca3442bSMatthias Ringwald if not t: 441ca3442bSMatthias Ringwald t = last_time 451ca3442bSMatthias Ringwald if t: 461ca3442bSMatthias Ringwald last_time = t 47ad6274a7SMatthias Ringwald 48ad6274a7SMatthias Ringwald # check for date 49ad6274a7SMatthias Ringwald parts = t.split(' ') 50ad6274a7SMatthias Ringwald have_date = True 51ad6274a7SMatthias Ringwald if len(parts) == 1: 52ad6274a7SMatthias Ringwald # only time, prepend fixed date 53ad6274a7SMatthias Ringwald have_date = False 54ad6274a7SMatthias Ringwald t = "2000-01-01 " + t; 55ad6274a7SMatthias Ringwald 561ca3442bSMatthias Ringwald # handle ms 571ca3442bSMatthias Ringwald try: 581ca3442bSMatthias Ringwald (t1, t2) = t.split('.') 591ca3442bSMatthias Ringwald if t1 and t2: 601ca3442bSMatthias Ringwald t_obj = time.strptime(t1, "%Y-%m-%d %H:%M:%S") 611ca3442bSMatthias Ringwald tv_sec = int(time.mktime(t_obj)) 62ad6274a7SMatthias Ringwald if not have_date: 63ad6274a7SMatthias Ringwald # start at 12:00 64ad6274a7SMatthias Ringwald tv_sec += 12*60*60 651ca3442bSMatthias Ringwald tv_usec = int(t2) * 1000 661ca3442bSMatthias Ringwald return (tv_sec, tv_usec) 671ca3442bSMatthias Ringwald except ValueError: 681ca3442bSMatthias Ringwald # print 'Cannot parse time', t 691ca3442bSMatthias Ringwald pass 70ad6274a7SMatthias Ringwald 711ca3442bSMatthias Ringwald packet_counter += 1 721ca3442bSMatthias Ringwald return (packet_counter, 0) 731ca3442bSMatthias Ringwald 741ca3442bSMatthias Ringwalddef dumpPacket(fout, timestamp, type, data): 751ca3442bSMatthias Ringwald length = 9 + len(data) 761ca3442bSMatthias Ringwald (tv_sec, tv_usec) = generateTimestamp(timestamp) 771ca3442bSMatthias Ringwald fout.write(arrayForNet32(length)) 781ca3442bSMatthias Ringwald fout.write(arrayForNet32(tv_sec)) 791ca3442bSMatthias Ringwald fout.write(arrayForNet32(tv_usec)) 801ca3442bSMatthias Ringwald fout.write(bytearray([type])) 811ca3442bSMatthias Ringwald fout.write(data) 821ca3442bSMatthias Ringwald 831ca3442bSMatthias Ringwalddef handleHexPacket(fout, timestamp, type, text): 841ca3442bSMatthias Ringwald try: 85f55dd2adSMatthias Ringwald data = bytearray(list(map(str2hex, text.strip().split()))) 861ca3442bSMatthias Ringwald dumpPacket(fout, timestamp, type, data) 871ca3442bSMatthias Ringwald except TypeError: 88f55dd2adSMatthias Ringwald print('Cannot parse hexdump', text.strip()) 891ca3442bSMatthias Ringwald 901ca3442bSMatthias Ringwaldif len(sys.argv) == 1: 91f55dd2adSMatthias Ringwald print('BTstack Console to PacketLogger converter') 92f55dd2adSMatthias Ringwald print('Copyright 2014, BlueKitchen GmbH') 93f55dd2adSMatthias Ringwald print('') 945c544019SMatthias Ringwald print('Usage: ', sys.argv[0], 'ascii-log-file.txt [hci_dump.pklg]') 95f55dd2adSMatthias Ringwald print('Converted hci_dump.pklg can be viewed with Wireshark and OS X PacketLogger') 961ca3442bSMatthias Ringwald exit(0) 971ca3442bSMatthias Ringwald 981ca3442bSMatthias Ringwaldinfile = sys.argv[1] 99d398ec02SMatthias Ringwaldoutfile = os.path.splitext(infile)[0] + ".pklg" 1001ca3442bSMatthias Ringwaldif len(sys.argv) > 2: 1011ca3442bSMatthias Ringwald outfile = sys.argv[2] 1021ca3442bSMatthias Ringwald 1031ca3442bSMatthias Ringwald# with open(outfile, 'w') as fout: 1041ca3442bSMatthias Ringwaldwith open (outfile, 'wb') as fout: 105f55dd2adSMatthias Ringwald with open (infile, 'rt') as fin: 1061ca3442bSMatthias Ringwald packet_counter = 0 1079aa76d51SMatthias Ringwald line_conter = 0 1081ca3442bSMatthias Ringwald for line in fin: 1099aa76d51SMatthias Ringwald try: 110e698d63dSMatthias Ringwald # try to deal with windows 16-bit unicode by dropping \0 characters 111e698d63dSMatthias Ringwald line = ''.join([c for c in line if c != '\0']) 112dde2e0f0SMatthias Ringwald # drop Segger RTT console prefix 113dde2e0f0SMatthias Ringwald if line.startswith('00> '): 114dde2e0f0SMatthias Ringwald line = line[4:] 1159aa76d51SMatthias Ringwald line_conter += 1 1161ca3442bSMatthias Ringwald timestamp = None 117e4aea38eSMatthias Ringwald # strip newlines 118e4aea38eSMatthias Ringwald line = line.strip("\n\r") 119e698d63dSMatthias Ringwald # skip empty lines 120e4aea38eSMatthias Ringwald if len(line) == 0: 121e4aea38eSMatthias Ringwald continue 122ad6274a7SMatthias Ringwald parts = re.match('\[(.*)\] (.*)', line) 1231ca3442bSMatthias Ringwald if parts and len(parts.groups()) == 2: 1241ca3442bSMatthias Ringwald (timestamp, line) = parts.groups() 1251ca3442bSMatthias Ringwald rest = chop(line,'CMD => ') 1261ca3442bSMatthias Ringwald if rest: 1271ca3442bSMatthias Ringwald handleHexPacket(fout, timestamp, 0, rest) 1281ca3442bSMatthias Ringwald continue 1291ca3442bSMatthias Ringwald rest = chop(line,'EVT <= ') 1301ca3442bSMatthias Ringwald if rest: 1311ca3442bSMatthias Ringwald handleHexPacket(fout, timestamp, 1, rest) 1321ca3442bSMatthias Ringwald continue 1331ca3442bSMatthias Ringwald rest = chop(line,'ACL => ') 1341ca3442bSMatthias Ringwald if rest: 1351ca3442bSMatthias Ringwald handleHexPacket(fout, timestamp, 2, rest) 1361ca3442bSMatthias Ringwald continue 1371ca3442bSMatthias Ringwald rest = chop(line,'ACL <= ') 1381ca3442bSMatthias Ringwald if rest: 1391ca3442bSMatthias Ringwald handleHexPacket(fout, timestamp, 3, rest) 1401ca3442bSMatthias Ringwald continue 141811ddedbSMatthias Ringwald rest = chop(line,'SCO => ') 142811ddedbSMatthias Ringwald if rest: 143811ddedbSMatthias Ringwald handleHexPacket(fout, timestamp, 8, rest) 144811ddedbSMatthias Ringwald continue 145811ddedbSMatthias Ringwald rest = chop(line,'SCO <= ') 146811ddedbSMatthias Ringwald if rest: 147811ddedbSMatthias Ringwald handleHexPacket(fout, timestamp, 9, rest) 148811ddedbSMatthias Ringwald continue 149*755c0732SMatthias Ringwald rest = chop(line,'ISO => ') 150*755c0732SMatthias Ringwald if rest: 151*755c0732SMatthias Ringwald handleHexPacket(fout, timestamp, 0x0c, rest) 152*755c0732SMatthias Ringwald continue 153*755c0732SMatthias Ringwald rest = chop(line,'ISO <= ') 154*755c0732SMatthias Ringwald if rest: 155*755c0732SMatthias Ringwald handleHexPacket(fout, timestamp, 0x0d, rest) 156*755c0732SMatthias Ringwald continue 1571ca3442bSMatthias Ringwald rest = chop(line,'LOG -- ') 1581ca3442bSMatthias Ringwald if rest: 1591ca3442bSMatthias Ringwald line = rest 160f55dd2adSMatthias Ringwald dumpPacket(fout, timestamp, 0xfc, line.encode('ascii')) 1619aa76d51SMatthias Ringwald except: 1629aa76d51SMatthias Ringwald print("Error in line %u: '%s'" % (line_conter, line)) 1639aa76d51SMatthias Ringwald 1649aa76d51SMatthias Ringwaldprint("\nPacket Log: %s" % outfile) 165