python: add set/show variable vdb-autodebug
authorJan Vrany <jan.vrany@labware.com>
Wed, 25 May 2022 17:03:30 +0100
changeset 258 789898d2b71a
parent 257 d775d7dc82de
child 259 554887d7dff9
python: add set/show variable vdb-autodebug When set to 'on', a Python debugger is automatically started upon error in Python code. This helps debugging Python scripts.
python/vdb/__init__.py
python/vdb/cli.py
python/vdb/printing.py
vdbinit.st
--- a/python/vdb/__init__.py	Wed May 25 17:02:01 2022 +0100
+++ b/python/vdb/__init__.py	Wed May 25 17:03:30 2022 +0100
@@ -108,3 +108,28 @@
     gdb.prompt_hook = lambda x: "%s > " % prompt
     sys.ps1 = "%s pi > " % prompt
     sys.ps2 = "%s    > " % (" " * len(prompt))
+
+
+autodebug = gdb.Parameter('vdb-autodebug', gdb.COMMAND_DATA, gdb.PARAM_AUTO_BOOLEAN)
+autodebug.value = False
+
+# Install automatic debugging hook
+def __autodebug_excepthook(typ, value, tb):
+    if autodebug.value == False: # hasattr(sys, 'ps1') or not sys.stderr.isatty():
+        sys.__excepthook__(typ, value, tb)
+        return
+    if autodebug.value == None: # set to 'auto'
+        pass
+
+    import traceback, pdb
+    if tb is None:
+        print("EXECEPTION IN PYTHON CODE:")
+        traceback.print_last()
+    else:
+        print("EXECEPTION IN PYTHON CODE, ENTERING DEBUGGER:")
+        traceback.print_exception(typ, value, tb)
+        # ...then start the debugger in post-mortem mode.
+        # pdb.pm() # deprecated
+        pdb.post_mortem(tb) # more "modern"
+
+sys.excepthook = __autodebug_excepthook
--- a/python/vdb/cli.py	Wed May 25 17:02:01 2022 +0100
+++ b/python/vdb/cli.py	Wed May 25 17:03:30 2022 +0100
@@ -16,7 +16,6 @@
 
 import gdb
 
