This is the mail archive of the gdb-cvs@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[binutils-gdb] Add Python rbreak command.


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=d8ae99a7b08e29e31446aee1e47e59943d7d9926

commit d8ae99a7b08e29e31446aee1e47e59943d7d9926
Author: Phil Muldoon <pmuldoon@redhat.com>
Date:   Thu Nov 16 14:14:03 2017 +0000

    Add Python rbreak command.
    
    gdb/Changelog
    
    2017-11-16  Phil Muldoon  <pmuldoon@redhat.com>
    
    	* python/python.c (gdbpy_rbreak): New function.
            * NEWS: Document Python rbreak feature.
    
    testsuite/Changelog
    
    2017-11-16  Phil Muldoon  <pmuldoon@redhat.com>
    
    	* gdb.python/py-rbreak.exp: New file.
    	* gdb.python/py-rbreak.c: New file.
    	* gdb.python/py-rbreak-func2.c: New file.
    
    doc/Changelog
    
    2017-11-16  Phil Muldoon  <pmuldoon@redhat.com>
    
    	* python.texi (Basic Python): Add rbreak documentation.

Diff:
---
 gdb/ChangeLog                              |   5 +
 gdb/NEWS                                   |   4 +
 gdb/doc/ChangeLog                          |   4 +
 gdb/doc/python.texi                        |  17 +++
 gdb/python/python.c                        | 188 ++++++++++++++++++++++++++++-
 gdb/testsuite/ChangeLog                    |   6 +
 gdb/testsuite/gdb.python/py-rbreak-func2.c |  34 ++++++
 gdb/testsuite/gdb.python/py-rbreak.c       |  70 +++++++++++
 gdb/testsuite/gdb.python/py-rbreak.exp     |  61 ++++++++++
 9 files changed, 388 insertions(+), 1 deletion(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index d05f7c3..a0efabc 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,8 @@
+2017-11-16  Phil Muldoon  <pmuldoon@redhat.com>
+
+	* python/python.c (gdbpy_rbreak): New function.
+	* NEWS: Document Python rbreak feature.
+
 2017-11-16  Yao Qi  <yao.qi@linaro.org>
 
 	* features/tic6x-c62x.xml: Remove.
diff --git a/gdb/NEWS b/gdb/NEWS
index aadb7a3..dc070fa 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -24,6 +24,10 @@
      gdb.new_thread are emitted.  See the manual for further
      description of these.
 
+  ** A new command, "rbreak" has been added to the Python API.  This
+     command allows the setting of a large number of breakpoints via a
+     regex pattern in Python.  See the manual for further details.
+
 * New features in the GDB remote stub, GDBserver
 
   ** GDBserver is now able to start inferior processes with a
diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog
index 98eab72..2d8f5af 100644
--- a/gdb/doc/ChangeLog
+++ b/gdb/doc/ChangeLog
@@ -1,3 +1,7 @@
+2017-11-16  Phil Muldoon  <pmuldoon@redhat.com>
+
+	* python.texi (Basic Python): Add rbreak documentation.
+
 2017-11-07  Xavier Roirand  <roirand@adacore.com>
 	    Pedro Alves  <palves@redhat.com>
 
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index f661e48..f411f60 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -243,6 +243,23 @@ were no breakpoints.  This peculiarity was subsequently fixed, and now
 @code{gdb.breakpoints} returns an empty sequence in this case.
 @end defun
 
+@defun gdb.rbreak (regex @r{[}, minsyms @r{[}, throttle, @r{[}, symtabs @r{]]]})
+Return a Python list holding a collection of newly set
+@code{gdb.Breakpoint} objects matching function names defined by the
+@var{regex} pattern.  If the @var{minsyms} keyword is @code{True}, all
+system functions (those not explicitly defined in the inferior) will
+also be included in the match.  The @var{throttle} keyword takes an
+integer that defines the maximum number of pattern matches for
+functions matched by the @var{regex} pattern.  If the number of
+matches exceeds the integer value of @var{throttle}, a
+@code{RuntimeError} will be raised and no breakpoints will be created.
+If @var{throttle} is not defined then there is no imposed limit on the
+maximum number of matches and breakpoints to be created.  The
+@var{symtabs} keyword takes a Python iterable that yields a collection
+of @code{gdb.Symtab} objects and will restrict the search to those
+functions only contained within the @code{gdb.Symtab} objects.
+@end defun
+
 @findex gdb.parameter
 @defun gdb.parameter (parameter)
 Return the value of a @value{GDBN} @var{parameter} given by its name,
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 03ea5d5..5f15261 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -640,6 +640,190 @@ gdbpy_solib_name (PyObject *self, PyObject *args)
   return str_obj;
 }
 
