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.
#
# jv:vdb - Visual / VM Debugger
# Copyright (C) 2015, 2021 Jan Vrany
#
# This software is licensed under 'Creative Commons Attribution-NonCommercial 4.0 International License'
#
# You may find a full license text in LICENSE.txt or at http://creativecommons.org/licenses/by-nc/4.0/
#
import os.path
import sys
try:
import importlib
except:
import imp as importlib
import gdb
class __PythonReload(gdb.Command):
"""
Reload Python code
Usage: pr
Reload Python modules, making best-effort to update all code to reflect
new version.However, this is not perfect due to limitations of Python's
importlib.reload().
NOTE, that this command only reload modules whose prefix is in
`vdb.cli.pr.prefixes`. So, in order to reload your project-specific
GDB modules, you need to do:
from vdb.cli import pr
pr.prefixes.append('coolproject')
assuming all your GDB scripts are either in top-level script
loaded by GDB or in module(s) starting with "coolproject" and
imported from there. By default, this command reloads __main__
and vdb modules.
"""
def __init__(self, name, cmd_type):
super().__init__(name, cmd_type)
self.prefixes = ["__main__", "vdb"]
def invoke(self, args, from_tty):
self()
def __call__(self):
def imports(module):
"""
Return a list of modules imported by given `module`.
This is mostly a guesswork, no guarantee it'd return
all of them, but should reasonably handle both,
import xyz
and
from abc import ijk as bfl
"""
imports = set()
def module_of(obj):
"""
Given an object, return a module it came from
(or itself, if the object is module itself).
Again, just a best-effort guesswork.
"""
if isinstance(obj, type(sys.modules["__main__"])):
return obj
elif hasattr(obj, "__module__"):
imported = getattr(obj, "__module__")
if isinstance(imported, type(sys.modules["__main__"])):
return sys.modules[imported.__name__]
elif isinstance(imported, str):
if imported in sys.modules:
return sys.modules[imported]
elif hasattr(obj, "__class__"):
return module_of(getattr(obj, "__class__"))
return None
for name in dir(module):
m = module_of(getattr(module, name))
if m != None and m != module:
imports.add(m)
# print("%s imports:" % module.__name__)
# for i in imports:
# print(" * %s" % i.__name__)
return imports
def reload(
module=sys.modules["__main__"], prefixes=self.prefixes, reloaded=set()
):
"""
Reload given module and all it's imported modules
"""
try_reload = any(
[module.__name__.startswith(prefix) for prefix in prefixes]
)
if try_reload:
if module not in reloaded:
reloaded.add(module)
for imported in imports(module):
reload(imported, prefixes)
print("Reloading %s..." % module.__name__)
# Sigh, importlib.reload() cannot reload __main__ (for whatever reason)
# So, we have to "reload" it manually by re-evaluating the source in
# context of __main__ module.
#
# TODO: Is there a betpiter way? Following code looks a wee bit too
# fragile
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)