]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Move to a single Python script that implements dd (#5444)
authorJames Jones <jejones3141@gmail.com>
Mon, 6 Jan 2025 16:30:01 +0000 (10:30 -0600)
committerGitHub <noreply@github.com>
Mon, 6 Jan 2025 16:30:01 +0000 (10:30 -0600)
This will pro9bably be the schema for any future commands added to
gdb and lldb.

debugger/dd.py [new file with mode: 0644]
debugger/gdb/dd.py [deleted file]
debugger/lldb/dd.py [deleted file]

diff --git a/debugger/dd.py b/debugger/dd.py
new file mode 100644 (file)
index 0000000..9c18427
--- /dev/null
@@ -0,0 +1,158 @@
+#
+# The "dd" command (no relation to Unix/Linux dd or JCL)
+#
+# Syntax: dd <variable name>
+#
+# where the variable named either has one of the types
+# shown in the _howTo list or whose address has one of the
+# types shown in the _howTo list.
+#
+# When the command is run, it calls the appropriate FreeRADIUS
+# function to display the value the variable points at (or
+# contains, if it's not a pointer).
+#
+# Apologia:
+#
+# Debuggers can print values, but they print them in
+# accordance with the C declaration. That's nice, but...
+# FreeRADIUS has lots of container types, implemented as
+# C's flavor of sum types, i.e. unions with some other
+# field used as a tag indicating which variant is in use.
+# Debuggers have no way to know which field that is or how
+# to interpret its value, so you get to see all those
+# variants, only one of which matters.
+#
+# gdb does support custom printing for data of the type
+# of your choice, but
+#
+# 1. lldb doesn't.
+# 2. We already have code to display these structures in a
+#    human-friendly form, used for logging. Why not use them?
+#
+# Both gdb and lldb support Python scripting, and provide
+# ways to
+#
+# 1. Given a string, get a Python value representing it as
+#    a variable (or possibly expression) that one can retrieve
+#    the type of (and get a textual representation of the type)
+# 2. Invoke debugger commands from the Python script.
+#
+# so we can write Python code to call the FreeRADIUS function
+# that displays the value as we want. Since we can tell whether
+# we're running under gdb or lldb, we can have a single Python
+# source that can do the right thing for the debugger it's running
+# under.
+
+import sys
+
+_howTo = {
+    'fr_value_box_t *'           : ('fr_value_box_debug',              True),
+    'fr_value_box_list_t *'      : ('fr_value_box_list_debug',         True),
+    'tmpl_t *'                   : ('tmpl_debug',                      True),
+    'CONF_ITEM *'                : ('_cf_debug',                       True),
+    'dl_loader_t *'              : ('dl_loader_debug',                 False),
+    'fr_dict_gctx_t * '          : ('fr_dict_global_ctx_debug',                True),
+    'fr_pair_t *'                : ('fr_pair_debug',                   True),
+    'fr_pair_list_t *'           : ('fr_pair_list_debug',              True),
+    'fr_sbuff_term_t *'          : ('fr_sbuff_terminal_debug',         True),
+    'fr_sbuff_parse_rules_t *'   : ('fr_sbuff_parse_rules_debug',      True),
+    'fr_sbuff_unescape_rules_t *': ('fr_sbuff_unescape_debug',         True),
+    'tmpl_attr_list_head_t *'    : ('tmpl_attr_ref_list_debug',                True),
+    'tmpl_attr_rules_t *'        : ('tmpl_attr_rules_debug',           True),
+    'fr_dlist_head_t *'          : ('tmpl_extents_debug',              False),
+    'tmpl_request_list_head_t *' : ('tmpl_request_ref_list_debug',     True),
+    'tmpl_rules_t *'             : ('tmpl_rules_debug',                        True),
+    'lua_State *'                : ('_util_log_debug',                 False),
+    'xlat_exp_t *'               : ('xlat_debug',                      True)
+}
+
+try:
+    import gdb  # Attempt to import GDB module
+    dbg = "gdb"
+except ImportError:
+    import lldb  # If not available, use LLDB module
+    dbg = "lldb"
+
+if dbg == "lldb":
+    # create LLDB command
+
+    def dd(debugger, command, exe_ctx, result, internal_dict):
+        target = debugger.GetSelectedTarget()
+        process = target.GetProcess()
+        thread = process.GetSelectedThread()
+        frame = thread.GetSelectedFrame()
+        sb_var = frame.FindVariable(command)
+        if not sb_var.IsValid():
+            sb_var = target.FindFirstGlobalVariable(command)
+        if not sb_var.IsValid():
+            result.SetError('{} is not a variable'.format(command))
+            return
+        arg = sb_var if sb_var.type.is_pointer else sb_var.address_of
+        type = arg.GetDisplayTypeName()
+        if not (type in _howTo):
+            result.SetError('unsupported type "{}"'.format(type))
+            return
+        function, const = _howTo[type]
+        cast = '({} const *)'.format(type[0:-2]) if const else ''
+        argName = arg.GetName()
+        cmd = 'expr {0}({1}({2}))'.format(function, cast, argName)
+        interpreter = debugger.GetCommandInterpreter()
+        if not interpreter.IsValid():
+           result.SetError("can't set up SBCommandInterpreter")
+           return
+        # The use of fr_value_box_t to represent two different structures
+        # makes the command fail for it, but only once(!). Until we find
+        # the right way to disambiguate, we'll give it up to two tries.
+        for i in range(2):
+            if (cmdStatus := interpreter.HandleCommand(cmd, result)) == lldb.eReturnStatusSuccessFinishResult:
+                return
+        result.SetError("command {} failed, status {}".format(cmd, cmdStatus))
+
+    # And then some boilerplate to set up the command and announce its availability.
+    # I'm guessing that the -f option is <file name without extension>.<name of function>,
+    # and it's followed by the name one uses on the lldb command line.
+    def __lldb_init_module(debugger, internal_dict):
+        debugger.HandleCommand('command script add -f dd.dd dd')
+        print('The "dd" python command has been installed and is ready for use.')
+
+    # To make this available to you in lldb, you need to do this:
+    #
+    # (lldb) command script import <path to this file>
+    #
+    # or have it done in your .lldbinit file, which needn't be in your home directory;
+    # giving lldb the --local-lldbinit option makes it look in the current directory.
+else:
+    # create GDB command
+    class DD (gdb.Command):
+        """Display selected data structures using FreeRADIUS C calls."""
+        def __init__ (self):
+            super (DD, self).__init__ ("dd", gdb.COMMAND_USER)
+            print('The "dd" command has been installed and is ready for use.')
+        def invoke (self, arg, from_tty):
+            # Python code goes here
+            var = gdb.parse_and_eval(arg) # really just sets up for eventual evaluation
+            isAddress = var.type.code == gdb.TYPE_CODE_PTR
+            if isAddress:
+                argMod = ''
+            else:
+                argMod = '&'
+                var = var.address
+            varType = str(var.type)
+            if not (varType in _howTo):
+                print('unsupported type "{}"'.format(varType))
+                return
+            function, const = _howTo[varType]
+            cast = '({} const *)'.format(varType[0:-2]) if const else ''
+            command = 'call {0}({1}{2}({3}))'.format(function, cast, argMod, arg)
+            try:
+                gdb.execute(command)
+            except:
+                print("command failed")
+    DD () # Create an instance so you can run it
+
+    # to make this available to you in gdb, execute the command
+    #
+    # (gdb) source <path to this file>
+    #
+    # or have that command run when gdb starts, which would involve putting it in
+    # the .gdbinit file
diff --git a/debugger/gdb/dd.py b/debugger/gdb/dd.py
deleted file mode 100644 (file)
index dad8109..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-import gdb
-
-_howTo = {
-    'fr_value_box_t *'           : ('fr_value_box_debug',              True),
-    'fr_value_box_list_t *'      : ('fr_value_box_list_debug',         True),
-    'tmpl_t *'                   : ('tmpl_debug',                      True),
-    'CONF_ITEM *'                : ('_cf_debug',                       True),
-    'dl_loader_t *'              : ('dl_loader_debug',                 False),
-    'fr_dict_gctx_t * '          : ('fr_dict_global_ctx_debug',                True),
-    'fr_pair_t *'                : ('fr_pair_debug',                   True),
-    'fr_pair_list_t *'           : ('fr_pair_list_debug',              True),
-    'fr_sbuff_term_t *'          : ('fr_sbuff_terminal_debug',         True),
-    'fr_sbuff_parse_rules_t *'   : ('fr_sbuff_parse_rules_debug',      True),
-    'fr_sbuff_unescape_rules_t *': ('fr_sbuff_unescape_debug',         True),
-    'tmpl_attr_list_head_t *'    : ('tmpl_attr_ref_list_debug',                True),
-    'tmpl_attr_rules_t *'        : ('tmpl_attr_rules_debug',           True),
-    'fr_dlist_head_t *'          : ('tmpl_extents_debug',              False),
-    'tmpl_request_list_head_t *' : ('tmpl_request_ref_list_debug',     True),
-    'tmpl_rules_t *'             : ('tmpl_rules_debug',                        True),
-    'lua_State *'                : ('_util_log_debug',                 False),
-    'xlat_exp_t *'               : ('xlat_debug',                      True)
-}
-
-class DD (gdb.Command):
-  """Display selected data structures using FreeRADIUS C calls."""
-
-  def __init__ (self):
-    super (DD, self).__init__ ("dd", gdb.COMMAND_USER)
-
-  def invoke (self, arg, from_tty):
-    # Python code goes here
-    var = gdb.parse_and_eval(arg) # really just sets up for eventual evaluation
-    isAddress = var.type.code == gdb.TYPE_CODE_PTR
-    if isAddress:
-       argMod = ''
-    else:
-       argMod = '&'
-       var = var.address
-    varType = str(var.type)
-    if not (varType in _howTo):
-       print('unsupported type "{}"'.format(varType))
-       return
-    function, const = _howTo[varType]
-    cast = '({} const *)'.format(varType[0:-2]) if const else ''
-    command = 'call {0}({1}{2}({3}))'.format(function, cast, argMod, arg)
-    try:
-       gdb.execute(command)
-    except:
-       print("command failed")
-
-DD () # Create an instance so you can run it
diff --git a/debugger/lldb/dd.py b/debugger/lldb/dd.py
deleted file mode 100644 (file)
index 26aa982..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-#!/usr/bin/env python
-
-import lldb
-import optparse
-import shlex
-
-_howTo = {
-    'fr_value_box_t *'           : ('fr_value_box_debug',              True),
-    'fr_value_box_list_t *'      : ('fr_value_box_list_debug',         True),
-    'tmpl_t *'                   : ('tmpl_debug',                      True),
-    'CONF_ITEM *'                : ('_cf_debug',                       True),
-    'dl_loader_t *'              : ('dl_loader_debug',                 False),
-    'fr_dict_gctx_t * '          : ('fr_dict_global_ctx_debug',                True),
-    'fr_pair_t *'                : ('fr_pair_debug',                   True),
-    'fr_pair_list_t *'           : ('fr_pair_list_debug',              True),
-    'fr_sbuff_term_t *'          : ('fr_sbuff_terminal_debug',         True),
-    'fr_sbuff_parse_rules_t *'   : ('fr_sbuff_parse_rules_debug',      True),
-    'fr_sbuff_unescape_rules_t *': ('fr_sbuff_unescape_debug',         True),
-    'tmpl_attr_list_head_t *'    : ('tmpl_attr_ref_list_debug',                True),
-    'tmpl_attr_rules_t *'        : ('tmpl_attr_rules_debug',           True),
-    'fr_dlist_head_t *'          : ('tmpl_extents_debug',              False),
-    'tmpl_request_list_head_t *' : ('tmpl_request_ref_list_debug',     True),
-    'tmpl_rules_t *'             : ('tmpl_rules_debug',                        True),
-    'lua_State *'                : ('_util_log_debug',                 False),
-    'xlat_exp_t *'               : ('xlat_debug',                      True)
-}
-
-# A Python function to be called from lldb must have the following parameters:
-#
-# debugger             the debugger currently running
-# command              the curiously named string containing the parameters
-# exe_ctx              the execution context 
-# result               an SBCommandReturnObject. We use its SetError() method
-#                      for error messages, but in this particular case the
-#                      real goal is to call the right foo_debug() function
-#                      which writes a readable version of the data to stderr.
-#                      In other cases, you may use result.PutOutput() or
-#                      result.PutCString().
-#                      gather that output and result.PutCString() it.
-# internal_dict                a Python dictionary containing all variables and
-#                       functions for the current embedded script session
-
-def dd(debugger, command, exe_ctx, result, internal_dict):
-    target = debugger.GetSelectedTarget()
-    process = target.GetProcess()
-    thread = process.GetSelectedThread()
-    frame = thread.GetSelectedFrame()
-    sb_var = frame.FindVariable(command)
-    if not sb_var.IsValid():
-        sb_var = target.FindFirstGlobalVariable(command)
-        if not sb_var.IsValid():
-            result.SetError('{} is not a variable'.format(command))
-            return
-    arg = sb_var if sb_var.type.is_pointer else sb_var.address_of
-    type = arg.GetDisplayTypeName()
-    if not (type in _howTo):
-        result.SetError('unsupported type "{}"'.format(type))
-        return
-    function, const = _howTo[type]
-    cast = '({} const *)'.format(type[0:-2]) if const else ''
-    argName = arg.GetName()
-    cmd = 'expr {0}({1}({2}))'.format(function, cast, argName)
-    interpreter = debugger.GetCommandInterpreter()
-    if not interpreter.IsValid():
-       result.SetError("can't set up SBCommandInterpreter")
-       return
-    # The use of fr_value_box_t to represent two different structures
-    # makes the command fail for it, but only once(!). Until we find
-    # the right way to disambiguate, we'll give it up to two tries.
-    for i in range(2):
-        if (cmdStatus := interpreter.HandleCommand(cmd, result)) == lldb.eReturnStatusSuccessFinishResult:
-            return
-    result.SetError("command {} failed, status {}".format(cmd, cmdStatus))
-
-# And then some boilerplate to set up the command and announce its availability.
-# I'm guessing that the -f option is <file name without extension>.<name of function>,
-# and it's followed by the name one uses on the lldb command line.
-def __lldb_init_module(debugger, internal_dict):
-    debugger.HandleCommand('command script add -f dd.dd dd')
-    print('The "dd" python command has been installed and is ready for use.')
-
-# To make this available to you in lldb, you need to do this:
-#
-# (lldb) command script import <path to this file>
-#
-# or have it done in your .lldbinit file, which needn't be in your home directory;
-# giving lldb the --local-lldbinit option makes it look in the current directory.