* [PATCH] Debug Methods in GDB Python
@ 2013-11-22 22:41 Siva Chandra
2013-11-26 3:22 ` Siva Chandra
0 siblings, 1 reply; 12+ messages in thread
From: Siva Chandra @ 2013-11-22 22:41 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey, Doug Evans
[-- Attachment #1: Type: text/plain, Size: 3251 bytes --]
Hi,
This is a continuation from a previous thread, but switching from RFC
to PATCH. For reference, my last message from the previous thread can
be found here: https://sourceware.org/ml/gdb-patches/2013-11/msg00303.html
Please find the patch attached. It now has tests, info/enable/disable
debug method commands, and addresses most previous comments from Tom
and Doug. For couple of them, I have responses inline.
Siva> +struct py_ext_object
Siva> +{
Siva> + /* Holds an instance of the DebugMethod class. */
Siva> + PyObject *object;
Siva> +
Siva> + /* Holds the type of the 'this' object. */
Siva> + PyObject *match_py_obj_type;
Siva> +
Siva> + /* Holds the matching method name. */
Siva> + const char *match_method;
Tom> How "match_method" is allocated and how its lifetime is
Tom> managed is really unclear to me.
The match_method is the name of the method to match. This is allocated
and managed by the agents calling into the debug methods
infrastructure.
Doug> For matching the class I would look into how the current
Doug> version of the libstdc++ pretty-printers do this.
There is a utility debug method class in the patch which does a very
simple match. But, the infrastructure provided in the patch can used
to implement any kind of matching that the user wants. For example,
look at testsuite/gdb.python/py-debugmethods.py in the attached patch.
2013-11-22 Siva Chandra Reddy <sivachandra@google.com>
Add Debug methods support in GDB Python.
* Makefile.in: Add entries for new files
* data-directory/Makefile.in: Add entry for new Python file
* eval.c: Invoke methods defined in extension methods.
* ext-function.c: Support for working with functions/methods
defined in an extension language.
* ext-function.h: Support for working with functions/methods
defined in an extension language.
* python/lib/gdb/__init__.py: Add an attribute 'debug_methods'
to the 'gdb' module.
* python/lib/gdb/debugmethods.py: Python side of the support
for debug methods in Python.
* python/lib/gdb/command/debug-methods.py: Definition of
new debug method commands (info/enable/disable).
* python/py-debugmethods.c: C side of the support for debug
methods in Python.
* python/py-objfile.c: Add 'debug_methods' attribute to
gdb.Objfile.
* python/py-progspace.c: Add 'debug_methods' attribute to
gdb.Progspace.
* python/python-internal.h: Add new function.
gdb.enable_debug_methods to the Python module 'gdb'.
* python/python.c: Add new function gdb.enable_debug_methods to
the Python module 'gdb'.
* python/python.h: Add declarations of new functions.
* valarith.c: Invoke operators defined in extension languages.
* valops.c: Lookup methods defined in extension languages.
* value.h: New signature for 'find_overload_match'.
testuite/
* gdb.python/py-debugmethods.cc: New testcase to test debug
methods.
* gdb.python/py-debugmethods.exp: New tests to test debug
methods.
* gdb.python/py-debugmethods.py: Python script supporting the
new testcase and tests.
[-- Attachment #2: dm_patch_v6.txt --]
[-- Type: text/plain, Size: 98052 bytes --]
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 0591279..75e2be6 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -287,6 +287,7 @@ SUBDIR_PYTHON_OBS = \
py-breakpoint.o \
py-cmd.o \
py-continueevent.o \
+ py-debugmethods.o \
py-event.o \
py-evtregistry.o \
py-evts.o \
@@ -323,6 +324,7 @@ SUBDIR_PYTHON_SRCS = \
python/py-breakpoint.c \
python/py-cmd.c \
python/py-continueevent.c \
+ python/py-debugmethods.c \
python/py-event.c \
python/py-evtregistry.c \
python/py-evts.c \
@@ -733,7 +735,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
dwarf2expr.c dwarf2loc.c dwarf2read.c dwarf2-frame.c \
dwarf2-frame-tailcall.c \
elfread.c environ.c eval.c event-loop.c event-top.c \
- exceptions.c expprint.c \
+ exceptions.c expprint.c ext-function.c \
f-exp.y f-lang.c f-typeprint.c f-valprint.c filesystem.c \
findcmd.c findvar.c frame.c frame-base.c frame-unwind.c \
gdbarch.c arch-utils.c gdb_bfd.c gdb_obstack.c \
@@ -835,7 +837,7 @@ rs6000-tdep.h rs6000-aix-tdep.h \
common/gdb_locale.h common/gdb_dirent.h arch-utils.h trad-frame.h gnu-nat.h \
language.h nbsd-tdep.h solib-svr4.h \
macroexp.h ui-file.h regcache.h tracepoint.h i386-tdep.h \
-inf-child.h p-lang.h event-top.h gdbtypes.h user-regs.h \
+inf-child.h p-lang.h event-top.h ext-function.h gdbtypes.h user-regs.h \
regformats/regdef.h config/alpha/nm-osf3.h config/i386/nm-i386gnu.h \
config/i386/nm-fbsd.h \
config/nm-nto.h config/sparc/nm-sol2.h config/nm-linux.h \
@@ -957,7 +959,8 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
inferior.o osdata.o gdb_usleep.o record.o record-full.o gcore.o \
gdb_vecs.o jit.o progspace.o skip.o probe.o \
common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o \
- format.o registry.o btrace.o record-btrace.o waitstatus.o
+ format.o registry.o btrace.o record-btrace.o waitstatus.o \
+ ext-function.o
TSOBS = inflow.o
@@ -2181,6 +2184,10 @@ py-continueevent.o: $(srcdir)/python/py-continueevent.c
$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-continueevent.c
$(POSTCOMPILE)
+py-debugmethods.o: $(srcdir)/python/py-debugmethods.c
+ $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-debugmethods.c
+ $(POSTCOMPILE)
+
py-event.o: $(srcdir)/python/py-event.c
$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-event.c
$(POSTCOMPILE)
diff --git a/gdb/data-directory/Makefile.in b/gdb/data-directory/Makefile.in
index 1e00c58..6146cf1 100644
--- a/gdb/data-directory/Makefile.in
+++ b/gdb/data-directory/Makefile.in
@@ -61,8 +61,10 @@ PYTHON_FILES = \
gdb/types.py \
gdb/printing.py \
gdb/prompt.py \
+ gdb/debugmethods.py \
gdb/command/bound_registers.py \
gdb/command/__init__.py \
+ gdb/command/debug_methods.py \
gdb/command/frame_filters.py \
gdb/command/type_printers.py \
gdb/command/pretty_printers.py \
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index ec9d901..9eabd1d 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -23526,6 +23526,7 @@ optional arguments while skipping others. Example:
* Selecting Pretty-Printers:: How GDB chooses a pretty-printer.
* Writing a Pretty-Printer:: Writing a Pretty-Printer.
* Type Printing API:: Pretty-printing types.
+* Debug Methods In Python:: Debug Methods In Python.
* Frame Filter API:: Filtering Frames.
* Frame Decorator API:: Decorating Frames.
* Writing a Frame Filter:: Writing a Frame Filter.
@@ -24909,6 +24910,75 @@ done then type printers would have to make use of the event system in
order to avoid holding information that could become stale as the
inferior changed.
+@node Debug Methods In Python
+@subsubsection Debug Methods In Python
+@cindex debug methods in Python
+
+Debug methods are, as the name suggests, method definitions on classes
+but defined in @value{GDBN} Python instead of the source language. If a
+particular method (which could be an overloaded operator) needs to be
+invoked to evaluate a certain expression in @value{GDBN}, then
+@value{GDBN} invokes the Python implementation of the method if the
+Python definition is better or as good a match as the implementation of
+the method in the source language. A benefit of having a debug method
+replacement for a source language method is that expressions involving
+the method can be evaluated in @value{GDBN} without running the method
+in the inferior. Another benefit is with regards to method which
+typically get optimized out by the compiler. Since debug methods live
+in @value{GDBN} Python and not in the source language, they are never
+optimized out. Lastly, one could define a debug method in @value{GDBN}
+Python which does not have an equivalent in the source language!
+
+The debug method API provided in @value{GDBN} Python can be illustrated
+with the following example. Consider a C++ code snippet as follows:
+
+@smallexample
+class A
+@{
+ public:
+ int geta(void)
+ @{
+ return a;
+ @}
+
+ private:
+ int a;
+@};
+@end smallexample
+
+In the above example, the method @code{geta} in @code{class A}, might
+get optimized out in a client executable if @code{geta} is never called.
+However, one might want to call @code{geta} from @value{GDBN} for
+debugging. For such cases, we could have an equivalent Python
+implementation as a replacement for the C++ method. The debug method
+can be defined in Python as follows:
+
+@smallexample
+def A_geta(obj):
+ """Function which implemenents a Python replacement for A::geta.
+ Args:
+ obj: This is equivalent of the C++ 'this'
+ """
+ return obj['a']
+
+dm_A_geta = gdb.debugmethods.SimpleDebugMethod(
+ 'A_geta', # A unique name to the debug method
+ 'DEFAULT_DEBUG_METHOD_GROUP', # A group name for the debug method
+ A_geta, # The function implementing the debug
+ # method
+ '^dop::A$', # The class to which this debug
+ # method should belong.
+ '^geta$' # The regex which matches the method
+ # which this debug method is replacing.
+)
+
+gdb.debugmethods.register_debug_methods(
+ gdb, # Register the debug method globally
+ [dm_A_geta] # The list of debug methods to register
+)
+@end smallexample
+
+
@node Frame Filter API
@subsubsection Filtering Frames.
@cindex frame filters api
diff --git a/gdb/eval.c b/gdb/eval.c
index 9d81a92..ebeb02d 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -22,6 +22,7 @@
#include "symtab.h"
#include "gdbtypes.h"
#include "value.h"
+#include "ext-function.h"
#include "expression.h"
#include "target.h"
#include "frame.h"
@@ -1596,7 +1597,7 @@ evaluate_subexp_standard (struct type *expect_type,
NON_METHOD, /* not method */
NULL, NULL, /* pass NULL symbol since
symbol is unknown */
- NULL, &symp, NULL, 0);
+ NULL, &symp, NULL, NULL, 0);
/* Now fix the expression being evaluated. */
exp->elts[save_pos1 + 2].symbol = symp;
@@ -1626,11 +1627,12 @@ evaluate_subexp_standard (struct type *expect_type,
/* Language is C++, do some overload resolution before
evaluation. */
struct value *valp = NULL;
+ struct ext_fn_descriptor *ext_fnp = NULL;
(void) find_overload_match (&argvec[1], nargs, tstr,
METHOD, /* method */
&arg2, /* the object */
- NULL, &valp, NULL,
+ NULL, &valp, NULL, &ext_fnp,
&static_memfuncp, 0);
if (op == OP_SCOPE && !static_memfuncp)
@@ -1640,9 +1642,36 @@ evaluate_subexp_standard (struct type *expect_type,
"`this' pointer"),
function_name);
}
- argvec[1] = arg2; /* the ``this'' pointer */
- argvec[0] = valp; /* Use the method found after overload
- resolution. */
+
+ if (ext_fnp)
+ {
+ if (ext_fn_is_method (ext_fnp))
+ {
+ struct value *ret_val;
+
+ ret_val = ext_fn_invoke_method (ext_fnp, arg2, argvec + 2,
+ nargs - 1);
+ if (ret_val == NULL)
+ error (_("Error invoking debug method for method %s."),
+ tstr);
+
+ xfree (ext_fnp);
+
+ return ret_val;
+ }
+ else
+ {
+ /* This else should not be taken as only debug methods
+ are supported currently. */
+ internal_error (__FILE__, __LINE__,
+ _("Invoking an extension language "
+ "function (not a method)."));
+ }
+ }
+
+ argvec[1] = arg2; /* the ``this'' pointer */
+ argvec[0] = valp; /* Use the method found after overload
+ resolution. */
}
else
/* Non-C++ case -- or no overload resolution. */
@@ -1701,7 +1730,7 @@ evaluate_subexp_standard (struct type *expect_type,
NULL, /* no need for name */
NON_METHOD, /* not method */
NULL, function, /* the function */
- NULL, &symp, NULL, no_adl);
+ NULL, &symp, NULL, NULL, no_adl);
if (op == OP_VAR_VALUE)
{
diff --git a/gdb/ext-function.c b/gdb/ext-function.c
new file mode 100644
index 0000000..311c5d5
--- /dev/null
+++ b/gdb/ext-function.c
@@ -0,0 +1,172 @@
+/* Support for functions defined in extension languages.
+
+ Copyright (C) 2013 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+#include "cleanups.h"
+#include "ext-function.h"
+
+#include "gdb_assert.h"
+#include "vec.h"
+
+struct ext_fn_descriptor
+ {
+ const struct ext_func_ops *lang;
+
+ int is_method;
+
+ void *ext_object;
+ };
+
+typedef struct ext_func_ops *ext_lang_p;
+DEF_VEC_P (ext_lang_p);
+static VEC (ext_lang_p) *ext_lang_vec = NULL;
+
+/* Registers an extension language with GDB. */
+
+void
+register_ext_lang (struct ext_func_ops *lang)
+{
+ if (ext_lang_vec == NULL)
+ ext_lang_vec = VEC_alloc (ext_lang_p, 1);
+
+ VEC_safe_push (ext_lang_p, ext_lang_vec, lang);
+}
+
+/* Returns a new ext_fn_descriptor object. LANG is the extention language the
+ new extension function is implemented in. IS_METHOD indicates whether the
+ new extension function is a method. EXT_OBJ is the extension language
+ specific data to be encapsulated in the ext_fn_descriptor. */
+
+struct ext_fn_descriptor *
+new_ext_function (const struct ext_func_ops *lang, int is_method, void *ext_obj)
+{
+ struct ext_fn_descriptor *ext_fn = XCNEW (struct ext_fn_descriptor);
+
+ ext_fn->is_method = is_method;
+ ext_fn->lang = lang;
+ ext_fn->ext_object = ext_obj;
+
+ return ext_fn;
+}
+
+/* Clones EXT_FN and returns a new but identical ext_fn_descriptor. */
+
+struct ext_fn_descriptor *
+ext_fn_clone (struct ext_fn_descriptor *ext_fn)
+{
+ struct ext_fn_descriptor *new_ext_fn;
+ const struct ext_func_ops *lang = ext_fn->lang;
+
+ new_ext_fn = new_ext_function (lang, ext_fn->is_method,
+ lang->clone_ext_object (ext_fn->ext_object));
+
+ return new_ext_fn;
+}
+
+/* If a method of name METHOD is to be invoked on an object of type TYPE, then
+ all entension languages are searched for implementations of methods with
+ name METHOD in the extension languages. All matches found are returned as
+ a vector 'struct ent_fn_descriptor' objects. If no matching methods are
+ found, NULL is returned. */
+
+VEC (ext_fn_descriptor_p) *
+get_matching_ext_methods (struct type *type, const char *method)
+{
+ VEC (ext_fn_descriptor_p) *ext_methods = NULL;
+ ext_lang_p lang;
+ int i;
+
+ for (i = 0; VEC_iterate (ext_lang_p, ext_lang_vec, i, lang); i++)
+ {
+ VEC (ext_fn_descriptor_p) *lang_methods, *new_vec;
+
+ lang_methods = lang->get_matching_ext_methods (type, method);
+ new_vec = VEC_merge (ext_fn_descriptor_p, ext_methods, lang_methods);
+
+ VEC_free (ext_fn_descriptor_p, ext_methods);
+ VEC_free (ext_fn_descriptor_p, lang_methods);
+ ext_methods = new_vec;
+ }
+
+ return ext_methods;
+}
+
+/* Given an function EXT_FN implemented in an extension language, returns an
+ array of types of the arguments the function accepts. The length of the
+ array is returned in NARGS. The type of the 'this' object is returned as
+ the first argument if EXT_FN is a method. If EXT_FN does not take any
+ arguments, then NULL is returned with 0 in NARGS. */
+
+struct type **
+ext_fn_get_argtypes (struct ext_fn_descriptor *ext_fn, int *nargs)
+{
+ gdb_assert (ext_fn && ext_fn->lang && ext_fn->lang->get_ext_fn_argtypes);
+
+ return ext_fn->lang->get_ext_fn_argtypes (ext_fn->ext_object, nargs);
+}
+
+/* If EXT_FN is a method implemented in an extension language, invokes it and
+ returns the resulting value. The method is invoked on OBJ with arguments
+ ARGS. NARGS is the length of the ARGS array. */
+
+struct value *
+ext_fn_invoke_method (struct ext_fn_descriptor *ext_fn, struct value *obj,
+ struct value **args, int nargs)
+{
+ gdb_assert (ext_fn && ext_fn->is_method && ext_fn->lang
+ && ext_fn->lang->invoke_method);
+
+ return ext_fn->lang->invoke_method (ext_fn->ext_object, obj, args, nargs);
+}
+
+/* Returns true if EXT_FN is a method, 0 otherwise. */
+
+int
+ext_fn_is_method (struct ext_fn_descriptor *ext_fn)
+{
+ if (ext_fn != NULL)
+ return ext_fn->is_method;
+
+ return 0;
+}
+
+/* Frees a vector of ext_fn_descriptors VEC. */
+
+static void
+ext_fn_vec_free (void *vec)
+{
+ int i;
+ struct ext_fn_descriptor *ext_fn;
+ VEC (ext_fn_descriptor_p) *v = (VEC (ext_fn_descriptor_p) *) vec;
+
+ for (i = 0; VEC_iterate (ext_fn_descriptor_p, v, i, ext_fn); i++)
+ {
+ ext_fn->lang->free_ext_obj (ext_fn->ext_object);
+ xfree (ext_fn);
+ }
+}
+
+/* Return a cleanup object to free a vector VEC of extension function
+ descriptors. */
+
+struct cleanup *
+make_ext_fn_vec_cleanup (VEC (ext_fn_descriptor_p) *vec)
+{
+ return make_cleanup (ext_fn_vec_free, (void *) vec);
+}
diff --git a/gdb/ext-function.h b/gdb/ext-function.h
new file mode 100644
index 0000000..db05009
--- /dev/null
+++ b/gdb/ext-function.h
@@ -0,0 +1,75 @@
+/* Support for functions defined in extension languages.
+
+ Copyright (C) 2013 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#if !defined (EXT_FUNCTION_H)
+#define EXT_FUNCTION_H
+
+#include "vec.h"
+
+struct cleanup;
+struct value;
+struct type;
+struct ext_fn_descriptor;
+
+typedef struct ext_fn_descriptor *ext_fn_descriptor_p;
+DEF_VEC_P (ext_fn_descriptor_p);
+typedef VEC (ext_fn_descriptor_p) ext_fn_vec;
+
+typedef struct value* (invoke_method_ftype) (void *ext_obj,
+ struct value *,
+ struct value **, int nargs);
+
+typedef void * (clone_ext_obj_ftype) (void *ext_obj);
+
+typedef void (free_ext_obj_ftype) (void *ext_obj);
+
+typedef ext_fn_vec *(get_matching_ext_methods_ftype) (struct type *type,
+ const char *method);
+
+typedef struct type** (get_ext_fn_argtypes_ftype) (void *ext_obj, int *nargs);
+
+struct ext_func_ops
+ {
+ clone_ext_obj_ftype *clone_ext_object;
+ free_ext_obj_ftype *free_ext_obj;
+ get_matching_ext_methods_ftype *get_matching_ext_methods;
+ get_ext_fn_argtypes_ftype *get_ext_fn_argtypes;
+ invoke_method_ftype *invoke_method;
+ };
+
+extern void register_ext_lang (struct ext_func_ops *lang);
+
+extern struct value *ext_fn_invoke_method (struct ext_fn_descriptor *,
+ struct value *,
+ struct value **, int nargs);
+
+extern struct ext_fn_descriptor *ext_fn_clone (struct ext_fn_descriptor *);
+
+extern struct ext_fn_descriptor *new_ext_function (
+ const struct ext_func_ops *lang, int is_method, void *ext_obj);
+
+extern ext_fn_vec *get_matching_ext_methods (struct type *, const char *);
+
+extern struct type **ext_fn_get_argtypes (struct ext_fn_descriptor *, int *);
+
+extern int ext_fn_is_method (struct ext_fn_descriptor *);
+
+extern struct cleanup* make_ext_fn_vec_cleanup (VEC (ext_fn_descriptor_p) *vec);
+
+#endif /* EXT_FUNCTION_H */
diff --git a/gdb/python/lib/gdb/__init__.py b/gdb/python/lib/gdb/__init__.py
index 61f5b5e..d4d7ba7 100644
--- a/gdb/python/lib/gdb/__init__.py
+++ b/gdb/python/lib/gdb/__init__.py
@@ -67,6 +67,8 @@ pretty_printers = []
# Initial type printers.
type_printers = []
+# Initial debug_methods.
+debug_methods = []
# Initial frame filters.
frame_filters = {}
diff --git a/gdb/python/lib/gdb/command/debug_methods.py b/gdb/python/lib/gdb/command/debug_methods.py
new file mode 100644
index 0000000..1d2eb25
--- /dev/null
+++ b/gdb/python/lib/gdb/command/debug_methods.py
@@ -0,0 +1,218 @@
+# Debug method commands.
+# Copyright 2013 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import gdb
+import re
+
+"""GDB commands for working with debug-methods."""
+
+
+def parse_dm_command_args(arg):
+ """Parses the arguments passed to a debug method command.
+
+ Arguments:
+ arg: The argument string passed to a debug method command.
+
+ Returns:
+ A 2-tuple: (<locus matching regular expression>,
+ <name matching regular expression>)
+ """
+ argv = gdb.string_to_argv(arg)
+ argc = len(argv)
+ if argc > 2:
+ raise SyntaxError("Too many arguments to command.")
+ locus_re = ''
+ name_re = ''
+ if argc >= 1:
+ locus_re = argv[0]
+ if argc == 2:
+ name_re = argv[1]
+ return locus_re, name_re
+
+
+def get_matching_global_methods(locus_re, name_re):
+ """Returns a dict of matching globally registered debug methods.
+
+ Arguments:
+ locus_re: Even though only globally registered debug methods are
+ looked up, they will be looked up only if 'global' matches
+ LOCUS_RE.
+ name_re: The regular expression matching the names of debug methods.
+ Returns:
+ A dict of matching globally registered debug methods. The only key in
+ the dict will be 'global'.
+ """
+ dm = {}
+ if re.match(locus_re, 'global'):
+ matches = [m for m in gdb.debug_methods if re.match(name_re, m.name)]
+ if matches:
+ dm['global'] = matches
+ return dm
+
+
+def get_matching_methods_in_loci(loci, locus_re, name_re):
+ """Returns a dict of macthing registered debug methods in the LOCI.
+
+ Arguments:
+ loci: The list of loci to lookup matching debug methods in.
+ locus_re: Debug methods will be looked up in a particular locus only
+ if its filename matches the regular expression LOCUS_RE.
+ name_re: The regular expression to match the names of debug methods.
+
+ Returns:
+ A dict of matching debug methods. The keys of the dict are the
+ filenames of the loci the debug methods belong to.
+ """
+ dm = {}
+ for locus in loci:
+ if not re.match(locus_re, locus.filename):
+ continue
+ matches = [m for m in locus.debug_methods if re.match(name_re, m.name)]
+ if matches:
+ dm[locus.filename] = matches
+ return dm
+
+
+def print_dm_info(header, dm_dict):
+ """Print a dictionary of debug methods with an optional header."""
+ if not dm_dict:
+ return
+ # Global dm info will not have a header. Space accordingly.
+ space = ''
+ if header:
+ print '%s:' % header
+ space = ' '
+ for locus, dm_list in dm_dict.iteritems():
+ if not dm_list:
+ continue
+ print '%s%s:' % (space, locus)
+ for dm in dm_list:
+ if dm.enabled:
+ status = 'enabled'
+ else:
+ status = 'disabled'
+ print ' %s%s - %s' % (space, dm.name, status)
+
+
+def set_dm_status(dm_dict, status):
+ """Set the status (enabled/disabled) of a dictionary of debug methods."""
+ for locus, dm_list in dm_dict.iteritems():
+ if not dm_list:
+ continue
+ for dm in dm_list:
+ dm.enabled = status
+
+
+def set_matching_dm_status(arg, status):
+ """Set the status (enabled/disabled) of debug methods matching ARG.
+ This is a helper function for enable/disable commands. ARG is the
+ argument string passed to the commands.
+ """
+ locus_re, name_re = parse_dm_command_args(arg)
+ set_dm_status(get_matching_global_methods(locus_re, name_re), status)
+ set_dm_status(
+ get_matching_methods_in_loci(gdb.progspaces(), locus_re, name_re),
+ status)
+ set_dm_status(
+ get_matching_methods_in_loci(gdb.objfiles(), locus_re, name_re),
+ status)
+
+
+class InfoDebugMethod(gdb.Command):
+ """GDB command to list registered debug-methods.
+
+ Usage: info debug-method [locus-regexp [name-regexp]]
+
+ LOCUS-REGEXP is a regular expression matching the location of the debug
+ methods. If it is ommitted, all registered debug methods from all loci
+ are listed. A locus could be 'global', a regular expression matching
+ filenames of program spaces or a regular expression matching filenames of
+ objfiles.
+
+ NAME-REGEXP is a regular expression matching the names of debug methods
+ within a given locus. If this omitted for a specified locus, then all
+ registered debug methods in the locus are listed.
+ """
+
+ def __init__(self):
+ super(InfoDebugMethod, self).__init__("info debug-method",
+ gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ locus_re, name_re = parse_dm_command_args(arg)
+ print_dm_info(None, get_matching_global_methods(locus_re, name_re))
+ print_dm_info('progspaces', get_matching_methods_in_loci(
+ gdb.progspaces(), locus_re, name_re))
+ print_dm_info('objfiles', get_matching_methods_in_loci(
+ gdb.objfiles(), locus_re, name_re))
+
+
+class EnableDebugMethod(gdb.Command):
+ """GDB command to enable a specified (group of) debug method(s).
+
+ Usage: enable debug-method [locus-regexp [name-regexp]]
+
+ LOCUS-REGEXP is a regular expression matching the location of the debug
+ methods. If it is ommitted, all registered debug methods from all loci
+ are enabled. A locus could be 'global', a regular expression matching
+ filenames of program spaces or a regular expression matching filenames of
+ objfiles.
+
+ NAME-REGEXP is a regular expression matching the names of debug methods
+ within a given locus. If this omitted for a specified locus, then all
+ registered debug methods in the locus are enabled.
+ """
+
+ def __init__(self):
+ super(EnableDebugMethod, self).__init__("enable debug-method",
+ gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ set_matching_dm_status(arg, True)
+
+
+class DisableDebugMethod(gdb.Command):
+ """GDB command to disable a specified (group of) debug method(s).
+
+ Usage: disable debug-method [locus-regexp [name-regexp]]
+
+ LOCUS-REGEXP is a regular expression matching the location of the debug
+ methods. If it is ommitted, all registered debug methods from all loci
+ are disabled. A locus could be 'global', a regular expression matching
+ filenames of program spaces or a regular expression matching filenames of
+ objfiles.
+
+ NAME-REGEXP is a regular expression matching the names of debug methods
+ within a given locus. If this omitted for a specified locus, then all
+ registered debug methods in the locus are disabled.
+ """
+
+ def __init__(self):
+ super(DisableDebugMethod, self).__init__("disable debug-method",
+ gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ set_matching_dm_status(arg, False)
+
+
+def register_debug_method_commands():
+ """Installs the debug method commands."""
+ InfoDebugMethod()
+ EnableDebugMethod()
+ DisableDebugMethod()
+
+
+register_debug_method_commands()
diff --git a/gdb/python/lib/gdb/debugmethods.py b/gdb/python/lib/gdb/debugmethods.py
new file mode 100644
index 0000000..7cfd2e3
--- /dev/null
+++ b/gdb/python/lib/gdb/debugmethods.py
@@ -0,0 +1,160 @@
+# Python side of the support for debug methods.
+# Copyright (C) 2013 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""Utilities for defining debug methods"""
+
+import gdb
+import re
+
+
+class DebugMethod(object):
+ """Base class for all debug methods defined in Python.
+
+ A debug method defined in Python should be derived from this class. The
+ derived classes should override the methods 'match', 'get_argtypes' and
+ 'invoke'.
+
+ Internally, GDB first invokes the 'match' method to match the class type
+ and the method name. It next gets the argument types of these methods via
+ the 'get_argtypes' method to perform overload resolution. If GDB selects
+ to invoke a Python debugmethod, then it invokes it via the overridden
+ 'invoke' method.
+ """
+
+ def __init__(self, name):
+ """
+ Args:
+ name: An identifying name for the debug method.
+ """
+ self.name = name
+ self.enabled = True
+
+ def match(self, class_type, method_name):
+ """Match class type and method name.
+
+ Args:
+ class_type: The class type to match
+ method_name: The name of the method to match.
+ """
+ gdb.GdbError('ERROR: Invoking abstract method \'match\'.')
+
+ def get_argtypes(self, class_type, method_name):
+ """Return a list of types, as gdb.Type objects, of the arguments of
+ the debug method.
+
+ Args:
+ class_type: The gdb.Type value of the class on which the method
+ is defined.
+ method_name: The name of the method whose argument types are to
+ be returned.
+ """
+ gdb.GdbError('ERROR: Invoking abstract method \'get_argtypes\'.')
+
+ def invoke(self, obj, method_name, args):
+ """Invoke the debug method.
+
+ Args:
+ obj: The gdb.Value of the object on which the method is to be
+ invoked.
+ method_name: The name of the method being invoked.
+ args: The tuple of arguments to the method.
+ """
+ gdb.GdbError('ERROR: Invoking abstract method \'invoke\'.')
+
+
+class SimpleDebugMethod(DebugMethod):
+ """This is a utility class which does name match by class name of the
+ objects on which the debug methods are defined. For simple classes and
+ methods, one can choose to use this class. For complex debug methods,
+ which need to replace/implement template source methods on possibly
+ template classes, one should implement their own debug method classes
+ deriving from the base class 'DebugMethod'. See the py-debugmethods.py
+ in the testsuite/gdb.python directory of the GDB source tree for
+ examples.
+ """
+
+ def __init__(self, name, method_function, class_matcher, method_matcher,
+ *argtypes):
+ """
+ Args:
+ name: Name of the debug method.
+ method_function: A Python callable which would be called via the
+ invoke them of this class to actually invoke the
+ debug method. This callable should accept the
+ object (*this) as the first argument followed by
+ the rest of the arguments to the method.
+ class_matcher: A regular expression used to match the name of the
+ class whose method this debug method is
+ implementing/replacing.
+ method_matcher: A regular expression used to match the name of the
+ method this debug method is implementing/replacing.
+ argtypes: The gdb.Type objects corresponding to the arguments that
+ this debug method takes.
+ """
+ DebugMethod.__init__(self, name)
+ assert callable(method_function), (
+ 'The "method_function" argument to "SimpleDebugMethod" '
+ 'constructor should be a callable.')
+ self._method_function = method_function
+ self._class_matcher = class_matcher
+ self._method_matcher = method_matcher
+ self._argtypes = argtypes
+
+ def match(self, class_type, method_name):
+ cm = re.match(self._class_matcher, str(class_type.unqualified().tag))
+ mm = re.match(self._method_matcher, method_name)
+ if cm and mm:
+ return True
+ else:
+ return False
+
+ def get_argtypes(self, class_type, method_name):
+ return self._argtypes
+
+ def invoke(self, obj, method_name, args):
+ return self._method_function(obj, *args)
+
+
+def register_debug_methods(locus, debug_methods):
+ """Registers a list of DEBUG_METHODS with a LOCUS.
+
+ Arguments:
+ locus: The locus in which the debug methods should be registered. It
+ can be 'None' to indicate that the debug methods should be
+ registered globally. Or, it could be a gdb.Objfile or a
+ gdb.Progspace object in which the debug methods should be
+ registered.
+ debug_methods: The list of debug methods to register.
+ """
+ if not locus:
+ locus = gdb
+ if locus == gdb:
+ locus_name = 'global'
+ else:
+ locus_name = locus.filename
+ existing_method_list = []
+ for new_method in debug_methods:
+ assert isinstance(new_method, DebugMethod), (
+ 'Debug method objects should be instances of a subclass '
+ 'of "DebugMethod".')
+ for old_method in locus.debug_methods:
+ if new_method.name == old_method.name:
+ print ('WARNING: Replacing debug method with name "%s" in '
+ ' "%s".' % (old_method.name, locus_name))
+ existing_method_list.append(old_method)
+ for old_method in existing_method_list:
+ locus.debug_methods.remove(old_method)
+ locus.debug_methods.extend(debug_methods)
diff --git a/gdb/python/py-debugmethods.c b/gdb/python/py-debugmethods.c
new file mode 100644
index 0000000..8e3f45a
--- /dev/null
+++ b/gdb/python/py-debugmethods.c
@@ -0,0 +1,583 @@
+/* Support for debug methods in Python.
+
+ Copyright (C) 2013 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+#include "arch-utils.h"
+#include "ext-function.h"
+#include "objfiles.h"
+#include "value.h"
+#include "language.h"
+
+#include "python.h"
+
+#ifdef HAVE_PYTHON
+#include "python-internal.h"
+
+struct py_ext_object
+{
+ /* Holds an instance of the DebugMethod class. */
+ PyObject *object;
+
+ /* Holds the type of the 'this' object. */
+ PyObject *match_py_obj_type;
+
+ /* Holds the matching method name. */
+ const char *match_method;
+};
+
+static const char *enabled_field_name = "enabled";
+static const char *match_method_name = "match";
+static const char *get_argtypes_method_name = "get_argtypes";
+static const char *invoke_method_name = "invoke";
+
+static PyObject *match_method_pystring = NULL;
+static PyObject *get_argtypes_method_pystring = NULL;
+static PyObject *invoke_method_pystring = NULL;
+
+static struct ext_fn_descriptor *new_python_ext_method (PyObject *item,
+ PyObject *py_obj_type,
+ const char *method);
+
+/* Implementation of free_ext_obj_ftype. */
+
+static void
+py_free_ext_object (void *ext_object)
+{
+ struct py_ext_object *o = ext_object;
+
+ Py_XDECREF (o->object);
+ Py_XDECREF (o->match_py_obj_type);
+ xfree (o);
+}
+
+/* Implementation of clone_ext_obj_ftype. */
+
+static void *
+py_clone_ext_object (void *obj)
+{
+ struct py_ext_object *new_obj, *old_obj;
+
+ old_obj = (struct py_ext_object *) obj;
+
+ new_obj = XCNEW (struct py_ext_object);
+ new_obj->object = old_obj->object;
+ new_obj->match_py_obj_type = old_obj->match_py_obj_type;
+ new_obj->match_method = old_obj->match_method;
+
+ Py_XINCREF (new_obj->object);
+ Py_XINCREF (new_obj->match_py_obj_type);
+
+ return (void *) new_obj;
+}
+
+/* Returns true if gdb.Type object PY_OBJ_TYPE has a method defined in Python
+ with name METHOD_NAME. */
+
+static int
+is_matching_debug_method (PyObject *dm_obj, PyObject *py_obj_type,
+ const char *method_name)
+{
+ PyObject *method_name_pystring;
+ PyObject *match_method, *enabled_field, *match_result;
+ struct cleanup *cleanups;
+ int enabled, match;
+
+ if (method_name == NULL)
+ return 0;
+
+ cleanups = make_cleanup (null_cleanup, NULL);
+
+ enabled_field = PyObject_GetAttrString (dm_obj, enabled_field_name);
+ if (enabled_field == NULL)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return 0;
+ }
+ make_cleanup_py_decref (enabled_field);
+
+ enabled = PyObject_IsTrue (enabled_field);
+ if (enabled == -1)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return 0;
+ }
+ if (enabled == 0)
+ {
+ do_cleanups (cleanups);
+ return 0;
+ }
+
+ match_method = PyObject_GetAttrString (dm_obj, match_method_name);
+ if (match_method == NULL)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return 0;
+ }
+ make_cleanup_py_decref (match_method);
+ if (!PyCallable_Check (match_method))
+ {
+ warning (_("Attribute '%s' of a registered Python debug method is not "
+ "callable. Ignored!"), match_method_name);
+ do_cleanups (cleanups);
+
+ return 0;
+ }
+
+ method_name_pystring = PyString_FromString (method_name);
+ if (method_name_pystring == NULL)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return 0;
+ }
+ make_cleanup_py_decref (method_name_pystring);
+
+ match_result = PyObject_CallMethodObjArgs (dm_obj,
+ match_method_pystring,
+ py_obj_type,
+ method_name_pystring,
+ NULL);
+ if (match_result == NULL)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return 0;
+ }
+ make_cleanup_py_decref (match_result);
+
+ match = PyObject_IsTrue (match_result);
+ if (match == -1)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return 0;
+ }
+
+ do_cleanups (cleanups);
+
+ return match;
+}
+
+/* Implementation of get_matching_ext_methods_ftype.
+ Return a of vector methods with name METHOD_NAME defined in Python for
+ objects of type OBJ_TYPE. Returns NULL if no matches are found. */
+
+static VEC (ext_fn_descriptor_p) *
+py_debugmethod_name_match (struct type *obj_type, const char *method_name)
+{
+ struct cleanup *cleanups;
+ struct objfile *objfile;
+ VEC (ext_fn_descriptor_p) *method_vec = NULL;
+ PyObject *py_type, *py_progspace;
+ PyObject *py_debugmethod_list = NULL, *list_iter, *item;
+
+ if (obj_type == NULL || method_name == NULL)
+ return method_vec;
+
+ py_type = type_to_type_object (obj_type);
+ if (py_type == NULL)
+ return method_vec;
+ make_cleanup_py_decref (py_type);
+
+ /* Create an empyt list of debug methods. */
+ py_debugmethod_list = PyList_New (0);
+ if (py_debugmethod_list == NULL)
+ return NULL;
+
+ cleanups = ensure_python_env (get_current_arch (), current_language);
+
+ /* Gather debug methods registered with the object files. */
+ ALL_OBJFILES (objfile)
+ {
+ PyObject *py_objfile = objfile_to_objfile_object (objfile);
+ PyObject *objfile_dmlist, *temp;
+
+ if (py_objfile == NULL)
+ {
+ gdbpy_print_stack ();
+ continue;
+ }
+
+ objfile_dmlist = objfpy_get_debug_methods (py_objfile, NULL);
+ temp = py_debugmethod_list;
+ py_debugmethod_list = PySequence_Concat (temp, objfile_dmlist);
+ if (py_debugmethod_list == NULL)
+ py_debugmethod_list = temp;
+ else
+ Py_DECREF (temp);
+ Py_XDECREF (objfile_dmlist);
+ }
+
+ /* Gather debug methods registered with the current program space. */
+ py_progspace = pspace_to_pspace_object (current_program_space);
+ if (py_progspace != NULL)
+ {
+ PyObject *temp = py_debugmethod_list;
+ PyObject *pspace_dmlist = pspy_get_debug_methods (py_progspace, NULL);
+
+ py_debugmethod_list = PySequence_Concat (temp, pspace_dmlist);
+ if (py_debugmethod_list == NULL)
+ py_debugmethod_list = temp;
+ else
+ Py_DECREF (temp);
+ Py_XDECREF (pspace_dmlist);
+ }
+
+ /* Gather debug methods registered globally. */
+ if (gdb_python_module != NULL
+ && PyObject_HasAttrString (gdb_python_module, "debug_methods"))
+ {
+ PyObject *gdb_dmlist;
+ PyObject *temp = py_debugmethod_list;
+
+ gdb_dmlist = PyObject_GetAttrString (gdb_python_module, "debug_methods");
+ if (gdb_dmlist != NULL && PyList_Check (gdb_dmlist))
+ {
+ py_debugmethod_list = PySequence_Concat (temp, gdb_dmlist);
+ if (py_debugmethod_list == NULL)
+ py_debugmethod_list = temp;
+ else
+ Py_DECREF (temp);
+ Py_XDECREF (gdb_dmlist);
+ }
+ }
+
+ list_iter = PyObject_GetIter (py_debugmethod_list);
+ if (list_iter == NULL)
+ {
+ gdbpy_print_stack ();
+ Py_DECREF (py_debugmethod_list);
+ do_cleanups (cleanups);
+ return NULL;
+ }
+
+ while ((item = PyIter_Next (list_iter)))
+ {
+ if (is_matching_debug_method (item, py_type, method_name))
+ {
+ struct ext_fn_descriptor *ext_fn;
+
+ ext_fn = new_python_ext_method (item, py_type, method_name);
+ VEC_safe_push (ext_fn_descriptor_p, method_vec, ext_fn);
+ }
+ Py_DECREF (item);
+ }
+ Py_DECREF (list_iter);
+ /* Report any error that could have occurred while iterating. */
+ if (PyErr_Occurred ())
+ gdbpy_print_stack ();
+
+ Py_DECREF (py_debugmethod_list);
+ do_cleanups (cleanups);
+
+ return method_vec;
+}
+
+/* Implementation of get_ext_fn_argtypes_ftype.
+ Return an arry of argument types for extension encapsulated in EXT_OBJ.
+ NARGS contains the length of the array. */
+
+static struct type **
+py_ext_fn_get_argtypes (void *ext_obj, int *nargs)
+{
+ struct py_ext_object *ext_object = (struct py_ext_object *) ext_obj;
+ PyObject *debug_method_obj = ext_object->object;
+ PyObject *get_argtypes_method;
+ PyObject *py_argtype_list, *list_iter, *item, *method_name_pystring;
+ struct cleanup *cleanups;
+ struct type **type_array, *obj_type;
+ int i = 1;
+
+ /* Set nargs to 0 so that any premature return from this function returns
+ 0 arg types. */
+ *nargs = 0;
+
+ cleanups = ensure_python_env (get_current_arch (), current_language);
+
+ get_argtypes_method = PyObject_GetAttrString (debug_method_obj,
+ get_argtypes_method_name);
+ if (get_argtypes_method == NULL)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return NULL;
+ }
+ make_cleanup_py_decref (get_argtypes_method);
+
+ method_name_pystring = PyString_FromString (ext_object->match_method);
+ if (method_name_pystring == NULL)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return 0;
+ }
+ make_cleanup_py_decref (method_name_pystring);
+
+ if (!PyCallable_Check (get_argtypes_method))
+ {
+ warning (_("Attribute '%s' of a registered Python debug method is not "
+ "callable. Ignored!"), get_argtypes_method_name);
+ do_cleanups (cleanups);
+
+ return NULL;
+ }
+
+ py_argtype_list = PyObject_CallMethodObjArgs (debug_method_obj,
+ get_argtypes_method_pystring,
+ ext_object->match_py_obj_type,
+ method_name_pystring,
+ NULL);
+ if (py_argtype_list == NULL)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return NULL;
+ }
+ make_cleanup_py_decref (py_argtype_list);
+
+ list_iter = PyObject_GetIter (py_argtype_list);
+ if (list_iter == NULL)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return NULL;
+ }
+ make_cleanup_py_decref (list_iter);
+
+ /* Include the 'this' argument in the size. */
+ type_array = XCNEWVEC (struct type *, PyList_GET_SIZE (py_argtype_list) + 1);
+ while ((item = PyIter_Next (list_iter)))
+ {
+ struct type *arg_type = type_object_to_type (item);
+
+ Py_DECREF (item);
+
+ if (arg_type == NULL)
+ {
+ i = -1;
+ break;
+ }
+
+ type_array[i] = arg_type;
+ i++;
+ }
+ if (PyErr_Occurred () || i == -1)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+ xfree (type_array);
+
+ return NULL;
+ }
+
+ /* Add the type of 'this' as the first argument. */
+ obj_type = type_object_to_type (ext_object->match_py_obj_type);
+ type_array[0] = make_cv_type (1, 0, lookup_pointer_type (obj_type), NULL);
+ *nargs = i;
+
+ do_cleanups (cleanups);
+
+ return type_array;
+}
+
+/* Implementation of invoke_method_ftype.
+ Invokes a method defined in Python. The value returned by the method is
+ returned. NULL is returned in case of errors. */
+
+static struct value *
+py_ext_fn_invoke_method (void *ext_obj, struct value *obj, struct value **args,
+ int nargs)
+{
+ int i;
+ struct cleanup *cleanups;
+ PyObject *py_value_obj, *py_arg_tuple, *py_result, *debug_method_obj;
+ PyObject *invoke_method, *method_name_pystring;
+ struct value *result = NULL;
+ struct py_ext_object *py_ext_obj = ext_obj;
+
+ cleanups = ensure_python_env (get_current_arch (), current_language);
+
+ debug_method_obj = py_ext_obj->object;
+
+ invoke_method = PyObject_GetAttrString (debug_method_obj,
+ invoke_method_name);
+ if (invoke_method == NULL)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return NULL;
+ }
+ make_cleanup_py_decref (invoke_method);
+
+ method_name_pystring = PyString_FromString (py_ext_obj->match_method);
+ if (method_name_pystring == NULL)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return 0;
+ }
+ make_cleanup_py_decref (method_name_pystring);
+
+ if (!PyCallable_Check (invoke_method))
+ {
+ warning (_("Attribute '%s' of a registered Python debug method is not "
+ "callable. Ignored!"), invoke_method_name);
+ do_cleanups (cleanups);
+
+ return NULL;
+ }
+
+ py_value_obj = value_to_value_object (obj);
+ if (py_value_obj == NULL)
+ {
+ do_cleanups (cleanups);
+ return NULL;
+ }
+ make_cleanup_py_decref (py_value_obj);
+
+ py_arg_tuple = PyTuple_New (nargs);
+ if (py_arg_tuple == NULL)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return NULL;
+ }
+ make_cleanup_py_decref (py_arg_tuple);
+
+ for (i = 0; i < nargs; i++)
+ {
+ PyObject *py_value_arg = value_to_value_object (args[i]);
+
+ if (py_value_arg == NULL)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+ return NULL;
+ }
+
+ if (PyTuple_SetItem (py_arg_tuple, i, py_value_arg))
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return NULL;
+ }
+ }
+
+ py_result = PyObject_CallMethodObjArgs (debug_method_obj,
+ invoke_method_pystring,
+ py_value_obj,
+ method_name_pystring,
+ py_arg_tuple, NULL);
+ if (py_result == NULL)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return NULL;
+ }
+ make_cleanup_py_decref (py_result);
+
+ /* Check that a debug method did not return None. */
+ if (py_result != Py_None)
+ {
+ result = convert_value_from_python (py_result);
+ if (result == NULL)
+ {
+ gdbpy_print_stack ();
+ }
+ }
+ else
+ result = allocate_value (lookup_typename (python_language, python_gdbarch,
+ "void", NULL, 0));
+
+ do_cleanups (cleanups);
+
+ return result;
+}
+
+static struct ext_func_ops python_ext_lang = {
+ py_clone_ext_object,
+ py_free_ext_object,
+ py_debugmethod_name_match,
+ py_ext_fn_get_argtypes,
+ py_ext_fn_invoke_method
+};
+
+/* Creates a new python ext_function_descriptor. DEBUG_METHOD is the
+ debug method object. PY_OBJ_TYPE is a gdb.Type value corresponding to the
+ type of the object on which the debug method with name METHOD will be
+ invoked. */
+
+static struct ext_fn_descriptor *
+new_python_ext_method (PyObject *debug_method, PyObject *py_obj_type,
+ const char *method)
+{
+ struct py_ext_object *ext_object;
+
+ ext_object = XCNEW (struct py_ext_object);
+ ext_object->object = debug_method;
+ ext_object->match_py_obj_type = py_obj_type;
+ ext_object->match_method = method;
+
+ Py_XINCREF (ext_object->object);
+ Py_XINCREF (ext_object->match_py_obj_type);
+
+ return new_ext_function (&python_ext_lang, 1, ext_object);
+}
+
+/* Initializes the Python debug method support. */
+
+int
+gdbpy_initialize_debugmethods (void)
+{
+ register_ext_lang (&python_ext_lang);
+
+ match_method_pystring = PyString_FromString (match_method_name);
+ if (match_method_pystring == NULL)
+ return -1;
+
+ invoke_method_pystring = PyString_FromString (invoke_method_name);
+ if (invoke_method_pystring == NULL)
+ return -1;
+
+ get_argtypes_method_pystring = PyString_FromString (get_argtypes_method_name);
+ if (get_argtypes_method_pystring == NULL)
+ return -1;
+
+ return 1;
+}
+
+#endif /* HAVE_PYTHON */
diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c
index 9bbd4c2..4aa0a1a 100644
--- a/gdb/python/py-objfile.c
+++ b/gdb/python/py-objfile.c
@@ -37,6 +37,9 @@ typedef struct
PyObject *frame_filters;
/* The type-printer list. */
PyObject *type_printers;
+
+ /* The debug method list. */
+ PyObject *debug_methods;
} objfile_object;
static PyTypeObject objfile_object_type
@@ -67,6 +70,7 @@ objfpy_dealloc (PyObject *o)
Py_XDECREF (self->printers);
Py_XDECREF (self->frame_filters);
Py_XDECREF (self->type_printers);
+ Py_XDECREF (self->debug_methods);
Py_TYPE (self)->tp_free (self);
}
@@ -99,6 +103,13 @@ objfpy_new (PyTypeObject *type, PyObject *args, PyObject *keywords)
Py_DECREF (self);
return NULL;
}
+
+ self->debug_methods = PyList_New (0);
+ if (self->debug_methods == NULL)
+ {
+ Py_DECREF (self);
+ return NULL;
+ }
}
return (PyObject *) self;
}
@@ -193,6 +204,17 @@ objfpy_get_type_printers (PyObject *o, void *ignore)
return self->type_printers;
}
+/* Get the 'debug_methods' attribute. */
+
+PyObject *
+objfpy_get_debug_methods (PyObject *o, void *ignore)
+{
+ objfile_object *self = (objfile_object *) o;
+
+ Py_INCREF (self->debug_methods);
+ return self->debug_methods;
+}
+
/* Set the 'type_printers' attribute. */
static int
@@ -292,6 +314,13 @@ objfile_to_objfile_object (struct objfile *objfile)
return NULL;
}
+ object->debug_methods = PyList_New (0);
+ if (object->debug_methods == NULL)
+ {
+ Py_DECREF (object);
+ return NULL;
+ }
+
set_objfile_data (objfile, objfpy_objfile_data_key, object);
}
}
@@ -333,6 +362,8 @@ static PyGetSetDef objfile_getset[] =
objfpy_set_frame_filters, "Frame Filters.", NULL },
{ "type_printers", objfpy_get_type_printers, objfpy_set_type_printers,
"Type printers.", NULL },
+ { "debug_methods", objfpy_get_debug_methods, NULL,
+ "Debug methods.", NULL },
{ NULL }
};
diff --git a/gdb/python/py-progspace.c b/gdb/python/py-progspace.c
index 910c6a3..d425159 100644
--- a/gdb/python/py-progspace.c
+++ b/gdb/python/py-progspace.c
@@ -39,6 +39,9 @@ typedef struct
PyObject *frame_filters;
/* The type-printer list. */
PyObject *type_printers;
+
+ /* The debug method list. */
+ PyObject *debug_methods;
} pspace_object;
static PyTypeObject pspace_object_type
@@ -75,6 +78,7 @@ pspy_dealloc (PyObject *self)
Py_XDECREF (ps_self->printers);
Py_XDECREF (ps_self->frame_filters);
Py_XDECREF (ps_self->type_printers);
+ Py_XDECREF (ps_self->debug_methods);
Py_TYPE (self)->tp_free (self);
}
@@ -107,6 +111,13 @@ pspy_new (PyTypeObject *type, PyObject *args, PyObject *keywords)
Py_DECREF (self);
return NULL;
}
+
+ self->debug_methods = PyList_New (0);
+ if (self->debug_methods == NULL)
+ {
+ Py_DECREF (self);
+ return NULL;
+ }
}
return (PyObject *) self;
}
@@ -201,6 +212,17 @@ pspy_get_type_printers (PyObject *o, void *ignore)
return self->type_printers;
}
+/* Get the 'debug_methods' attribute. */
+
+PyObject *
+pspy_get_debug_methods (PyObject *o, void *ignore)
+{
+ pspace_object *self = (pspace_object *) o;
+
+ Py_INCREF (self->debug_methods);
+ return self->debug_methods;
+}
+
/* Set the 'type_printers' attribute. */
static int
@@ -288,6 +310,13 @@ pspace_to_pspace_object (struct program_space *pspace)
return NULL;
}
+ object->debug_methods = PyList_New (0);
+ if (object->debug_methods == NULL)
+ {
+ Py_DECREF (object);
+ return NULL;
+ }
+
set_program_space_data (pspace, pspy_pspace_data_key, object);
}
}
@@ -320,6 +349,8 @@ static PyGetSetDef pspace_getset[] =
"Frame filters.", NULL },
{ "type_printers", pspy_get_type_printers, pspy_set_type_printers,
"Type printers.", NULL },
+ { "debug_methods", pspy_get_debug_methods, NULL,
+ "Debug methods.", NULL },
{ NULL }
};
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 7d8c4ad..62eb49b 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -316,11 +316,13 @@ PyObject *pspace_to_pspace_object (struct program_space *)
CPYCHECKER_RETURNS_BORROWED_REF;
PyObject *pspy_get_printers (PyObject *, void *);
PyObject *pspy_get_frame_filters (PyObject *, void *);
+PyObject *pspy_get_debug_methods (PyObject *, void *);
PyObject *objfile_to_objfile_object (struct objfile *)
CPYCHECKER_RETURNS_BORROWED_REF;
PyObject *objfpy_get_printers (PyObject *, void *);
PyObject *objfpy_get_frame_filters (PyObject *, void *);
+PyObject *objfpy_get_debug_methods (PyObject *, void *);
PyObject *gdbarch_to_arch_object (struct gdbarch *gdbarch);
@@ -401,6 +403,8 @@ int gdbpy_initialize_new_objfile_event (void)
CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
int gdbpy_initialize_arch (void)
CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
+int gdbpy_initialize_debugmethods (void)
+ CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
struct cleanup *make_cleanup_py_decref (PyObject *py);
struct cleanup *make_cleanup_py_xdecref (PyObject *py);
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 7cdf8a7..f5a0132 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1687,7 +1687,8 @@ message == an error message without a stack will be printed."),
|| gdbpy_initialize_exited_event () < 0
|| gdbpy_initialize_thread_event () < 0
|| gdbpy_initialize_new_objfile_event () < 0
- || gdbpy_initialize_arch () < 0)
+ || gdbpy_initialize_arch () < 0
+ || gdbpy_initialize_debugmethods () < 0)
goto fail;
observer_attach_before_prompt (before_prompt_hook);
diff --git a/gdb/testsuite/gdb.python/py-debugmethods.cc b/gdb/testsuite/gdb.python/py-debugmethods.cc
new file mode 100644
index 0000000..f292a62
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-debugmethods.cc
@@ -0,0 +1,173 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2013 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <iostream>
+
+using namespace std;
+
+namespace dop
+{
+
+class A
+{
+ public:
+ int a;
+ int array [10];
+ virtual ~A ();
+ int operator+ (const A &obj);
+ virtual int operator- (const A &obj);
+ virtual int geta ();
+};
+
+A::~A () { }
+
+int
+A::operator+ (const A &obj)
+{
+ cout << "From CC <A_plus_A>:" << endl;
+ return a + obj.a;
+}
+
+int A::operator- (const A &obj)
+{
+ cout << "From CC <A_minus_A>:" << endl;
+ return a - obj.a;
+}
+
+int A::geta (void)
+{
+ cout << "From CC geta:" << endl;
+ return a;
+}
+
+class B : public A
+{
+ public:
+ virtual int operator* (const B &obj);
+};
+
+int
+B::operator* (const B &obj)
+{
+ cout << "From CC <B_star_B>:" << endl;
+ return a * obj.a;
+}
+
+typedef B Bt;
+
+typedef Bt Btt;
+
+class C : public Bt
+{
+ public:
+ virtual ~C();
+};
+
+C::~C () { }
+
+class D : public B
+{
+ public:
+ virtual int operator* (const B &obj);
+};
+
+int
+D::operator* (const B &obj)
+{
+ cout << "From CC <D_star_D>:" << endl;
+ return a * obj.a;
+}
+
+template <typename T>
+class G
+{
+ public:
+ template <typename T1>
+ int size_diff ();
+
+ template <int M>
+ int size_mul ();
+
+ template <typename T1>
+ T mul(const T1 t1);
+
+ public:
+ T t;
+};
+
+template <typename T>
+template <typename T1>
+int
+G<T>::size_diff ()
+{
+ cout << "From CC G<>::size_diff:" << endl;
+ return sizeof (T1) - sizeof (T);
+}
+
+template <typename T>
+template <int M>
+int
+G<T>::size_mul ()
+{
+ cout << "From CC G<>::size_mul:" << endl;
+ return M * sizeof (T);
+}
+
+template <typename T>
+template <typename T1>
+T
+G<T>::mul (const T1 t1)
+{
+ cout << "From CC G<>::mul:" << endl;
+ return t1 * t;
+}
+
+}
+
+using namespace dop;
+
+int main(void)
+{
+ A a1, a2;
+ a1.a = 5;
+ a2.a = 10;
+ C c1;
+ c1.a = 20;
+ B b1;
+ b1.a = 30;
+ D d1;
+ d1.a = 50;
+ Bt bt;
+ bt.a = 40;
+ A &ref_c = c1;
+ B &ref_d = d1;
+ Btt btt;
+ btt.a = -5;
+ G<int> g;
+ g.t = 5;
+
+ int diff = g.size_diff<float> ();
+ int smul = g.size_mul<2> ();
+ int mul = g.mul (1.0);
+
+ for (int i = 0; i < 10; i++)
+ {
+ a1.array[i] = a2.array[i] = i;
+ }
+
+ return 0; /* Break here. */
+}
diff --git a/gdb/testsuite/gdb.python/py-debugmethods.exp b/gdb/testsuite/gdb.python/py-debugmethods.exp
new file mode 100644
index 0000000..802fdba
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-debugmethods.exp
@@ -0,0 +1,112 @@
+# Copyright 2013 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite. It tests the debug methods
+# feature in the Python extension language.
+
+load_lib gdb-python.exp
+
+if { [skip_cplus_tests] } { continue }
+
+standard_testfile py-debugmethods.cc
+
+if {[prepare_for_testing $testfile.exp $testfile $srcfile {debug c++}]} {
+ return -1
+}
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+if ![runto_main] {
+ return -1
+}
+
+set debug_methods_script "${srcdir}/${subdir}/${testfile}.py"
+
+gdb_breakpoint [gdb_get_line_number "Break here."]
+gdb_continue_to_breakpoint "Break here" ".*Break here.*"
+
+# Tests before loading the debug methods.
+gdb_test "p a1 + a2" "From CC <A_plus_A>.*15" "Before: a1 + a2"
+gdb_test "p a2 - a1" "From CC <A_minus_A>.*5" "Before: a1 - a2"
+gdb_test "p b1 - a1" "From CC <A_minus_A>.*25" "Before: b1 - a1"
+gdb_test "p c1 - a1" "From CC <A_minus_A>.*15" "Before: c1 - a1"
+gdb_test "p c1 - c1" "From CC <A_minus_A>.*0" "Before: c1 - c1"
+gdb_test "p a1.geta()" "From CC geta.*5" "Before: a1.geta()"
+gdb_test "p ref_c - a1" "From CC <A_minus_A>.*15" "Before: ref_c - a1"
+gdb_test "p ref_c - c1" "From CC <A_minus_A>.*0" "Before: ref_c - c1"
+gdb_test "p b1 * b1" "From CC <B_star_B>.*900" "Before: b1 * b1"
+gdb_test "p ref_c * b1" "No symbol.*" "Before: ref_c * b1"
+gdb_test "p ref_d * b1" "From CC <D_star_D>.*1500" "Before: ref_d * b1"
+gdb_test "p bt * c1" "From CC <B_star_B>.*800" "Before: bt * c1"
+gdb_test "p ++a1" "No symbol.*" "Before: ++a1"
+gdb_test "p a1.getarrayind(5)" "Couldn't find method.*" \
+ "Before: a1.getarrayind(5)"
+gdb_test "p g.size_diff<float>()" "From CC G<>::size_diff.*" \
+ "Before: g.size_diff<float>()"
+gdb_test "p g.size_diff<unsigned long>()" "Couldn't find method.*" \
+ "Before: g.size_diff<unsigned long>()"
+gdb_test "p g.size_mul<2>()" "From CC G<>::size_mul.*" \
+ "Before: g.size_mul<2>()"
+gdb_test "p g.size_mul<5>()" "Couldn't find method.*" \
+ "Before: g.size_mul<5>()"
+gdb_test "p g.mul<double>(2.0)" "From CC G<>::mul.*" \
+ "Before: g.mul<double>(2.0)"
+gdb_test "p g.mul<char>('a')" "Couldn't find method.*" \
+ "Before: g.mul<char>('a')"
+
+# Load the script which adds the debug methods.
+gdb_test_no_output "source ${debug_methods_script}" "load the script file"
+
+# Tests after loading debug methods.
+gdb_test "p a1 + a2" "From Python <A_plus_A>.*15" "After: a1 + a2"
+gdb_test "p a2 - a1" "From CC <A_minus_A>.*5" "After: a1 - a2"
+gdb_test "p b1 - a1" "From CC <A_minus_A>.*25" "After: b1 - a2"
+gdb_test "p c1 - a1" "From CC <A_minus_A>.*15" "After: c1 - a1"
+gdb_test "p c1 - c1" "From Python <C_minus_C>.*0" "After: c1 - c1"
+gdb_test "p a1.geta()" "From Python <A_geta>.*5" "After: a1.geta()"
+gdb_test "p ref_c - a1" "From CC <A_minus_A>.*15" "After: ref_c - a1"
+gdb_test "p ref_c - c1" "From Python <C_minus_C>.*0" "After: ref_c - c1"
+gdb_test "p b1 * b1" "From Python <B_star_B>.*900" "After: b1 * b1"
+gdb_test "p ref_c * b1" "No symbol.*" "After: ref_c * b1 failure"
+gdb_test "p ref_d * b1" "From CC <D_star_D>.*1500" "After: ref_d * b1"
+gdb_test "p bt * c1" "From Python <B_star_B>.*800" "After: bt * c1"
+gdb_test "p ++a1" "From Python <plus_plus_A>.*6" "After: ++a1"
+gdb_test "p a1.getarrayind(5)" "From Python <A_getarrayind>.*5" \
+ "After: a1.getarrayind(5)"
+gdb_test "p g.size_diff<float> ()" "From Python G<>::size_diff.*" \
+ "After: g.size_diff<float>()"
+gdb_test "p g.size_diff< unsigned long >()" "From Python G<>::size_diff.*" \
+ "After: g.size_diff<unsigned long>()"
+gdb_test "p g.size_mul<2>()" "From Python G<>::size_mul.*" \
+ "After: g.size_mul<2>()"
+gdb_test "p g.size_mul< 5 >()" "From Python G<>::size_mul.*" \
+ "After: g.size_mul< 5 >()"
+gdb_test "p g.mul<double>(2.0)" "From Python G<>::mul.*" \
+ "After: g.mul<double>(2.0)"
+gdb_test "p g.mul<char>('a')" "From Python G<>::mul.*" \
+ "After: g.mul<char>('a')"
+
+# Tests for 'disable/enable debug-method' command.
+gdb_test_no_output "disable debug-method .*debugmethods G_mul" "Disable G_mul"
+gdb_test "p g.mul<char>('a')" "Couldn't find method.*" \
+ "After disabling g.mul<char>('a')"
+gdb_test_no_output "enable debug-method .*debugmethods G_mul" "Enable G_mul"
+gdb_test "p g.mul<char>('a')" "From Python G<>::mul.*" \
+ "After enabling g.mul<char>('a')"
+
+# Test for 'info debug-methods' command
+gdb_test "info debug-method global plus" "global.*plus_plus_A - enabled" \
+ "info debug-method global plus"
diff --git a/gdb/testsuite/gdb.python/py-debugmethods.py b/gdb/testsuite/gdb.python/py-debugmethods.py
new file mode 100644
index 0000000..8147611
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-debugmethods.py
@@ -0,0 +1,180 @@
+# Copyright 2013 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite. It implements debug methods
+# in the Python extension language.
+
+import gdb
+import re
+
+from gdb.debugmethods import DebugMethod, SimpleDebugMethod
+
+def A_plus_A(obj, opr):
+ print 'From Python <A_plus_A>:'
+ return obj['a'] + opr['a']
+
+def plus_plus_A(obj):
+ print 'From Python <plus_plus_A>:'
+ return obj['a'] + 1
+
+def C_minus_C(obj, opr):
+ print 'From Python <C_minus_C>:'
+ return obj['a'] - opr['a']
+
+def B_star_B(obj, opr):
+ print 'From Python <B_star_B>:'
+ return obj['a'] * opr['a']
+
+def A_geta(obj):
+ print 'From Python <A_geta>:'
+ return obj['a']
+
+def A_getarrayind(obj, index):
+ print 'From Python <A_getarrayind>:'
+ return obj['array'][index]
+
+
+type_A = gdb.parse_and_eval('(dop::A *) 0').type.target()
+type_B = gdb.parse_and_eval('(dop::B *) 0').type.target()
+type_C = gdb.parse_and_eval('(dop::C *) 0').type.target()
+type_int = gdb.parse_and_eval('(int *) 0').type.target()
+
+
+class G_size_diff(DebugMethod):
+ def __init__(self):
+ DebugMethod.__init__(self, 'G_size_diff')
+
+ def match(self, class_type, method_name):
+ if not re.match('^dop::G<[ ]*[_a-zA-Z][ _a-zA-Z0-9]*>$',
+ class_type.unqualified().tag):
+ return False
+ if not re.match('^size_diff<[ ]*[_a-zA-Z][ _a-zA-Z0-9]*>$',
+ method_name):
+ return False
+ return True
+
+ def get_argtypes(self, class_type, method_name):
+ return []
+
+ def invoke(self, obj, method_name, args):
+ t1_search = re.search('<[ ]*[_a-zA-Z][ _a-zA-Z0-9]*>', method_name)
+ if not t1_search:
+ raise gdb.GdbError(
+ 'Unable to find template argument from %s.' % method_name)
+ t1_name = t1_search.group(0)[1:-1]
+ try:
+ t1_type = gdb.lookup_type(t1_name)
+ except Exception:
+ raise gdb.GdbError('Unknown template type %s.' % t1_name)
+ print 'From Python G<>::size_diff<%s>' % t1_name
+ return t1_type.sizeof - obj['t'].type.sizeof
+
+
+class G_size_mul(DebugMethod):
+ def __init__(self):
+ DebugMethod.__init__(self, 'G_size_mull')
+
+ def match(self, class_type, method_name):
+ if not re.match('^dop::G<[ ]*[_a-zA-Z][ _a-zA-Z0-9]*>$',
+ class_type.unqualified().tag):
+ return False
+ if not re.match('^size_mul<[ ]*[0-9]+[ ]*>$',
+ method_name):
+ return False
+ return True
+
+ def get_argtypes(self, class_type, method_name):
+ return []
+
+ def invoke(self, obj, method_name, args):
+ m_search = re.search('<[ ]*[0-9]+[ ]*>', method_name)
+ if not m_search:
+ raise gdb.GdbError(
+ 'Unable to find template argument from %s.' % method_name)
+ m_val = int(m_search.group(0)[1:-1])
+ print 'From Python G<>::size_mul<%d>' % m_val
+ return m_val * obj['t'].type.sizeof
+
+
+class G_mul(DebugMethod):
+ def __init__(self):
+ DebugMethod.__init__(self, 'G_mul')
+
+ def match(self, class_type, method_name):
+ if not re.match('^dop::G<[ ]*[_a-zA-Z][ _a-zA-Z0-9]*>$',
+ class_type.unqualified().tag):
+ return False
+ if not re.match('^mul<[ ]*[_a-zA-Z][ _a-zA-Z0-9]*>$',
+ method_name):
+ return False
+ return True
+
+ def get_argtypes(self, class_type, method_name):
+ t1_search = re.search('<[ ]*[_a-zA-Z][ _a-zA-Z0-9]*>', method_name)
+ if not t1_search:
+ raise gdb.GdbError(
+ 'Unable to find template argument from %s.' % method_name)
+ t1_name = t1_search.group(0)[1:-1]
+ try:
+ t1_type = gdb.lookup_type(t1_name)
+ except Exception:
+ raise gdb.GdbError('Unknown template type %s.' % t1_name)
+ return [t1_type]
+
+ def invoke(self, obj, method_name, args):
+ print 'From Python G<>::mul<%s>' % args[0].type
+ return args[0] * obj['t']
+
+
+global_dm_list = [
+ SimpleDebugMethod('A_plus_A',
+ A_plus_A,
+ '^dop::A$',
+ 'operator\+',
+ # This is a replacement, hence match the arg type
+ # exactly!
+ type_A.const().reference()),
+ SimpleDebugMethod('plus_plus_A',
+ plus_plus_A,
+ '^dop::A$',
+ 'operator\+\+'),
+ SimpleDebugMethod('C_minus_C',
+ C_minus_C,
+ '^dop::C$',
+ 'operator\-',
+ type_C),
+ SimpleDebugMethod('B_star_B',
+ B_star_B,
+ '^dop::B$',
+ 'operator\*',
+ # This is a replacement, hence match the arg type
+ # exactly!
+ type_B.const().reference()),
+ SimpleDebugMethod('A_geta',
+ A_geta,
+ '^dop::A$',
+ '^geta$'),
+ SimpleDebugMethod('A_getarrayind',
+ A_getarrayind,
+ '^dop::A$',
+ '^getarrayind$',
+ type_int),
+]
+
+pspace_dm_list = [G_size_diff(), G_size_mul(), G_mul()]
+
+gdb.debugmethods.register_debug_methods(gdb, global_dm_list)
+gdb.debugmethods.register_debug_methods(gdb.current_progspace(),
+ pspace_dm_list)
diff --git a/gdb/valarith.c b/gdb/valarith.c
index 49c8bdc..e486b24 100644
--- a/gdb/valarith.c
+++ b/gdb/valarith.c
@@ -30,6 +30,7 @@
#include <math.h>
#include "infcall.h"
#include "exceptions.h"
+#include "ext-function.h"
/* Define whether or not the C operator '/' truncates towards zero for
differently signed operands (truncation direction is undefined in C). */
@@ -285,21 +286,27 @@ unop_user_defined_p (enum exp_opcode op, struct value *arg1)
explicitly, and perform correct overload resolution in all of the above
situations or combinations thereof. */
-static struct value *
+static void
value_user_defined_cpp_op (struct value **args, int nargs, char *operator,
+ struct value **src_fn,
+ struct ext_fn_descriptor **ext_fn,
int *static_memfuncp)
{
struct symbol *symp = NULL;
struct value *valp = NULL;
+ struct ext_fn_descriptor *ext_fnp = NULL;
find_overload_match (args, nargs, operator, BOTH /* could be method */,
&args[0] /* objp */,
NULL /* pass NULL symbol since symbol is unknown */,
- &valp, &symp, static_memfuncp, 0);
+ &valp, &symp, &ext_fnp, static_memfuncp, 0);
if (valp)
- return valp;
+ {
+ *src_fn = valp;
+ return;
+ }
if (symp)
{
@@ -307,7 +314,14 @@ value_user_defined_cpp_op (struct value **args, int nargs, char *operator,
expect a reference as its first argument
rather the explicit structure. */
args[0] = value_ind (args[0]);
- return value_of_variable (symp, 0);
+ *src_fn = value_of_variable (symp, 0);
+ return;
+ }
+
+ if (ext_fnp)
+ {
+ *ext_fn = ext_fnp;
+ return;
}
error (_("Could not find %s."), operator);
@@ -316,19 +330,22 @@ value_user_defined_cpp_op (struct value **args, int nargs, char *operator,
/* Lookup user defined operator NAME. Return a value representing the
function, otherwise return NULL. */
-static struct value *
+static void
value_user_defined_op (struct value **argp, struct value **args, char *name,
- int *static_memfuncp, int nargs)
+ int *static_memfuncp, int nargs,
+ struct value **src_fn, struct ext_fn_descriptor **ext_fn)
{
struct value *result = NULL;
if (current_language->la_language == language_cplus)
- result = value_user_defined_cpp_op (args, nargs, name, static_memfuncp);
+ value_user_defined_cpp_op (args, nargs, name, src_fn, ext_fn,
+ static_memfuncp);
else
- result = value_struct_elt (argp, args, name, static_memfuncp,
- "structure");
-
- return result;
+ {
+ result = value_struct_elt (argp, args, name, static_memfuncp,
+ "structure");
+ *src_fn = result;
+ }
}
/* We know either arg1 or arg2 is a structure, so try to find the right
@@ -345,6 +362,7 @@ value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op,
enum exp_opcode otherop, enum noside noside)
{
struct value **argvec;
+ struct ext_fn_descriptor *ext_fn = NULL;
char *ptr;
char tstr[13];
int static_memfuncp;
@@ -359,6 +377,7 @@ value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op,
error (_("Can't do that binary op on that type")); /* FIXME be explicit */
argvec = (struct value **) alloca (sizeof (struct value *) * 4);
+ argvec[0] = NULL;
argvec[1] = value_addr (arg1);
argvec[2] = arg2;
argvec[3] = 0;
@@ -471,8 +490,8 @@ value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op,
error (_("Invalid binary operation specified."));
}
- argvec[0] = value_user_defined_op (&arg1, argvec + 1, tstr,
- &static_memfuncp, 2);
+ value_user_defined_op (&arg1, argvec + 1, tstr, &static_memfuncp, 2,
+ &argvec[0], &ext_fn);
if (argvec[0])
{
@@ -492,6 +511,29 @@ value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op,
return call_function_by_hand (argvec[0], 2 - static_memfuncp,
argvec + 1);
}
+ if (ext_fn)
+ {
+ if (ext_fn_is_method (ext_fn))
+ {
+ struct value *ret_val = ext_fn_invoke_method (ext_fn, arg1, &arg2, 1);
+
+ if (ret_val == NULL)
+ error (_("Error invoking debug method implementation for "
+ "method %s"), tstr);
+
+ xfree (ext_fn);
+
+ return ret_val;
+ }
+ else
+ {
+ /* This else should not be taken as only debug methods
+ are supported currently. */
+ internal_error (__FILE__, __LINE__,
+ _("Invoking an extension language "
+ "function (not a method)."));
+ }
+ }
throw_error (NOT_FOUND_ERROR,
_("member function %s not found"), tstr);
#ifdef lint
@@ -510,6 +552,7 @@ value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside)
{
struct gdbarch *gdbarch = get_type_arch (value_type (arg1));
struct value **argvec;
+ struct ext_fn_descriptor *ext_fn;
char *ptr;
char tstr[13], mangle_tstr[13];
int static_memfuncp, nargs;
@@ -523,6 +566,7 @@ value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside)
error (_("Can't do that unary op on that type")); /* FIXME be explicit */
argvec = (struct value **) alloca (sizeof (struct value *) * 4);
+ argvec[0] = NULL;
argvec[1] = value_addr (arg1);
argvec[2] = 0;
@@ -574,8 +618,8 @@ value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside)
error (_("Invalid unary operation specified."));
}
- argvec[0] = value_user_defined_op (&arg1, argvec + 1, tstr,
- &static_memfuncp, nargs);
+ value_user_defined_op (&arg1, argvec + 1, tstr, &static_memfuncp, nargs,
+ &argvec[0], &ext_fn);
if (argvec[0])
{
@@ -594,6 +638,29 @@ value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside)
return value_zero (return_type, VALUE_LVAL (arg1));
}
return call_function_by_hand (argvec[0], nargs, argvec + 1);
+ }
+ if (ext_fn)
+ {
+ if (ext_fn_is_method (ext_fn))
+ {
+ struct value *ret_val = ext_fn_invoke_method (ext_fn, arg1, NULL, 0);
+
+ if (ret_val == NULL)
+ error (_("Error invoking debug method implementation for "
+ "method %s"), tstr);
+
+ xfree (ext_fn);
+
+ return ret_val;
+ }
+ else
+ {
+ /* This else should not be taken as only debug methods
+ are supported currently. */
+ internal_error (__FILE__, __LINE__,
+ _("Invoking an extension language "
+ "function (not a method)."));
+ }
}
throw_error (NOT_FOUND_ERROR,
_("member function %s not found"), tstr);
diff --git a/gdb/valops.c b/gdb/valops.c
index 4fc57ec..9dbf333 100644
--- a/gdb/valops.c
+++ b/gdb/valops.c
@@ -44,6 +44,7 @@
#include "objfiles.h"
#include "symtab.h"
#include "exceptions.h"
+#include "ext-function.h"
extern unsigned int overload_debug;
/* Local functions. */
@@ -72,8 +73,8 @@ int find_oload_champ_namespace_loop (struct value **, int,
const int no_adl);
static int find_oload_champ (struct value **, int, int, int,
- struct fn_field *, struct symbol **,
- struct badness_vector **);
+ struct fn_field *, VEC (ext_fn_descriptor_p) *,
+ struct symbol **, struct badness_vector **);
static int oload_method_static (int, struct fn_field *, int);
@@ -100,9 +101,10 @@ static CORE_ADDR allocate_space_in_inferior (int);
static struct value *cast_into_complex (struct type *, struct value *);
-static struct fn_field *find_method_list (struct value **, const char *,
- int, struct type *, int *,
- struct type **, int *);
+static void find_method_list (struct value **, const char *,
+ int, struct type *, struct fn_field **, int *,
+ VEC (ext_fn_descriptor_p) **,
+ struct type **, int *);
void _initialize_valops (void);
@@ -2261,40 +2263,58 @@ value_struct_elt (struct value **argp, struct value **args,
method is found.
BOFFSET is the offset of the base subobject where the method is found. */
-static struct fn_field *
+static void
find_method_list (struct value **argp, const char *method,
- int offset, struct type *type, int *num_fns,
+ int offset, struct type *type,
+ struct fn_field **fn_list, int *num_fns,
+ VEC (ext_fn_descriptor_p) **ext_fn_vec,
struct type **basetype, int *boffset)
{
int i;
- struct fn_field *f;
- CHECK_TYPEDEF (type);
+ struct fn_field *f = NULL;
- *num_fns = 0;
+ CHECK_TYPEDEF (type);
/* First check in object itself. */
- for (i = TYPE_NFN_FIELDS (type) - 1; i >= 0; i--)
- {
- /* pai: FIXME What about operators and type conversions? */
- const char *fn_field_name = TYPE_FN_FIELDLIST_NAME (type, i);
- if (fn_field_name && (strcmp_iw (fn_field_name, method) == 0))
- {
- int len = TYPE_FN_FIELDLIST_LENGTH (type, i);
- struct fn_field *f = TYPE_FN_FIELDLIST1 (type, i);
+ if (fn_list && !(*fn_list))
+ for (i = TYPE_NFN_FIELDS (type) - 1; i >= 0; i--)
+ {
+ /* pai: FIXME What about operators and type conversions? */
+ const char *fn_field_name = TYPE_FN_FIELDLIST_NAME (type, i);
+
+ if (fn_field_name && (strcmp_iw (fn_field_name, method) == 0))
+ {
+ int len = TYPE_FN_FIELDLIST_LENGTH (type, i);
+ f = TYPE_FN_FIELDLIST1 (type, i);
+ *fn_list = f;
+
+ *num_fns = len;
+ *basetype = type;
+ *boffset = offset;
+
+ /* Resolve any stub methods. */
+ check_stub_method_group (type, i);
+
+ break;
+ }
+ }
- *num_fns = len;
- *basetype = type;
- *boffset = offset;
+ if (ext_fn_vec)
+ {
+ VEC (ext_fn_descriptor_p) *ef_vec = NULL, *new_vec = NULL;
- /* Resolve any stub methods. */
- check_stub_method_group (type, i);
+ ef_vec = get_matching_ext_methods (type, method);
+ new_vec = VEC_merge (ext_fn_descriptor_p, *ext_fn_vec, ef_vec);
- return f;
- }
+ VEC_free (ext_fn_descriptor_p, *ext_fn_vec);
+ VEC_free (ext_fn_descriptor_p, ef_vec);
+ *ext_fn_vec = new_vec;
}
- /* Not found in object, check in base subobjects. */
+ /* If source methods are not found in current class, look for them in the
+ base classes. We have to go through the base classes to gather extension
+ methods anyway. */
for (i = TYPE_N_BASECLASSES (type) - 1; i >= 0; i--)
{
int base_offset;
@@ -2311,13 +2331,11 @@ find_method_list (struct value **argp, const char *method,
{
base_offset = TYPE_BASECLASS_BITPOS (type, i) / 8;
}
- f = find_method_list (argp, method, base_offset + offset,
- TYPE_BASECLASS (type, i), num_fns,
- basetype, boffset);
- if (f)
- return f;
+
+ find_method_list (argp, method, base_offset + offset,
+ TYPE_BASECLASS (type, i), fn_list, num_fns,
+ ext_fn_vec, basetype, boffset);
}
- return NULL;
}
/* Return the list of overloaded methods of a specified name.
@@ -2330,9 +2348,11 @@ find_method_list (struct value **argp, const char *method,
method.
BOFFSET is the offset of the base subobject which defines the method. */
-static struct fn_field *
+static void
value_find_oload_method_list (struct value **argp, const char *method,
- int offset, int *num_fns,
+ int offset, struct fn_field **fn_list,
+ int *num_fns,
+ VEC (ext_fn_descriptor_p) **ext_fn_vec,
struct type **basetype, int *boffset)
{
struct type *t;
@@ -2354,8 +2374,32 @@ value_find_oload_method_list (struct value **argp, const char *method,
error (_("Attempt to extract a component of a "
"value that is not a struct or union"));
- return find_method_list (argp, method, 0, t, num_fns,
- basetype, boffset);
+ /* Clear the lists. */
+ if (fn_list)
+ {
+ *fn_list = NULL;
+ *num_fns = 0;
+ }
+ if (ext_fn_vec)
+ *ext_fn_vec = VEC_alloc (ext_fn_descriptor_p, 1);
+
+ find_method_list (argp, method, 0, t, fn_list, num_fns, ext_fn_vec,
+ basetype, boffset);
+}
+
+static struct type *
+value_has_indirect_dynamic_type (struct value *obj)
+{
+ struct type *stype, *dtype, *dtype_ind;
+
+ stype = check_typedef (TYPE_TARGET_TYPE (value_type (obj)));
+ dtype_ind = value_rtti_indirect_type (obj, NULL, NULL, NULL);
+ dtype = dtype_ind ? check_typedef (TYPE_TARGET_TYPE (dtype_ind)) : stype;
+
+ if (class_types_same_p (stype, dtype))
+ return NULL;
+ else
+ return dtype_ind;
}
/* Given an array of arguments (ARGS) (which includes an
@@ -2403,6 +2447,7 @@ find_overload_match (struct value **args, int nargs,
const char *name, enum oload_search_type method,
struct value **objp, struct symbol *fsym,
struct value **valp, struct symbol **symp,
+ struct ext_fn_descriptor **ext_fn,
int *staticp, const int no_adl)
{
struct value *obj = (objp ? *objp : NULL);
@@ -2410,16 +2455,24 @@ find_overload_match (struct value **args, int nargs,
/* Index of best overloaded function. */
int func_oload_champ = -1;
int method_oload_champ = -1;
+ int src_method_oload_champ = -1;
+ int src_method_oload_champ_bkp = -1;
+ int ext_method_oload_champ = -1;
+ int src_and_ext_equal = 0;
/* The measure for the current best match. */
struct badness_vector *method_badness = NULL;
struct badness_vector *func_badness = NULL;
+ struct badness_vector *ext_method_badness = NULL;
+ struct badness_vector *src_method_badness = NULL;
struct value *temp = obj;
/* For methods, the list of overloaded methods. */
struct fn_field *fns_ptr = NULL;
/* For non-methods, the list of overloaded function symbols. */
struct symbol **oload_syms = NULL;
+ /* For extension functions, the VEC of extension function descriptors. */
+ VEC (ext_fn_descriptor_p) *ext_fn_vec = NULL;
/* Number of overloaded instances being considered. */
int num_fns = 0;
struct type *basetype = NULL;
@@ -2431,6 +2484,8 @@ find_overload_match (struct value **args, int nargs,
const char *func_name = NULL;
enum oload_classification match_quality;
enum oload_classification method_match_quality = INCOMPATIBLE;
+ enum oload_classification src_method_match_quality = INCOMPATIBLE;
+ enum oload_classification ext_method_match_quality = INCOMPATIBLE;
enum oload_classification func_match_quality = INCOMPATIBLE;
/* Get the list of overloaded methods or functions. */
@@ -2459,12 +2514,12 @@ find_overload_match (struct value **args, int nargs,
}
/* Retrieve the list of methods with the name NAME. */
- fns_ptr = value_find_oload_method_list (&temp, name,
- 0, &num_fns,
- &basetype, &boffset);
+ value_find_oload_method_list (&temp, name, 0, &fns_ptr, &num_fns,
+ ext_fn ? &ext_fn_vec : NULL,
+ &basetype, &boffset);
/* If this is a method only search, and no methods were found
the search has faild. */
- if (method == METHOD && (!fns_ptr || !num_fns))
+ if (method == METHOD && (!fns_ptr || !num_fns) && !ext_fn_vec)
error (_("Couldn't find method %s%s%s"),
obj_type_name,
(obj_type_name && *obj_type_name) ? "::" : "",
@@ -2475,18 +2530,82 @@ find_overload_match (struct value **args, int nargs,
if (fns_ptr)
{
gdb_assert (TYPE_DOMAIN_TYPE (fns_ptr[0].type) != NULL);
- method_oload_champ = find_oload_champ (args, nargs, method,
- num_fns, fns_ptr,
- oload_syms, &method_badness);
-
- method_match_quality =
- classify_oload_match (method_badness, nargs,
- oload_method_static (method, fns_ptr,
- method_oload_champ));
-
- make_cleanup (xfree, method_badness);
+ src_method_oload_champ = find_oload_champ (args, nargs, method,
+ num_fns, fns_ptr, NULL,
+ oload_syms,
+ &src_method_badness);
+
+ src_method_match_quality =
+ classify_oload_match (
+ src_method_badness, nargs,
+ oload_method_static (method, fns_ptr,
+ src_method_oload_champ));
+
+ make_cleanup (xfree, src_method_badness);
}
+ if (ext_fn && VEC_length (ext_fn_descriptor_p, ext_fn_vec))
+ {
+ ext_method_oload_champ = find_oload_champ (args, nargs, method,
+ 0, NULL, ext_fn_vec,
+ NULL, &ext_method_badness);
+ ext_method_match_quality = classify_oload_match (ext_method_badness,
+ nargs, 0);
+ make_cleanup (xfree, ext_method_badness);
+ make_ext_fn_vec_cleanup (ext_fn_vec);
+ }
+
+ if (src_method_oload_champ >= 0 && ext_method_oload_champ >= 0)
+ {
+ switch (compare_badness (ext_method_badness, src_method_badness))
+ {
+ case 0: /* Src method and ext method are equally good. */
+ src_and_ext_equal = 1;
+ case 1: /* Src method and ext method are incompatible */
+ /* if ext method match is not standard, then let source method
+ win. */
+ if (ext_method_match_quality != STANDARD)
+ {
+ method_oload_champ = src_method_oload_champ;
+ method_badness = src_method_badness;
+ ext_method_oload_champ = -1;
+ method_match_quality = src_method_match_quality;
+ break;
+ }
+ case 2: /* Ext method is champion. */
+ method_oload_champ = ext_method_oload_champ;
+ method_badness = ext_method_badness;
+ src_method_oload_champ_bkp = src_method_oload_champ;
+ src_method_oload_champ = -1;
+ method_match_quality = ext_method_match_quality;
+ break;
+ case 3: /* Src method is champion. */
+ method_oload_champ = src_method_oload_champ;
+ method_badness = src_method_badness;
+ ext_method_oload_champ = -1;
+ method_match_quality = src_method_match_quality;
+ break;
+ default:
+ error (_("Internal error: unexpected overload comparison "
+ "result"));
+ break;
+ }
+ }
+ else
+ {
+ if (src_method_oload_champ >= 0)
+ {
+ method_oload_champ = src_method_oload_champ;
+ method_badness = src_method_badness;
+ method_match_quality = src_method_match_quality;
+ }
+ if (ext_method_oload_champ >= 0)
+ {
+ method_oload_champ = ext_method_oload_champ;
+ method_badness = ext_method_badness;
+ method_match_quality = ext_method_match_quality;
+ }
+ }
}
if (method == NON_METHOD || method == BOTH)
@@ -2629,21 +2748,6 @@ find_overload_match (struct value **args, int nargs,
func_name);
}
- if (staticp != NULL)
- *staticp = oload_method_static (method, fns_ptr, method_oload_champ);
-
- if (method_oload_champ >= 0)
- {
- if (TYPE_FN_FIELD_VIRTUAL_P (fns_ptr, method_oload_champ))
- *valp = value_virtual_fn_field (&temp, fns_ptr, method_oload_champ,
- basetype, boffset);
- else
- *valp = value_fn_field (&temp, fns_ptr, method_oload_champ,
- basetype, boffset);
- }
- else
- *symp = oload_syms[func_oload_champ];
-
if (objp)
{
struct type *temp_type = check_typedef (value_type (temp));
@@ -2653,11 +2757,66 @@ find_overload_match (struct value **args, int nargs,
&& (TYPE_CODE (objtype) == TYPE_CODE_PTR
|| TYPE_CODE (objtype) == TYPE_CODE_REF))
{
- temp = value_addr (temp);
+ *objp = value_addr (temp);
}
- *objp = temp;
+ else
+ *objp = temp;
}
+ if (staticp != NULL)
+ *staticp = oload_method_static (method, fns_ptr, method_oload_champ);
+
+ if (method_oload_champ >= 0)
+ {
+ if (src_method_oload_champ >= 0)
+ {
+ if (TYPE_FN_FIELD_VIRTUAL_P (fns_ptr, method_oload_champ))
+ {
+ struct type *dtype;
+
+ dtype = value_has_indirect_dynamic_type (args[0]);
+ if (dtype)
+ {
+ args[0] = value_cast (dtype, args[0]);
+ do_cleanups (all_cleanups);
+ return find_overload_match (args, nargs, name, method,
+ objp, fsym, valp, symp, ext_fn,
+ staticp, no_adl);
+ }
+ else
+ *valp = value_virtual_fn_field (&temp, fns_ptr,
+ method_oload_champ,
+ basetype, boffset);
+ }
+ else
+ *valp = value_fn_field (&temp, fns_ptr, method_oload_champ,
+ basetype, boffset);
+ }
+ else
+ {
+ if (src_and_ext_equal
+ && TYPE_FN_FIELD_VIRTUAL_P (fns_ptr, src_method_oload_champ_bkp))
+ {
+ struct type *dtype;
+
+ dtype = value_has_indirect_dynamic_type (args[0]);
+ if (dtype)
+ {
+ args[0] = value_cast (dtype, args[0]);
+ do_cleanups (all_cleanups);
+ return find_overload_match (args, nargs, name, method,
+ objp, fsym, valp, symp, ext_fn,
+ staticp, no_adl);
+ }
+ }
+
+ *ext_fn = ext_fn_clone (VEC_index (ext_fn_descriptor_p, ext_fn_vec,
+ ext_method_oload_champ));
+ }
+ }
+ else
+ *symp = oload_syms[func_oload_champ];
+
do_cleanups (all_cleanups);
switch (match_quality)
@@ -2791,7 +2950,7 @@ find_oload_champ_namespace_loop (struct value **args, int nargs,
++num_fns;
new_oload_champ = find_oload_champ (args, nargs, 0, num_fns,
- NULL, new_oload_syms,
+ NULL, NULL, new_oload_syms,
&new_oload_champ_bv);
/* Case 1: We found a good match. Free earlier matches (if any),
@@ -2839,10 +2998,12 @@ find_oload_champ_namespace_loop (struct value **args, int nargs,
static int
find_oload_champ (struct value **args, int nargs, int method,
int num_fns, struct fn_field *fns_ptr,
+ VEC (ext_fn_descriptor_p) *ext_fn_vec,
struct symbol **oload_syms,
struct badness_vector **oload_champ_bv)
{
int ix;
+ int ext_fn_vec_n = 0;
/* A measure of how good an overloaded instance is. */
struct badness_vector *bv;
/* Index of best overloaded function. */
@@ -2852,18 +3013,27 @@ find_oload_champ (struct value **args, int nargs, int method,
/* 0 => no ambiguity, 1 => two good funcs, 2 => incomparable funcs. */
*oload_champ_bv = NULL;
+ ext_fn_vec_n = ext_fn_vec ? VEC_length (ext_fn_descriptor_p, ext_fn_vec) : 0;
/* Consider each candidate in turn. */
- for (ix = 0; ix < num_fns; ix++)
+ for (ix = 0; (ix < num_fns) || (ix < ext_fn_vec_n); ix++)
{
int jj;
- int static_offset = oload_method_static (method, fns_ptr, ix);
+ int static_offset = 0;
int nparms;
struct type **parm_types;
+ struct ext_fn_descriptor *ext_fn = NULL;
+
+ if (ext_fn_vec)
+ ext_fn = VEC_index (ext_fn_descriptor_p, ext_fn_vec, ix);
if (method)
{
- nparms = TYPE_NFIELDS (TYPE_FN_FIELD_TYPE (fns_ptr, ix));
+ if (fns_ptr)
+ {
+ static_offset = oload_method_static (method, fns_ptr, ix);
+ nparms = TYPE_NFIELDS (TYPE_FN_FIELD_TYPE (fns_ptr, ix));
+ }
}
else
{
@@ -2872,13 +3042,20 @@ find_oload_champ (struct value **args, int nargs, int method,
}
/* Prepare array of parameter types. */
- parm_types = (struct type **)
- xmalloc (nparms * (sizeof (struct type *)));
- for (jj = 0; jj < nparms; jj++)
- parm_types[jj] = (method
- ? (TYPE_FN_FIELD_ARGS (fns_ptr, ix)[jj].type)
- : TYPE_FIELD_TYPE (SYMBOL_TYPE (oload_syms[ix]),
- jj));
+ if (fns_ptr || oload_syms)
+ {
+ parm_types = (struct type **)
+ xmalloc (nparms * (sizeof (struct type *)));
+ for (jj = 0; jj < nparms; jj++)
+ parm_types[jj] = (method
+ ? (TYPE_FN_FIELD_ARGS (fns_ptr, ix)[jj].type)
+ : TYPE_FIELD_TYPE (SYMBOL_TYPE (oload_syms[ix]),
+ jj));
+ }
+ else
+ {
+ parm_types = ext_fn_get_argtypes (ext_fn, &nparms);
+ }
/* Compare parameter types to supplied argument types. Skip
THIS for static methods. */
diff --git a/gdb/value.h b/gdb/value.h
index db964e3..3216249 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -31,6 +31,7 @@ struct type;
struct ui_file;
struct language_defn;
struct value_print_options;
+struct ext_fn_descriptor;
/* The structure which defines the type of a value. It should never
be possible for a program lval value to survive over a call to the
@@ -671,6 +672,7 @@ extern int find_overload_match (struct value **args, int nargs,
enum oload_search_type method,
struct value **objp, struct symbol *fsym,
struct value **valp, struct symbol **symp,
+ struct ext_fn_descriptor **ext_fn,
int *staticp, const int no_adl);
extern struct value *value_field (struct value *arg1, int fieldno);
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH] Debug Methods in GDB Python
2013-11-22 22:41 [PATCH] Debug Methods in GDB Python Siva Chandra
@ 2013-11-26 3:22 ` Siva Chandra
2013-12-06 6:31 ` Doug Evans
0 siblings, 1 reply; 12+ messages in thread
From: Siva Chandra @ 2013-11-26 3:22 UTC (permalink / raw)
To: gdb-patches; +Cc: Doug Evans
[-- Attachment #1: Type: text/plain, Size: 2498 bytes --]
Doug pointed out offlist that the ChangeLog was improperly done. Below
is the corrected ChangeLog entry. For convenience, I have also
attached the same patch that I have posted last time.
2013-11-25 Siva Chandra Reddy <sivachandra@google.com>
Add Debug methods support in GDB Python.
* Makefile.in: Add entries for new files.
* data-directory/Makefile.in: Add entries for new Python files.
* eval.c (evaluate_subexp_standard): Lookup and invoke methods
defined in extension languages.
* valarith.c (value_x_binop, value_x_unop): Lookup and invoke
overloaded operator methods defined in extension languages.
* valops.c (find_oload_method_list, find_method_list,
find_overload_match, find_oload_champ): Lookup methods defined
in extension languages.
(value_has_indirect_dynamic_type): New function to determine
the indirect dynamic type of a value.
* value.h (find_overload_match): Update signature.
* ext-function.c: New file.
* ext-function.h: New file.
* python/py-debugmethods.c: New file.
* python/py-objfile.c (objfile_object): New field
'debug_methods'.
(objfpy_dealloc): XDECREF on the new debug_methods field.
(objfpy_new, objfile_to_objfile_object): Initialize
debug_methods field.
(objfpy_get_debug_methods): New function.
(objfile_getset): New entry 'debug_methods'.
* python/py-progspace.c (pspace_object): New field
'debug_methods'.
(pspy_dealloc): XDECREF on the new debug_methods field.
(pspy_new, pspace_to_pspace_object): Initialize
debug_methods field.
(pspy_get_debug_methods): New function.
(pspace_getset): New entry 'debug_methods'.
* python/python-internal.h: Add declarations for new functions.
* python/python.c (_initialize_python): Invoke
gdbpy_initialize_debugmethods.
* python/python.h: Add declarations of new functions.
* python/lib/gdb/__init__.py (debug_methods): New attribute.
* python/lib/gdb/debugmethods.py: New file.
* python/lib/gdb/command/debug-methods.py: New file.
testuite/
* gdb.python/py-debugmethods.cc: New testcase to test debug
methods.
* gdb.python/py-debugmethods.exp: New tests to test debug
methods.
* gdb.python/py-debugmethods.py: Python script supporting the
new testcase and tests.
[-- Attachment #2: dm_patch_v6.txt --]
[-- Type: text/plain, Size: 98052 bytes --]
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 0591279..75e2be6 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -287,6 +287,7 @@ SUBDIR_PYTHON_OBS = \
py-breakpoint.o \
py-cmd.o \
py-continueevent.o \
+ py-debugmethods.o \
py-event.o \
py-evtregistry.o \
py-evts.o \
@@ -323,6 +324,7 @@ SUBDIR_PYTHON_SRCS = \
python/py-breakpoint.c \
python/py-cmd.c \
python/py-continueevent.c \
+ python/py-debugmethods.c \
python/py-event.c \
python/py-evtregistry.c \
python/py-evts.c \
@@ -733,7 +735,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
dwarf2expr.c dwarf2loc.c dwarf2read.c dwarf2-frame.c \
dwarf2-frame-tailcall.c \
elfread.c environ.c eval.c event-loop.c event-top.c \
- exceptions.c expprint.c \
+ exceptions.c expprint.c ext-function.c \
f-exp.y f-lang.c f-typeprint.c f-valprint.c filesystem.c \
findcmd.c findvar.c frame.c frame-base.c frame-unwind.c \
gdbarch.c arch-utils.c gdb_bfd.c gdb_obstack.c \
@@ -835,7 +837,7 @@ rs6000-tdep.h rs6000-aix-tdep.h \
common/gdb_locale.h common/gdb_dirent.h arch-utils.h trad-frame.h gnu-nat.h \
language.h nbsd-tdep.h solib-svr4.h \
macroexp.h ui-file.h regcache.h tracepoint.h i386-tdep.h \
-inf-child.h p-lang.h event-top.h gdbtypes.h user-regs.h \
+inf-child.h p-lang.h event-top.h ext-function.h gdbtypes.h user-regs.h \
regformats/regdef.h config/alpha/nm-osf3.h config/i386/nm-i386gnu.h \
config/i386/nm-fbsd.h \
config/nm-nto.h config/sparc/nm-sol2.h config/nm-linux.h \
@@ -957,7 +959,8 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
inferior.o osdata.o gdb_usleep.o record.o record-full.o gcore.o \
gdb_vecs.o jit.o progspace.o skip.o probe.o \
common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o \
- format.o registry.o btrace.o record-btrace.o waitstatus.o
+ format.o registry.o btrace.o record-btrace.o waitstatus.o \
+ ext-function.o
TSOBS = inflow.o
@@ -2181,6 +2184,10 @@ py-continueevent.o: $(srcdir)/python/py-continueevent.c
$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-continueevent.c
$(POSTCOMPILE)
+py-debugmethods.o: $(srcdir)/python/py-debugmethods.c
+ $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-debugmethods.c
+ $(POSTCOMPILE)
+
py-event.o: $(srcdir)/python/py-event.c
$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-event.c
$(POSTCOMPILE)
diff --git a/gdb/data-directory/Makefile.in b/gdb/data-directory/Makefile.in
index 1e00c58..6146cf1 100644
--- a/gdb/data-directory/Makefile.in
+++ b/gdb/data-directory/Makefile.in
@@ -61,8 +61,10 @@ PYTHON_FILES = \
gdb/types.py \
gdb/printing.py \
gdb/prompt.py \
+ gdb/debugmethods.py \
gdb/command/bound_registers.py \
gdb/command/__init__.py \
+ gdb/command/debug_methods.py \
gdb/command/frame_filters.py \
gdb/command/type_printers.py \
gdb/command/pretty_printers.py \
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index ec9d901..9eabd1d 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -23526,6 +23526,7 @@ optional arguments while skipping others. Example:
* Selecting Pretty-Printers:: How GDB chooses a pretty-printer.
* Writing a Pretty-Printer:: Writing a Pretty-Printer.
* Type Printing API:: Pretty-printing types.
+* Debug Methods In Python:: Debug Methods In Python.
* Frame Filter API:: Filtering Frames.
* Frame Decorator API:: Decorating Frames.
* Writing a Frame Filter:: Writing a Frame Filter.
@@ -24909,6 +24910,75 @@ done then type printers would have to make use of the event system in
order to avoid holding information that could become stale as the
inferior changed.
+@node Debug Methods In Python
+@subsubsection Debug Methods In Python
+@cindex debug methods in Python
+
+Debug methods are, as the name suggests, method definitions on classes
+but defined in @value{GDBN} Python instead of the source language. If a
+particular method (which could be an overloaded operator) needs to be
+invoked to evaluate a certain expression in @value{GDBN}, then
+@value{GDBN} invokes the Python implementation of the method if the
+Python definition is better or as good a match as the implementation of
+the method in the source language. A benefit of having a debug method
+replacement for a source language method is that expressions involving
+the method can be evaluated in @value{GDBN} without running the method
+in the inferior. Another benefit is with regards to method which
+typically get optimized out by the compiler. Since debug methods live
+in @value{GDBN} Python and not in the source language, they are never
+optimized out. Lastly, one could define a debug method in @value{GDBN}
+Python which does not have an equivalent in the source language!
+
+The debug method API provided in @value{GDBN} Python can be illustrated
+with the following example. Consider a C++ code snippet as follows:
+
+@smallexample
+class A
+@{
+ public:
+ int geta(void)
+ @{
+ return a;
+ @}
+
+ private:
+ int a;
+@};
+@end smallexample
+
+In the above example, the method @code{geta} in @code{class A}, might
+get optimized out in a client executable if @code{geta} is never called.
+However, one might want to call @code{geta} from @value{GDBN} for
+debugging. For such cases, we could have an equivalent Python
+implementation as a replacement for the C++ method. The debug method
+can be defined in Python as follows:
+
+@smallexample
+def A_geta(obj):
+ """Function which implemenents a Python replacement for A::geta.
+ Args:
+ obj: This is equivalent of the C++ 'this'
+ """
+ return obj['a']
+
+dm_A_geta = gdb.debugmethods.SimpleDebugMethod(
+ 'A_geta', # A unique name to the debug method
+ 'DEFAULT_DEBUG_METHOD_GROUP', # A group name for the debug method
+ A_geta, # The function implementing the debug
+ # method
+ '^dop::A$', # The class to which this debug
+ # method should belong.
+ '^geta$' # The regex which matches the method
+ # which this debug method is replacing.
+)
+
+gdb.debugmethods.register_debug_methods(
+ gdb, # Register the debug method globally
+ [dm_A_geta] # The list of debug methods to register
+)
+@end smallexample
+
+
@node Frame Filter API
@subsubsection Filtering Frames.
@cindex frame filters api
diff --git a/gdb/eval.c b/gdb/eval.c
index 9d81a92..ebeb02d 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -22,6 +22,7 @@
#include "symtab.h"
#include "gdbtypes.h"
#include "value.h"
+#include "ext-function.h"
#include "expression.h"
#include "target.h"
#include "frame.h"
@@ -1596,7 +1597,7 @@ evaluate_subexp_standard (struct type *expect_type,
NON_METHOD, /* not method */
NULL, NULL, /* pass NULL symbol since
symbol is unknown */
- NULL, &symp, NULL, 0);
+ NULL, &symp, NULL, NULL, 0);
/* Now fix the expression being evaluated. */
exp->elts[save_pos1 + 2].symbol = symp;
@@ -1626,11 +1627,12 @@ evaluate_subexp_standard (struct type *expect_type,
/* Language is C++, do some overload resolution before
evaluation. */
struct value *valp = NULL;
+ struct ext_fn_descriptor *ext_fnp = NULL;
(void) find_overload_match (&argvec[1], nargs, tstr,
METHOD, /* method */
&arg2, /* the object */
- NULL, &valp, NULL,
+ NULL, &valp, NULL, &ext_fnp,
&static_memfuncp, 0);
if (op == OP_SCOPE && !static_memfuncp)
@@ -1640,9 +1642,36 @@ evaluate_subexp_standard (struct type *expect_type,
"`this' pointer"),
function_name);
}
- argvec[1] = arg2; /* the ``this'' pointer */
- argvec[0] = valp; /* Use the method found after overload
- resolution. */
+
+ if (ext_fnp)
+ {
+ if (ext_fn_is_method (ext_fnp))
+ {
+ struct value *ret_val;
+
+ ret_val = ext_fn_invoke_method (ext_fnp, arg2, argvec + 2,
+ nargs - 1);
+ if (ret_val == NULL)
+ error (_("Error invoking debug method for method %s."),
+ tstr);
+
+ xfree (ext_fnp);
+
+ return ret_val;
+ }
+ else
+ {
+ /* This else should not be taken as only debug methods
+ are supported currently. */
+ internal_error (__FILE__, __LINE__,
+ _("Invoking an extension language "
+ "function (not a method)."));
+ }
+ }
+
+ argvec[1] = arg2; /* the ``this'' pointer */
+ argvec[0] = valp; /* Use the method found after overload
+ resolution. */
}
else
/* Non-C++ case -- or no overload resolution. */
@@ -1701,7 +1730,7 @@ evaluate_subexp_standard (struct type *expect_type,
NULL, /* no need for name */
NON_METHOD, /* not method */
NULL, function, /* the function */
- NULL, &symp, NULL, no_adl);
+ NULL, &symp, NULL, NULL, no_adl);
if (op == OP_VAR_VALUE)
{
diff --git a/gdb/ext-function.c b/gdb/ext-function.c
new file mode 100644
index 0000000..311c5d5
--- /dev/null
+++ b/gdb/ext-function.c
@@ -0,0 +1,172 @@
+/* Support for functions defined in extension languages.
+
+ Copyright (C) 2013 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+#include "cleanups.h"
+#include "ext-function.h"
+
+#include "gdb_assert.h"
+#include "vec.h"
+
+struct ext_fn_descriptor
+ {
+ const struct ext_func_ops *lang;
+
+ int is_method;
+
+ void *ext_object;
+ };
+
+typedef struct ext_func_ops *ext_lang_p;
+DEF_VEC_P (ext_lang_p);
+static VEC (ext_lang_p) *ext_lang_vec = NULL;
+
+/* Registers an extension language with GDB. */
+
+void
+register_ext_lang (struct ext_func_ops *lang)
+{
+ if (ext_lang_vec == NULL)
+ ext_lang_vec = VEC_alloc (ext_lang_p, 1);
+
+ VEC_safe_push (ext_lang_p, ext_lang_vec, lang);
+}
+
+/* Returns a new ext_fn_descriptor object. LANG is the extention language the
+ new extension function is implemented in. IS_METHOD indicates whether the
+ new extension function is a method. EXT_OBJ is the extension language
+ specific data to be encapsulated in the ext_fn_descriptor. */
+
+struct ext_fn_descriptor *
+new_ext_function (const struct ext_func_ops *lang, int is_method, void *ext_obj)
+{
+ struct ext_fn_descriptor *ext_fn = XCNEW (struct ext_fn_descriptor);
+
+ ext_fn->is_method = is_method;
+ ext_fn->lang = lang;
+ ext_fn->ext_object = ext_obj;
+
+ return ext_fn;
+}
+
+/* Clones EXT_FN and returns a new but identical ext_fn_descriptor. */
+
+struct ext_fn_descriptor *
+ext_fn_clone (struct ext_fn_descriptor *ext_fn)
+{
+ struct ext_fn_descriptor *new_ext_fn;
+ const struct ext_func_ops *lang = ext_fn->lang;
+
+ new_ext_fn = new_ext_function (lang, ext_fn->is_method,
+ lang->clone_ext_object (ext_fn->ext_object));
+
+ return new_ext_fn;
+}
+
+/* If a method of name METHOD is to be invoked on an object of type TYPE, then
+ all entension languages are searched for implementations of methods with
+ name METHOD in the extension languages. All matches found are returned as
+ a vector 'struct ent_fn_descriptor' objects. If no matching methods are
+ found, NULL is returned. */
+
+VEC (ext_fn_descriptor_p) *
+get_matching_ext_methods (struct type *type, const char *method)
+{
+ VEC (ext_fn_descriptor_p) *ext_methods = NULL;
+ ext_lang_p lang;
+ int i;
+
+ for (i = 0; VEC_iterate (ext_lang_p, ext_lang_vec, i, lang); i++)
+ {
+ VEC (ext_fn_descriptor_p) *lang_methods, *new_vec;
+
+ lang_methods = lang->get_matching_ext_methods (type, method);
+ new_vec = VEC_merge (ext_fn_descriptor_p, ext_methods, lang_methods);
+
+ VEC_free (ext_fn_descriptor_p, ext_methods);
+ VEC_free (ext_fn_descriptor_p, lang_methods);
+ ext_methods = new_vec;
+ }
+
+ return ext_methods;
+}
+
+/* Given an function EXT_FN implemented in an extension language, returns an
+ array of types of the arguments the function accepts. The length of the
+ array is returned in NARGS. The type of the 'this' object is returned as
+ the first argument if EXT_FN is a method. If EXT_FN does not take any
+ arguments, then NULL is returned with 0 in NARGS. */
+
+struct type **
+ext_fn_get_argtypes (struct ext_fn_descriptor *ext_fn, int *nargs)
+{
+ gdb_assert (ext_fn && ext_fn->lang && ext_fn->lang->get_ext_fn_argtypes);
+
+ return ext_fn->lang->get_ext_fn_argtypes (ext_fn->ext_object, nargs);
+}
+
+/* If EXT_FN is a method implemented in an extension language, invokes it and
+ returns the resulting value. The method is invoked on OBJ with arguments
+ ARGS. NARGS is the length of the ARGS array. */
+
+struct value *
+ext_fn_invoke_method (struct ext_fn_descriptor *ext_fn, struct value *obj,
+ struct value **args, int nargs)
+{
+ gdb_assert (ext_fn && ext_fn->is_method && ext_fn->lang
+ && ext_fn->lang->invoke_method);
+
+ return ext_fn->lang->invoke_method (ext_fn->ext_object, obj, args, nargs);
+}
+
+/* Returns true if EXT_FN is a method, 0 otherwise. */
+
+int
+ext_fn_is_method (struct ext_fn_descriptor *ext_fn)
+{
+ if (ext_fn != NULL)
+ return ext_fn->is_method;
+
+ return 0;
+}
+
+/* Frees a vector of ext_fn_descriptors VEC. */
+
+static void
+ext_fn_vec_free (void *vec)
+{
+ int i;
+ struct ext_fn_descriptor *ext_fn;
+ VEC (ext_fn_descriptor_p) *v = (VEC (ext_fn_descriptor_p) *) vec;
+
+ for (i = 0; VEC_iterate (ext_fn_descriptor_p, v, i, ext_fn); i++)
+ {
+ ext_fn->lang->free_ext_obj (ext_fn->ext_object);
+ xfree (ext_fn);
+ }
+}
+
+/* Return a cleanup object to free a vector VEC of extension function
+ descriptors. */
+
+struct cleanup *
+make_ext_fn_vec_cleanup (VEC (ext_fn_descriptor_p) *vec)
+{
+ return make_cleanup (ext_fn_vec_free, (void *) vec);
+}
diff --git a/gdb/ext-function.h b/gdb/ext-function.h
new file mode 100644
index 0000000..db05009
--- /dev/null
+++ b/gdb/ext-function.h
@@ -0,0 +1,75 @@
+/* Support for functions defined in extension languages.
+
+ Copyright (C) 2013 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#if !defined (EXT_FUNCTION_H)
+#define EXT_FUNCTION_H
+
+#include "vec.h"
+
+struct cleanup;
+struct value;
+struct type;
+struct ext_fn_descriptor;
+
+typedef struct ext_fn_descriptor *ext_fn_descriptor_p;
+DEF_VEC_P (ext_fn_descriptor_p);
+typedef VEC (ext_fn_descriptor_p) ext_fn_vec;
+
+typedef struct value* (invoke_method_ftype) (void *ext_obj,
+ struct value *,
+ struct value **, int nargs);
+
+typedef void * (clone_ext_obj_ftype) (void *ext_obj);
+
+typedef void (free_ext_obj_ftype) (void *ext_obj);
+
+typedef ext_fn_vec *(get_matching_ext_methods_ftype) (struct type *type,
+ const char *method);
+
+typedef struct type** (get_ext_fn_argtypes_ftype) (void *ext_obj, int *nargs);
+
+struct ext_func_ops
+ {
+ clone_ext_obj_ftype *clone_ext_object;
+ free_ext_obj_ftype *free_ext_obj;
+ get_matching_ext_methods_ftype *get_matching_ext_methods;
+ get_ext_fn_argtypes_ftype *get_ext_fn_argtypes;
+ invoke_method_ftype *invoke_method;
+ };
+
+extern void register_ext_lang (struct ext_func_ops *lang);
+
+extern struct value *ext_fn_invoke_method (struct ext_fn_descriptor *,
+ struct value *,
+ struct value **, int nargs);
+
+extern struct ext_fn_descriptor *ext_fn_clone (struct ext_fn_descriptor *);
+
+extern struct ext_fn_descriptor *new_ext_function (
+ const struct ext_func_ops *lang, int is_method, void *ext_obj);
+
+extern ext_fn_vec *get_matching_ext_methods (struct type *, const char *);
+
+extern struct type **ext_fn_get_argtypes (struct ext_fn_descriptor *, int *);
+
+extern int ext_fn_is_method (struct ext_fn_descriptor *);
+
+extern struct cleanup* make_ext_fn_vec_cleanup (VEC (ext_fn_descriptor_p) *vec);
+
+#endif /* EXT_FUNCTION_H */
diff --git a/gdb/python/lib/gdb/__init__.py b/gdb/python/lib/gdb/__init__.py
index 61f5b5e..d4d7ba7 100644
--- a/gdb/python/lib/gdb/__init__.py
+++ b/gdb/python/lib/gdb/__init__.py
@@ -67,6 +67,8 @@ pretty_printers = []
# Initial type printers.
type_printers = []
+# Initial debug_methods.
+debug_methods = []
# Initial frame filters.
frame_filters = {}
diff --git a/gdb/python/lib/gdb/command/debug_methods.py b/gdb/python/lib/gdb/command/debug_methods.py
new file mode 100644
index 0000000..1d2eb25
--- /dev/null
+++ b/gdb/python/lib/gdb/command/debug_methods.py
@@ -0,0 +1,218 @@
+# Debug method commands.
+# Copyright 2013 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import gdb
+import re
+
+"""GDB commands for working with debug-methods."""
+
+
+def parse_dm_command_args(arg):
+ """Parses the arguments passed to a debug method command.
+
+ Arguments:
+ arg: The argument string passed to a debug method command.
+
+ Returns:
+ A 2-tuple: (<locus matching regular expression>,
+ <name matching regular expression>)
+ """
+ argv = gdb.string_to_argv(arg)
+ argc = len(argv)
+ if argc > 2:
+ raise SyntaxError("Too many arguments to command.")
+ locus_re = ''
+ name_re = ''
+ if argc >= 1:
+ locus_re = argv[0]
+ if argc == 2:
+ name_re = argv[1]
+ return locus_re, name_re
+
+
+def get_matching_global_methods(locus_re, name_re):
+ """Returns a dict of matching globally registered debug methods.
+
+ Arguments:
+ locus_re: Even though only globally registered debug methods are
+ looked up, they will be looked up only if 'global' matches
+ LOCUS_RE.
+ name_re: The regular expression matching the names of debug methods.
+ Returns:
+ A dict of matching globally registered debug methods. The only key in
+ the dict will be 'global'.
+ """
+ dm = {}
+ if re.match(locus_re, 'global'):
+ matches = [m for m in gdb.debug_methods if re.match(name_re, m.name)]
+ if matches:
+ dm['global'] = matches
+ return dm
+
+
+def get_matching_methods_in_loci(loci, locus_re, name_re):
+ """Returns a dict of macthing registered debug methods in the LOCI.
+
+ Arguments:
+ loci: The list of loci to lookup matching debug methods in.
+ locus_re: Debug methods will be looked up in a particular locus only
+ if its filename matches the regular expression LOCUS_RE.
+ name_re: The regular expression to match the names of debug methods.
+
+ Returns:
+ A dict of matching debug methods. The keys of the dict are the
+ filenames of the loci the debug methods belong to.
+ """
+ dm = {}
+ for locus in loci:
+ if not re.match(locus_re, locus.filename):
+ continue
+ matches = [m for m in locus.debug_methods if re.match(name_re, m.name)]
+ if matches:
+ dm[locus.filename] = matches
+ return dm
+
+
+def print_dm_info(header, dm_dict):
+ """Print a dictionary of debug methods with an optional header."""
+ if not dm_dict:
+ return
+ # Global dm info will not have a header. Space accordingly.
+ space = ''
+ if header:
+ print '%s:' % header
+ space = ' '
+ for locus, dm_list in dm_dict.iteritems():
+ if not dm_list:
+ continue
+ print '%s%s:' % (space, locus)
+ for dm in dm_list:
+ if dm.enabled:
+ status = 'enabled'
+ else:
+ status = 'disabled'
+ print ' %s%s - %s' % (space, dm.name, status)
+
+
+def set_dm_status(dm_dict, status):
+ """Set the status (enabled/disabled) of a dictionary of debug methods."""
+ for locus, dm_list in dm_dict.iteritems():
+ if not dm_list:
+ continue
+ for dm in dm_list:
+ dm.enabled = status
+
+
+def set_matching_dm_status(arg, status):
+ """Set the status (enabled/disabled) of debug methods matching ARG.
+ This is a helper function for enable/disable commands. ARG is the
+ argument string passed to the commands.
+ """
+ locus_re, name_re = parse_dm_command_args(arg)
+ set_dm_status(get_matching_global_methods(locus_re, name_re), status)
+ set_dm_status(
+ get_matching_methods_in_loci(gdb.progspaces(), locus_re, name_re),
+ status)
+ set_dm_status(
+ get_matching_methods_in_loci(gdb.objfiles(), locus_re, name_re),
+ status)
+
+
+class InfoDebugMethod(gdb.Command):
+ """GDB command to list registered debug-methods.
+
+ Usage: info debug-method [locus-regexp [name-regexp]]
+
+ LOCUS-REGEXP is a regular expression matching the location of the debug
+ methods. If it is ommitted, all registered debug methods from all loci
+ are listed. A locus could be 'global', a regular expression matching
+ filenames of program spaces or a regular expression matching filenames of
+ objfiles.
+
+ NAME-REGEXP is a regular expression matching the names of debug methods
+ within a given locus. If this omitted for a specified locus, then all
+ registered debug methods in the locus are listed.
+ """
+
+ def __init__(self):
+ super(InfoDebugMethod, self).__init__("info debug-method",
+ gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ locus_re, name_re = parse_dm_command_args(arg)
+ print_dm_info(None, get_matching_global_methods(locus_re, name_re))
+ print_dm_info('progspaces', get_matching_methods_in_loci(
+ gdb.progspaces(), locus_re, name_re))
+ print_dm_info('objfiles', get_matching_methods_in_loci(
+ gdb.objfiles(), locus_re, name_re))
+
+
+class EnableDebugMethod(gdb.Command):
+ """GDB command to enable a specified (group of) debug method(s).
+
+ Usage: enable debug-method [locus-regexp [name-regexp]]
+
+ LOCUS-REGEXP is a regular expression matching the location of the debug
+ methods. If it is ommitted, all registered debug methods from all loci
+ are enabled. A locus could be 'global', a regular expression matching
+ filenames of program spaces or a regular expression matching filenames of
+ objfiles.
+
+ NAME-REGEXP is a regular expression matching the names of debug methods
+ within a given locus. If this omitted for a specified locus, then all
+ registered debug methods in the locus are enabled.
+ """
+
+ def __init__(self):
+ super(EnableDebugMethod, self).__init__("enable debug-method",
+ gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ set_matching_dm_status(arg, True)
+
+
+class DisableDebugMethod(gdb.Command):
+ """GDB command to disable a specified (group of) debug method(s).
+
+ Usage: disable debug-method [locus-regexp [name-regexp]]
+
+ LOCUS-REGEXP is a regular expression matching the location of the debug
+ methods. If it is ommitted, all registered debug methods from all loci
+ are disabled. A locus could be 'global', a regular expression matching
+ filenames of program spaces or a regular expression matching filenames of
+ objfiles.
+
+ NAME-REGEXP is a regular expression matching the names of debug methods
+ within a given locus. If this omitted for a specified locus, then all
+ registered debug methods in the locus are disabled.
+ """
+
+ def __init__(self):
+ super(DisableDebugMethod, self).__init__("disable debug-method",
+ gdb.COMMAND_DATA)
+
+ def invoke(self, arg, from_tty):
+ set_matching_dm_status(arg, False)
+
+
+def register_debug_method_commands():
+ """Installs the debug method commands."""
+ InfoDebugMethod()
+ EnableDebugMethod()
+ DisableDebugMethod()
+
+
+register_debug_method_commands()
diff --git a/gdb/python/lib/gdb/debugmethods.py b/gdb/python/lib/gdb/debugmethods.py
new file mode 100644
index 0000000..7cfd2e3
--- /dev/null
+++ b/gdb/python/lib/gdb/debugmethods.py
@@ -0,0 +1,160 @@
+# Python side of the support for debug methods.
+# Copyright (C) 2013 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""Utilities for defining debug methods"""
+
+import gdb
+import re
+
+
+class DebugMethod(object):
+ """Base class for all debug methods defined in Python.
+
+ A debug method defined in Python should be derived from this class. The
+ derived classes should override the methods 'match', 'get_argtypes' and
+ 'invoke'.
+
+ Internally, GDB first invokes the 'match' method to match the class type
+ and the method name. It next gets the argument types of these methods via
+ the 'get_argtypes' method to perform overload resolution. If GDB selects
+ to invoke a Python debugmethod, then it invokes it via the overridden
+ 'invoke' method.
+ """
+
+ def __init__(self, name):
+ """
+ Args:
+ name: An identifying name for the debug method.
+ """
+ self.name = name
+ self.enabled = True
+
+ def match(self, class_type, method_name):
+ """Match class type and method name.
+
+ Args:
+ class_type: The class type to match
+ method_name: The name of the method to match.
+ """
+ gdb.GdbError('ERROR: Invoking abstract method \'match\'.')
+
+ def get_argtypes(self, class_type, method_name):
+ """Return a list of types, as gdb.Type objects, of the arguments of
+ the debug method.
+
+ Args:
+ class_type: The gdb.Type value of the class on which the method
+ is defined.
+ method_name: The name of the method whose argument types are to
+ be returned.
+ """
+ gdb.GdbError('ERROR: Invoking abstract method \'get_argtypes\'.')
+
+ def invoke(self, obj, method_name, args):
+ """Invoke the debug method.
+
+ Args:
+ obj: The gdb.Value of the object on which the method is to be
+ invoked.
+ method_name: The name of the method being invoked.
+ args: The tuple of arguments to the method.
+ """
+ gdb.GdbError('ERROR: Invoking abstract method \'invoke\'.')
+
+
+class SimpleDebugMethod(DebugMethod):
+ """This is a utility class which does name match by class name of the
+ objects on which the debug methods are defined. For simple classes and
+ methods, one can choose to use this class. For complex debug methods,
+ which need to replace/implement template source methods on possibly
+ template classes, one should implement their own debug method classes
+ deriving from the base class 'DebugMethod'. See the py-debugmethods.py
+ in the testsuite/gdb.python directory of the GDB source tree for
+ examples.
+ """
+
+ def __init__(self, name, method_function, class_matcher, method_matcher,
+ *argtypes):
+ """
+ Args:
+ name: Name of the debug method.
+ method_function: A Python callable which would be called via the
+ invoke them of this class to actually invoke the
+ debug method. This callable should accept the
+ object (*this) as the first argument followed by
+ the rest of the arguments to the method.
+ class_matcher: A regular expression used to match the name of the
+ class whose method this debug method is
+ implementing/replacing.
+ method_matcher: A regular expression used to match the name of the
+ method this debug method is implementing/replacing.
+ argtypes: The gdb.Type objects corresponding to the arguments that
+ this debug method takes.
+ """
+ DebugMethod.__init__(self, name)
+ assert callable(method_function), (
+ 'The "method_function" argument to "SimpleDebugMethod" '
+ 'constructor should be a callable.')
+ self._method_function = method_function
+ self._class_matcher = class_matcher
+ self._method_matcher = method_matcher
+ self._argtypes = argtypes
+
+ def match(self, class_type, method_name):
+ cm = re.match(self._class_matcher, str(class_type.unqualified().tag))
+ mm = re.match(self._method_matcher, method_name)
+ if cm and mm:
+ return True
+ else:
+ return False
+
+ def get_argtypes(self, class_type, method_name):
+ return self._argtypes
+
+ def invoke(self, obj, method_name, args):
+ return self._method_function(obj, *args)
+
+
+def register_debug_methods(locus, debug_methods):
+ """Registers a list of DEBUG_METHODS with a LOCUS.
+
+ Arguments:
+ locus: The locus in which the debug methods should be registered. It
+ can be 'None' to indicate that the debug methods should be
+ registered globally. Or, it could be a gdb.Objfile or a
+ gdb.Progspace object in which the debug methods should be
+ registered.
+ debug_methods: The list of debug methods to register.
+ """
+ if not locus:
+ locus = gdb
+ if locus == gdb:
+ locus_name = 'global'
+ else:
+ locus_name = locus.filename
+ existing_method_list = []
+ for new_method in debug_methods:
+ assert isinstance(new_method, DebugMethod), (
+ 'Debug method objects should be instances of a subclass '
+ 'of "DebugMethod".')
+ for old_method in locus.debug_methods:
+ if new_method.name == old_method.name:
+ print ('WARNING: Replacing debug method with name "%s" in '
+ ' "%s".' % (old_method.name, locus_name))
+ existing_method_list.append(old_method)
+ for old_method in existing_method_list:
+ locus.debug_methods.remove(old_method)
+ locus.debug_methods.extend(debug_methods)
diff --git a/gdb/python/py-debugmethods.c b/gdb/python/py-debugmethods.c
new file mode 100644
index 0000000..8e3f45a
--- /dev/null
+++ b/gdb/python/py-debugmethods.c
@@ -0,0 +1,583 @@
+/* Support for debug methods in Python.
+
+ Copyright (C) 2013 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+#include "arch-utils.h"
+#include "ext-function.h"
+#include "objfiles.h"
+#include "value.h"
+#include "language.h"
+
+#include "python.h"
+
+#ifdef HAVE_PYTHON
+#include "python-internal.h"
+
+struct py_ext_object
+{
+ /* Holds an instance of the DebugMethod class. */
+ PyObject *object;
+
+ /* Holds the type of the 'this' object. */
+ PyObject *match_py_obj_type;
+
+ /* Holds the matching method name. */
+ const char *match_method;
+};
+
+static const char *enabled_field_name = "enabled";
+static const char *match_method_name = "match";
+static const char *get_argtypes_method_name = "get_argtypes";
+static const char *invoke_method_name = "invoke";
+
+static PyObject *match_method_pystring = NULL;
+static PyObject *get_argtypes_method_pystring = NULL;
+static PyObject *invoke_method_pystring = NULL;
+
+static struct ext_fn_descriptor *new_python_ext_method (PyObject *item,
+ PyObject *py_obj_type,
+ const char *method);
+
+/* Implementation of free_ext_obj_ftype. */
+
+static void
+py_free_ext_object (void *ext_object)
+{
+ struct py_ext_object *o = ext_object;
+
+ Py_XDECREF (o->object);
+ Py_XDECREF (o->match_py_obj_type);
+ xfree (o);
+}
+
+/* Implementation of clone_ext_obj_ftype. */
+
+static void *
+py_clone_ext_object (void *obj)
+{
+ struct py_ext_object *new_obj, *old_obj;
+
+ old_obj = (struct py_ext_object *) obj;
+
+ new_obj = XCNEW (struct py_ext_object);
+ new_obj->object = old_obj->object;
+ new_obj->match_py_obj_type = old_obj->match_py_obj_type;
+ new_obj->match_method = old_obj->match_method;
+
+ Py_XINCREF (new_obj->object);
+ Py_XINCREF (new_obj->match_py_obj_type);
+
+ return (void *) new_obj;
+}
+
+/* Returns true if gdb.Type object PY_OBJ_TYPE has a method defined in Python
+ with name METHOD_NAME. */
+
+static int
+is_matching_debug_method (PyObject *dm_obj, PyObject *py_obj_type,
+ const char *method_name)
+{
+ PyObject *method_name_pystring;
+ PyObject *match_method, *enabled_field, *match_result;
+ struct cleanup *cleanups;
+ int enabled, match;
+
+ if (method_name == NULL)
+ return 0;
+
+ cleanups = make_cleanup (null_cleanup, NULL);
+
+ enabled_field = PyObject_GetAttrString (dm_obj, enabled_field_name);
+ if (enabled_field == NULL)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return 0;
+ }
+ make_cleanup_py_decref (enabled_field);
+
+ enabled = PyObject_IsTrue (enabled_field);
+ if (enabled == -1)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return 0;
+ }
+ if (enabled == 0)
+ {
+ do_cleanups (cleanups);
+ return 0;
+ }
+
+ match_method = PyObject_GetAttrString (dm_obj, match_method_name);
+ if (match_method == NULL)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return 0;
+ }
+ make_cleanup_py_decref (match_method);
+ if (!PyCallable_Check (match_method))
+ {
+ warning (_("Attribute '%s' of a registered Python debug method is not "
+ "callable. Ignored!"), match_method_name);
+ do_cleanups (cleanups);
+
+ return 0;
+ }
+
+ method_name_pystring = PyString_FromString (method_name);
+ if (method_name_pystring == NULL)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return 0;
+ }
+ make_cleanup_py_decref (method_name_pystring);
+
+ match_result = PyObject_CallMethodObjArgs (dm_obj,
+ match_method_pystring,
+ py_obj_type,
+ method_name_pystring,
+ NULL);
+ if (match_result == NULL)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return 0;
+ }
+ make_cleanup_py_decref (match_result);
+
+ match = PyObject_IsTrue (match_result);
+ if (match == -1)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return 0;
+ }
+
+ do_cleanups (cleanups);
+
+ return match;
+}
+
+/* Implementation of get_matching_ext_methods_ftype.
+ Return a of vector methods with name METHOD_NAME defined in Python for
+ objects of type OBJ_TYPE. Returns NULL if no matches are found. */
+
+static VEC (ext_fn_descriptor_p) *
+py_debugmethod_name_match (struct type *obj_type, const char *method_name)
+{
+ struct cleanup *cleanups;
+ struct objfile *objfile;
+ VEC (ext_fn_descriptor_p) *method_vec = NULL;
+ PyObject *py_type, *py_progspace;
+ PyObject *py_debugmethod_list = NULL, *list_iter, *item;
+
+ if (obj_type == NULL || method_name == NULL)
+ return method_vec;
+
+ py_type = type_to_type_object (obj_type);
+ if (py_type == NULL)
+ return method_vec;
+ make_cleanup_py_decref (py_type);
+
+ /* Create an empyt list of debug methods. */
+ py_debugmethod_list = PyList_New (0);
+ if (py_debugmethod_list == NULL)
+ return NULL;
+
+ cleanups = ensure_python_env (get_current_arch (), current_language);
+
+ /* Gather debug methods registered with the object files. */
+ ALL_OBJFILES (objfile)
+ {
+ PyObject *py_objfile = objfile_to_objfile_object (objfile);
+ PyObject *objfile_dmlist, *temp;
+
+ if (py_objfile == NULL)
+ {
+ gdbpy_print_stack ();
+ continue;
+ }
+
+ objfile_dmlist = objfpy_get_debug_methods (py_objfile, NULL);
+ temp = py_debugmethod_list;
+ py_debugmethod_list = PySequence_Concat (temp, objfile_dmlist);
+ if (py_debugmethod_list == NULL)
+ py_debugmethod_list = temp;
+ else
+ Py_DECREF (temp);
+ Py_XDECREF (objfile_dmlist);
+ }
+
+ /* Gather debug methods registered with the current program space. */
+ py_progspace = pspace_to_pspace_object (current_program_space);
+ if (py_progspace != NULL)
+ {
+ PyObject *temp = py_debugmethod_list;
+ PyObject *pspace_dmlist = pspy_get_debug_methods (py_progspace, NULL);
+
+ py_debugmethod_list = PySequence_Concat (temp, pspace_dmlist);
+ if (py_debugmethod_list == NULL)
+ py_debugmethod_list = temp;
+ else
+ Py_DECREF (temp);
+ Py_XDECREF (pspace_dmlist);
+ }
+
+ /* Gather debug methods registered globally. */
+ if (gdb_python_module != NULL
+ && PyObject_HasAttrString (gdb_python_module, "debug_methods"))
+ {
+ PyObject *gdb_dmlist;
+ PyObject *temp = py_debugmethod_list;
+
+ gdb_dmlist = PyObject_GetAttrString (gdb_python_module, "debug_methods");
+ if (gdb_dmlist != NULL && PyList_Check (gdb_dmlist))
+ {
+ py_debugmethod_list = PySequence_Concat (temp, gdb_dmlist);
+ if (py_debugmethod_list == NULL)
+ py_debugmethod_list = temp;
+ else
+ Py_DECREF (temp);
+ Py_XDECREF (gdb_dmlist);
+ }
+ }
+
+ list_iter = PyObject_GetIter (py_debugmethod_list);
+ if (list_iter == NULL)
+ {
+ gdbpy_print_stack ();
+ Py_DECREF (py_debugmethod_list);
+ do_cleanups (cleanups);
+ return NULL;
+ }
+
+ while ((item = PyIter_Next (list_iter)))
+ {
+ if (is_matching_debug_method (item, py_type, method_name))
+ {
+ struct ext_fn_descriptor *ext_fn;
+
+ ext_fn = new_python_ext_method (item, py_type, method_name);
+ VEC_safe_push (ext_fn_descriptor_p, method_vec, ext_fn);
+ }
+ Py_DECREF (item);
+ }
+ Py_DECREF (list_iter);
+ /* Report any error that could have occurred while iterating. */
+ if (PyErr_Occurred ())
+ gdbpy_print_stack ();
+
+ Py_DECREF (py_debugmethod_list);
+ do_cleanups (cleanups);
+
+ return method_vec;
+}
+
+/* Implementation of get_ext_fn_argtypes_ftype.
+ Return an arry of argument types for extension encapsulated in EXT_OBJ.
+ NARGS contains the length of the array. */
+
+static struct type **
+py_ext_fn_get_argtypes (void *ext_obj, int *nargs)
+{
+ struct py_ext_object *ext_object = (struct py_ext_object *) ext_obj;
+ PyObject *debug_method_obj = ext_object->object;
+ PyObject *get_argtypes_method;
+ PyObject *py_argtype_list, *list_iter, *item, *method_name_pystring;
+ struct cleanup *cleanups;
+ struct type **type_array, *obj_type;
+ int i = 1;
+
+ /* Set nargs to 0 so that any premature return from this function returns
+ 0 arg types. */
+ *nargs = 0;
+
+ cleanups = ensure_python_env (get_current_arch (), current_language);
+
+ get_argtypes_method = PyObject_GetAttrString (debug_method_obj,
+ get_argtypes_method_name);
+ if (get_argtypes_method == NULL)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return NULL;
+ }
+ make_cleanup_py_decref (get_argtypes_method);
+
+ method_name_pystring = PyString_FromString (ext_object->match_method);
+ if (method_name_pystring == NULL)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return 0;
+ }
+ make_cleanup_py_decref (method_name_pystring);
+
+ if (!PyCallable_Check (get_argtypes_method))
+ {
+ warning (_("Attribute '%s' of a registered Python debug method is not "
+ "callable. Ignored!"), get_argtypes_method_name);
+ do_cleanups (cleanups);
+
+ return NULL;
+ }
+
+ py_argtype_list = PyObject_CallMethodObjArgs (debug_method_obj,
+ get_argtypes_method_pystring,
+ ext_object->match_py_obj_type,
+ method_name_pystring,
+ NULL);
+ if (py_argtype_list == NULL)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return NULL;
+ }
+ make_cleanup_py_decref (py_argtype_list);
+
+ list_iter = PyObject_GetIter (py_argtype_list);
+ if (list_iter == NULL)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return NULL;
+ }
+ make_cleanup_py_decref (list_iter);
+
+ /* Include the 'this' argument in the size. */
+ type_array = XCNEWVEC (struct type *, PyList_GET_SIZE (py_argtype_list) + 1);
+ while ((item = PyIter_Next (list_iter)))
+ {
+ struct type *arg_type = type_object_to_type (item);
+
+ Py_DECREF (item);
+
+ if (arg_type == NULL)
+ {
+ i = -1;
+ break;
+ }
+
+ type_array[i] = arg_type;
+ i++;
+ }
+ if (PyErr_Occurred () || i == -1)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+ xfree (type_array);
+
+ return NULL;
+ }
+
+ /* Add the type of 'this' as the first argument. */
+ obj_type = type_object_to_type (ext_object->match_py_obj_type);
+ type_array[0] = make_cv_type (1, 0, lookup_pointer_type (obj_type), NULL);
+ *nargs = i;
+
+ do_cleanups (cleanups);
+
+ return type_array;
+}
+
+/* Implementation of invoke_method_ftype.
+ Invokes a method defined in Python. The value returned by the method is
+ returned. NULL is returned in case of errors. */
+
+static struct value *
+py_ext_fn_invoke_method (void *ext_obj, struct value *obj, struct value **args,
+ int nargs)
+{
+ int i;
+ struct cleanup *cleanups;
+ PyObject *py_value_obj, *py_arg_tuple, *py_result, *debug_method_obj;
+ PyObject *invoke_method, *method_name_pystring;
+ struct value *result = NULL;
+ struct py_ext_object *py_ext_obj = ext_obj;
+
+ cleanups = ensure_python_env (get_current_arch (), current_language);
+
+ debug_method_obj = py_ext_obj->object;
+
+ invoke_method = PyObject_GetAttrString (debug_method_obj,
+ invoke_method_name);
+ if (invoke_method == NULL)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return NULL;
+ }
+ make_cleanup_py_decref (invoke_method);
+
+ method_name_pystring = PyString_FromString (py_ext_obj->match_method);
+ if (method_name_pystring == NULL)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return 0;
+ }
+ make_cleanup_py_decref (method_name_pystring);
+
+ if (!PyCallable_Check (invoke_method))
+ {
+ warning (_("Attribute '%s' of a registered Python debug method is not "
+ "callable. Ignored!"), invoke_method_name);
+ do_cleanups (cleanups);
+
+ return NULL;
+ }
+
+ py_value_obj = value_to_value_object (obj);
+ if (py_value_obj == NULL)
+ {
+ do_cleanups (cleanups);
+ return NULL;
+ }
+ make_cleanup_py_decref (py_value_obj);
+
+ py_arg_tuple = PyTuple_New (nargs);
+ if (py_arg_tuple == NULL)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return NULL;
+ }
+ make_cleanup_py_decref (py_arg_tuple);
+
+ for (i = 0; i < nargs; i++)
+ {
+ PyObject *py_value_arg = value_to_value_object (args[i]);
+
+ if (py_value_arg == NULL)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+ return NULL;
+ }
+
+ if (PyTuple_SetItem (py_arg_tuple, i, py_value_arg))
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return NULL;
+ }
+ }
+
+ py_result = PyObject_CallMethodObjArgs (debug_method_obj,
+ invoke_method_pystring,
+ py_value_obj,
+ method_name_pystring,
+ py_arg_tuple, NULL);
+ if (py_result == NULL)
+ {
+ gdbpy_print_stack ();
+ do_cleanups (cleanups);
+
+ return NULL;
+ }
+ make_cleanup_py_decref (py_result);
+
+ /* Check that a debug method did not return None. */
+ if (py_result != Py_None)
+ {
+ result = convert_value_from_python (py_result);
+ if (result == NULL)
+ {
+ gdbpy_print_stack ();
+ }
+ }
+ else
+ result = allocate_value (lookup_typename (python_language, python_gdbarch,
+ "void", NULL, 0));
+
+ do_cleanups (cleanups);
+
+ return result;
+}
+
+static struct ext_func_ops python_ext_lang = {
+ py_clone_ext_object,
+ py_free_ext_object,
+ py_debugmethod_name_match,
+ py_ext_fn_get_argtypes,
+ py_ext_fn_invoke_method
+};
+
+/* Creates a new python ext_function_descriptor. DEBUG_METHOD is the
+ debug method object. PY_OBJ_TYPE is a gdb.Type value corresponding to the
+ type of the object on which the debug method with name METHOD will be
+ invoked. */
+
+static struct ext_fn_descriptor *
+new_python_ext_method (PyObject *debug_method, PyObject *py_obj_type,
+ const char *method)
+{
+ struct py_ext_object *ext_object;
+
+ ext_object = XCNEW (struct py_ext_object);
+ ext_object->object = debug_method;
+ ext_object->match_py_obj_type = py_obj_type;
+ ext_object->match_method = method;
+
+ Py_XINCREF (ext_object->object);
+ Py_XINCREF (ext_object->match_py_obj_type);
+
+ return new_ext_function (&python_ext_lang, 1, ext_object);
+}
+
+/* Initializes the Python debug method support. */
+
+int
+gdbpy_initialize_debugmethods (void)
+{
+ register_ext_lang (&python_ext_lang);
+
+ match_method_pystring = PyString_FromString (match_method_name);
+ if (match_method_pystring == NULL)
+ return -1;
+
+ invoke_method_pystring = PyString_FromString (invoke_method_name);
+ if (invoke_method_pystring == NULL)
+ return -1;
+
+ get_argtypes_method_pystring = PyString_FromString (get_argtypes_method_name);
+ if (get_argtypes_method_pystring == NULL)
+ return -1;
+
+ return 1;
+}
+
+#endif /* HAVE_PYTHON */
diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c
index 9bbd4c2..4aa0a1a 100644
--- a/gdb/python/py-objfile.c
+++ b/gdb/python/py-objfile.c
@@ -37,6 +37,9 @@ typedef struct
PyObject *frame_filters;
/* The type-printer list. */
PyObject *type_printers;
+
+ /* The debug method list. */
+ PyObject *debug_methods;
} objfile_object;
static PyTypeObject objfile_object_type
@@ -67,6 +70,7 @@ objfpy_dealloc (PyObject *o)
Py_XDECREF (self->printers);
Py_XDECREF (self->frame_filters);
Py_XDECREF (self->type_printers);
+ Py_XDECREF (self->debug_methods);
Py_TYPE (self)->tp_free (self);
}
@@ -99,6 +103,13 @@ objfpy_new (PyTypeObject *type, PyObject *args, PyObject *keywords)
Py_DECREF (self);
return NULL;
}
+
+ self->debug_methods = PyList_New (0);
+ if (self->debug_methods == NULL)
+ {
+ Py_DECREF (self);
+ return NULL;
+ }
}
return (PyObject *) self;
}
@@ -193,6 +204,17 @@ objfpy_get_type_printers (PyObject *o, void *ignore)
return self->type_printers;
}
+/* Get the 'debug_methods' attribute. */
+
+PyObject *
+objfpy_get_debug_methods (PyObject *o, void *ignore)
+{
+ objfile_object *self = (objfile_object *) o;
+
+ Py_INCREF (self->debug_methods);
+ return self->debug_methods;
+}
+
/* Set the 'type_printers' attribute. */
static int
@@ -292,6 +314,13 @@ objfile_to_objfile_object (struct objfile *objfile)
return NULL;
}
+ object->debug_methods = PyList_New (0);
+ if (object->debug_methods == NULL)
+ {
+ Py_DECREF (object);
+ return NULL;
+ }
+
set_objfile_data (objfile, objfpy_objfile_data_key, object);
}
}
@@ -333,6 +362,8 @@ static PyGetSetDef objfile_getset[] =
objfpy_set_frame_filters, "Frame Filters.", NULL },
{ "type_printers", objfpy_get_type_printers, objfpy_set_type_printers,
"Type printers.", NULL },
+ { "debug_methods", objfpy_get_debug_methods, NULL,
+ "Debug methods.", NULL },
{ NULL }
};
diff --git a/gdb/python/py-progspace.c b/gdb/python/py-progspace.c
index 910c6a3..d425159 100644
--- a/gdb/python/py-progspace.c
+++ b/gdb/python/py-progspace.c
@@ -39,6 +39,9 @@ typedef struct
PyObject *frame_filters;
/* The type-printer list. */
PyObject *type_printers;
+
+ /* The debug method list. */
+ PyObject *debug_methods;
} pspace_object;
static PyTypeObject pspace_object_type
@@ -75,6 +78,7 @@ pspy_dealloc (PyObject *self)
Py_XDECREF (ps_self->printers);
Py_XDECREF (ps_self->frame_filters);
Py_XDECREF (ps_self->type_printers);
+ Py_XDECREF (ps_self->debug_methods);
Py_TYPE (self)->tp_free (self);
}
@@ -107,6 +111,13 @@ pspy_new (PyTypeObject *type, PyObject *args, PyObject *keywords)
Py_DECREF (self);
return NULL;
}
+
+ self->debug_methods = PyList_New (0);
+ if (self->debug_methods == NULL)
+ {
+ Py_DECREF (self);
+ return NULL;
+ }
}
return (PyObject *) self;
}
@@ -201,6 +212,17 @@ pspy_get_type_printers (PyObject *o, void *ignore)
return self->type_printers;
}
+/* Get the 'debug_methods' attribute. */
+
+PyObject *
+pspy_get_debug_methods (PyObject *o, void *ignore)
+{
+ pspace_object *self = (pspace_object *) o;
+
+ Py_INCREF (self->debug_methods);
+ return self->debug_methods;
+}
+
/* Set the 'type_printers' attribute. */
static int
@@ -288,6 +310,13 @@ pspace_to_pspace_object (struct program_space *pspace)
return NULL;
}
+ object->debug_methods = PyList_New (0);
+ if (object->debug_methods == NULL)
+ {
+ Py_DECREF (object);
+ return NULL;
+ }
+
set_program_space_data (pspace, pspy_pspace_data_key, object);
}
}
@@ -320,6 +349,8 @@ static PyGetSetDef pspace_getset[] =
"Frame filters.", NULL },
{ "type_printers", pspy_get_type_printers, pspy_set_type_printers,
"Type printers.", NULL },
+ { "debug_methods", pspy_get_debug_methods, NULL,
+ "Debug methods.", NULL },
{ NULL }
};
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 7d8c4ad..62eb49b 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -316,11 +316,13 @@ PyObject *pspace_to_pspace_object (struct program_space *)
CPYCHECKER_RETURNS_BORROWED_REF;
PyObject *pspy_get_printers (PyObject *, void *);
PyObject *pspy_get_frame_filters (PyObject *, void *);
+PyObject *pspy_get_debug_methods (PyObject *, void *);
PyObject *objfile_to_objfile_object (struct objfile *)
CPYCHECKER_RETURNS_BORROWED_REF;
PyObject *objfpy_get_printers (PyObject *, void *);
PyObject *objfpy_get_frame_filters (PyObject *, void *);
+PyObject *objfpy_get_debug_methods (PyObject *, void *);
PyObject *gdbarch_to_arch_object (struct gdbarch *gdbarch);
@@ -401,6 +403,8 @@ int gdbpy_initialize_new_objfile_event (void)
CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
int gdbpy_initialize_arch (void)
CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
+int gdbpy_initialize_debugmethods (void)
+ CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
struct cleanup *make_cleanup_py_decref (PyObject *py);
struct cleanup *make_cleanup_py_xdecref (PyObject *py);
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 7cdf8a7..f5a0132 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1687,7 +1687,8 @@ message == an error message without a stack will be printed."),
|| gdbpy_initialize_exited_event () < 0
|| gdbpy_initialize_thread_event () < 0
|| gdbpy_initialize_new_objfile_event () < 0
- || gdbpy_initialize_arch () < 0)
+ || gdbpy_initialize_arch () < 0
+ || gdbpy_initialize_debugmethods () < 0)
goto fail;
observer_attach_before_prompt (before_prompt_hook);
diff --git a/gdb/testsuite/gdb.python/py-debugmethods.cc b/gdb/testsuite/gdb.python/py-debugmethods.cc
new file mode 100644
index 0000000..f292a62
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-debugmethods.cc
@@ -0,0 +1,173 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2013 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <iostream>
+
+using namespace std;
+
+namespace dop
+{
+
+class A
+{
+ public:
+ int a;
+ int array [10];
+ virtual ~A ();
+ int operator+ (const A &obj);
+ virtual int operator- (const A &obj);
+ virtual int geta ();
+};
+
+A::~A () { }
+
+int
+A::operator+ (const A &obj)
+{
+ cout << "From CC <A_plus_A>:" << endl;
+ return a + obj.a;
+}
+
+int A::operator- (const A &obj)
+{
+ cout << "From CC <A_minus_A>:" << endl;
+ return a - obj.a;
+}
+
+int A::geta (void)
+{
+ cout << "From CC geta:" << endl;
+ return a;
+}
+
+class B : public A
+{
+ public:
+ virtual int operator* (const B &obj);
+};
+
+int
+B::operator* (const B &obj)
+{
+ cout << "From CC <B_star_B>:" << endl;
+ return a * obj.a;
+}
+
+typedef B Bt;
+
+typedef Bt Btt;
+
+class C : public Bt
+{
+ public:
+ virtual ~C();
+};
+
+C::~C () { }
+
+class D : public B
+{
+ public:
+ virtual int operator* (const B &obj);
+};
+
+int
+D::operator* (const B &obj)
+{
+ cout << "From CC <D_star_D>:" << endl;
+ return a * obj.a;
+}
+
+template <typename T>
+class G
+{
+ public:
+ template <typename T1>
+ int size_diff ();
+
+ template <int M>
+ int size_mul ();
+
+ template <typename T1>
+ T mul(const T1 t1);
+
+ public:
+ T t;
+};
+
+template <typename T>
+template <typename T1>
+int
+G<T>::size_diff ()
+{
+ cout << "From CC G<>::size_diff:" << endl;
+ return sizeof (T1) - sizeof (T);
+}
+
+template <typename T>
+template <int M>
+int
+G<T>::size_mul ()
+{
+ cout << "From CC G<>::size_mul:" << endl;
+ return M * sizeof (T);
+}
+
+template <typename T>
+template <typename T1>
+T
+G<T>::mul (const T1 t1)
+{
+ cout << "From CC G<>::mul:" << endl;
+ return t1 * t;
+}
+
+}
+
+using namespace dop;
+
+int main(void)
+{
+ A a1, a2;
+ a1.a = 5;
+ a2.a = 10;
+ C c1;
+ c1.a = 20;
+ B b1;
+ b1.a = 30;
+ D d1;
+ d1.a = 50;
+ Bt bt;
+ bt.a = 40;
+ A &ref_c = c1;
+ B &ref_d = d1;
+ Btt btt;
+ btt.a = -5;
+ G<int> g;
+ g.t = 5;
+
+ int diff = g.size_diff<float> ();
+ int smul = g.size_mul<2> ();
+ int mul = g.mul (1.0);
+
+ for (int i = 0; i < 10; i++)
+ {
+ a1.array[i] = a2.array[i] = i;
+ }
+
+ return 0; /* Break here. */
+}
diff --git a/gdb/testsuite/gdb.python/py-debugmethods.exp b/gdb/testsuite/gdb.python/py-debugmethods.exp
new file mode 100644
index 0000000..802fdba
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-debugmethods.exp
@@ -0,0 +1,112 @@
+# Copyright 2013 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite. It tests the debug methods
+# feature in the Python extension language.
+
+load_lib gdb-python.exp
+
+if { [skip_cplus_tests] } { continue }
+
+standard_testfile py-debugmethods.cc
+
+if {[prepare_for_testing $testfile.exp $testfile $srcfile {debug c++}]} {
+ return -1
+}
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+if ![runto_main] {
+ return -1
+}
+
+set debug_methods_script "${srcdir}/${subdir}/${testfile}.py"
+
+gdb_breakpoint [gdb_get_line_number "Break here."]
+gdb_continue_to_breakpoint "Break here" ".*Break here.*"
+
+# Tests before loading the debug methods.
+gdb_test "p a1 + a2" "From CC <A_plus_A>.*15" "Before: a1 + a2"
+gdb_test "p a2 - a1" "From CC <A_minus_A>.*5" "Before: a1 - a2"
+gdb_test "p b1 - a1" "From CC <A_minus_A>.*25" "Before: b1 - a1"
+gdb_test "p c1 - a1" "From CC <A_minus_A>.*15" "Before: c1 - a1"
+gdb_test "p c1 - c1" "From CC <A_minus_A>.*0" "Before: c1 - c1"
+gdb_test "p a1.geta()" "From CC geta.*5" "Before: a1.geta()"
+gdb_test "p ref_c - a1" "From CC <A_minus_A>.*15" "Before: ref_c - a1"
+gdb_test "p ref_c - c1" "From CC <A_minus_A>.*0" "Before: ref_c - c1"
+gdb_test "p b1 * b1" "From CC <B_star_B>.*900" "Before: b1 * b1"
+gdb_test "p ref_c * b1" "No symbol.*" "Before: ref_c * b1"
+gdb_test "p ref_d * b1" "From CC <D_star_D>.*1500" "Before: ref_d * b1"
+gdb_test "p bt * c1" "From CC <B_star_B>.*800" "Before: bt * c1"
+gdb_test "p ++a1" "No symbol.*" "Before: ++a1"
+gdb_test "p a1.getarrayind(5)" "Couldn't find method.*" \
+ "Before: a1.getarrayind(5)"
+gdb_test "p g.size_diff<float>()" "From CC G<>::size_diff.*" \
+ "Before: g.size_diff<float>()"
+gdb_test "p g.size_diff<unsigned long>()" "Couldn't find method.*" \
+ "Before: g.size_diff<unsigned long>()"
+gdb_test "p g.size_mul<2>()" "From CC G<>::size_mul.*" \
+ "Before: g.size_mul<2>()"
+gdb_test "p g.size_mul<5>()" "Couldn't find method.*" \
+ "Before: g.size_mul<5>()"
+gdb_test "p g.mul<double>(2.0)" "From CC G<>::mul.*" \
+ "Before: g.mul<double>(2.0)"
+gdb_test "p g.mul<char>('a')" "Couldn't find method.*" \
+ "Before: g.mul<char>('a')"
+
+# Load the script which adds the debug methods.
+gdb_test_no_output "source ${debug_methods_script}" "load the script file"
+
+# Tests after loading debug methods.
+gdb_test "p a1 + a2" "From Python <A_plus_A>.*15" "After: a1 + a2"
+gdb_test "p a2 - a1" "From CC <A_minus_A>.*5" "After: a1 - a2"
+gdb_test "p b1 - a1" "From CC <A_minus_A>.*25" "After: b1 - a2"
+gdb_test "p c1 - a1" "From CC <A_minus_A>.*15" "After: c1 - a1"
+gdb_test "p c1 - c1" "From Python <C_minus_C>.*0" "After: c1 - c1"
+gdb_test "p a1.geta()" "From Python <A_geta>.*5" "After: a1.geta()"
+gdb_test "p ref_c - a1" "From CC <A_minus_A>.*15" "After: ref_c - a1"
+gdb_test "p ref_c - c1" "From Python <C_minus_C>.*0" "After: ref_c - c1"
+gdb_test "p b1 * b1" "From Python <B_star_B>.*900" "After: b1 * b1"
+gdb_test "p ref_c * b1" "No symbol.*" "After: ref_c * b1 failure"
+gdb_test "p ref_d * b1" "From CC <D_star_D>.*1500" "After: ref_d * b1"
+gdb_test "p bt * c1" "From Python <B_star_B>.*800" "After: bt * c1"
+gdb_test "p ++a1" "From Python <plus_plus_A>.*6" "After: ++a1"
+gdb_test "p a1.getarrayind(5)" "From Python <A_getarrayind>.*5" \
+ "After: a1.getarrayind(5)"
+gdb_test "p g.size_diff<float> ()" "From Python G<>::size_diff.*" \
+ "After: g.size_diff<float>()"
+gdb_test "p g.size_diff< unsigned long >()" "From Python G<>::size_diff.*" \
+ "After: g.size_diff<unsigned long>()"
+gdb_test "p g.size_mul<2>()" "From Python G<>::size_mul.*" \
+ "After: g.size_mul<2>()"
+gdb_test "p g.size_mul< 5 >()" "From Python G<>::size_mul.*" \
+ "After: g.size_mul< 5 >()"
+gdb_test "p g.mul<double>(2.0)" "From Python G<>::mul.*" \
+ "After: g.mul<double>(2.0)"
+gdb_test "p g.mul<char>('a')" "From Python G<>::mul.*" \
+ "After: g.mul<char>('a')"
+
+# Tests for 'disable/enable debug-method' command.
+gdb_test_no_output "disable debug-method .*debugmethods G_mul" "Disable G_mul"
+gdb_test "p g.mul<char>('a')" "Couldn't find method.*" \
+ "After disabling g.mul<char>('a')"
+gdb_test_no_output "enable debug-method .*debugmethods G_mul" "Enable G_mul"
+gdb_test "p g.mul<char>('a')" "From Python G<>::mul.*" \
+ "After enabling g.mul<char>('a')"
+
+# Test for 'info debug-methods' command
+gdb_test "info debug-method global plus" "global.*plus_plus_A - enabled" \
+ "info debug-method global plus"
diff --git a/gdb/testsuite/gdb.python/py-debugmethods.py b/gdb/testsuite/gdb.python/py-debugmethods.py
new file mode 100644
index 0000000..8147611
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-debugmethods.py
@@ -0,0 +1,180 @@
+# Copyright 2013 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite. It implements debug methods
+# in the Python extension language.
+
+import gdb
+import re
+
+from gdb.debugmethods import DebugMethod, SimpleDebugMethod
+
+def A_plus_A(obj, opr):
+ print 'From Python <A_plus_A>:'
+ return obj['a'] + opr['a']
+
+def plus_plus_A(obj):
+ print 'From Python <plus_plus_A>:'
+ return obj['a'] + 1
+
+def C_minus_C(obj, opr):
+ print 'From Python <C_minus_C>:'
+ return obj['a'] - opr['a']
+
+def B_star_B(obj, opr):
+ print 'From Python <B_star_B>:'
+ return obj['a'] * opr['a']
+
+def A_geta(obj):
+ print 'From Python <A_geta>:'
+ return obj['a']
+
+def A_getarrayind(obj, index):
+ print 'From Python <A_getarrayind>:'
+ return obj['array'][index]
+
+
+type_A = gdb.parse_and_eval('(dop::A *) 0').type.target()
+type_B = gdb.parse_and_eval('(dop::B *) 0').type.target()
+type_C = gdb.parse_and_eval('(dop::C *) 0').type.target()
+type_int = gdb.parse_and_eval('(int *) 0').type.target()
+
+
+class G_size_diff(DebugMethod):
+ def __init__(self):
+ DebugMethod.__init__(self, 'G_size_diff')
+
+ def match(self, class_type, method_name):
+ if not re.match('^dop::G<[ ]*[_a-zA-Z][ _a-zA-Z0-9]*>$',
+ class_type.unqualified().tag):
+ return False
+ if not re.match('^size_diff<[ ]*[_a-zA-Z][ _a-zA-Z0-9]*>$',
+ method_name):
+ return False
+ return True
+
+ def get_argtypes(self, class_type, method_name):
+ return []
+
+ def invoke(self, obj, method_name, args):
+ t1_search = re.search('<[ ]*[_a-zA-Z][ _a-zA-Z0-9]*>', method_name)
+ if not t1_search:
+ raise gdb.GdbError(
+ 'Unable to find template argument from %s.' % method_name)
+ t1_name = t1_search.group(0)[1:-1]
+ try:
+ t1_type = gdb.lookup_type(t1_name)
+ except Exception:
+ raise gdb.GdbError('Unknown template type %s.' % t1_name)
+ print 'From Python G<>::size_diff<%s>' % t1_name
+ return t1_type.sizeof - obj['t'].type.sizeof
+
+
+class G_size_mul(DebugMethod):
+ def __init__(self):
+ DebugMethod.__init__(self, 'G_size_mull')
+
+ def match(self, class_type, method_name):
+ if not re.match('^dop::G<[ ]*[_a-zA-Z][ _a-zA-Z0-9]*>$',
+ class_type.unqualified().tag):
+ return False
+ if not re.match('^size_mul<[ ]*[0-9]+[ ]*>$',
+ method_name):
+ return False
+ return True
+
+ def get_argtypes(self, class_type, method_name):
+ return []
+
+ def invoke(self, obj, method_name, args):
+ m_search = re.search('<[ ]*[0-9]+[ ]*>', method_name)
+ if not m_search:
+ raise gdb.GdbError(
+ 'Unable to find template argument from %s.' % method_name)
+ m_val = int(m_search.group(0)[1:-1])
+ print 'From Python G<>::size_mul<%d>' % m_val
+ return m_val * obj['t'].type.sizeof
+
+
+class G_mul(DebugMethod):
+ def __init__(self):
+ DebugMethod.__init__(self, 'G_mul')
+
+ def match(self, class_type, method_name):
+ if not re.match('^dop::G<[ ]*[_a-zA-Z][ _a-zA-Z0-9]*>$',
+ class_type.unqualified().tag):
+ return False
+ if not re.match('^mul<[ ]*[_a-zA-Z][ _a-zA-Z0-9]*>$',
+ method_name):
+ return False
+ return True
+
+ def get_argtypes(self, class_type, method_name):
+ t1_search = re.search('<[ ]*[_a-zA-Z][ _a-zA-Z0-9]*>', method_name)
+ if not t1_search:
+ raise gdb.GdbError(
+ 'Unable to find template argument from %s.' % method_name)
+ t1_name = t1_search.group(0)[1:-1]
+ try:
+ t1_type = gdb.lookup_type(t1_name)
+ except Exception:
+ raise gdb.GdbError('Unknown template type %s.' % t1_name)
+ return [t1_type]
+
+ def invoke(self, obj, method_name, args):
+ print 'From Python G<>::mul<%s>' % args[0].type
+ return args[0] * obj['t']
+
+
+global_dm_list = [
+ SimpleDebugMethod('A_plus_A',
+ A_plus_A,
+ '^dop::A$',
+ 'operator\+',
+ # This is a replacement, hence match the arg type
+ # exactly!
+ type_A.const().reference()),
+ SimpleDebugMethod('plus_plus_A',
+ plus_plus_A,
+ '^dop::A$',
+ 'operator\+\+'),
+ SimpleDebugMethod('C_minus_C',
+ C_minus_C,
+ '^dop::C$',
+ 'operator\-',
+ type_C),
+ SimpleDebugMethod('B_star_B',
+ B_star_B,
+ '^dop::B$',
+ 'operator\*',
+ # This is a replacement, hence match the arg type
+ # exactly!
+ type_B.const().reference()),
+ SimpleDebugMethod('A_geta',
+ A_geta,
+ '^dop::A$',
+ '^geta$'),
+ SimpleDebugMethod('A_getarrayind',
+ A_getarrayind,
+ '^dop::A$',
+ '^getarrayind$',
+ type_int),
+]
+
+pspace_dm_list = [G_size_diff(), G_size_mul(), G_mul()]
+
+gdb.debugmethods.register_debug_methods(gdb, global_dm_list)
+gdb.debugmethods.register_debug_methods(gdb.current_progspace(),
+ pspace_dm_list)
diff --git a/gdb/valarith.c b/gdb/valarith.c
index 49c8bdc..e486b24 100644
--- a/gdb/valarith.c
+++ b/gdb/valarith.c
@@ -30,6 +30,7 @@
#include <math.h>
#include "infcall.h"
#include "exceptions.h"
+#include "ext-function.h"
/* Define whether or not the C operator '/' truncates towards zero for
differently signed operands (truncation direction is undefined in C). */
@@ -285,21 +286,27 @@ unop_user_defined_p (enum exp_opcode op, struct value *arg1)
explicitly, and perform correct overload resolution in all of the above
situations or combinations thereof. */
-static struct value *
+static void
value_user_defined_cpp_op (struct value **args, int nargs, char *operator,
+ struct value **src_fn,
+ struct ext_fn_descriptor **ext_fn,
int *static_memfuncp)
{
struct symbol *symp = NULL;
struct value *valp = NULL;
+ struct ext_fn_descriptor *ext_fnp = NULL;
find_overload_match (args, nargs, operator, BOTH /* could be method */,
&args[0] /* objp */,
NULL /* pass NULL symbol since symbol is unknown */,
- &valp, &symp, static_memfuncp, 0);
+ &valp, &symp, &ext_fnp, static_memfuncp, 0);
if (valp)
- return valp;
+ {
+ *src_fn = valp;
+ return;
+ }
if (symp)
{
@@ -307,7 +314,14 @@ value_user_defined_cpp_op (struct value **args, int nargs, char *operator,
expect a reference as its first argument
rather the explicit structure. */
args[0] = value_ind (args[0]);
- return value_of_variable (symp, 0);
+ *src_fn = value_of_variable (symp, 0);
+ return;
+ }
+
+ if (ext_fnp)
+ {
+ *ext_fn = ext_fnp;
+ return;
}
error (_("Could not find %s."), operator);
@@ -316,19 +330,22 @@ value_user_defined_cpp_op (struct value **args, int nargs, char *operator,
/* Lookup user defined operator NAME. Return a value representing the
function, otherwise return NULL. */
-static struct value *
+static void
value_user_defined_op (struct value **argp, struct value **args, char *name,
- int *static_memfuncp, int nargs)
+ int *static_memfuncp, int nargs,
+ struct value **src_fn, struct ext_fn_descriptor **ext_fn)
{
struct value *result = NULL;
if (current_language->la_language == language_cplus)
- result = value_user_defined_cpp_op (args, nargs, name, static_memfuncp);
+ value_user_defined_cpp_op (args, nargs, name, src_fn, ext_fn,
+ static_memfuncp);
else
- result = value_struct_elt (argp, args, name, static_memfuncp,
- "structure");
-
- return result;
+ {
+ result = value_struct_elt (argp, args, name, static_memfuncp,
+ "structure");
+ *src_fn = result;
+ }
}
/* We know either arg1 or arg2 is a structure, so try to find the right
@@ -345,6 +362,7 @@ value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op,
enum exp_opcode otherop, enum noside noside)
{
struct value **argvec;
+ struct ext_fn_descriptor *ext_fn = NULL;
char *ptr;
char tstr[13];
int static_memfuncp;
@@ -359,6 +377,7 @@ value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op,
error (_("Can't do that binary op on that type")); /* FIXME be explicit */
argvec = (struct value **) alloca (sizeof (struct value *) * 4);
+ argvec[0] = NULL;
argvec[1] = value_addr (arg1);
argvec[2] = arg2;
argvec[3] = 0;
@@ -471,8 +490,8 @@ value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op,
error (_("Invalid binary operation specified."));
}
- argvec[0] = value_user_defined_op (&arg1, argvec + 1, tstr,
- &static_memfuncp, 2);
+ value_user_defined_op (&arg1, argvec + 1, tstr, &static_memfuncp, 2,
+ &argvec[0], &ext_fn);
if (argvec[0])
{
@@ -492,6 +511,29 @@ value_x_binop (struct value *arg1, struct value *arg2, enum exp_opcode op,
return call_function_by_hand (argvec[0], 2 - static_memfuncp,
argvec + 1);
}
+ if (ext_fn)
+ {
+ if (ext_fn_is_method (ext_fn))
+ {
+ struct value *ret_val = ext_fn_invoke_method (ext_fn, arg1, &arg2, 1);
+
+ if (ret_val == NULL)
+ error (_("Error invoking debug method implementation for "
+ "method %s"), tstr);
+
+ xfree (ext_fn);
+
+ return ret_val;
+ }
+ else
+ {
+ /* This else should not be taken as only debug methods
+ are supported currently. */
+ internal_error (__FILE__, __LINE__,
+ _("Invoking an extension language "
+ "function (not a method)."));
+ }
+ }
throw_error (NOT_FOUND_ERROR,
_("member function %s not found"), tstr);
#ifdef lint
@@ -510,6 +552,7 @@ value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside)
{
struct gdbarch *gdbarch = get_type_arch (value_type (arg1));
struct value **argvec;
+ struct ext_fn_descriptor *ext_fn;
char *ptr;
char tstr[13], mangle_tstr[13];
int static_memfuncp, nargs;
@@ -523,6 +566,7 @@ value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside)
error (_("Can't do that unary op on that type")); /* FIXME be explicit */
argvec = (struct value **) alloca (sizeof (struct value *) * 4);
+ argvec[0] = NULL;
argvec[1] = value_addr (arg1);
argvec[2] = 0;
@@ -574,8 +618,8 @@ value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside)
error (_("Invalid unary operation specified."));
}
- argvec[0] = value_user_defined_op (&arg1, argvec + 1, tstr,
- &static_memfuncp, nargs);
+ value_user_defined_op (&arg1, argvec + 1, tstr, &static_memfuncp, nargs,
+ &argvec[0], &ext_fn);
if (argvec[0])
{
@@ -594,6 +638,29 @@ value_x_unop (struct value *arg1, enum exp_opcode op, enum noside noside)
return value_zero (return_type, VALUE_LVAL (arg1));
}
return call_function_by_hand (argvec[0], nargs, argvec + 1);
+ }
+ if (ext_fn)
+ {
+ if (ext_fn_is_method (ext_fn))
+ {
+ struct value *ret_val = ext_fn_invoke_method (ext_fn, arg1, NULL, 0);
+
+ if (ret_val == NULL)
+ error (_("Error invoking debug method implementation for "
+ "method %s"), tstr);
+
+ xfree (ext_fn);
+
+ return ret_val;
+ }
+ else
+ {
+ /* This else should not be taken as only debug methods
+ are supported currently. */
+ internal_error (__FILE__, __LINE__,
+ _("Invoking an extension language "
+ "function (not a method)."));
+ }
}
throw_error (NOT_FOUND_ERROR,
_("member function %s not found"), tstr);
diff --git a/gdb/valops.c b/gdb/valops.c
index 4fc57ec..9dbf333 100644
--- a/gdb/valops.c
+++ b/gdb/valops.c
@@ -44,6 +44,7 @@
#include "objfiles.h"
#include "symtab.h"
#include "exceptions.h"
+#include "ext-function.h"
extern unsigned int overload_debug;
/* Local functions. */
@@ -72,8 +73,8 @@ int find_oload_champ_namespace_loop (struct value **, int,
const int no_adl);
static int find_oload_champ (struct value **, int, int, int,
- struct fn_field *, struct symbol **,
- struct badness_vector **);
+ struct fn_field *, VEC (ext_fn_descriptor_p) *,
+ struct symbol **, struct badness_vector **);
static int oload_method_static (int, struct fn_field *, int);
@@ -100,9 +101,10 @@ static CORE_ADDR allocate_space_in_inferior (int);
static struct value *cast_into_complex (struct type *, struct value *);
-static struct fn_field *find_method_list (struct value **, const char *,
- int, struct type *, int *,
- struct type **, int *);
+static void find_method_list (struct value **, const char *,
+ int, struct type *, struct fn_field **, int *,
+ VEC (ext_fn_descriptor_p) **,
+ struct type **, int *);
void _initialize_valops (void);
@@ -2261,40 +2263,58 @@ value_struct_elt (struct value **argp, struct value **args,
method is found.
BOFFSET is the offset of the base subobject where the method is found. */
-static struct fn_field *
+static void
find_method_list (struct value **argp, const char *method,
- int offset, struct type *type, int *num_fns,
+ int offset, struct type *type,
+ struct fn_field **fn_list, int *num_fns,
+ VEC (ext_fn_descriptor_p) **ext_fn_vec,
struct type **basetype, int *boffset)
{
int i;
- struct fn_field *f;
- CHECK_TYPEDEF (type);
+ struct fn_field *f = NULL;
- *num_fns = 0;
+ CHECK_TYPEDEF (type);
/* First check in object itself. */
- for (i = TYPE_NFN_FIELDS (type) - 1; i >= 0; i--)
- {
- /* pai: FIXME What about operators and type conversions? */
- const char *fn_field_name = TYPE_FN_FIELDLIST_NAME (type, i);
- if (fn_field_name && (strcmp_iw (fn_field_name, method) == 0))
- {
- int len = TYPE_FN_FIELDLIST_LENGTH (type, i);
- struct fn_field *f = TYPE_FN_FIELDLIST1 (type, i);
+ if (fn_list && !(*fn_list))
+ for (i = TYPE_NFN_FIELDS (type) - 1; i >= 0; i--)
+ {
+ /* pai: FIXME What about operators and type conversions? */
+ const char *fn_field_name = TYPE_FN_FIELDLIST_NAME (type, i);
+
+ if (fn_field_name && (strcmp_iw (fn_field_name, method) == 0))
+ {
+ int len = TYPE_FN_FIELDLIST_LENGTH (type, i);
+ f = TYPE_FN_FIELDLIST1 (type, i);
+ *fn_list = f;
+
+ *num_fns = len;
+ *basetype = type;
+ *boffset = offset;
+
+ /* Resolve any stub methods. */
+ check_stub_method_group (type, i);
+
+ break;
+ }
+ }
- *num_fns = len;
- *basetype = type;
- *boffset = offset;
+ if (ext_fn_vec)
+ {
+ VEC (ext_fn_descriptor_p) *ef_vec = NULL, *new_vec = NULL;
- /* Resolve any stub methods. */
- check_stub_method_group (type, i);
+ ef_vec = get_matching_ext_methods (type, method);
+ new_vec = VEC_merge (ext_fn_descriptor_p, *ext_fn_vec, ef_vec);
- return f;
- }
+ VEC_free (ext_fn_descriptor_p, *ext_fn_vec);
+ VEC_free (ext_fn_descriptor_p, ef_vec);
+ *ext_fn_vec = new_vec;
}
- /* Not found in object, check in base subobjects. */
+ /* If source methods are not found in current class, look for them in the
+ base classes. We have to go through the base classes to gather extension
+ methods anyway. */
for (i = TYPE_N_BASECLASSES (type) - 1; i >= 0; i--)
{
int base_offset;
@@ -2311,13 +2331,11 @@ find_method_list (struct value **argp, const char *method,
{
base_offset = TYPE_BASECLASS_BITPOS (type, i) / 8;
}
- f = find_method_list (argp, method, base_offset + offset,
- TYPE_BASECLASS (type, i), num_fns,
- basetype, boffset);
- if (f)
- return f;
+
+ find_method_list (argp, method, base_offset + offset,
+ TYPE_BASECLASS (type, i), fn_list, num_fns,
+ ext_fn_vec, basetype, boffset);
}
- return NULL;
}
/* Return the list of overloaded methods of a specified name.
@@ -2330,9 +2348,11 @@ find_method_list (struct value **argp, const char *method,
method.
BOFFSET is the offset of the base subobject which defines the method. */
-static struct fn_field *
+static void
value_find_oload_method_list (struct value **argp, const char *method,
- int offset, int *num_fns,
+ int offset, struct fn_field **fn_list,
+ int *num_fns,
+ VEC (ext_fn_descriptor_p) **ext_fn_vec,
struct type **basetype, int *boffset)
{
struct type *t;
@@ -2354,8 +2374,32 @@ value_find_oload_method_list (struct value **argp, const char *method,
error (_("Attempt to extract a component of a "
"value that is not a struct or union"));
- return find_method_list (argp, method, 0, t, num_fns,
- basetype, boffset);
+ /* Clear the lists. */
+ if (fn_list)
+ {
+ *fn_list = NULL;
+ *num_fns = 0;
+ }
+ if (ext_fn_vec)
+ *ext_fn_vec = VEC_alloc (ext_fn_descriptor_p, 1);
+
+ find_method_list (argp, method, 0, t, fn_list, num_fns, ext_fn_vec,
+ basetype, boffset);
+}
+
+static struct type *
+value_has_indirect_dynamic_type (struct value *obj)
+{
+ struct type *stype, *dtype, *dtype_ind;
+
+ stype = check_typedef (TYPE_TARGET_TYPE (value_type (obj)));
+ dtype_ind = value_rtti_indirect_type (obj, NULL, NULL, NULL);
+ dtype = dtype_ind ? check_typedef (TYPE_TARGET_TYPE (dtype_ind)) : stype;
+
+ if (class_types_same_p (stype, dtype))
+ return NULL;
+ else
+ return dtype_ind;
}
/* Given an array of arguments (ARGS) (which includes an
@@ -2403,6 +2447,7 @@ find_overload_match (struct value **args, int nargs,
const char *name, enum oload_search_type method,
struct value **objp, struct symbol *fsym,
struct value **valp, struct symbol **symp,
+ struct ext_fn_descriptor **ext_fn,
int *staticp, const int no_adl)
{
struct value *obj = (objp ? *objp : NULL);
@@ -2410,16 +2455,24 @@ find_overload_match (struct value **args, int nargs,
/* Index of best overloaded function. */
int func_oload_champ = -1;
int method_oload_champ = -1;
+ int src_method_oload_champ = -1;
+ int src_method_oload_champ_bkp = -1;
+ int ext_method_oload_champ = -1;
+ int src_and_ext_equal = 0;
/* The measure for the current best match. */
struct badness_vector *method_badness = NULL;
struct badness_vector *func_badness = NULL;
+ struct badness_vector *ext_method_badness = NULL;
+ struct badness_vector *src_method_badness = NULL;
struct value *temp = obj;
/* For methods, the list of overloaded methods. */
struct fn_field *fns_ptr = NULL;
/* For non-methods, the list of overloaded function symbols. */
struct symbol **oload_syms = NULL;
+ /* For extension functions, the VEC of extension function descriptors. */
+ VEC (ext_fn_descriptor_p) *ext_fn_vec = NULL;
/* Number of overloaded instances being considered. */
int num_fns = 0;
struct type *basetype = NULL;
@@ -2431,6 +2484,8 @@ find_overload_match (struct value **args, int nargs,
const char *func_name = NULL;
enum oload_classification match_quality;
enum oload_classification method_match_quality = INCOMPATIBLE;
+ enum oload_classification src_method_match_quality = INCOMPATIBLE;
+ enum oload_classification ext_method_match_quality = INCOMPATIBLE;
enum oload_classification func_match_quality = INCOMPATIBLE;
/* Get the list of overloaded methods or functions. */
@@ -2459,12 +2514,12 @@ find_overload_match (struct value **args, int nargs,
}
/* Retrieve the list of methods with the name NAME. */
- fns_ptr = value_find_oload_method_list (&temp, name,
- 0, &num_fns,
- &basetype, &boffset);
+ value_find_oload_method_list (&temp, name, 0, &fns_ptr, &num_fns,
+ ext_fn ? &ext_fn_vec : NULL,
+ &basetype, &boffset);
/* If this is a method only search, and no methods were found
the search has faild. */
- if (method == METHOD && (!fns_ptr || !num_fns))
+ if (method == METHOD && (!fns_ptr || !num_fns) && !ext_fn_vec)
error (_("Couldn't find method %s%s%s"),
obj_type_name,
(obj_type_name && *obj_type_name) ? "::" : "",
@@ -2475,18 +2530,82 @@ find_overload_match (struct value **args, int nargs,
if (fns_ptr)
{
gdb_assert (TYPE_DOMAIN_TYPE (fns_ptr[0].type) != NULL);
- method_oload_champ = find_oload_champ (args, nargs, method,
- num_fns, fns_ptr,
- oload_syms, &method_badness);
-
- method_match_quality =
- classify_oload_match (method_badness, nargs,
- oload_method_static (method, fns_ptr,
- method_oload_champ));
-
- make_cleanup (xfree, method_badness);
+ src_method_oload_champ = find_oload_champ (args, nargs, method,
+ num_fns, fns_ptr, NULL,
+ oload_syms,
+ &src_method_badness);
+
+ src_method_match_quality =
+ classify_oload_match (
+ src_method_badness, nargs,
+ oload_method_static (method, fns_ptr,
+ src_method_oload_champ));
+
+ make_cleanup (xfree, src_method_badness);
}
+ if (ext_fn && VEC_length (ext_fn_descriptor_p, ext_fn_vec))
+ {
+ ext_method_oload_champ = find_oload_champ (args, nargs, method,
+ 0, NULL, ext_fn_vec,
+ NULL, &ext_method_badness);
+ ext_method_match_quality = classify_oload_match (ext_method_badness,
+ nargs, 0);
+ make_cleanup (xfree, ext_method_badness);
+ make_ext_fn_vec_cleanup (ext_fn_vec);
+ }
+
+ if (src_method_oload_champ >= 0 && ext_method_oload_champ >= 0)
+ {
+ switch (compare_badness (ext_method_badness, src_method_badness))
+ {
+ case 0: /* Src method and ext method are equally good. */
+ src_and_ext_equal = 1;
+ case 1: /* Src method and ext method are incompatible */
+ /* if ext method match is not standard, then let source method
+ win. */
+ if (ext_method_match_quality != STANDARD)
+ {
+ method_oload_champ = src_method_oload_champ;
+ method_badness = src_method_badness;
+ ext_method_oload_champ = -1;
+ method_match_quality = src_method_match_quality;
+ break;
+ }
+ case 2: /* Ext method is champion. */
+ method_oload_champ = ext_method_oload_champ;
+ method_badness = ext_method_badness;
+ src_method_oload_champ_bkp = src_method_oload_champ;
+ src_method_oload_champ = -1;
+ method_match_quality = ext_method_match_quality;
+ break;
+ case 3: /* Src method is champion. */
+ method_oload_champ = src_method_oload_champ;
+ method_badness = src_method_badness;
+ ext_method_oload_champ = -1;
+ method_match_quality = src_method_match_quality;
+ break;
+ default:
+ error (_("Internal error: unexpected overload comparison "
+ "result"));
+ break;
+ }
+ }
+ else
+ {
+ if (src_method_oload_champ >= 0)
+ {
+ method_oload_champ = src_method_oload_champ;
+ method_badness = src_method_badness;
+ method_match_quality = src_method_match_quality;
+ }
+ if (ext_method_oload_champ >= 0)
+ {
+ method_oload_champ = ext_method_oload_champ;
+ method_badness = ext_method_badness;
+ method_match_quality = ext_method_match_quality;
+ }
+ }
}
if (method == NON_METHOD || method == BOTH)
@@ -2629,21 +2748,6 @@ find_overload_match (struct value **args, int nargs,
func_name);
}
- if (staticp != NULL)
- *staticp = oload_method_static (method, fns_ptr, method_oload_champ);
-
- if (method_oload_champ >= 0)
- {
- if (TYPE_FN_FIELD_VIRTUAL_P (fns_ptr, method_oload_champ))
- *valp = value_virtual_fn_field (&temp, fns_ptr, method_oload_champ,
- basetype, boffset);
- else
- *valp = value_fn_field (&temp, fns_ptr, method_oload_champ,
- basetype, boffset);
- }
- else
- *symp = oload_syms[func_oload_champ];
-
if (objp)
{
struct type *temp_type = check_typedef (value_type (temp));
@@ -2653,11 +2757,66 @@ find_overload_match (struct value **args, int nargs,
&& (TYPE_CODE (objtype) == TYPE_CODE_PTR
|| TYPE_CODE (objtype) == TYPE_CODE_REF))
{
- temp = value_addr (temp);
+ *objp = value_addr (temp);
}
- *objp = temp;
+ else
+ *objp = temp;
}
+ if (staticp != NULL)
+ *staticp = oload_method_static (method, fns_ptr, method_oload_champ);
+
+ if (method_oload_champ >= 0)
+ {
+ if (src_method_oload_champ >= 0)
+ {
+ if (TYPE_FN_FIELD_VIRTUAL_P (fns_ptr, method_oload_champ))
+ {
+ struct type *dtype;
+
+ dtype = value_has_indirect_dynamic_type (args[0]);
+ if (dtype)
+ {
+ args[0] = value_cast (dtype, args[0]);
+ do_cleanups (all_cleanups);
+ return find_overload_match (args, nargs, name, method,
+ objp, fsym, valp, symp, ext_fn,
+ staticp, no_adl);
+ }
+ else
+ *valp = value_virtual_fn_field (&temp, fns_ptr,
+ method_oload_champ,
+ basetype, boffset);
+ }
+ else
+ *valp = value_fn_field (&temp, fns_ptr, method_oload_champ,
+ basetype, boffset);
+ }
+ else
+ {
+ if (src_and_ext_equal
+ && TYPE_FN_FIELD_VIRTUAL_P (fns_ptr, src_method_oload_champ_bkp))
+ {
+ struct type *dtype;
+
+ dtype = value_has_indirect_dynamic_type (args[0]);
+ if (dtype)
+ {
+ args[0] = value_cast (dtype, args[0]);
+ do_cleanups (all_cleanups);
+ return find_overload_match (args, nargs, name, method,
+ objp, fsym, valp, symp, ext_fn,
+ staticp, no_adl);
+ }
+ }
+
+ *ext_fn = ext_fn_clone (VEC_index (ext_fn_descriptor_p, ext_fn_vec,
+ ext_method_oload_champ));
+ }
+ }
+ else
+ *symp = oload_syms[func_oload_champ];
+
do_cleanups (all_cleanups);
switch (match_quality)
@@ -2791,7 +2950,7 @@ find_oload_champ_namespace_loop (struct value **args, int nargs,
++num_fns;
new_oload_champ = find_oload_champ (args, nargs, 0, num_fns,
- NULL, new_oload_syms,
+ NULL, NULL, new_oload_syms,
&new_oload_champ_bv);
/* Case 1: We found a good match. Free earlier matches (if any),
@@ -2839,10 +2998,12 @@ find_oload_champ_namespace_loop (struct value **args, int nargs,
static int
find_oload_champ (struct value **args, int nargs, int method,
int num_fns, struct fn_field *fns_ptr,
+ VEC (ext_fn_descriptor_p) *ext_fn_vec,
struct symbol **oload_syms,
struct badness_vector **oload_champ_bv)
{
int ix;
+ int ext_fn_vec_n = 0;
/* A measure of how good an overloaded instance is. */
struct badness_vector *bv;
/* Index of best overloaded function. */
@@ -2852,18 +3013,27 @@ find_oload_champ (struct value **args, int nargs, int method,
/* 0 => no ambiguity, 1 => two good funcs, 2 => incomparable funcs. */
*oload_champ_bv = NULL;
+ ext_fn_vec_n = ext_fn_vec ? VEC_length (ext_fn_descriptor_p, ext_fn_vec) : 0;
/* Consider each candidate in turn. */
- for (ix = 0; ix < num_fns; ix++)
+ for (ix = 0; (ix < num_fns) || (ix < ext_fn_vec_n); ix++)
{
int jj;
- int static_offset = oload_method_static (method, fns_ptr, ix);
+ int static_offset = 0;
int nparms;
struct type **parm_types;
+ struct ext_fn_descriptor *ext_fn = NULL;
+
+ if (ext_fn_vec)
+ ext_fn = VEC_index (ext_fn_descriptor_p, ext_fn_vec, ix);
if (method)
{
- nparms = TYPE_NFIELDS (TYPE_FN_FIELD_TYPE (fns_ptr, ix));
+ if (fns_ptr)
+ {
+ static_offset = oload_method_static (method, fns_ptr, ix);
+ nparms = TYPE_NFIELDS (TYPE_FN_FIELD_TYPE (fns_ptr, ix));
+ }
}
else
{
@@ -2872,13 +3042,20 @@ find_oload_champ (struct value **args, int nargs, int method,
}
/* Prepare array of parameter types. */
- parm_types = (struct type **)
- xmalloc (nparms * (sizeof (struct type *)));
- for (jj = 0; jj < nparms; jj++)
- parm_types[jj] = (method
- ? (TYPE_FN_FIELD_ARGS (fns_ptr, ix)[jj].type)
- : TYPE_FIELD_TYPE (SYMBOL_TYPE (oload_syms[ix]),
- jj));
+ if (fns_ptr || oload_syms)
+ {
+ parm_types = (struct type **)
+ xmalloc (nparms * (sizeof (struct type *)));
+ for (jj = 0; jj < nparms; jj++)
+ parm_types[jj] = (method
+ ? (TYPE_FN_FIELD_ARGS (fns_ptr, ix)[jj].type)
+ : TYPE_FIELD_TYPE (SYMBOL_TYPE (oload_syms[ix]),
+ jj));
+ }
+ else
+ {
+ parm_types = ext_fn_get_argtypes (ext_fn, &nparms);
+ }
/* Compare parameter types to supplied argument types. Skip
THIS for static methods. */
diff --git a/gdb/value.h b/gdb/value.h
index db964e3..3216249 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -31,6 +31,7 @@ struct type;
struct ui_file;
struct language_defn;
struct value_print_options;
+struct ext_fn_descriptor;
/* The structure which defines the type of a value. It should never
be possible for a program lval value to survive over a call to the
@@ -671,6 +672,7 @@ extern int find_overload_match (struct value **args, int nargs,
enum oload_search_type method,
struct value **objp, struct symbol *fsym,
struct value **valp, struct symbol **symp,
+ struct ext_fn_descriptor **ext_fn,
int *staticp, const int no_adl);
extern struct value *value_field (struct value *arg1, int fieldno);
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH] Debug Methods in GDB Python
2013-11-26 3:22 ` Siva Chandra
@ 2013-12-06 6:31 ` Doug Evans
2013-12-06 23:24 ` Siva Chandra
0 siblings, 1 reply; 12+ messages in thread
From: Doug Evans @ 2013-12-06 6:31 UTC (permalink / raw)
To: Siva Chandra; +Cc: gdb-patches
Siva Chandra <sivachandra@google.com> writes:
> Doug pointed out offlist that the ChangeLog was improperly done. Below
> is the corrected ChangeLog entry. For convenience, I have also
> attached the same patch that I have posted last time.
>
> 2013-11-25 Siva Chandra Reddy <sivachandra@google.com>
>
> Add Debug methods support in GDB Python.
>
> * Makefile.in: Add entries for new files.
> * data-directory/Makefile.in: Add entries for new Python files.
> * eval.c (evaluate_subexp_standard): Lookup and invoke methods
> defined in extension languages.
> * valarith.c (value_x_binop, value_x_unop): Lookup and invoke
> overloaded operator methods defined in extension languages.
> * valops.c (find_oload_method_list, find_method_list,
> find_overload_match, find_oload_champ): Lookup methods defined
> in extension languages.
> (value_has_indirect_dynamic_type): New function to determine
> the indirect dynamic type of a value.
> * value.h (find_overload_match): Update signature.
> * ext-function.c: New file.
> * ext-function.h: New file.
> * python/py-debugmethods.c: New file.
> * python/py-objfile.c (objfile_object): New field
> 'debug_methods'.
> (objfpy_dealloc): XDECREF on the new debug_methods field.
> (objfpy_new, objfile_to_objfile_object): Initialize
> debug_methods field.
> (objfpy_get_debug_methods): New function.
> (objfile_getset): New entry 'debug_methods'.
> * python/py-progspace.c (pspace_object): New field
> 'debug_methods'.
> (pspy_dealloc): XDECREF on the new debug_methods field.
> (pspy_new, pspace_to_pspace_object): Initialize
> debug_methods field.
> (pspy_get_debug_methods): New function.
> (pspace_getset): New entry 'debug_methods'.
> * python/python-internal.h: Add declarations for new functions.
> * python/python.c (_initialize_python): Invoke
> gdbpy_initialize_debugmethods.
> * python/python.h: Add declarations of new functions.
> * python/lib/gdb/__init__.py (debug_methods): New attribute.
> * python/lib/gdb/debugmethods.py: New file.
> * python/lib/gdb/command/debug-methods.py: New file.
>
> testuite/
> * gdb.python/py-debugmethods.cc: New testcase to test debug
> methods.
> * gdb.python/py-debugmethods.exp: New tests to test debug
> methods.
> * gdb.python/py-debugmethods.py: Python script supporting the
> new testcase and tests.
Sorry for the delay.
I've been wanting to find time to investigate some of the suggestions I made.
e.g., instead of DEFAULT_DEBUG_METHOD_GROUP, etc.
have the grouping work the same way it does for the libstdc++
pretty-printers. Have you thought about it?
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH] Debug Methods in GDB Python
2013-12-06 6:31 ` Doug Evans
@ 2013-12-06 23:24 ` Siva Chandra
2013-12-12 5:26 ` Doug Evans
0 siblings, 1 reply; 12+ messages in thread
From: Siva Chandra @ 2013-12-06 23:24 UTC (permalink / raw)
To: Doug Evans; +Cc: gdb-patches
On Thu, Dec 5, 2013 at 10:30 PM, Doug Evans <xdje42@gmail.com> wrote:
> Sorry for the delay.
> I've been wanting to find time to investigate some of the suggestions I made.
> e.g., instead of DEFAULT_DEBUG_METHOD_GROUP, etc.
> have the grouping work the same way it does for the libstdc++
> pretty-printers. Have you thought about it?
I have removed DEFAULT_DEBUG_METHOD_GROUP from the code in my latest
patch. However, there is one reference to it in the brief (really
brief!) doc change I have in the patch [1]. About grouping in
libstdc++, is it not doing a grouping within what is facilitated by
the pretty printing API? For debug methods, I have added grouping wrt
obj file, progspace, and global. I have not implemented the notion of
sub debug methods (pretty printing API allows the user to bring in a
notion of sub printers). There are two reason for this. 1. It is a
convenience facility for the user which can be added easily if the
base API is agreed upon. 2. This is a personal reason, which you could
override: In the pretty printers world, each class would typically
have only one pretty printer implementation. For debug methods though,
each class could possibly have multiple debug methods. In which case,
having a notion of sub debug methods is probably more meaningful at
the class level. That is, it is more meaningful to have a "debug
methods of a class" grouping. And, this grouping can be easily
achieved by a debug method naming convention.
[1] I am really scared to write a full doc entry if the base API is
not agreed upon yet.
Thanks,
Siva Chandra
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH] Debug Methods in GDB Python
2013-12-06 23:24 ` Siva Chandra
@ 2013-12-12 5:26 ` Doug Evans
2013-12-13 19:25 ` Siva Chandra
0 siblings, 1 reply; 12+ messages in thread
From: Doug Evans @ 2013-12-12 5:26 UTC (permalink / raw)
To: Siva Chandra; +Cc: gdb-patches
On Fri, Dec 6, 2013 at 3:24 PM, Siva Chandra <sivachandra@google.com> wrote:
> On Thu, Dec 5, 2013 at 10:30 PM, Doug Evans <xdje42@gmail.com> wrote:
>> Sorry for the delay.
>> I've been wanting to find time to investigate some of the suggestions I made.
>> e.g., instead of DEFAULT_DEBUG_METHOD_GROUP, etc.
>> have the grouping work the same way it does for the libstdc++
>> pretty-printers. Have you thought about it?
>
> I have removed DEFAULT_DEBUG_METHOD_GROUP from the code in my latest
> patch. However, there is one reference to it in the brief (really
> brief!) doc change I have in the patch [1]. About grouping in
> libstdc++, is it not doing a grouping within what is facilitated by
> the pretty printing API?
Not sure I understand the question.
> For debug methods, I have added grouping wrt
> obj file, progspace, and global. I have not implemented the notion of
> sub debug methods (pretty printing API allows the user to bring in a
> notion of sub printers). There are two reason for this. 1. It is a
> convenience facility for the user which can be added easily if the
> base API is agreed upon. 2. This is a personal reason, which you could
> override: In the pretty printers world, each class would typically
> have only one pretty printer implementation. For debug methods though,
> each class could possibly have multiple debug methods. In which case,
> having a notion of sub debug methods is probably more meaningful at
> the class level. That is, it is more meaningful to have a "debug
> methods of a class" grouping. And, this grouping can be easily
> achieved by a debug method naming convention.
That's a good point.
Part of the reason for a grouping is that one can then have the list
of all methods usefully sorted.
One would want to see all the libstdc++ debug methods listed together
and then within that one would want to see all the debug methods for a
particular class/template listed together.
Sorting by objfile is easy of course, that's how they're recorded.
Within an objfile it would depend on how debug methods are registered.
In pretty-printer land "one" pretty-printer can actually be a
collection of several within it.
Thus in order to enable/disable individual printers we need to be able
to specify the top level object and then the printer within it.
e.g.
(gdb) disable pretty .*libstd.* libstdc[+][+]-v6;.*vector.*
5 printers disabled
123 of 128 printers enabled
A bit awkward, granted, (eg. having to deal with + in libstdc++).
Anyways, using naming convention sounds reasonable to me for version 1.
[Though if the names get kinda long, splitting them up as you have
with group name and debug method name does become appealing.
If "group name" was intended to be, or include, the class name,
apologies! I didn't see it. :-(]
> [1] I am really scared to write a full doc entry if the base API is
> not agreed upon yet.
Yep. Totally understand.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH] Debug Methods in GDB Python
2013-12-12 5:26 ` Doug Evans
@ 2013-12-13 19:25 ` Siva Chandra
2013-12-16 17:56 ` Doug Evans
0 siblings, 1 reply; 12+ messages in thread
From: Siva Chandra @ 2013-12-13 19:25 UTC (permalink / raw)
To: Doug Evans; +Cc: gdb-patches
> On Fri, Dec 6, 2013 at 3:24 PM, Siva Chandra <sivachandra@google.com> wrote:
> I have removed DEFAULT_DEBUG_METHOD_GROUP from the code in my latest
> patch. However, there is one reference to it in the brief (really
> brief!) doc change I have in the patch [1]. About grouping in
> libstdc++, is it not doing a grouping within what is facilitated by
> the pretty printing API?
dje > Not sure I understand the question.
libstdc++ pretty printers use the pretty printing API provided by GDB
Python. So, if we are to compare debug methods API to something,
shouldn't we be comparing with the pretty printing API? About the
matching API provided by the debug methods API in my patch, there
isn't anything specific at all. The users can match class name and
method name in whatever manner they please. If they like the libstdc++
pretty printers way, then they can do it that way.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH] Debug Methods in GDB Python
2013-12-13 19:25 ` Siva Chandra
@ 2013-12-16 17:56 ` Doug Evans
2013-12-16 22:43 ` Siva Chandra
2014-01-01 12:23 ` Siva Chandra
0 siblings, 2 replies; 12+ messages in thread
From: Doug Evans @ 2013-12-16 17:56 UTC (permalink / raw)
To: Siva Chandra; +Cc: gdb-patches
On Fri, Dec 13, 2013 at 11:24 AM, Siva Chandra <sivachandra@google.com> wrote:
>> On Fri, Dec 6, 2013 at 3:24 PM, Siva Chandra <sivachandra@google.com> wrote:
>> I have removed DEFAULT_DEBUG_METHOD_GROUP from the code in my latest
>> patch. However, there is one reference to it in the brief (really
>> brief!) doc change I have in the patch [1]. About grouping in
>> libstdc++, is it not doing a grouping within what is facilitated by
>> the pretty printing API?
>
> dje > Not sure I understand the question.
>
> libstdc++ pretty printers use the pretty printing API provided by GDB
> Python. So, if we are to compare debug methods API to something,
> shouldn't we be comparing with the pretty printing API? About the
> matching API provided by the debug methods API in my patch, there
> isn't anything specific at all. The users can match class name and
> method name in whatever manner they please. If they like the libstdc++
> pretty printers way, then they can do it that way.
Ah.
Note that the grouping in the pretty-printer API is because one
pretty-printer can pretty-print several objects, and the functionality
is split into two steps: the lookup object returns a separate object
which is then later used to do the printing, and the lookup can record
in the returned pretty-printer-worker object whatever state it wants.
In dm-v6 AFAICT lookup and functionality exist in the same object so
that's not possible (e.g., the DebugMethod.match method returns
True/False instead of, e.g., a debug-method-worker object).
We could, I think(!), go with debug-methods as in v6, sans method
groups, and later extend it if a more formal grouping is needed.
I wonder, though, about the match method returning True/False vs the
pretty-printer way of the lookup object returning an object that then
does the printing.
[In the debug method case the lookup method might want to return a
list if the method is overloaded.]
Unless there's a compelling reason to be different, I like
"Consistency Is Good", so that's what I'm shooting for.
[But if there is a compelling reason to be different, I'm not opposed
to being different here.]
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH] Debug Methods in GDB Python
2013-12-16 17:56 ` Doug Evans
@ 2013-12-16 22:43 ` Siva Chandra
2014-01-01 12:23 ` Siva Chandra
1 sibling, 0 replies; 12+ messages in thread
From: Siva Chandra @ 2013-12-16 22:43 UTC (permalink / raw)
To: Doug Evans; +Cc: gdb-patches
On Mon, Dec 16, 2013 at 9:56 AM, Doug Evans <xdje42@gmail.com> wrote:
> Note that the grouping in the pretty-printer API is because one
> pretty-printer can pretty-print several objects, and the functionality
> is split into two steps: the lookup object returns a separate object
> which is then later used to do the printing, and the lookup can record
> in the returned pretty-printer-worker object whatever state it wants.
> In dm-v6 AFAICT lookup and functionality exist in the same object so
> that's not possible (e.g., the DebugMethod.match method returns
> True/False instead of, e.g., a debug-method-worker object).
>
> We could, I think(!), go with debug-methods as in v6, sans method
> groups, and later extend it if a more formal grouping is needed.
> I wonder, though, about the match method returning True/False vs the
> pretty-printer way of the lookup object returning an object that then
> does the printing.
> [In the debug method case the lookup method might want to return a
> list if the method is overloaded.]
> Unless there's a compelling reason to be different, I like
> "Consistency Is Good", so that's what I'm shooting for.
> [But if there is a compelling reason to be different, I'm not opposed
> to being different here.]
I do not have a compelling reason to be different here. I only have
couple of personal reasons for doing it that way:
1. <locus>.pretty_printers is actually not a list of pretty printers,
but a list of functions which return a pretty printer. I wanted
<locus>.debug_methods to be a list of actual debug methods.
2. Debug methods are queried for a match twice; Once for object type
and name match, next for argument type match. I wanted these to be
together instead of in separate places (as in, an object-method
matching function and a stateful worker object which does arg type
matching). But I agree that this makes the debug methods stateless
(almost! They do have a name and enabled flag.) for most cases.
I can modify and send a new patch if you feel debug methods should
parallel pretty printers here.
Thanks,
Siva Chandra
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH] Debug Methods in GDB Python
2013-12-16 17:56 ` Doug Evans
2013-12-16 22:43 ` Siva Chandra
@ 2014-01-01 12:23 ` Siva Chandra
2014-01-03 18:52 ` Doug Evans
1 sibling, 1 reply; 12+ messages in thread
From: Siva Chandra @ 2014-01-01 12:23 UTC (permalink / raw)
To: gdb-patches
On Mon, Dec 16, 2013 at 9:56 AM, Doug Evans <xdje42@gmail.com> wrote:
> I wonder, though, about the match method returning True/False vs the
> pretty-printer way of the lookup object returning an object that then
> does the printing.
> [In the debug method case the lookup method might want to return a
> list if the method is overloaded.]
> Unless there's a compelling reason to be different, I like
> "Consistency Is Good", so that's what I'm shooting for.
> [But if there is a compelling reason to be different, I'm not opposed
> to being different here.]
Though I had responded to this part before, I had made a note to
revisit it. Some of the points I make are really subjective based on
my personal feel.
In the pretty printers world, a pretty printer is a callable which
returns an object (which has a specific API) which can do the
printing. So, a pretty printer is really not doing the printing, it
returns a printer. For me, this is really odd: an object which is an
instance of PrettyPrinter does not do the printing, but returns a
printer in turn.
I did not want debug methods to be like pretty printers. As in, a
debug method should be _the_ method and not return a method. If a
debug method were to be a match-er which returns a method, then we
would need to enforce two APIs, one for the match-er and another for
the method. Also, for debug methods, the method part will not be
purely a method as it still needs to do argument matching.
As it stands in the latest patch, I feel the debug methods API is a
bit verbose. The debug method base class has a "match" method and a
"get_argtypes" method which need to be overridden by a specific debug
method implementation. To cater to templates, both these methods
currently take the "class_type" and "method_name" arguments. This
makes the debug method objects almost stateless (almost because they
have "name" and "enabled" fields). A simple way to fix this is to
rename the "match" method to "init". The "init" method will be exactly
the same as the match method. It will take exactly the same arguments
as the "match" method and return True/False based on a match/mismatch.
However, since it is named "init", it will be expected to save enough
state so that the "get_argtypes" method need not take any arguments.
If we can agree on the basic API, I will send out a patch which
addresses the formatting issues which Doug pointed out sometime back.
Thanks,
Siva Chandra
[I have just noticed that it is a week shy of an year that I had sent
my v1 patch for this feature. Hope this feature lands in 2014.]
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH] Debug Methods in GDB Python
2014-01-01 12:23 ` Siva Chandra
@ 2014-01-03 18:52 ` Doug Evans
2014-01-08 0:49 ` Siva Chandra
0 siblings, 1 reply; 12+ messages in thread
From: Doug Evans @ 2014-01-03 18:52 UTC (permalink / raw)
To: Siva Chandra; +Cc: gdb-patches
On Wed, Jan 1, 2014 at 4:23 AM, Siva Chandra <sivachandra@google.com> wrote:
> On Mon, Dec 16, 2013 at 9:56 AM, Doug Evans <xdje42@gmail.com> wrote:
>> I wonder, though, about the match method returning True/False vs the
>> pretty-printer way of the lookup object returning an object that then
>> does the printing.
>> [In the debug method case the lookup method might want to return a
>> list if the method is overloaded.]
>> Unless there's a compelling reason to be different, I like
>> "Consistency Is Good", so that's what I'm shooting for.
>> [But if there is a compelling reason to be different, I'm not opposed
>> to being different here.]
>
> Though I had responded to this part before, I had made a note to
> revisit it. Some of the points I make are really subjective based on
> my personal feel.
>
> In the pretty printers world, a pretty printer is a callable which
> returns an object (which has a specific API) which can do the
> printing. So, a pretty printer is really not doing the printing, it
> returns a printer. For me, this is really odd: an object which is an
> instance of PrettyPrinter does not do the printing, but returns a
> printer in turn.
The concept feels sufficiently useful that I cope with the naming.
> I did not want debug methods to be like pretty printers. As in, a
> debug method should be _the_ method and not return a method. If a
> debug method were to be a match-er which returns a method, then we
> would need to enforce two APIs, one for the match-er and another for
> the method. Also, for debug methods, the method part will not be
> purely a method as it still needs to do argument matching.
That's a fair point, but we need to not conflate how things are named
with how they are used / what they look like.
I like having the object gdb calls to do the lookup return another
object that gdb calls to implement the functionality.
1) It supports more flexibility in how the user can organize things
(e.g., one "lookup" object can handle different "worker" objects).
2) The worker object can obtain state at runtime (it is constructed at
lookup time).
[I'm not sure I phrased that sufficiently well. I'm trying to
enumerate what the pretty-printer way of doing things brings to the
table, so to speak.]
> As it stands in the latest patch, I feel the debug methods API is a
> bit verbose. The debug method base class has a "match" method and a
> "get_argtypes" method which need to be overridden by a specific debug
> method implementation. To cater to templates, both these methods
> currently take the "class_type" and "method_name" arguments. This
> makes the debug method objects almost stateless (almost because they
> have "name" and "enabled" fields). A simple way to fix this is to
> rename the "match" method to "init". The "init" method will be exactly
> the same as the match method. It will take exactly the same arguments
> as the "match" method and return True/False based on a match/mismatch.
> However, since it is named "init", it will be expected to save enough
> state so that the "get_argtypes" method need not take any arguments.
Note that this kind of simplification can fall out of the two step
lookup+worker approach: construction of the worker could save the
needed state so that it didn't have to be passed back to subsequent
methods. [Blech. "method" is getting too overloaded here. :-)]
["init" is a bit too close to __init__, but that could be fixed if one
went that route.]
> If we can agree on the basic API, I will send out a patch which
> addresses the formatting issues which Doug pointed out sometime back.
>
> Thanks,
> Siva Chandra
> [I have just noticed that it is a week shy of an year that I had sent
> my v1 patch for this feature. Hope this feature lands in 2014.]
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH] Debug Methods in GDB Python
2014-01-03 18:52 ` Doug Evans
@ 2014-01-08 0:49 ` Siva Chandra
2014-01-09 19:01 ` Doug Evans
0 siblings, 1 reply; 12+ messages in thread
From: Siva Chandra @ 2014-01-08 0:49 UTC (permalink / raw)
To: Doug Evans; +Cc: gdb-patches
I am assuming you have not prescribed anything here, but are only
presenting pros of doing things in a certain way. I am also assuming
that you are waiting for others to chime in with their views.
On Fri, Jan 3, 2014 at 10:52 AM, Doug Evans <dje@google.com> wrote:
> I like having the object gdb calls to do the lookup return another
> object that gdb calls to implement the functionality.
>
> 1) It supports more flexibility in how the user can organize things
> (e.g., one "lookup" object can handle different "worker" objects).
For doing such things, I had a design like this in mind: We setup up a
hierarchy of DebugMethod classes. The base class only does type and
method matching. The derived concrete classes extend the base class by
adding arg matching and the method invocation. This way, 'lookup' and
'worker' functionalities are separated code wise but still
encapsulated in a single DebugMethod object.
> 2) The worker object can obtain state at runtime (it is constructed at
> lookup time).
This can be done on a single object by providing a setter?
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH] Debug Methods in GDB Python
2014-01-08 0:49 ` Siva Chandra
@ 2014-01-09 19:01 ` Doug Evans
0 siblings, 0 replies; 12+ messages in thread
From: Doug Evans @ 2014-01-09 19:01 UTC (permalink / raw)
To: Siva Chandra; +Cc: gdb-patches
On Tue, Jan 7, 2014 at 4:48 PM, Siva Chandra <sivachandra@google.com> wrote:
> I am assuming you have not prescribed anything here, but are only
> presenting pros of doing things in a certain way. I am also assuming
> that you are waiting for others to chime in with their views.
For the most part, correct.
Though I haven't seen a compelling argument to be different than how
pretty-printers do things.
> On Fri, Jan 3, 2014 at 10:52 AM, Doug Evans <dje@google.com> wrote:
>> I like having the object gdb calls to do the lookup return another
>> object that gdb calls to implement the functionality.
>>
>> 1) It supports more flexibility in how the user can organize things
>> (e.g., one "lookup" object can handle different "worker" objects).
>
> For doing such things, I had a design like this in mind: We setup up a
> hierarchy of DebugMethod classes. The base class only does type and
> method matching. The derived concrete classes extend the base class by
> adding arg matching and the method invocation. This way, 'lookup' and
> 'worker' functionalities are separated code wise but still
> encapsulated in a single DebugMethod object.
>
>> 2) The worker object can obtain state at runtime (it is constructed at
>> lookup time).
>
> This can be done on a single object by providing a setter?
But if this single object isn't constructed at lookup time this would
be modifying, essentially, global state.
[Maybe I'm misunderstanding.]
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2014-01-09 19:01 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-11-22 22:41 [PATCH] Debug Methods in GDB Python Siva Chandra
2013-11-26 3:22 ` Siva Chandra
2013-12-06 6:31 ` Doug Evans
2013-12-06 23:24 ` Siva Chandra
2013-12-12 5:26 ` Doug Evans
2013-12-13 19:25 ` Siva Chandra
2013-12-16 17:56 ` Doug Evans
2013-12-16 22:43 ` Siva Chandra
2014-01-01 12:23 ` Siva Chandra
2014-01-03 18:52 ` Doug Evans
2014-01-08 0:49 ` Siva Chandra
2014-01-09 19:01 ` Doug Evans
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).