PR 15276: Add $_caller_is, $_caller_matches, $_any_caller_is, $_any_caller_matches
authorDoug Evans <xdje42@gmail.com>
Sat, 6 Sep 2014 16:15:44 +0000 (09:15 -0700)
committerDoug Evans <xdje42@gmail.com>
Sat, 6 Sep 2014 16:15:44 +0000 (09:15 -0700)
gdb/ChangeLog:

PR 15276
* NEWS: Mention $_caller_is, $_caller_matches, $_any_caller_is,
$_any_caller_matches.
* data-directory/Makefile.in (PYTHON_FILE_LIST): Add caller_is.py.
* python/lib/gdb/function/caller_is.py: New file.

gdb/testsuite/ChangeLog:

PR 15276
* gdb.python/py-caller-is.c: New file.
* gdb.python/py-caller-is.exp: New file.

gdb/doc/ChangeLog:

PR 15276
* gdb.texinfo (Convenience Funs): Document $_caller_is,
$_caller_matches, $_any_caller_is, $_any_caller_matches.

gdb/ChangeLog
gdb/NEWS
gdb/data-directory/Makefile.in
gdb/doc/ChangeLog
gdb/doc/gdb.texinfo
gdb/python/lib/gdb/function/caller_is.py [new file with mode: 0644]
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.python/py-caller-is.c [new file with mode: 0644]
gdb/testsuite/gdb.python/py-caller-is.exp [new file with mode: 0644]

index b7f798a5433838b56defe6a4d6112fdc292d5402..3ae664973f7213b3ab5510ac4be6ad92a6890dc6 100644 (file)
@@ -1,3 +1,11 @@
+2014-09-06  Doug Evans  <xdje42@gmail.com>
+
+       PR 15276
+       * NEWS: Mention $_caller_is, $_caller_matches, $_any_caller_is,
+       $_any_caller_matches.
+       * data-directory/Makefile.in (PYTHON_FILE_LIST): Add caller_is.py.
+       * python/lib/gdb/function/caller_is.py: New file.
+
 2014-09-06  Doug Evans  <xdje42@gmail.com>
 
        * infcmd.c (program_info): Fix typo.
index 46c6a8746b2ecd3934eb5bfa3550e4586e388a47..3bb1c742aa1fc5dc9b5d5e8840c4d4ee0373c5d5 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -6,6 +6,13 @@
 * Python Scripting
   You can now access frame registers from Python scripts.
 
