From f1ccfa0c2c200c9443fbfc3f1ac3acbdd3e35559 Mon Sep 17 00:00:00 2001 From: Matt Valentine-House Date: Wed, 13 Jul 2022 13:18:03 +0100 Subject: [ci-skip][Feature #18910][lldb] Provide class framework for lldb commands `lldb_cruby.py` manages lldb custom commands using functions. The file is a large list of Python functions, and an init handler to map some of the Python functions into the debugger, to enable execution of custom logic during a debugging session. Since LLDB 3.7 (September 2015) there has also been support for using python classes rather than bare functions, as long as those classes implement a specific interface. This PR Introduces some more defined structure to the LLDB helper functions by switching from the function based implementation to the class based one, and providing an auto-loading mechanism by which new functions can be loaded. The intention behind this change is to make working with the LLDB helpers easier, by reducing code duplication, providing a consistent structure and a clearer API for developers. The current function based approach has some advantages and disadvantages Advantages: - Adding new code is easy. - All the code is self contained and searchable. Disadvantages: - No visible organisation of the file contents. This means - Hard to tell which functions are utility functions and which are available to you in a debugging session - Lots of code duplication within lldb functions - Large files quickly become intimidating to work with - for example, `lldb_disasm.py` was implemented as a seperate Python module because it was easier to start with a clean slate than add significant amounts of code to `lldb_cruby.py` This PR attempts, to fix the disadvantages of the current approach and maintain, or enhance, the benefits. The new structure of a command looks like this; ``` class TestCommand(RbBaseCommand): # program is the keyword the user will type in lldb to execute this command program = "test" # help_string will be displayed in lldb when the user uses the help functions help_string = "This is a test command to show how to implement lldb commands" # call is where our command logic will be implemented def call(self, debugger, command, exe_ctx, result): pass ``` If the command fulfils the following criteria it will then be auto-loaded when an lldb session is started: - The package file must exist inside the `commands` directory and the filename must end in `_command.py` - The package must implement a class whose name ends in `Command` - The class inherits from `RbBaseCommand` or at minimum a class that shares the same interface as `RbBaseCommand` (at minimum this means defining `__init__` and `__call__`, and using `__call__` to call `call` which is defined in the subclasses). - The class must have a class variable `package` that is a String. This is the name of the command you'll call in the `lldb` debugger. --- misc/lldb_cruby.py | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) (limited to 'misc/lldb_cruby.py') diff --git a/misc/lldb_cruby.py b/misc/lldb_cruby.py index c38b9c62a0..de8628754c 100755 --- a/misc/lldb_cruby.py +++ b/misc/lldb_cruby.py @@ -9,15 +9,16 @@ from __future__ import print_function import lldb import os +import inspect +import sys import shlex import platform +import glob -HEAP_PAGE_ALIGN_LOG = 16 - -HEAP_PAGE_ALIGN_MASK = (~(~0 << HEAP_PAGE_ALIGN_LOG)) -HEAP_PAGE_ALIGN = (1 << HEAP_PAGE_ALIGN_LOG) -HEAP_PAGE_SIZE = HEAP_PAGE_ALIGN +from constants import * +# BEGIN FUNCTION STYLE DECLS +# This will be refactored to use class style decls in the misc/commands dir class BackTrace: VM_FRAME_MAGIC_METHOD = 0x11110001 VM_FRAME_MAGIC_BLOCK = 0x22220001 @@ -740,9 +741,27 @@ def rb_rclass_ext(debugger, command, result, internal_dict): rclass_addr = target.EvaluateExpression(command).Cast(uintptr_t) rclass_ext_addr = (rclass_addr.GetValueAsUnsigned() + rclass_t.GetByteSize()) debugger.HandleCommand("p *(rb_classext_t *)%0#x" % rclass_ext_addr) +# END FUNCTION STYLE DECLS + + +load_dir, _ = os.path.split(os.path.realpath(__file__)) + +for fname in glob.glob(f"{load_dir}/commands/*_command.py"): + _, basename = os.path.split(fname) + mname, _ = os.path.splitext(basename) + exec(f"import commands.{mname}") def __lldb_init_module(debugger, internal_dict): + # Register all classes that subclass RbBaseCommand + + for memname, mem in inspect.getmembers(sys.modules["lldb_rb.rb_base_command"]): + if inspect.isclass(mem): + for sclass in mem.__subclasses__(): + sclass.register_lldb_command(debugger, f"{__name__}.{sclass.__module__}") + + + ## FUNCTION INITS - These should be removed when converted to class commands debugger.HandleCommand("command script add -f lldb_cruby.lldb_rp rp") debugger.HandleCommand("command script add -f lldb_cruby.count_objects rb_count_objects") debugger.HandleCommand("command script add -f lldb_cruby.stack_dump_raw SDR") -- cgit v1.2.1