+/* Implementation of Python rbreak command.  Take a REGEX and
+   optionally a MINSYMS, THROTTLE and SYMTABS keyword and return a
+   Python list that contains newly set breakpoints that match that
+   criteria.  REGEX refers to a GDB format standard regex pattern of
+   symbols names to search; MINSYMS is an optional boolean (default
+   False) that indicates if the function should search GDB's minimal
+   symbols; THROTTLE is an optional integer (default unlimited) that
+   indicates the maximum amount of breakpoints allowable before the
+   function exits (note, if the throttle bound is passed, no
+   breakpoints will be set and a runtime error returned); SYMTABS is
+   an optional Python iterable that contains a set of gdb.Symtabs to
+   constrain the search within.  */
+
+static PyObject *
+gdbpy_rbreak (PyObject *self, PyObject *args, PyObject *kw)
+{
+  /* A simple type to ensure clean up of a vector of allocated strings
+     when a C interface demands a const char *array[] type
+     interface.  */
+  struct symtab_list_type
+  {
+    ~symtab_list_type ()
+    {
+      for (const char *elem: vec)
+	xfree ((void *) elem);
+    }
+    std::vector<const char *> vec;
+  };
+
+  char *regex = NULL;
+  std::vector<symbol_search> symbols;
+  unsigned long count = 0;
+  PyObject *symtab_list = NULL;
+  PyObject *minsyms_p_obj = NULL;
+  int minsyms_p = 0;
+  unsigned int throttle = 0;
+  static const char *keywords[] = {"regex","minsyms", "throttle",
+				   "symtabs", NULL};
+  symtab_list_type symtab_paths;
+
+  if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "s|O!IO", keywords,
+					&regex, &PyBool_Type,
+					&minsyms_p_obj, &throttle,
+					&symtab_list))
+    return NULL;
+
+  /* Parse minsyms keyword.  */
+  if (minsyms_p_obj != NULL)
+    {
+      int cmp = PyObject_IsTrue (minsyms_p_obj);
+      if (cmp < 0)
+	return NULL;
+      minsyms_p = cmp;
+    }
+
+  /* The "symtabs" keyword is any Python iterable object that returns
+     a gdb.Symtab on each iteration.  If specified, iterate through
+     the provided gdb.Symtabs and extract their full path.  As
+     python_string_to_target_string returns a
+     gdb::unique_xmalloc_ptr<char> and a vector containing these types
+     cannot be coerced to a const char **p[] via the vector.data call,
+     release the value from the unique_xmalloc_ptr and place it in a
+     simple type symtab_list_type (which holds the vector and a
+     destructor that frees the contents of the allocated strings.  */
+  if (symtab_list != NULL)
+    {
+      gdbpy_ref<> iter (PyObject_GetIter (symtab_list));
+
+      if (iter == NULL)
+	return NULL;
+
+      while (true)
+	{
+	  gdbpy_ref<> next (PyIter_Next (iter.get ()));
+
+	  if (next == NULL)
+	    {
+	      if (PyErr_Occurred ())
+		return NULL;
+	      break;
+	    }
+
+	  gdbpy_ref<> obj_name (PyObject_GetAttrString (next.get (),
+							"filename"));
+
+	  if (obj_name == NULL)
+	    return NULL;
+
+	  /* Is the object file still valid?  */
+	  if (obj_name == Py_None)
+	    continue;
+
+	  gdb::unique_xmalloc_ptr<char> filename =
+	    python_string_to_target_string (obj_name.get ());
+
+	  if (filename == NULL)
+	    return NULL;
+
+	  /* Make sure there is a definite place to store the value of
+	     filename before it is released.  */
+	  symtab_paths.vec.push_back (nullptr);
+	  symtab_paths.vec.back () = filename.release ();
+	}
+    }
+
+  if (symtab_list)
+    {
+      const char **files = symtab_paths.vec.data ();
+
+      symbols = search_symbols (regex, FUNCTIONS_DOMAIN,
+				symtab_paths.vec.size (), files);
+    }
+  else
+    symbols = search_symbols (regex, FUNCTIONS_DOMAIN, 0, NULL);
+
+  /* Count the number of symbols (both symbols and optionally minimal
+     symbols) so we can correctly check the throttle limit.  */
+  for (const symbol_search &p : symbols)
+    {
+      /* Minimal symbols included?  */
+      if (minsyms_p)
+	{
+	  if (p.msymbol.minsym != NULL)
+	    count++;
+	}
+
+      if (p.symbol != NULL)
+	count++;
+    }
+
+  /* Check throttle bounds and exit if in excess.  */
+  if (throttle != 0 && count > throttle)
+    {
+      PyErr_SetString (PyExc_RuntimeError,
+		       _("Number of breakpoints exceeds throttled maximum."));
+      return NULL;
+    }
+
+  gdbpy_ref<> return_list (PyList_New (0));
+
+  if (return_list == NULL)
+    return NULL;
+
+  /* Construct full path names for symbols and call the Python
+     breakpoint constructor on the resulting names.  Be tolerant of
+     individual breakpoint failures.  */
+  for (const symbol_search &p : symbols)
+    {
+      std::string symbol_name;
+
+      /* Skipping minimal symbols?  */
+      if (minsyms_p == 0)
+	if (p.msymbol.minsym != NULL)
+	  continue;
+
+      if (p.msymbol.minsym == NULL)
+	{
+	  struct symtab *symtab = symbol_symtab (p.symbol);
+	  const char *fullname = symtab_to_fullname (symtab);
+
+	  symbol_name = fullname;
+	  symbol_name  += ":";
+	  symbol_name  += SYMBOL_LINKAGE_NAME (p.symbol);
+	}
+      else
+	symbol_name = MSYMBOL_LINKAGE_NAME (p.msymbol.minsym);
+
+      gdbpy_ref<> argList (Py_BuildValue("(s)", symbol_name.c_str ()));
+      gdbpy_ref<> obj (PyObject_CallObject ((PyObject *)
+					    &breakpoint_object_type,
+					    argList.get ()));
+
+      /* Tolerate individual breakpoint failures.  */
+      if (obj == NULL)
+	gdbpy_print_stack ();
+      else
+	{
+	  if (PyList_Append (return_list.get (), obj.get ()) == -1)
+	    return NULL;
+	}
+    }
+  return return_list.release ();
+}
+
 /* A Python function which is a wrapper for decode_line_1.  */
 
 static PyObject *
