gdb.Frame.name() can return None when GDB cannot determine a frame's

function name.   and  currently

pass that value directly to Python's regular expression functions,

causing a TypeError.

 

Treat unnamed frames as non-matches.  For None, keep

walking older frames after encountering an unnamed frame.

 

Add regression tests for both convenience functions.

---

 

This is my first GDB contribution.

 

I used ChatGPT to help investigate the issue and draft an initial

version of the patch (it's fairly simple but still worth mentioning).

I reviewed, simplified, and tested the final change myself.

 

Tested with:

 

  make -C gdb check RUNTESTFLAGS='gdb.python/py-caller-is.exp'

 

gdb/python/lib/gdb/function/caller_is.py  |  6 ++++--

gdb/testsuite/gdb.python/py-caller-is.exp | 20 ++++++++++++++++++++

2 files changed, 24 insertions(+), 2 deletions(-)

 

diff --git a/gdb/python/lib/gdb/function/caller_is.py b/gdb/python/lib/gdb/function/caller_is.py

index bca09590811..a2a5798b914 100644

--- a/gdb/python/lib/gdb/function/caller_is.py

+++ b/gdb/python/lib/gdb/function/caller_is.py

@@ -80,7 +80,8 @@ class CallerMatches(gdb.Function):

             if frame is None:

                 return False

             nframes = nframes - 1

-        return re.match(name.string(), frame.name()) is not None

+        frame_name = frame.name()

+        return frame_name is not None and re.match(name.string(), frame_name) is not None

 

 

 class AnyCallerIs(gdb.Function):

@@ -143,7 +144,8 @@ class AnyCallerMatches(gdb.Function):

         frame = gdb.selected_frame()

         name_re = re.compile(name.string())

         while nframes >= 0:

-            if name_re.match(frame.name()) is not None:

+            frame_name = frame.name()

+            if frame_name is not None and name_re.match(frame_name) is not None:

                 return True

             frame = frame.older()

             if frame is None:

diff --git a/gdb/testsuite/gdb.python/py-caller-is.exp b/gdb/testsuite/gdb.python/py-caller-is.exp

index d9b826cb6a2..74ee2ae645a 100644

--- a/gdb/testsuite/gdb.python/py-caller-is.exp

+++ b/gdb/testsuite/gdb.python/py-caller-is.exp

@@ -70,3 +70,23 @@ proc test_all_caller_is_fns { } {

}

 

 test_all_caller_is_fns

+

+# Prepend an unnamed frame to the current stack without depending on

+# missing symbols in the host's runtime libraries.

+gdb_test_multiline "define unnamed frame helper" \

+    "python" "" \

+    "from unittest.mock import Mock, patch" "" \

+    "def check_unnamed_frame(expr):" "" \

+    "    frame = Mock()" "" \

+    "    frame.name.return_value = None" "" \

+    "    frame.older.return_value = gdb.selected_frame()" "" \

+    "    with patch.object(gdb, 'selected_frame', return_value=frame):" "" \

+    "        print(int(gdb.parse_and_eval(expr)))" "" \

+    "end" ""

+

+gdb_test {python check_unnamed_frame('$_caller_matches(".*", 0)')} \

+    "0" "caller_matches with unnamed frame"

+gdb_test {python check_unnamed_frame('$_any_caller_matches(".*", 0)')} \

+    "0" "any_caller_matches with unnamed frame"

+gdb_test {python check_unnamed_frame('$_any_caller_matches("^bottom_func$", 1)')} \

+    "1" "any_caller_matches skips unnamed frame"

--

2.34.1