1#!/usr/bin/python
2
3#----------------------------------------------------------------------
4# This module will enable GDB remote packet logging when the
5# 'start_gdb_log' command is called with a filename to log to. When the
6# 'stop_gdb_log' command is called, it will disable the logging and
7# print out statistics about how long commands took to execute and also
8# will primnt ou
9# Be sure to add the python path that points to the LLDB shared library.
10#
11# To use this in the embedded python interpreter using "lldb" just
12# import it with the full path using the "command script import"
13# command. This can be done from the LLDB command line:
14#   (lldb) command script import /path/to/gdbremote.py
15# Or it can be added to your ~/.lldbinit file so this module is always
16# available.
17#----------------------------------------------------------------------
18
19import commands
20from __future__ import print_function
21
22import optparse
23import os
24import shlex
25import re
26import tempfile
27
28
29def start_gdb_log(debugger, command, result, dict):
30    '''Start logging GDB remote packets by enabling logging with timestamps and
31    thread safe logging. Follow a call to this function with a call to "stop_gdb_log"
32    in order to dump out the commands.'''
33    global log_file
34    if log_file:
35        result.PutCString(
36            'error: logging is already in progress with file "%s"',
37            log_file)
38    else:
39        args_len = len(args)
40        if args_len == 0:
41            log_file = tempfile.mktemp()
42        elif len(args) == 1:
43            log_file = args[0]
44
45        if log_file:
46            debugger.HandleCommand(
47                'log enable --threadsafe --timestamp --file "%s" gdb-remote packets' %
48                log_file)
49            result.PutCString(
50                "GDB packet logging enable with log file '%s'\nUse the 'stop_gdb_log' command to stop logging and show packet statistics." %
51                log_file)
52            return
53
54        result.PutCString('error: invalid log file path')
55    result.PutCString(usage)
56
57
58def parse_time_log(debugger, command, result, dict):
59    # Any commands whose names might be followed by more valid C identifier
60    # characters must be listed here
61    command_args = shlex.split(command)
62    parse_time_log_args(command_args)
63
64
65def parse_time_log_args(command_args):
66    usage = "usage: parse_time_log [options] [<LOGFILEPATH>]"
67    description = '''Parse a log file that contains timestamps and convert the timestamps to delta times between log lines.'''
68    parser = optparse.OptionParser(
69        description=description,
70        prog='parse_time_log',
71        usage=usage)
72    parser.add_option(
73        '-v',
74        '--verbose',
75        action='store_true',
76        dest='verbose',
77        help='display verbose debug info',
78        default=False)
79    try:
80        (options, args) = parser.parse_args(command_args)
81    except:
82        return
83    for log_file in args:
84        parse_log_file(log_file, options)
85
86
87def parse_log_file(file, options):
88    '''Parse a log file that was contains timestamps. These logs are typically
89    generated using:
90    (lldb) log enable --threadsafe --timestamp --file <FILE> ....
91
92    This log file will contain timestamps and this function will then normalize
93    those packets to be relative to the first value timestamp that is found and
94    show delta times between log lines and also keep track of how long it takes
95    for GDB remote commands to make a send/receive round trip. This can be
96    handy when trying to figure out why some operation in the debugger is taking
97    a long time during a preset set of debugger commands.'''
98
99    print('#----------------------------------------------------------------------')
100    print("# Log file: '%s'" % file)
101    print('#----------------------------------------------------------------------')
102
103    timestamp_regex = re.compile('(\s*)([1-9][0-9]+\.[0-9]+)([^0-9].*)$')
104
105    base_time = 0.0
106    last_time = 0.0
107    file = open(file)
108    lines = file.read().splitlines()
109    for line in lines:
110        match = timestamp_regex.match(line)
111        if match:
112            curr_time = float(match.group(2))
113            delta = 0.0
114            if base_time:
115                delta = curr_time - last_time
116            else:
117                base_time = curr_time
118
119            print('%s%.6f %+.6f%s' % (match.group(1), curr_time - base_time, delta, match.group(3)))
120            last_time = curr_time
121        else:
122            print(line)
123
124
125if __name__ == '__main__':
126    import sys
127    parse_time_log_args(sys.argv[1:])
128
129else:
130    import lldb
131    if lldb.debugger:
132        # This initializer is being run from LLDB in the embedded command interpreter
133        # Add any commands contained in this module to LLDB
134        lldb.debugger.HandleCommand(
135            'command script add -f delta.parse_time_log parse_time_log')
136        print('The "parse_time_log" command is now installed and ready for use, type "parse_time_log --help" for more information')
137