1515bc8c1Sserge-sans-paille#!/usr/bin/env python
20f729e9bSGreg Clayton
30f729e9bSGreg Clayton#----------------------------------------------------------------------
40f729e9bSGreg Clayton# This module will enable GDB remote packet logging when the
50f729e9bSGreg Clayton# 'start_gdb_log' command is called with a filename to log to. When the
60f729e9bSGreg Clayton# 'stop_gdb_log' command is called, it will disable the logging and
70f729e9bSGreg Clayton# print out statistics about how long commands took to execute and also
80f729e9bSGreg Clayton# will primnt ou
90f729e9bSGreg Clayton# Be sure to add the python path that points to the LLDB shared library.
100f729e9bSGreg Clayton#
110f729e9bSGreg Clayton# To use this in the embedded python interpreter using "lldb" just
120f729e9bSGreg Clayton# import it with the full path using the "command script import"
130f729e9bSGreg Clayton# command. This can be done from the LLDB command line:
140f729e9bSGreg Clayton#   (lldb) command script import /path/to/gdbremote.py
150f729e9bSGreg Clayton# Or it can be added to your ~/.lldbinit file so this module is always
160f729e9bSGreg Clayton# available.
170f729e9bSGreg Clayton#----------------------------------------------------------------------
180f729e9bSGreg Clayton
19525cd59fSSerge Gueltonfrom __future__ import print_function
20525cd59fSSerge Guelton
210f729e9bSGreg Claytonimport optparse
220f729e9bSGreg Claytonimport os
230f729e9bSGreg Claytonimport shlex
240f729e9bSGreg Claytonimport re
250f729e9bSGreg Claytonimport tempfile
260f729e9bSGreg Clayton
27b9c1b51eSKate Stone
280f729e9bSGreg Claytondef start_gdb_log(debugger, command, result, dict):
290f729e9bSGreg Clayton    '''Start logging GDB remote packets by enabling logging with timestamps and
300f729e9bSGreg Clayton    thread safe logging. Follow a call to this function with a call to "stop_gdb_log"
310f729e9bSGreg Clayton    in order to dump out the commands.'''
320f729e9bSGreg Clayton    global log_file
330f729e9bSGreg Clayton    if log_file:
34b9c1b51eSKate Stone        result.PutCString(
35b9c1b51eSKate Stone            'error: logging is already in progress with file "%s"',
36b9c1b51eSKate Stone            log_file)
370f729e9bSGreg Clayton    else:
380f729e9bSGreg Clayton        args_len = len(args)
390f729e9bSGreg Clayton        if args_len == 0:
400f729e9bSGreg Clayton            log_file = tempfile.mktemp()
410f729e9bSGreg Clayton        elif len(args) == 1:
420f729e9bSGreg Clayton            log_file = args[0]
430f729e9bSGreg Clayton
440f729e9bSGreg Clayton        if log_file:
45b9c1b51eSKate Stone            debugger.HandleCommand(
46b9c1b51eSKate Stone                'log enable --threadsafe --timestamp --file "%s" gdb-remote packets' %
47b9c1b51eSKate Stone                log_file)
48b9c1b51eSKate Stone            result.PutCString(
49b9c1b51eSKate Stone                "GDB packet logging enable with log file '%s'\nUse the 'stop_gdb_log' command to stop logging and show packet statistics." %
50b9c1b51eSKate Stone                log_file)
510f729e9bSGreg Clayton            return
520f729e9bSGreg Clayton
530f729e9bSGreg Clayton        result.PutCString('error: invalid log file path')
540f729e9bSGreg Clayton    result.PutCString(usage)
550f729e9bSGreg Clayton
56b9c1b51eSKate Stone
570f729e9bSGreg Claytondef parse_time_log(debugger, command, result, dict):
580f729e9bSGreg Clayton    # Any commands whose names might be followed by more valid C identifier
590f729e9bSGreg Clayton    # characters must be listed here
600f729e9bSGreg Clayton    command_args = shlex.split(command)
6118eca8a0SGreg Clayton    parse_time_log_args(command_args)
6218eca8a0SGreg Clayton
63b9c1b51eSKate Stone
6418eca8a0SGreg Claytondef parse_time_log_args(command_args):
650f729e9bSGreg Clayton    usage = "usage: parse_time_log [options] [<LOGFILEPATH>]"
660f729e9bSGreg Clayton    description = '''Parse a log file that contains timestamps and convert the timestamps to delta times between log lines.'''
67b9c1b51eSKate Stone    parser = optparse.OptionParser(
68b9c1b51eSKate Stone        description=description,
69b9c1b51eSKate Stone        prog='parse_time_log',
70b9c1b51eSKate Stone        usage=usage)
71b9c1b51eSKate Stone    parser.add_option(
72b9c1b51eSKate Stone        '-v',
73b9c1b51eSKate Stone        '--verbose',
74b9c1b51eSKate Stone        action='store_true',
75b9c1b51eSKate Stone        dest='verbose',
76b9c1b51eSKate Stone        help='display verbose debug info',
77b9c1b51eSKate Stone        default=False)
780f729e9bSGreg Clayton    try:
790f729e9bSGreg Clayton        (options, args) = parser.parse_args(command_args)
800f729e9bSGreg Clayton    except:
810f729e9bSGreg Clayton        return
820f729e9bSGreg Clayton    for log_file in args:
830f729e9bSGreg Clayton        parse_log_file(log_file, options)
840f729e9bSGreg Clayton
85b9c1b51eSKate Stone
860f729e9bSGreg Claytondef parse_log_file(file, options):
870f729e9bSGreg Clayton    '''Parse a log file that was contains timestamps. These logs are typically
880f729e9bSGreg Clayton    generated using:
890f729e9bSGreg Clayton    (lldb) log enable --threadsafe --timestamp --file <FILE> ....
900f729e9bSGreg Clayton
91d93c4a33SBruce Mitchener    This log file will contain timestamps and this function will then normalize
920f729e9bSGreg Clayton    those packets to be relative to the first value timestamp that is found and
930f729e9bSGreg Clayton    show delta times between log lines and also keep track of how long it takes
940f729e9bSGreg Clayton    for GDB remote commands to make a send/receive round trip. This can be
950f729e9bSGreg Clayton    handy when trying to figure out why some operation in the debugger is taking
960f729e9bSGreg Clayton    a long time during a preset set of debugger commands.'''
970f729e9bSGreg Clayton
98525cd59fSSerge Guelton    print('#----------------------------------------------------------------------')
99525cd59fSSerge Guelton    print("# Log file: '%s'" % file)
100525cd59fSSerge Guelton    print('#----------------------------------------------------------------------')
10118eca8a0SGreg Clayton
1020f729e9bSGreg Clayton    timestamp_regex = re.compile('(\s*)([1-9][0-9]+\.[0-9]+)([^0-9].*)$')
1030f729e9bSGreg Clayton
1040f729e9bSGreg Clayton    base_time = 0.0
1050f729e9bSGreg Clayton    last_time = 0.0
1060f729e9bSGreg Clayton    file = open(file)
1070f729e9bSGreg Clayton    lines = file.read().splitlines()
1080f729e9bSGreg Clayton    for line in lines:
1090f729e9bSGreg Clayton        match = timestamp_regex.match(line)
1100f729e9bSGreg Clayton        if match:
1110f729e9bSGreg Clayton            curr_time = float(match.group(2))
1120f729e9bSGreg Clayton            delta = 0.0
1130f729e9bSGreg Clayton            if base_time:
1140f729e9bSGreg Clayton                delta = curr_time - last_time
1150f729e9bSGreg Clayton            else:
1160f729e9bSGreg Clayton                base_time = curr_time
1170f729e9bSGreg Clayton
118525cd59fSSerge Guelton            print('%s%.6f %+.6f%s' % (match.group(1), curr_time - base_time, delta, match.group(3)))
1190f729e9bSGreg Clayton            last_time = curr_time
1200f729e9bSGreg Clayton        else:
121525cd59fSSerge Guelton            print(line)
1220f729e9bSGreg Clayton
1230f729e9bSGreg Clayton
1240f729e9bSGreg Claytonif __name__ == '__main__':
1250f729e9bSGreg Clayton    import sys
12618eca8a0SGreg Clayton    parse_time_log_args(sys.argv[1:])
1270f729e9bSGreg Clayton
128*1441ffe6SDave Lee
129*1441ffe6SDave Leedef __lldb_init_module(debugger, internal_dict):
1300f729e9bSGreg Clayton        # This initializer is being run from LLDB in the embedded command interpreter
1310f729e9bSGreg Clayton        # Add any commands contained in this module to LLDB
132*1441ffe6SDave Lee        debugger.HandleCommand(
133b9c1b51eSKate Stone            'command script add -f delta.parse_time_log parse_time_log')
134525cd59fSSerge Guelton        print('The "parse_time_log" command is now installed and ready for use, type "parse_time_log --help" for more information')
135