@@ -1910,7 +2094,9 @@ Return the name of the current target charset." },
   { "target_wide_charset", gdbpy_target_wide_charset, METH_NOARGS,
     "target_wide_charset () -> string.\n\
 Return the name of the current target wide charset." },
-
+  { "rbreak", (PyCFunction) gdbpy_rbreak, METH_VARARGS | METH_KEYWORDS,
+    "rbreak (Regex) -> List.\n\
+Return a Tuple containing gdb.Breakpoint objects that match the given Regex." },
   { "string_to_argv", gdbpy_string_to_argv, METH_VARARGS,
     "string_to_argv (String) -> Array.\n\
 Parse String and return an argv-like array.\n\
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index 547a3be..d2b4983 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,9 @@
+2017-11-16  Phil Muldoon  <pmuldoon@redhat.com>
+
+	* gdb.python/py-rbreak.exp: New file.
+	* gdb.python/py-rbreak.c: New file.
+	* gdb.python/py-rbreak-func2.c: New file.
+
 2017-11-16  Pedro Alves  <palves@redhat.com>
 
 	* gdb.base/starti.exp ("continue" test): Remove ".*"s from
diff --git a/gdb/testsuite/gdb.python/py-rbreak-func2.c b/gdb/testsuite/gdb.python/py-rbreak-func2.c
new file mode 100644
index 0000000..2d24b6b
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-rbreak-func2.c
@@ -0,0 +1,34 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2017 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/>.  */
+
+int
+efunc1 ()
+{
+  return 1;
+}
+
+int
+efunc2 ()
+{
+  return 2;
+}
+
+int
+efunc3 ()
+{
+  return 3;
+}
diff --git a/gdb/testsuite/gdb.python/py-rbreak.c b/gdb/testsuite/gdb.python/py-rbreak.c
new file mode 100644
index 0000000..e79d2a3
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-rbreak.c
@@ -0,0 +1,70 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2013-2017 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/>.  */
+
+int
+func1 ()
+{
+  return 1;
+}
+
+int
+func2 ()
+{
+  return 2;
+}
+
+int
+func3 ()
+{
+  return 3;
+}
+
+int
+func4 ()
+{
+  return 4;
+}
+
+int
+func5 ()
+{
+  return 5;
+}
+
+void
+func6 ()
+{
+  return;
+}
+
+void
+outside_scope ()
+{
+  return;
+}
+
+int
+main()
+{
+  func1 (); /* Break func1.  */
+  func2 ();
+  func3 ();
+  func4 ();
+  func5 ();
+  func6 ();
+  outside_scope ();
+}
diff --git a/gdb/testsuite/gdb.python/py-rbreak.exp b/gdb/testsuite/gdb.python/py-rbreak.exp
new file mode 100644
index 0000000..5aaf297
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-rbreak.exp
@@ -0,0 +1,61 @@
+# Copyright (C) 2017 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 mechanism
+# exposing values to Python.
+
+load_lib gdb-python.exp
+
+standard_testfile py-rbreak.c py-rbreak-func2.c
+
+if {[prepare_for_testing "failed to prepare" ${testfile} [list $srcfile $srcfile2]] } {
+    return 1
+}
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+if ![runto_main] then {
+    fail "can't run to main"
+    return 0
+}
+
+gdb_py_test_silent_cmd "py sl = gdb.rbreak(\"\",minsyms=False)" \
+    "get all function breakpoints" 0
+gdb_test "py print(len(sl))" "11" \
+    "check number of returned breakpoints is 11"
+gdb_py_test_silent_cmd "py sl = gdb.rbreak(\"main\.\*\",minsyms=False)" \
+    "get main function breakpoint" 0
+gdb_test "py print(len(sl))" "1" \
+    "check number of returned breakpoints is 1"
+gdb_py_test_silent_cmd "py sl = gdb.rbreak(\"func\.\*\",minsyms=False,throttle=10)" \
+    "get functions matching func.*" 0
+gdb_test "py print(len(sl))" "9" \
+    "check number of returned breakpoints is 9"
+gdb_test "py gdb.rbreak(\"func\.\*\",minsyms=False,throttle=5)" \
+    "Number of breakpoints exceeds throttled maximum.*" \
+    "check throttle errors on too many breakpoints"
+gdb_py_test_silent_cmd "py sl = gdb.rbreak(\"func1\",minsyms=True)" \
+    "including minimal symbols, get functions matching func.*" 0
+gdb_test "py print(len(sl))" "2" \
+    "check number of returned breakpoints is 2"
+gdb_py_test_silent_cmd "python sym = gdb.lookup_symbol(\"efunc1\")" \
+    "find a symbol in objfile" 1
+gdb_py_test_silent_cmd "python symtab = sym\[0\].symtab" \
+    "get backing symbol table" 1
+gdb_py_test_silent_cmd "py sl = gdb.rbreak(\"func\.\*\",minsyms=False,throttle=10,symtabs=\[symtab\])" \
+    "get functions matching func.* in one symtab only" 0
+gdb_test "py print(len(sl))" "3" \
+    "check number of returned breakpoints is 3"


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]