From 584c4a3cd6cf75be838f0a41480164e223229bc0 Mon Sep 17 00:00:00 2001 From: Brad Murray Date: Thu, 29 Aug 2013 15:48:50 -0400 Subject: [PATCH] Implement log dumping in libpebble --- p.py | 11 ++++++ pebble/LightBluePebble.py | 2 +- pebble/pebble.py | 70 +++++++++++++++++++++++++++++++++------ 3 files changed, 72 insertions(+), 11 deletions(-) diff --git a/p.py b/p.py index 7a6342a..3cfa50b 100755 --- a/p.py +++ b/p.py @@ -90,6 +90,13 @@ def cmd_logcat(pebble, args): except KeyboardInterrupt: return +def cmd_log_dump(pebble, args): + if args.generation_number is not None: + pebble.dump_logs(args.generation_number) + else: + for i in xrange(4): + pebble.dump_logs(i) + def cmd_list_apps(pebble, args): apps = pebble.get_appbank_status() if apps is not False: @@ -194,6 +201,10 @@ def main(): logcat_parser = subparsers.add_parser('logcat', help='view logs sent from a connected watch') logcat_parser.set_defaults(func=cmd_logcat) + log_dump_parser = subparsers.add_parser('log_dump', help='dump logs stored on the watch for previous generations') + log_dump_parser.add_argument('--generation_number', type=int, help='the generation to dump') + log_dump_parser.set_defaults(func=cmd_log_dump) + list_apps_parser = subparsers.add_parser('list', help='list installed apps') list_apps_parser.set_defaults(func=cmd_list_apps) diff --git a/pebble/LightBluePebble.py b/pebble/LightBluePebble.py index 732f031..e593b53 100644 --- a/pebble/LightBluePebble.py +++ b/pebble/LightBluePebble.py @@ -160,7 +160,7 @@ def autodetect(self): # TODO: Should probably have some kind of timeout here pass try: - print (endpoint, resp, rec_data) + # print (endpoint, resp, rec_data) self.rec_queue.put((endpoint, resp, rec_data)) except (IOError, EOFError): diff --git a/pebble/pebble.py b/pebble/pebble.py index 553901b..db6b0f7 100755 --- a/pebble/pebble.py +++ b/pebble/pebble.py @@ -1,6 +1,7 @@ #!/usr/bin/env python import binascii +import datetime import glob import itertools import json @@ -714,6 +715,54 @@ def reset(self): self._send_message("RESET", "\x00") + def dump_logs(self, generation_number): + """Dump the saved logs from the watch. + + Arguments: + generation_number -- The genration to dump, where 0 is the current boot and 3 is the oldest boot. + """ + + if generation_number > 3: + raise Exception("Invalid generation number %u, should be [0-3]" % generation_number) + + log.info('=== Generation %u ===' % generation_number) + + class LogDumpClient(object): + def __init__(self, pebble): + self.done = False + self._pebble = pebble + + def parse_log_dump_response(self, endpoint, data): + if (len(data) < 5): + log.warn("Unable to decode log dump message (length %d is less than 8)" % len(data)) + return + + response_type, response_cookie = unpack("!BI", data[:5]) + if response_type == 0x81: + self.done = True + return + elif response_type != 0x80 or response_cookie != cookie: + log.info("Received unexpected message with type 0x%x cookie %u expected 0x80 %u" % + (response_type, response_cookie, cookie)) + self.done = True + return + + timestamp, str_level, filename, linenumber, message = self._pebble._parse_log_response(data[5:]) + + timestamp_str = datetime.datetime.fromtimestamp(timestamp).strftime('%Y-%m-%d %H:%M:%S') + + log.info("{} {} {}:{}> {}".format(str_level, timestamp_str, filename, linenumber, message)) + + client = LogDumpClient(self) + self.register_endpoint("LOG_DUMP", client.parse_log_dump_response) + + import random + cookie = random.randint(0, pow(2, 32) - 1) + self._send_message("LOG_DUMP", pack("!BBI", 0x10, generation_number, cookie)) + + while not client.done: + time.sleep(1) + def disconnect(self): """Disconnect from the target Pebble.""" @@ -739,16 +788,21 @@ def _system_message_response(self, endpoint, data): else: log.info("Got 'unknown' system message...") + def _parse_log_response(self, log_message_data): + timestamp, level, msgsize, linenumber = unpack("!IBBH", log_message_data[:8]) + filename = log_message_data[8:24].decode('utf-8') + message = log_message_data[24:24+msgsize].decode('utf-8') + + str_level = self.log_levels[level] if level in self.log_levels else "?" + + return timestamp, str_level, filename, linenumber, message + def _log_response(self, endpoint, data): if (len(data) < 8): log.warn("Unable to decode log message (length %d is less than 8)" % len(data)) return - timestamp, level, msgsize, linenumber = unpack("!IBBH", data[:8]) - filename = data[8:24].decode('utf-8') - message = data[24:24+msgsize].decode('utf-8') - - str_level = self.log_levels[level] if level in self.log_levels else "?" + timestamp, str_level, filename, linenumber, message = self._parse_log_response(data) log.info("{} {} {} {} {}".format(timestamp, str_level, filename, linenumber, message)) @@ -758,11 +812,7 @@ def _app_log_response(self, endpoint, data): return app_uuid = uuid.UUID(bytes=data[0:16]) - timestamp, level, msgsize, linenumber = unpack("!IBBH", data[16:24]) - filename = data[24:40].decode('utf-8') - message = data[40:40+msgsize].decode('utf-8') - - str_level = self.log_levels[level] if level in self.log_levels else "?" + timestamp, str_level, filename, linenumber, message = self._parse_log_response(data[16:]) log.info("{} {}:{} {}".format(str_level, filename, linenumber, message))