+* New Python-based convenience functions:
+
+  ** $_caller_is(name [, number_of_frames])
+  ** $_caller_matches(regexp [, number_of_frames])
+  ** $_any_caller_is(name [, number_of_frames])
+  ** $_any_caller_matches(regexp [, number_of_frames])
+
 * On resume, GDB now always passes the signal the program had stopped
   for to the thread the signal was sent to, even if the user changed
   threads before resuming.  Previously GDB would often (but not
index 509f8885a92dc9f70010e54d43ed72693f66fdc3..1e8cd4b666a2f4c521fd03c945ba9398cb7c0ba0 100644 (file)
@@ -73,6 +73,7 @@ PYTHON_FILE_LIST = \
        gdb/command/prompt.py \
        gdb/command/explore.py \
        gdb/function/__init__.py \
+       gdb/function/caller_is.py \
        gdb/function/strfns.py
 
 @HAVE_PYTHON_TRUE@PYTHON_FILES = $(PYTHON_FILE_LIST)
index 2b453e7ef69036d3b8240e7bb98ec4eb5735c3d3..ddc11f0e7452d511a310a8b32dcce6787dcb620d 100644 (file)
@@ -1,3 +1,9 @@
+2014-09-06  Doug Evans  <xdje42@gmail.com>
+
+       PR 15276
+       * gdb.texinfo (Convenience Funs): Document $_caller_is,
+       $_caller_matches, $_any_caller_is, $_any_caller_matches.
+
 2014-09-03  Justin Lebar <jlebar@google.com>
 
        * python.texi (Types In Python): Type.template_argument(n) returns a
index 8d9148cd1cafbbe9b707dcd92a0a14959e759575..facbd16cf674c5245ebe38e5e8c433a5f4912b67 100644 (file)
@@ -10098,6 +10098,70 @@ Otherwise it returns zero.
 @findex $_strlen@r{, convenience function}
 Returns the length of string @var{str}.
 
+@item $_caller_is(@var{name}@r{[}, @var{number_of_frames}@r{]})
+@findex $_caller_is@r{, convenience function}
+Returns one if the calling function's name is equal to @var{name}.
+Otherwise it returns zero.
+
+If the optional argument @var{number_of_frames} is provided,
+it is the number of frames up in the stack to look.
+The default is 1.
+
+Example:
+
+@smallexample
+(gdb) backtrace
+#0  bottom_func ()
+    at testsuite/gdb.python/py-caller-is.c:21
+#1  0x00000000004005a0 in middle_func ()
+    at testsuite/gdb.python/py-caller-is.c:27
+#2  0x00000000004005ab in top_func ()
+    at testsuite/gdb.python/py-caller-is.c:33
+#3  0x00000000004005b6 in main ()
+    at testsuite/gdb.python/py-caller-is.c:39
+(gdb) print $_caller_is ("middle_func")
+$1 = 1
+(gdb) print $_caller_is ("top_func", 2)
+$1 = 1
+@end smallexample
+
+@item $_caller_matches(@var{regexp}@r{[}, @var{number_of_frames}@r{]})
+@findex $_caller_matches@r{, convenience function}
+Returns one if the calling function's name matches the regular expression
+@var{regexp}.  Otherwise it returns zero.
+
+If the optional argument @var{number_of_frames} is provided,
+it is the number of frames up in the stack to look.
+The default is 1.
+
+@item $_any_caller_is(@var{name}@r{[}, @var{number_of_frames}@r{]})
+@findex $_any_caller_is@r{, convenience function}
+Returns one if any calling function's name is equal to @var{name}.
+Otherwise it returns zero.
+
+If the optional argument @var{number_of_frames} is provided,
+it is the number of frames up in the stack to look.
+The default is 1.
+
+This function differs from @code{$_caller_is} in that this function
+checks all stack frames from the immediate caller to the frame specified
+by @var{number_of_frames}, whereas @code{$_caller_is} only checks the
+frame specified by @var{number_of_frames}.
+
+@item $_any_caller_matches(@var{regexp}@r{[}, @var{number_of_frames}@r{]})
+@findex $_any_caller_matches@r{, convenience function}
+Returns one if any calling function's name matches the regular expression
+@var{regexp}.  Otherwise it returns zero.
+
+If the optional argument @var{number_of_frames} is provided,
+it is the number of frames up in the stack to look.
+The default is 1.
+
+This function differs from @code{$_caller_matches} in that this function
+checks all stack frames from the immediate caller to the frame specified
+by @var{number_of_frames}, whereas @code{$_caller_matches} only checks the
+frame specified by @var{number_of_frames}.
+
 @end table
 
 @value{GDBN} provides the ability to list and get help on
diff --git a/gdb/python/lib/gdb/function/caller_is.py b/gdb/python/lib/gdb/function/caller_is.py
new file mode 100644 (file)
index 0000000..f5b0a58
--- /dev/null
@@ -0,0 +1,160 @@
+# Caller-is functions.
+# Copyright (C) 2008, 2014 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
+
+class CallerIs(gdb.Function):
+    """Check the calling function's name.
+
+Usage:
+  $_caller_is(name [, number_of_frames])
+
+Arguments:
+
+  name: The name of the function to search for.
+
+  number_of_frames: How many stack frames to traverse back from the currently
+    selected frame to compare with.  If the value is greater than the depth of
+    the stack from that point then the result is False.
+    The default is 1.
+
+Returns:
+  True if the function's name at the specified frame is equal to name.
+"""
+
+    def __init__(self):
+        super(CallerIs, self).__init__("_caller_is")
+
+    def invoke(self, name, nframes = 1):
+        if nframes < 0:
+            raise ValueError("nframes must be >= 0")
+        frame = gdb.selected_frame()
+        while nframes > 0:
+            frame = frame.older()
+            if frame is None:
+                return False
+            nframes = nframes - 1
+        return frame.name() == name.string()
+
+class CallerMatches(gdb.Function):
+    """Compare the calling function's name with a regexp.
+
+Usage:
+  $_caller_matches(regex [, number_of_frames])
+
+Arguments:
+
+  regex: The regular expression to compare the function's name with.
+
+  number_of_frames: How many stack frames to traverse back from the currently
+    selected frame to compare with.  If the value is greater than the depth of
+    the stack from that point then the result is False.
+    The default is 1.
+
+Returns:
+  True if the function's name at the specified frame matches regex.
+"""
+
+    def __init__(self):
+        super(CallerMatches, self).__init__("_caller_matches")
+
+    def invoke(self, name, nframes = 1):
+        if nframes < 0:
+            raise ValueError("nframes must be >= 0")
+        frame = gdb.selected_frame()
+        while nframes > 0:
+            frame = frame.older()
+            if frame is None:
+                return False
+            nframes = nframes - 1
+        return re.match(name.string(), frame.name()) is not None
+
+class AnyCallerIs(gdb.Function):
+    """Check all calling function's names.
+
+Usage:
+  $_any_caller_is(name [, number_of_frames])
+
+Arguments:
+
+  name: The name of the function to search for.
+
+  number_of_frames: How many stack frames to traverse back from the currently
+    selected frame to compare with.  If the value is greater than the depth of
+    the stack from that point then the result is False.
+    The default is 1.
+
+Returns:
+  True if any function's name is equal to name.
+"""
+
+    def __init__(self):
+        super(AnyCallerIs, self).__init__("_any_caller_is")
+
+    def invoke(self, name, nframes = 1):
+        if nframes < 0:
+            raise ValueError("nframes must be >= 0")
+        frame = gdb.selected_frame()
+        while nframes >= 0:
+            if frame.name() == name.string():
+                return True 
+            frame = frame.older()
+            if frame is None:
+                return False
+            nframes = nframes - 1
+        return False
+
+class AnyCallerMatches(gdb.Function):
+    """Compare all calling function's names with a regexp.
+
+Usage:
+  $_any_caller_matches(regex [, number_of_frames])
+
+Arguments:
+
+  regex: The regular expression to compare the function's name with.
+
+  number_of_frames: How many stack frames to traverse back from the currently
+    selected frame to compare with.  If the value is greater than the depth of
+    the stack from that point then the result is False.
+    The default is 1.
+
+Returns:
+  True if any function's name matches regex.
+"""
+
+    def __init__(self):
+        super(AnyCallerMatches, self).__init__("_any_caller_matches")
+
+    def invoke(self, name, nframes = 1):
+        if nframes < 0:
+            raise ValueError("nframes must be >= 0")
+        frame = gdb.selected_frame()
+        name_re = re.compile(name.string())
+        while nframes >= 0:
+            if name_re.match(frame.name()) is not None:
+                return True
+            frame = frame.older()
+            if frame is None:
+                return False
+            nframes = nframes - 1
+        return False
+
+CallerIs()
+CallerMatches()
+AnyCallerIs()
+AnyCallerMatches()
index 9fa19bee37be9a995f3250240f683bbac99159f7..cc95f5e7733fd0f2b2f8bb11acf2e8f3477e0b25 100644 (file)
@@ -1,3 +1,9 @@
+2014-09-06  Doug Evans  <xdje42@gmail.com>
+
+       PR 15276
+       * gdb.python/py-caller-is.c: New file.
+       * gdb.python/py-caller-is.exp: New file.
+
 2014-09-05  Sergio Durigan Junior  <sergiodj@redhat.com>
 
        PR gdb/17235
diff --git a/gdb/testsuite/gdb.python/py-caller-is.c b/gdb/testsuite/gdb.python/py-caller-is.c
new file mode 100644 (file)
index 0000000..2c86388
--- /dev/null
@@ -0,0 +1,41 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2012-2014 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/>.  */
+
+static void
+bottom_func (void)
+{
+  return; /* Break bottom_func here.  */
+}
+
+static void
+middle_func (void)
+{
+  bottom_func ();
+}
+
+static void
+top_func (void)
+{
+  middle_func ();
+}
+
+int
+main ()
+{
+  top_func ();
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.python/py-caller-is.exp b/gdb/testsuite/gdb.python/py-caller-is.exp
new file mode 100644 (file)
index 0000000..23c58ea
--- /dev/null
@@ -0,0 +1,73 @@
+# Copyright (C) 2012-2014 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 convenience
+# functions in caller_is.py.
+
+load_lib gdb-python.exp
+
+standard_testfile
+
+if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } {
+    return -1
+}
+
+if ![runto_main] {
+    return 0
+}
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+gdb_breakpoint "bottom_func"
+gdb_continue_to_breakpoint "bottom_func"
+
+proc test_all_caller_is_fns { } {
+    gdb_test "p \$_caller_is (\"bottom_func\", 0)" " = 1"
+    gdb_test "p \$_caller_is (\"middle_func\")" " = 1"
+    gdb_test "p \$_caller_is (\"top_func\")" " = 0"
+    gdb_test "p \$_caller_is (\"middle_func\", 2)" " = 0"
+    gdb_test "p \$_caller_is (\"top_func\", 2)" " = 1"
+    gdb_test "p \$_caller_is (\"foo\", 100)" " = 0"
+    gdb_test "p \$_caller_is (\"foo\", -1)" "nframes must be >= 0"
+
+    gdb_test "p \$_caller_matches (\"^bottom\", 0)" " = 1"
+    gdb_test "p \$_caller_matches (\"^middle_\")" " = 1"
+    gdb_test "p \$_caller_matches (\"^top_\")" " = 0"
+    gdb_test "p \$_caller_matches (\"^middle_\", 2)" " = 0"
+    gdb_test "p \$_caller_matches (\"^top_f\", 2)" " = 1"
+    gdb_test "p \$_caller_matches (\"foo\", 100)" " = 0"
+    gdb_test "p \$_caller_matches (\"foo\", -1)" "nframes must be >= 0"
+
+    gdb_test "p \$_any_caller_is (\"bottom_func\", 0)" " = 1"
+    gdb_test "p \$_any_caller_is (\"middle_func\")" " = 1"
+    gdb_test "p \$_any_caller_is (\"top_func\")" " = 0"
+    gdb_test "p \$_any_caller_is (\"middle_func\", 2)" " = 1"
+    gdb_test "p \$_any_caller_is (\"top_func\", 2)" " = 1"
+    gdb_test "p \$_any_caller_is (\"main\", 100)" " = 1"
+    gdb_test "p \$_any_caller_is (\"foo\", 100)" " = 0"
+    gdb_test "p \$_any_caller_is (\"foo\", -1)" "nframes must be >= 0"
+
+    gdb_test "p \$_any_caller_matches (\"^bottom\", 0)" " = 1"
+    gdb_test "p \$_any_caller_matches (\"^middle_\")" " = 1"
+    gdb_test "p \$_any_caller_matches (\"^top_\")" " = 0"
+    gdb_test "p \$_any_caller_matches (\"^middle_\", 2)" " = 1"
+    gdb_test "p \$_any_caller_matches (\"^top_f\", 2)" " = 1"
+    gdb_test "p \$_any_caller_matches (\"^main\", 100)" " = 1"
+    gdb_test "p \$_any_caller_matches (\"foo\", 100)" " = 0"
+    gdb_test "p \$_any_caller_matches (\"foo\", -1)" "nframes must be >= 0"
+}
+
+test_all_caller_is_fns