-
 class __PythonReload(gdb.Command):
     """
     Reload Python code
@@ -116,20 +115,78 @@
                     # So, we have to "reload" it manually by re-evaluating the source in
                     # context of __main__ module.
                     #
-                    # TODO: Is there a better way? Following code looks a wee bit too
+                    # TODO: Is there a betpiter way? Following code looks a wee bit too
                     # fragile
-                    if module.__name__ == "__main__":
-                        if hasattr(module.__loader__, "path"):
-                            source_path = module.__loader__.path
-                            if os.path.exists(source_path):
-                                with open(source_path) as source_file:
-                                    source = source_file.read()
-                                    exec(source, module.__dict__)
-                    else:
-                        importlib.reload(module)
+                    try:
+                        if module.__name__ == "__main__":
+                            if hasattr(module.__loader__, "path"):
+                                source_path = module.__loader__.path
+                                if os.path.exists(source_path):
+                                    with open(source_path) as source_file:
+                                        source = source_file.read()
+                                        exec(source, module.__dict__)
+                        else:
+                            importlib.reload(module)
+                    except Exception as ex:
+                        print("Failed to reload module '%s': %s" % (module.__name__, ex))
 
         reload()
         print("Done!")
 
 
 pr = __PythonReload("pr", gdb.COMMAND_MAINTENANCE)
+
+class __DumpObjectCmd(gdb.Command):
+    """
+    Dump contents or expression EXP
+    Usage: do EXP [EXP...]
+
+    An expression EXP can be either OOP as numerical constant
+    (for example '0x1ff10028') or C/C++ expression which is
+    evaluated in current frame (for example, 'kernel->header.module')
+    """
+    def invoke (self, args, from_tty):
+        self(*gdb.string_to_argv(args))
+
+    def __call__(self, *exprs):
+        for expr in exprs:
+            self.dump(expr)
+
+    def complete(self, text, word):
+        return gdb.COMPLETE_EXPRESSION
+
+    def dump(self, expr):
+        import vdb
+        value = None
+        if isinstance(expr, gdb.Value):
+            value = expr
+        else:
+            try:
+                value = vdb.parse_and_eval(expr)
+            except:
+                print("Failed to evaluate '%s'" % expr)
+                return
+
+        printer = gdb.default_visualizer(value)
+        if not printer:
+            ty = value.dynamic_type
+            if ty.code == gdb.TYPE_CODE_STRUCT or (ty.code == gdb.TYPE_CODE_PTR and ty.target().code == gdb.TYPE_CODE_STRUCT):
+                from vdb.printing import CxxPrettyPrinter
+                printer = CxxPrettyPrinter(value)
+
+        if not printer:
+            print("%s (%s)" % ( value, value.dynamic_type))
+        else:
+            print("%s (%s)" % ( printer.to_string(), value.dynamic_type))
+            for name, child in printer.children():
+                pp = gdb.default_visualizer(child)
+                if pp == None:
+                    print("  %-20s:  %s (%s)" % ( name , child, child.dynamic_type ))
+                else:
+                    try:
+                        print("  %-20s:  %s (%s)" % ( name , pp.to_string(), child.dynamic_type ))
+                    except Exception as e:
+                        print("  %-20s:  %s (%s) <error: %s>" % ( name , child, child.dynamic_type, e ))
+
+
+do = __DumpObjectCmd('do', gdb.COMMAND_DATA)
--- a/python/vdb/printing.py	Wed May 25 17:02:01 2022 +0100
+++ b/python/vdb/printing.py	Wed May 25 17:03:30 2022 +0100
@@ -58,7 +58,10 @@
         self._val = val.cast(self._type)
 
     def to_string(self):
-        return self._val.format_string(raw = True)
+        if self._val.type.code == gdb.TYPE_CODE_PTR:
+            return "(%s) 0x%x" % ( self._type, int(self._val) )
+        else:
+            return "(%s) ... " % ( self._type )
 
     def fields_of(self, ty):
         return filter(lambda f : f.is_base_class == False and f.artificial == False, ty.fields())
@@ -83,6 +86,19 @@
 
         return map(lambda f : (f.name , val[f.name] ), self.fields())
 
+class CxxPtrPrettyPrinter():
+    """
+    A custom pretty printer for printing pointers (to C++ types)
+    """
+    def __init__(self, val, contents_visualizer):
+        assert val.type.code == gdb.TYPE_CODE_PTR
+        self._val = val
+        self.contents_visualizer = contents_visualizer
+
+    def to_string(self):
+        return self.contents_visualizer(self._val).to_string()
+
+
 class CxxNullptrPrettyPrinter():
     """
     A custom pretty printer for NULL / nullptr values.
@@ -141,8 +157,11 @@
             # is NULL or not.
             #
             # Note, that C++ defines nullptr having value of 0
-            if val.dynamic_type.code == gdb.TYPE_CODE_PTR and int(val) == 0x0:
-                return CxxNullptrPrettyPrinter(val)
+            if val.dynamic_type.code == gdb.TYPE_CODE_PTR:
+                if int(val) == 0x0:
+                    return CxxNullptrPrettyPrinter(val)
+                else:
+                    return CxxPtrPrettyPrinter(val, self.printer)
 
             if self.printer is not None:
                 return self.printer(val)
--- a/vdbinit.st	Wed May 25 17:02:01 2022 +0100
+++ b/vdbinit.st	Wed May 25 17:03:30 2022 +0100
@@ -6,14 +6,27 @@
 
 "/ Load plugins.
 "/
-"/   Smalltalk loadPackage: 'jv:vdb/plugins/bee'
+"/   Smalltalk loadPackage: 'jv:vdb/plugins/xyz'
 
 "/ Set console prompt
 "/
 "/   vdb debugger send: 'set prompt vdb > '.
 
+"/ Set light mode (theme)
+"/
+"/   View defaultStyle:#'Adwaita'.
+"/   UserPreferences current codeViewTheme: Solarized new.    "/ choose one...
+"/   UserPreferences current codeViewTheme: nil.              "/ ...or the other
+"/
+"/ Set dark mode
+"/
+"/   View defaultStyle:#'Adwaita_dark'.
+"/   UserPreferences current codeViewTheme: SolarizedDark new. "/ choose one...
+"/   UserPreferences current codeViewTheme: Monokai new.       "/ ...or the other
+
+
 "/ Set default text font for all text and list views
-"/ 
+"/
 "/   VDBAbstractApplication defaultTextFont: (FontDescription family:#'DejaVu Sans Mono' face:#normal size:12).
 "/   VDBAbstractApplication defaultTextFont: (CodeView defaultFont asSize: 16).