#!/usr/bin/env python

# ---------------------------------------------------------------------
# Be sure to add the python path that points to the LLDB shared library.
#
# # To use this in the embedded python interpreter using "lldb" just
# import it with the full path using the "command script import"
# command
#   (lldb) command script import /path/to/cmdtemplate.py
# ---------------------------------------------------------------------

import inspect
import lldb
import sys
from lldb.plugins.parsed_cmd import ParsedCommand

class FrameStatCommand(ParsedCommand):
    program = "framestats"

    @classmethod
    def register_lldb_command(cls, debugger, module_name):
        ParsedCommand.do_register_cmd(cls, debugger, module_name)
        print(
            'The "{0}" command has been installed, type "help {0}" or "{0} '
            '--help" for detailed help.'.format(cls.program)
        )

    def get_flags(self):
        return lldb.eCommandRequiresFrame | lldb.eCommandProcessMustBePaused

    def setup_command_definition(self):
        ov_parser = self.get_parser()
        ov_parser.add_option(
            "i",
            "in-scope",
            help = "in_scope_only = True",
            value_type = lldb.eArgTypeBoolean,
            dest = "bool_arg",
            default = True,
        )

        ov_parser.add_option(
            "i",
            "in-scope",
            help = "in_scope_only = True",
            value_type = lldb.eArgTypeBoolean,
            dest = "inscope",
            default=True,
        )
        
        ov_parser.add_option(
            "a",
            "arguments",
            help = "arguments = True",
            value_type = lldb.eArgTypeBoolean,
            dest = "arguments",
            default = True,
        )

        ov_parser.add_option(
            "l",
            "locals",
            help = "locals = True",
            value_type = lldb.eArgTypeBoolean,
            dest = "locals",
            default = True,
        )

        ov_parser.add_option(
            "s",
            "statics",
            help = "statics = True",
            value_type = lldb.eArgTypeBoolean,
            dest = "statics",
            default = True,
        )

    def get_repeat_command(self, args):
        """As an example, make the command not auto-repeat:"""
        return ""

    def get_short_help(self):
        return "Example command for use in debugging"

    def get_long_help(self):
        return ("This command is meant to be an example of how to make "
            "an LLDB command that does something useful, follows "
            "best practices, and exploits the SB API. "
            "Specifically, this command computes the aggregate "
            "and average size of the variables in the current "
            "frame and allows you to tweak exactly which variables "
            "are to be accounted in the computation.")


    def __init__(self, debugger, unused):
        super().__init__(debugger, unused)

    def __call__(self, debugger, command, exe_ctx, result):
        # Always get program state from the lldb.SBExecutionContext passed
        # in as exe_ctx
        frame = exe_ctx.GetFrame()
        if not frame.IsValid():
            result.SetError("invalid frame")
            return

        ov_parser = self.get_parser()
        variables_list = frame.GetVariables(
            ov_parser.arguments, ov_parser.locals, ov_parser.statics, ov_parser.inscope
        )
        variables_count = variables_list.GetSize()
        if variables_count == 0:
            print("no variables here", file=result)
            return
        total_size = 0
        for i in range(0, variables_count):
            variable = variables_list.GetValueAtIndex(i)
            variable_type = variable.GetType()
            total_size = total_size + variable_type.GetByteSize()
            average_size = float(total_size) / variables_count
            print(
                "Your frame has %d variables. Their total size "
                "is %d bytes. The average size is %f bytes"
                % (variables_count, total_size, average_size),
                file=result,
            )
        # not returning anything is akin to returning success


def __lldb_init_module(debugger, dict):
    # Register all classes that have a register_lldb_command method
    for _name, cls in inspect.getmembers(sys.modules[__name__]):
        if inspect.isclass(cls) and callable(
            getattr(cls, "register_lldb_command", None)
        ):
            cls.register_lldb_command(debugger, __name__)
