python: add new module `vdb.printing`
...containing a set of reusable pretty printers and other
pretty-printing utilities.
--- a/Make.proto Wed Jan 20 08:16:34 2021 +0000
+++ b/Make.proto Mon May 17 11:45:10 2021 +0100
@@ -63,9 +63,11 @@
all:: preMake classLibRule postMake
-pre_objs::
+pre_objs:: python
+python:
+
$(INSTALLLIB_DIR)/python:
mkdir -p $@
@@ -206,7 +208,7 @@
$(OUTDIR)VDBSourceAndDisassemblyApplication.$(O) VDBSourceAndDisassemblyApplication.$(C) VDBSourceAndDisassemblyApplication.$(H): VDBSourceAndDisassemblyApplication.st $(INCLUDE_TOP)/jv/vdb/VDBAbstractApplication.$(H) $(INCLUDE_TOP)/jv/vdb/VDBAbstractContainer.$(H) $(INCLUDE_TOP)/jv/vdb/VDBAbstractContentsApplication.$(H) $(INCLUDE_TOP)/jv/vdb/VDBTabbingContainer.$(H) $(INCLUDE_TOP)/stx/libbasic/Object.$(H) $(INCLUDE_TOP)/stx/libview2/ApplicationModel.$(H) $(INCLUDE_TOP)/stx/libview2/Model.$(H) $(STCHDR)
$(OUTDIR)VDBStackApplication.$(O) VDBStackApplication.$(C) VDBStackApplication.$(H): VDBStackApplication.st $(INCLUDE_TOP)/jv/vdb/VDBAbstractApplication.$(H) $(INCLUDE_TOP)/jv/vdb/VDBAbstractContentsApplication.$(H) $(INCLUDE_TOP)/jv/vdb/VDBAbstractListApplication.$(H) $(INCLUDE_TOP)/jv/vdb/VDBAbstractTreeApplication.$(H) $(INCLUDE_TOP)/stx/libbasic/Object.$(H) $(INCLUDE_TOP)/stx/libview2/ApplicationModel.$(H) $(INCLUDE_TOP)/stx/libview2/Model.$(H) $(STCHDR)
$(OUTDIR)VDBVariableObjectListApplication.$(O) VDBVariableObjectListApplication.$(C) VDBVariableObjectListApplication.$(H): VDBVariableObjectListApplication.st $(INCLUDE_TOP)/jv/vdb/VDBAbstractApplication.$(H) $(INCLUDE_TOP)/jv/vdb/VDBAbstractContentsApplication.$(H) $(INCLUDE_TOP)/jv/vdb/VDBAbstractListApplication.$(H) $(INCLUDE_TOP)/jv/vdb/VDBAbstractTreeApplication.$(H) $(INCLUDE_TOP)/stx/libbasic/Object.$(H) $(INCLUDE_TOP)/stx/libview2/ApplicationModel.$(H) $(INCLUDE_TOP)/stx/libview2/Model.$(H) $(STCHDR)
-$(OUTDIR)extensions.$(O): extensions.st $(INCLUDE_TOP)/jv/libgdbs/GDBDebugger.$(H) $(INCLUDE_TOP)/jv/libgdbs/GDBDebuggerObject.$(H) $(INCLUDE_TOP)/jv/libgdbs/GDBFrame.$(H) $(INCLUDE_TOP)/jv/libgdbs/GDBObject.$(H) $(INCLUDE_TOP)/jv/libgdbs/GDBVariable.$(H) $(INCLUDE_TOP)/jv/libgdbs/GDBVariableObject.$(H) $(INCLUDE_TOP)/stx/libbasic/Object.$(H) $(STCHDR)
+$(OUTDIR)extensions.$(O): extensions.st $(INCLUDE_TOP)/jv/libgdbs/GDBDebugger.$(H) $(INCLUDE_TOP)/jv/libgdbs/GDBDebuggerObject.$(H) $(INCLUDE_TOP)/jv/libgdbs/GDBFrame.$(H) $(INCLUDE_TOP)/jv/libgdbs/GDBObject.$(H) $(INCLUDE_TOP)/jv/libgdbs/GDBVariable.$(H) $(INCLUDE_TOP)/jv/libgdbs/GDBVariableObject.$(H) $(INCLUDE_TOP)/stx/libbasic/Object.$(H) $(INCLUDE_TOP)/stx/libview2/Model.$(H) $(STCHDR)
# ENDMAKEDEPEND --- do not remove this line
--- a/bc.mak Wed Jan 20 08:16:34 2021 +0000
+++ b/bc.mak Mon May 17 11:45:10 2021 +0100
@@ -43,7 +43,7 @@
OBJS= $(COMMON_OBJS) $(WIN32_OBJS)
-ALL:: classLibRule
+ALL:: python classLibRule
classLibRule: $(OUTDIR) $(OUTDIR)$(LIBNAME).dll
@@ -70,6 +70,8 @@
+python:
+
$(INSTALLLIB_DIR)\python:
md $@
@@ -154,7 +156,7 @@
$(OUTDIR)VDBSourceAndDisassemblyApplication.$(O) VDBSourceAndDisassemblyApplication.$(C) VDBSourceAndDisassemblyApplication.$(H): VDBSourceAndDisassemblyApplication.st $(INCLUDE_TOP)\jv\vdb\VDBAbstractApplication.$(H) $(INCLUDE_TOP)\jv\vdb\VDBAbstractContainer.$(H) $(INCLUDE_TOP)\jv\vdb\VDBAbstractContentsApplication.$(H) $(INCLUDE_TOP)\jv\vdb\VDBTabbingContainer.$(H) $(INCLUDE_TOP)\stx\libbasic\Object.$(H) $(INCLUDE_TOP)\stx\libview2\ApplicationModel.$(H) $(INCLUDE_TOP)\stx\libview2\Model.$(H) $(STCHDR)
$(OUTDIR)VDBStackApplication.$(O) VDBStackApplication.$(C) VDBStackApplication.$(H): VDBStackApplication.st $(INCLUDE_TOP)\jv\vdb\VDBAbstractApplication.$(H) $(INCLUDE_TOP)\jv\vdb\VDBAbstractContentsApplication.$(H) $(INCLUDE_TOP)\jv\vdb\VDBAbstractListApplication.$(H) $(INCLUDE_TOP)\jv\vdb\VDBAbstractTreeApplication.$(H) $(INCLUDE_TOP)\stx\libbasic\Object.$(H) $(INCLUDE_TOP)\stx\libview2\ApplicationModel.$(H) $(INCLUDE_TOP)\stx\libview2\Model.$(H) $(STCHDR)
$(OUTDIR)VDBVariableObjectListApplication.$(O) VDBVariableObjectListApplication.$(C) VDBVariableObjectListApplication.$(H): VDBVariableObjectListApplication.st $(INCLUDE_TOP)\jv\vdb\VDBAbstractApplication.$(H) $(INCLUDE_TOP)\jv\vdb\VDBAbstractContentsApplication.$(H) $(INCLUDE_TOP)\jv\vdb\VDBAbstractListApplication.$(H) $(INCLUDE_TOP)\jv\vdb\VDBAbstractTreeApplication.$(H) $(INCLUDE_TOP)\stx\libbasic\Object.$(H) $(INCLUDE_TOP)\stx\libview2\ApplicationModel.$(H) $(INCLUDE_TOP)\stx\libview2\Model.$(H) $(STCHDR)
-$(OUTDIR)extensions.$(O): extensions.st $(INCLUDE_TOP)\jv\libgdbs\GDBDebugger.$(H) $(INCLUDE_TOP)\jv\libgdbs\GDBDebuggerObject.$(H) $(INCLUDE_TOP)\jv\libgdbs\GDBFrame.$(H) $(INCLUDE_TOP)\jv\libgdbs\GDBObject.$(H) $(INCLUDE_TOP)\jv\libgdbs\GDBVariable.$(H) $(INCLUDE_TOP)\jv\libgdbs\GDBVariableObject.$(H) $(INCLUDE_TOP)\stx\libbasic\Object.$(H) $(STCHDR)
+$(OUTDIR)extensions.$(O): extensions.st $(INCLUDE_TOP)\jv\libgdbs\GDBDebugger.$(H) $(INCLUDE_TOP)\jv\libgdbs\GDBDebuggerObject.$(H) $(INCLUDE_TOP)\jv\libgdbs\GDBFrame.$(H) $(INCLUDE_TOP)\jv\libgdbs\GDBObject.$(H) $(INCLUDE_TOP)\jv\libgdbs\GDBVariable.$(H) $(INCLUDE_TOP)\jv\libgdbs\GDBVariableObject.$(H) $(INCLUDE_TOP)\stx\libbasic\Object.$(H) $(INCLUDE_TOP)\stx\libview2\Model.$(H) $(STCHDR)
# ENDMAKEDEPEND --- do not remove this line
--- a/jv_vdbWINrc.rc Wed Jan 20 08:16:34 2021 +0000
+++ b/jv_vdbWINrc.rc Mon May 17 11:45:10 2021 +0100
@@ -3,8 +3,8 @@
// automagically generated from the projectDefinition: jv_vdb.
//
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 6,2,2,2
- PRODUCTVERSION 6,2,6,0
+ FILEVERSION 8,0,32767,32767
+ PRODUCTVERSION 8,0,99,0
#if (__BORLANDC__)
FILEFLAGSMASK VS_FF_DEBUG | VS_FF_PRERELEASE
FILEFLAGS VS_FF_PRERELEASE | VS_FF_SPECIALBUILD
@@ -18,14 +18,14 @@
BEGIN
BLOCK "040904E4"
BEGIN
- VALUE "CompanyName", "My Company\0"
- VALUE "FileDescription", "Class Library (LIB)\0"
- VALUE "FileVersion", "6.2.2.2\0"
+ VALUE "CompanyName", "Jan Vrany\0"
+ VALUE "FileDescription", "Visual / VM Debugger Library (LIB)\0"
+ VALUE "FileVersion", "8.0.32767.32767\0"
VALUE "InternalName", "jv:vdb\0"
- VALUE "LegalCopyright", "My CopyRight or CopyLeft\0"
- VALUE "ProductName", "LibraryName\0"
- VALUE "ProductVersion", "6.2.6.0\0"
- VALUE "ProductDate", "Thu, 01 Jun 2017 11:16:29 GMT\0"
+ VALUE "LegalCopyright", "Copyright (C) Jan Vrany 2015-now\0"
+ VALUE "ProductName", "Visual / VM Debugger Library\0"
+ VALUE "ProductVersion", "8.0.99.0\0"
+ VALUE "ProductDate", "Mon, 17 May 2021 10:54:16 GMT\0"
END
END
--- a/python/vdb/__init__.py Wed Jan 20 08:16:34 2021 +0000
+++ b/python/vdb/__init__.py Mon May 17 11:45:10 2021 +0100
@@ -7,18 +7,19 @@
# You may find a full license text in LICENSE.txt or at http://creativecommons.org/licenses/by-nc/4.0/
#
-import sys
-import itertools
import argparse
import copy
+import itertools
+import sys
+
import gdb
class Error(Exception):
pass
+
class _VarObjRegistry(object):
-
def __init__(self):
self._seqno = 0
self._varobjs = {}
@@ -36,7 +37,7 @@
"""
Generate and return a new, unique variable name.
"""
- name = 'vdbvar' + str(self._seqno)
+ name = "vdbvar" + str(self._seqno)
self._seqno = self._seqno + 1
return name
@@ -59,11 +60,12 @@
def unregister_all(self):
self._varobjs = {}
+
_VarObjs = _VarObjRegistry()
class _VarObj(object):
- def __init__(self, name, expr, value, parent = None):
+ def __init__(self, name, expr, value, parent=None):
self._name = name
self._expr = expr
self._value = value
@@ -73,11 +75,25 @@
def __copy__(self):
return _VarObj(None, self._expr, self._value, self._parent)
- def to_mi(self, properties = [ 'name', 'exp', 'numchild', 'value', 'type', 'thread_id', 'frozen', 'has_more', 'displayhint', 'dynamic' ]):
+ def to_mi(
+ self,
+ properties=[
+ "name",
+ "exp",
+ "numchild",
+ "value",
+ "type",
+ "thread_id",
+ "frozen",
+ "has_more",
+ "displayhint",
+ "dynamic",
+ ],
+ ):
"""
Return MI record representing this variable object.
"""
- return { p : getattr(self, p) for p in properties if getattr(self, p) != None}
+ return {p: getattr(self, p) for p in properties if getattr(self, p) != None}
def children(self):
"""
@@ -85,15 +101,18 @@
Children are NOT registered, the caller must take care
about this.
"""
- if not hasattr(self, '_children'):
+ if not hasattr(self, "_children"):
+
def child(exp, value):
- child_name = self._name + '.' + exp
+ child_name = self._name + "." + exp
child = _VarObj(child_name, exp, value, self)
return child
+
if not hasattr(self._visualizer, "children"):
self._children = []
else:
- class lazy():
+
+ class lazy:
def __init__(self, iterator):
self._head = []
self._rest = iterator
@@ -104,10 +123,16 @@
for each in self._rest:
self._head.append(each)
yield each
- self._children = lazy(map(lambda pair : child(pair[0], pair[1]), self._visualizer.children()))
+
+ self._children = lazy(
+ map(
+ lambda pair: child(pair[0], pair[1]),
+ self._visualizer.children(),
+ )
+ )
return iter(self._children)
- def set_visualizer(self,visualizer):
+ def set_visualizer(self, visualizer):
"""
Set new visualizer
"""
@@ -115,8 +140,6 @@
self._visualizer = visualizer
if self._visualizer == None:
self._visualizer = gdb.default_visualizer(self._value)
- if self._visualizer == None:
- self._visualizer = BasicPrinter(self._value)
if hasattr(self, "_children"):
del self._children
@@ -150,7 +173,10 @@
aggregate (e.g., a struct), or for a dynamic varobj, this value will not
be interesting.
"""
- return str(self._visualizer.to_string())
+ if self._visualizer != None:
+ return str(self._visualizer.to_string())
+ else:
+ return str(self._value)
@property
def type(self):
@@ -230,13 +256,15 @@
"""
default_visualizer = gdb.default_visualizer(value)
if hasattr(default_visualizer, "contents_visualizer"):
- return default_visualizer.contents_visualizer(value)
+ return default_visualizer.contents_visualizer(value)
else:
- return default_visualizer
+ return default_visualizer
+
_VarEvaluators = []
-def register_evaluator(callable, replace = True):
+
+def register_evaluator(callable, replace=True):
"""
Register `callable` in list of evaluators which are tried when creating
-vdb-var-create. To evaluate and expression, `callable` is invoked passing
@@ -244,6 +272,7 @@
"""
_VarEvaluators.append(callable)
+
def unregister_evaluator(callable):
"""
Unregister previously registered evaluator.
@@ -251,12 +280,14 @@
if callable in _VarEvaluators:
_VarEvaluators.remove(callable)
+
def unregister_evaluators():
"""
Unregister all evaluators. To be used mainly in tests
"""
_VarEvaluators = []
+
def parse_and_eval(expression):
"""
Parse and evaluate expression and return a gdb.Value() as follows:
@@ -294,27 +325,32 @@
# Nothing works, give up.
raise Error("Cannot evaluate expression: %s" % expression)
+
def micommand(name):
def _command(func):
class _Command(gdb.MICommand):
def invoke(*args, **kwargs):
return func(*args[1:], **kwargs)
+
def __call__(self, *args, **kwargs):
return self.invoke(*args, **kwargs)
+
return _Command(name)
+
return _command
-@micommand('-vdb-var-create')
+
+@micommand("-vdb-var-create")
def _var_create(argv):
argv_parser = argparse.ArgumentParser()
- argv_parser.add_argument('name')
- argv_parser.add_argument('frame')
- argv_parser.add_argument('expr')
+ argv_parser.add_argument("name")
+ argv_parser.add_argument("frame")
+ argv_parser.add_argument("expr")
args = argv_parser.parse_args(argv)
- if args.frame != '*':
+ if args.frame != "*":
raise Exception("frame designator must be '*'")
- if args.name == '-':
+ if args.name == "-":
args.name = _VarObjs.generate_name()
_VarObjs.validate_name(args.name)
@@ -322,11 +358,12 @@
_VarObjs.register(varobj)
return varobj.to_mi()
-@micommand('-vdb-var-delete')
+
+@micommand("-vdb-var-delete")
def _var_delete(argv):
argv_parser = argparse.ArgumentParser()
- argv_parser.add_argument('name')
- argv_parser.add_argument('-c', dest="children_only", action="store_true")
+ argv_parser.add_argument("name")
+ argv_parser.add_argument("-c", dest="children_only", action="store_true")
args = argv_parser.parse_args(argv)
if args.children_only:
@@ -335,15 +372,17 @@
_VarObjs.unregister(_VarObjs[args.name])
return None
-@micommand('-vdb-var-update')
+
+@micommand("-vdb-var-update")
def _var_update(argv):
# Not yet supported
return []
-@micommand('-vdb-var-duplicate')
+
+@micommand("-vdb-var-duplicate")
def _var_duplicate(argv):
argv_parser = argparse.ArgumentParser()
- argv_parser.add_argument('name')
+ argv_parser.add_argument("name")
args = argv_parser.parse_args(argv)
orig = _VarObjs[args.name]
@@ -352,12 +391,13 @@
_VarObjs.register(dup)
return dup.to_mi()
-@micommand('-vdb-var-list-children')
+
+@micommand("-vdb-var-list-children")
def _var_list_children(argv):
argv_parser = argparse.ArgumentParser()
- argv_parser.add_argument('--all-values', dest="values", action="store_true")
- argv_parser.add_argument('--no-values', dest="values", action="store_false")
- argv_parser.add_argument('name')
+ argv_parser.add_argument("--all-values", dest="values", action="store_true")
+ argv_parser.add_argument("--no-values", dest="values", action="store_false")
+ argv_parser.add_argument("name")
args = argv_parser.parse_args(argv)
varobj = _VarObjs[args.name]
@@ -368,23 +408,26 @@
return map(_VarObj.to_mi, children)
-@micommand('-vdb-var-info-path-expression')
+@micommand("-vdb-var-info-path-expression")
def _var_info_path_expression(argv):
argv_parser = argparse.ArgumentParser()
- argv_parser.add_argument('name')
+ argv_parser.add_argument("name")
args = argv_parser.parse_args(argv)
varobj = _VarObjs[args.name]
if varobj.parent != None:
- raise Error("-vdb-var-info-path-expression is not supported for children of dynamic varobjs")
+ raise Error(
+ "-vdb-var-info-path-expression is not supported for children of dynamic varobjs"
+ )
else:
return varobj.exp
-@micommand('-vdb-var-set-visualizer')
+
+@micommand("-vdb-var-set-visualizer")
def _var_set_visualizer(argv):
argv_parser = argparse.ArgumentParser()
- argv_parser.add_argument('name')
- argv_parser.add_argument('visualizer')
+ argv_parser.add_argument("name")
+ argv_parser.add_argument("visualizer")
args = argv_parser.parse_args(argv)
varobj = _VarObjs[args.name]
@@ -401,13 +444,15 @@
visualizer_factory = eval(args.visualizer)
except:
import __main__
- visualizer_factory = eval(args.visualizer, __main__.__dict__ , __main__.__dict__)
+
+ visualizer_factory = eval(args.visualizer, __main__.__dict__, __main__.__dict__)
visualizer = visualizer_factory(varobj._value)
varobj.set_visualizer(visualizer)
+
# Set prompt for both, GDBCLI and Python CLI
-gdb.prompt_hook = lambda x: 'vdb > '
-sys.ps1 = 'vdb pi > '
-sys.ps2 = ' > '
+gdb.prompt_hook = lambda x: "vdb > "
+sys.ps1 = "vdb pi > "
+sys.ps2 = " > "
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python/vdb/printing.py Mon May 17 11:45:10 2021 +0100
@@ -0,0 +1,113 @@
+#
+# 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/
+#
+
+"""
+Pretty printing utilities
+"""
+import re
+
+import gdb
+import gdb.printing
+
+
+class CxxCollectionPrettyPrinter(gdb.printing.PrettyPrinter):
+ """
+ Class implementing a collection of (sub)pretty-printers specialized
+ for use with C++ class types.
+
+ It behaves much like GDB's RegexpCollectionPrettyPrinter except that it
+ takes C++ inheritance into the account. if there's no pretty-printer
+ registered for given (dynamic) type, it looks if there's a pretty-printer
+ for supertype(s).
+
+ CxxCollectionPrettyPrinter may nest.
+ """
+
+ class CxxSubPrinter(gdb.printing.SubPrettyPrinter):
+ def __init__(self, name, regexp, printer):
+ super().__init__(name)
+ self.regexp = regexp
+ self.regexp_c = re.compile(regexp)
+ self.printer = printer
+
+ def _lookup(self, t):
+ """
+ Return self if self is suitable for given type `t`, `None` otherwise.
+ """
+ if t.code == gdb.TYPE_CODE_PTR:
+ t = t.target()
+ try:
+ if t.name != None and self.regexp_c.search(t.name):
+ return self
+ except:
+ pass
+ return None
+
+ def __call__(self, val):
+ """
+ Lookup the pretty-printer for the provided value.
+ """
+ return self.printer(val)
+
+ def add_printer(self, name, regexp, printer):
+ """Add a printer to the list.
+
+ Add printer to the list.
+
+ Arguments:
+ name: name of this pretty printer (for enable/disable)
+ regexp: the regular expression, as a string.
+ printer: a callable that given a value returns an object to
+ pretty-print it.
+
+ Returns:
+ Nothing.
+ """
+ if self.subprinters == None:
+ self.subprinters = []
+ for subprinter in self.subprinters:
+ if subprinter.regexp == regexp:
+ subprinter.printer = printer
+ return
+ self.subprinters.append(
+ CxxCollectionPrettyPrinter.CxxSubPrinter(name, regexp, printer)
+ )
+
+ def _lookup(self, t):
+ """
+ Lookup a pretty-printer for given type `t`, traversing supertypes if necessary.
+ Returns the pretty-printer factory or None if no pretty-printer is found.
+ """
+
+ def basetypes(t):
+ if t.code != gdb.TYPE_CODE_STRUCT:
+ return None
+ for f in t.fields():
+ if f.is_base_class:
+ return f.type
+ return None
+
+ if self.subprinters == None:
+ return None
+ if t.code == gdb.TYPE_CODE_PTR:
+ t = t.target()
+ while t != None:
+ for subprinter in self.subprinters:
+ if subprinter.enabled:
+ printer = subprinter._lookup(t)
+ if printer is not None:
+ return printer
+ t = basetypes(t)
+ return None
+
+ def __call__(self, val):
+ printer = self._lookup(val.dynamic_type)
+ if printer is not None:
+ return printer(val)
+ return None