xref: /btstack/tool/create_packet_log.py (revision 755c0732ebd94ff8e946ea8b138fa599ff3146a7)
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