[v13,1/4] Documentation of the debug method support in GDB Python API

Message ID CAGyQ6gxANoq6wQFZw_28ss40sSPqr_3ouqkaRoc9eGOW5dk6XQ@mail.gmail.com
State Superseded
Headers

Commit Message

Siva Chandra Reddy April 9, 2014, 7 p.m. UTC
  This version addresses all of Eli's and Doug's comments on previous versions.

ChangeLog:
2014-04-09  Siva Chandra Reddy  <sivachandra@google.com>

        * NEWS (Python Scripting): Add entry about this new feature.

        doc/
        *  python.texi (Debug Methods In Python): New node.
        (Debug Method API): Likewise.
        (Writing a Debug Method): Likewise.
  

Comments

Eli Zaretskii April 11, 2014, 8:41 a.m. UTC | #1
> Date: Wed, 9 Apr 2014 12:00:32 -0700
> From: Siva Chandra <sivachandra@google.com>
> 
> This version addresses all of Eli's and Doug's comments on previous versions.

This is OK, with one gotcha in the ChangeLog:

>         doc/
>         *  python.texi (Debug Methods In Python): New node.
>         (Debug Method API): Likewise.
>         (Writing a Debug Method): Likewise.

I believe the GNU standard way of saying this is as follows:

	* python.texi (Debug Methods In Python, Debug Method API)
	(Writing a Debug Method): New nodes.

Also, the menu additions in the parent node should be mentioned in
ChangeLog.

Thanks.
  

Patch

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..0097f5f 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,266 @@  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.
+@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 name
+The name of the matcher.
+@end defvar
+
+@defvar enabled
+A boolean value indicating whether the matcher is enabled or disabled.
+@end defvar
+
+@defvar 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