tools: add the parse_log script we used to dump the raw btsnoop logs

Better keep this in a public place where it won't vanish from our disks.
Note that this is a python 2 script (because btsnoop), and it should not
be part of the installation, ever.
This commit is contained in:
Benjamin Tissoires 2018-02-01 15:24:45 +01:00 committed by Peter Hutterer
parent 31e1f2ad3b
commit 0c829754cd
1 changed files with 168 additions and 0 deletions

168
tools/parse_log.py Executable file
View File

@ -0,0 +1,168 @@
#!/usr/bin/env python
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# This Python 2 program allows to translate btsnoop capture files to
# raw data coming from the various endpoints.
#
# You need to retrieve a btsnoop capture file from Android:
# * Set up your device you want to snoop with your Android phone
# * Install some Android file manager
# * Enable developer mode on your Android device
# * In Settings - General - Developer Options, enable "Bluetooth HCI snoop
# log". This will log all bluetooth traffic to a file
# `/Android/data/btsnoop_hci.log` (the location may differ, search for it)
# * Use the app to produce some bluetooth data you want to capture
# * disable bluetooth snooping
# * Copy the `btsnoop_hci.log` file into `Downloads`, connect the Android
# device to a computer and download the file. Or mail it to yourself. Or
# whatever other way you find to get that file onto your computer.
from __future__ import print_function
import sys
import binascii
# https://github.com/joekickass/python-btsnoop
import btsnoop.btsnoop.btsnoop as btsnoop
import btsnoop.bt.hci_uart as hci_uart
import btsnoop.bt.hci_acl as hci_acl
import btsnoop.bt.l2cap as l2cap
import btsnoop.bt.att as att
NORDIC_UART_SERVICE_UUID = '6e400001-b5a3-f393-e0a9-e50e24dcca9e'
NORDIC_UART_CHRC_TX_UUID = '6e400002-b5a3-f393-e0a9-e50e24dcca9e'
NORDIC_UART_CHRC_RX_UUID = '6e400003-b5a3-f393-e0a9-e50e24dcca9e'
WACOM_LIVE_SERVICE_UUID = '00001523-1212-efde-1523-785feabcd123'
WACOM_CHRC_LIVE_PEN_DATA_UUID = '00001524-1212-efde-1523-785feabcd123'
WACOM_OFFLINE_SERVICE_UUID = 'ffee0001-bbaa-9988-7766-554433221100'
WACOM_OFFLINE_FW_DATA_UUID = 'ffee0002-bbaa-9988-7766-554433221100'
WACOM_OFFLINE_CHRC_PEN_DATA_UUID = 'ffee0003-bbaa-9988-7766-554433221100'
MYSTERIOUS_NOTIFICATION_SERVICE_UUID = '3a340720-c572-11e5-86c5-0002a5d5c51b'
MYSTERIOUS_NOTIFICATION_CHRC_UUID = '3a340721-c572-11e5-86c5-0002a5d5c51b'
# http://developer.nordicsemi.com/nRF51_SDK/nRF51_SDK_v7.x.x/doc/7.2.0/s110/html/a00071.html#ota_spec_sec
NORDIC_DFU_SERVICE_UUID = '00001530-1212-efde-1523-785feabcd123'
NORDIC_DFU_CTL_POINT_CHRC_UUID = '00001531-1212-efde-1523-785feabcd123'
NORDIC_DFU_PACKET_CHRC_UUID = '00001532-1212-efde-1523-785feabcd123'
NORDIC_DFU_UNKNONWN_CHRC_UUID = '00001534-1212-efde-1523-785feabcd123'
desc_uuids = {
NORDIC_UART_SERVICE_UUID: 'NORDIC_UART_SERVICE_UUID',
NORDIC_UART_CHRC_TX_UUID: 'Nordic UART TX -->',
NORDIC_UART_CHRC_RX_UUID: 'Nordic UART RX <--',
NORDIC_DFU_SERVICE_UUID: 'NORDIC_DFU_SERVICE_UUID',
NORDIC_DFU_CTL_POINT_CHRC_UUID: 'Nordic DFU Ctl Point',
NORDIC_DFU_PACKET_CHRC_UUID: 'Nordic DFU packet',
NORDIC_DFU_UNKNONWN_CHRC_UUID: 'Nordic DFU Unknown',
WACOM_LIVE_SERVICE_UUID: 'WACOM_LIVE_SERVICE_UUID',
WACOM_CHRC_LIVE_PEN_DATA_UUID: 'Wacom Live <----',
WACOM_OFFLINE_SERVICE_UUID: 'WACOM_OFFLINE_SERVICE_UUID',
WACOM_OFFLINE_FW_DATA_UUID: 'Sending FW Data --->',
WACOM_OFFLINE_CHRC_PEN_DATA_UUID: 'Wacom RX <----',
MYSTERIOUS_NOTIFICATION_SERVICE_UUID: 'MYSTERIOUS_NOTIFICATION_SERVICE_UUID',
MYSTERIOUS_NOTIFICATION_CHRC_UUID: 'Mysterious Notification',
}
handles = {}
def att_data_to_uuid(data):
# reverse the string
data = data[::-1]
uuid = binascii.hexlify(data[:4]) + '-' + \
binascii.hexlify(data[4:6]) + '-' + \
binascii.hexlify(data[6:8]) + '-' + \
binascii.hexlify(data[8:10]) + '-' + \
binascii.hexlify(data[10:])
return uuid
def get_rows(records):
rows = []
for record in records:
seq_nbr = record[0]
# time = record[3].strftime("%b-%d %H:%M:%S.%f")
hci_pkt_type, hci_pkt_data = hci_uart.parse(record[4])
# hci = hci_uart.type_to_str(hci_pkt_type)
if hci_pkt_type != hci_uart.ACL_DATA:
continue
hci_data = hci_acl.parse(hci_pkt_data)
l2cap_length, l2cap_cid, l2cap_data = l2cap.parse(hci_data[2], hci_data[4])
if l2cap_cid != l2cap.L2CAP_CID_ATT:
continue
att_opcode, att_data = att.parse(l2cap_data)
# cmd_evt_l2cap = att.opcode_to_str(att_opcode)
data = att_data
if att_opcode == 0x11:
length = ord(data[0])
if length == 20:
start = binascii.hexlify(data[1:3])
end = binascii.hexlify(data[3:5])
print('{:>6} service handle from {} to {}: {} '.format(seq_nbr, start, end, att_data_to_uuid(data[5:])))
continue
elif att_opcode == 0x09:
length = ord(data[0])
if length == 21:
value_handle = binascii.hexlify(data[4:6])
uuid = att_data_to_uuid(data[6:])
desc_uuid = uuid
try:
desc_uuid = desc_uuids[uuid]
except KeyError:
pass
print('{:>6} chrc at handle {}: {}'.format(seq_nbr, value_handle, uuid))
handles[value_handle] = (uuid, desc_uuid)
continue
if att_opcode not in [0x52, 0x1b]:
continue
data = binascii.hexlify(data)
handle = data[:4]
if handle not in handles:
continue
rows.append(['{:>6}'.format(seq_nbr), handles[handle][1], data[4:]])
return rows
def main(filename):
records = btsnoop.parse(filename)
rows = get_rows(records)
for r in rows:
print(' '.join(r))
if __name__ == "__main__":
if len(sys.argv) == 2:
main(sys.argv[1])
else:
sys.exit(-1)