diff --git a/gdb/NEWS b/gdb/NEWS index df233fc..133d02a 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -94,6 +94,11 @@ qXfer:btrace:read's annex ** Valid Python operations on gdb.Value objects representing structs/classes invoke the corresponding overloaded operators if available. + ** New `Debug Methods' feature in the Python API. Debug methods are + additional methods or replacements for existing methods of a C++ + class. This feature is useful for those cases where a method + defined in C++ source code could be inlined or optimized out by + the compiler, making it unavailable to GDB. * New targets PowerPC64 GNU/Linux little-endian powerpc64le-*-linux* diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index 90b7074..f149ea0 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -144,6 +144,9 @@ optional arguments while skipping others. Example: * Frame Filter API:: Filtering Frames. * Frame Decorator API:: Decorating Frames. * Writing a Frame Filter:: Writing a Frame Filter. +* Debug Methods In Python:: Adding and replacing methods of C++ classes. +* Debug Method API:: Debug method types. +* Writing a Debug Method:: Writing a debug method. * Inferiors In Python:: Python representation of inferiors (processes) * Events In Python:: Listening for events from @value{GDBN}. * Threads In Python:: Accessing inferior threads from Python. @@ -2195,6 +2198,267 @@ printed hierarchically. Another approach would be to combine the marker in the inlined frame, and also show the hierarchical relationship. +@node Debug Methods In Python +@subsubsection Debug Methods In Python +@cindex debug methods in Python + +@dfn{Debug methods} are additional methods or replacements for existing +methods of a C@t{++} class. This feature is useful for those cases +where a method defined in C@t{++} source code could be inlined or +optimized out by the compiler, making it unavailable to @value{GDBN}. +For such cases, one can define a debug method to serve as a replacement +for the method defined in the C@t{++} source code. @value{GDBN} will +then invoke the debug method, instead of the C@t{++} method, to +evaluate expressions. One can also use debug methods when debugging +with core files. Moreover, when debugging live programs, invoking a +debug method need not involve running the inferior (which can potentially +perturb its state). Hence, even if the C@t{++} method is available, it +is better to use its replacement debug method if one is defined. + +The debug methods feature in Python is available via the concepts of a +@dfn{debug method matcher} and a @dfn{debug method worker}. To +implement a debug method, one has to implement a matcher and a +corresponding worker for it (more than one worker can be +implemented, each catering to a different overloaded instance of the +method). Internally, @value{GDBN} invokes the @code{match} method of a +matcher to match the class type and method name. On a match, the +@code{match} method returns a list of matching @emph{worker} objects. +Each worker object typically corresponds to an overloaded instance of +the debug method. They implement a @code{get_arg_types} method which +returns a sequence of types corresponding to the arguments the debug +method requires. @value{GDBN} uses this sequence of types to perform +overload resolution and picks a winning debug method worker. A winner +is also selected from among the methods @value{GDBN} finds in the +C@t{++} source code. Next, the winning debug method worker and the +winning C@t{++} method are compared to select an overall winner. In +case of a tie between a debug method worker and a C@t{++} method, the +debug method worker is selected as the winner. That is, if a winning +debug method worker is found to be equivalent to the winning C@t{++} +method, then the debug method worker is treated as a replacement for +the C@t{++} method. @value{GDBN} uses the overall winner to invoke the +method. If the winning debug method worker is the overall winner, then +the corresponding debug method is invoked via the @code{invoke} method +of the worker object. + +If one wants to implement a debug method as a replacement for an +existing C@t{++} method, then they have to implement an equivalent +debug method which has exactly the same name and takes arguments of +exactly the same type as the C@t{++} method. If the user wants to +invoke the C@t{++} method even though a replacement debug method is +available for that method, then they can disable the debug method. + +@xref{Debug Method API}, for API to implement debug methods in Python. +@xref{Writing a Debug Method}, for implementing debug methods in Python. + +@node Debug Method API +@subsubsection Debug Method API +@cindex debug method API + +The @value{GDBN} Python API provides classes, interfaces and functions +to implement, register and manipulate debug methods. The core concepts +involved in implementing debug methods are @emph{debug method matchers} +and @emph{debug method workers}. @xref{Debug Methods In Python}. + +A debug method matcher should be an instance of a class derived from +@code{DebugMethodMatcher} defined in the module +@code{gdb.debug_method}. An instance of @code{DebugMethodMatcher} has +the following attributes: + +@defvar DebugMethodMatcher.name +The name of the matcher. +@end defvar + +@defvar DebugMethodMatcher.enabled +A boolean value indicating whether the matcher is enabled or disabled. +@end defvar + +@defvar DebugMethodMatcher.methods +A list of named methods managed by the matcher. Each object in the list +is an instance of the class @code{DebugMethod} defined in the module +@code{gdb.debug_method}, or any object with the following attributes: + +@table @code + +@item name +Name of the debug method which should be unique for each debug method +managed by the matcher. + +@item enabled +A boolean value indicating whether the debug method is enabled or +disabled. + +@end table + +The class @code{DebugMethod} is a convenience class with same +attributes as above along with the following constructor: + +@defun DebugMethod.__init__(self, name) +Constructs an enabled debug method with name @var{name}. +@end defun +@end defvar + +@noindent +The @code{DebugMethodMatcher} class has the following methods: + +@defun DebugMethodMatcher.__init__(self, name) +Constructs an enabled debug method matcher with name @var{name}. The +@code{methods} attribute is initialized to @code{None}. +@end defun + +@defun DebugMethodMatcher.match(self, class_type, method_name) +Derived classes should override this method. It should return a +debug method worker object (or a sequence of debug method worker +objects) matching the @var{class_type} and @var{method_name}. +@var{class_type} is a @code{gdb.Type} object, and @var{method_name} +is a string value. If the matcher manages named methods as listed in +its @code{methods} attribute, then only those worker objects whose +corresponding entries in the @code{methods} list are enabled should be +returned. +@end defun + +A debug method worker should be an instance of a class derived from +@code{DebugMethodWorker} defined in the module @code{gdb.debug_method}, +or support the following interface: + +@defun DebugMethodWorker.get_arg_types(self) +This method returns a sequence of @code{gdb.Type} objects corresponding +to the arguments that the debug method takes. It can return an empty +sequence or @code{None} if the debug method does not take any arguments. +If the debug method takes a single argument, then a single +@code{gdb.Type} object corresponding to it can be returned. +@end defun + +@defun DebugMethodWorker.invoke(self, obj, args) +This is the method which does the @emph{work} of the debug method. +@var{obj} is the object on which the method is being invoked, and +@var{args} is the tuple of arguments to the method. @var{obj} and the +elements of @var{args} are @code{gdb.Value} objects. +@end defun + +For @value{GDBN} to lookup debug methods, the debug method matchers +should be registered using the following function defined in the module +@code{gdb.debug_method}: + +@defun register_debug_method_matcher(locus, matcher, replace=False) +The @code{matcher} is registered with @code{locus}, replacing an +existing matcher with the same name as @code{matcher} if +@code{replace} is @code{True}. @code{locus} can be a +@code{gdb.Objfile} object (@pxref{Objfiles In Python}), or a +@code{gdb.Progspace} object (@pxref{Program Spaces In Python}), or +@code{None}. If it is @code{None}, then @code{matcher} is registered +globally. +@end defun + +@node Writing a Debug Method +@subsubsection Writing a Debug Method +@cindex writing debug methods in Python + +Implementing debug methods in Python will require implementing debug +method matchers and debug method workers +(@pxref{Debug Methods In Python}). Consider the following C@t{++} +class: + +@smallexample +class MyClass +@{ + public: + MyClass (int a) : a_(a) {} + + int geta (void) { return a_; } + int operator+ (int b); + + private: + int a_; +@}; + +int +MyClass::operator+ (int b) +@{ + return a_ + b; +@} +@end smallexample + +@noindent +Let us define two debug methods for the class @code{MyClass}, one +replacing the method @code{geta}, and another adding an overloaded +flavor of @code{operator+} which takes a @code{MyClass} argument. The +debug method matcher can be defined as follows: + +@smallexample +class MyClassMatcher(gdb.debug_method.DebugMethodMatcher): + def __init__(self): + gdb.debug_method.DebugMethodMatcher.__init__(self, 'MyMatcher') + # List of methods 'managed' by this matcher + self.methods = [gdb.debug_method.DebugMethod('geta'), + gdb.debug_method.DebugMethod('sum')] + + def match(self, class_type, method_name): + if class_type.tag != 'MyClass': + return None + if method_name == 'geta' and self.methods[0].enabled: + return MyClassWorker_geta() + elif method_name == 'operator+' and self.methods[1].enabled: + return MyClassWorker_plus() + else: + return None +@end smallexample + +@noindent +Notice that the @code{match} method of @code{MyClassMatcher} returns +a worker object of type @code{MyClassWorker_geta} for the @code{geta} +method, and a worker object of type @code{MyClassWorker_plus} for the +@code{operator+} method. Also, a worker object is returned only if the +corresponding entry in the @code{methods} attribute is enabled. + +The implementation of the worker classes returned by the matcher above +is as follows: + +@smallexample +class MyClassWorker_geta(gdb.debug_method.DebugMethodWorker): + def get_arg_types(self): + return None + + def invoke(self, obj, args): + return obj['a_'] + + +class MyClassWorker_plus(gdb.debug_method.DebugMethodWorker): + def get_arg_types(self): + return gdb.lookup_type('MyClass') + + def invoke(self, obj, args): + return obj['a_'] + args[0]['a_'] +@end smallexample + +For @value{GDBN} to actually lookup a debug method, it has to be +registered with it. The matcher defined above is registered with +@value{GDBN} globally as follows: + +@smallexample +gdb.debug_method.register_debug_method_matcher(None, MyClassMatcher()) +@end smallexample + +If an object @code{obj} of type @code{MyClass} is initialized in C@t{++} +code as follows: + +@smallexample +MyClass obj(5); +@end smallexample + +@noindent +then, after loading the Python script defining the debug method matchers +and workers into @code{GDBN}, invoking the method @code{geta} or using +the operator @code{+} on @code{obj} will invoke the debug methods +defined above: + +@smallexample +(gdb) p obj.geta() +$1 = 5 + +(gdb) p obj + obj +$2 = 10 +@end smallexample + @node Inferiors In Python @subsubsection Inferiors In Python @cindex inferiors in Python