1#!/usr/bin/python
2
3# ---------------------------------------------------------------------
4# Be sure to add the python path that points to the LLDB shared library.
5#
6# # To use this in the embedded python interpreter using "lldb" just
7# import it with the full path using the "command script import"
8# command
9#   (lldb) command script import /path/to/cmdtemplate.py
10# ---------------------------------------------------------------------
11
12from __future__ import print_function
13
14import inspect
15import lldb
16import optparse
17import shlex
18import sys
19
20
21class FrameStatCommand:
22    program = 'framestats'
23
24    @classmethod
25    def register_lldb_command(cls, debugger, module_name):
26        parser = cls.create_options()
27        cls.__doc__ = parser.format_help()
28        # Add any commands contained in this module to LLDB
29        command = 'command script add -c %s.%s %s' % (module_name,
30                                                      cls.__name__,
31                                                      cls.program)
32        debugger.HandleCommand(command)
33        print('The "{0}" command has been installed, type "help {0}" or "{0} '
34              '--help" for detailed help.'.format(cls.program))
35
36    @classmethod
37    def create_options(cls):
38
39        usage = "usage: %prog [options]"
40        description = ('This command is meant to be an example of how to make '
41                       'an LLDB command that does something useful, follows '
42                       'best practices, and exploits the SB API. '
43                       'Specifically, this command computes the aggregate '
44                       'and average size of the variables in the current '
45                       'frame and allows you to tweak exactly which variables '
46                       'are to be accounted in the computation.')
47
48        # Pass add_help_option = False, since this keeps the command in line
49        #  with lldb commands, and we wire up "help command" to work by
50        # providing the long & short help methods below.
51        parser = optparse.OptionParser(
52            description=description,
53            prog=cls.program,
54            usage=usage,
55            add_help_option=False)
56
57        parser.add_option(
58            '-i',
59            '--in-scope',
60            action='store_true',
61            dest='inscope',
62            help='in_scope_only = True',
63            default=True)
64
65        parser.add_option(
66            '-a',
67            '--arguments',
68            action='store_true',
69            dest='arguments',
70            help='arguments = True',
71            default=True)
72
73        parser.add_option(
74            '-l',
75            '--locals',
76            action='store_true',
77            dest='locals',
78            help='locals = True',
79            default=True)
80
81        parser.add_option(
82            '-s',
83            '--statics',
84            action='store_true',
85            dest='statics',
86            help='statics = True',
87            default=True)
88
89        return parser
90
91    def get_short_help(self):
92        return "Example command for use in debugging"
93
94    def get_long_help(self):
95        return self.help_string
96
97    def __init__(self, debugger, unused):
98        self.parser = self.create_options()
99        self.help_string = self.parser.format_help()
100
101    def __call__(self, debugger, command, exe_ctx, result):
102        # Use the Shell Lexer to properly parse up command options just like a
103        # shell would
104        command_args = shlex.split(command)
105
106        try:
107            (options, args) = self.parser.parse_args(command_args)
108        except:
109            # if you don't handle exceptions, passing an incorrect argument to
110            # the OptionParser will cause LLDB to exit (courtesy of OptParse
111            # dealing with argument errors by throwing SystemExit)
112            result.SetError("option parsing failed")
113            return
114
115        # Always get program state from the lldb.SBExecutionContext passed
116        # in as exe_ctx
117        frame = exe_ctx.GetFrame()
118        if not frame.IsValid():
119            result.SetError("invalid frame")
120            return
121
122        variables_list = frame.GetVariables(
123            options.arguments,
124            options.locals,
125            options.statics,
126            options.inscope)
127        variables_count = variables_list.GetSize()
128        if variables_count == 0:
129            print("no variables here", file=result)
130            return
131        total_size = 0
132        for i in range(0, variables_count):
133            variable = variables_list.GetValueAtIndex(i)
134            variable_type = variable.GetType()
135            total_size = total_size + variable_type.GetByteSize()
136            average_size = float(total_size) / variables_count
137            print("Your frame has %d variables. Their total size "
138                             "is %d bytes. The average size is %f bytes" % (
139                                    variables_count, total_size, average_size), file=result)
140        # not returning anything is akin to returning success
141
142
143def __lldb_init_module(debugger, dict):
144    # Register all classes that have a register_lldb_command method
145    for _name, cls in inspect.getmembers(sys.modules[__name__]):
146        if inspect.isclass(cls) and callable(getattr(cls,
147                                                     "register_lldb_command",
148                                                     None)):
149            cls.register_lldb_command(debugger, __name__)
150