[v6,10/10] gdb: Filter trampoline frames in backtrace when using Python frame-filters.

Message ID 20240328120528.30382-11-abdul.b.ijaz@intel.com
State New
Headers
Series GDB support for DW_AT_trampoline |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_gdb_build--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gdb_check--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 success Testing passed

Commit Message

Ijaz, Abdul B March 28, 2024, 12:05 p.m. UTC
  From: "Ijaz, Abdul B" <abdul.b.ijaz@intel.com>

In case of the Python frame-filters, this change filters trampoline functions
when the option 'skip-trampoline-functions' is set to 'on'.  GDB recently
added the option to hide frames that are marked by the compiler with
"DW_AT_trampoline", see commit 'Skip trampoline frames for the backtrace
command'.  This implements the same functionality when python frame filters
are in use.

In this example the IFX compiler emits "DW_AT_trampoline" tag for the 'first'
and 'second' trampoline functions like following:

function second (x, y) result(z)
  integer, intent(in) :: x, y
  integer :: z
  z = x * y ! breakpt-framefilter
end function second

function first (num1, num2) result(total)
  integer, intent(in) :: num1, num2
  integer  :: total
  total = second (num1 + 4, num2 * 3) ! first-breakpt
  total = total + 30
end function first

Related Dwarf:

0x0000013f:   DW_TAG_subprogram
                DW_AT_low_pc    (0x0000000000404350)
                DW_AT_high_pc   (0x000000000040435f)
                DW_AT_frame_base        (DW_OP_reg6 RBP)
                DW_AT_linkage_name      ("second_.t74p.t75p")
                DW_AT_name      ("second_.t74p.t75p")
                DW_AT_trampoline        ("second_")

0x0000015a:   DW_TAG_subprogram
                DW_AT_low_pc    (0x00000000004044a0)
                DW_AT_high_pc   (0x00000000004044af)
                DW_AT_frame_base        (DW_OP_reg6 RBP)
                DW_AT_linkage_name      ("first_.t104p.t105p")
                DW_AT_name      ("first_.t104p.t105p")
                DW_AT_trampoline        ("first_")

Once frame filters are enabled the backtrace command output before this
change looks like:

'''
(gdb) info frame-filter
global frame-filters:
  Priority  Enabled  Name
  100       Yes      TestTrampolineFrameFilter
(gdb) backtrace 3
\#0  second (x=20, y=9) at test.f90:4
\#1  0x0000000000405209 in second_.t74p.t75p () at test.f90:12
\#2  0x00000000004051e3 in first (num1=16, num2=3) at test.f90:10
(gdb) backtrace -3
\#2  0x00000000004051e3 in first (num1=16, num2=3) at test.f90:10
\#3  0x0000000000405309 in first_.t95p.t96p () at test.f90:21
\#4  0x0000000000405234 in func_trampoline () at test.f90:17
'''

After this change:

'''
(gdb) info frame-filter
global frame-filters:
  Priority  Enabled  Name
  100       Yes      TestTrampolineFrameFilter
(gdb) backtrace 3
\#0  second (x=20, y=9) at test.f90:4
\#2  0x00000000004051e3 in first (num1=16, num2=3) at test.f90:10
\#4  0x0000000000405234 in func_trampoline () at test.f90:17
(gdb) backtrace -3
\#2  0x00000000004051e3 in first (num1=16, num2=3) at test.f90:10
\#4  0x0000000000405234 in func_trampoline () at test.f90:17
'''

New test gdb.python/py-framefilter-trampoline.exp is added to test this change.

2024-03-28 Ijaz, Abdul B <abdul.b.ijaz@intel.com>
---
 gdb/python/py-frame.c                         | 11 +++
 .../gdb.python/py-framefilter-trampoline.exp  | 77 +++++++++++++++++++
 .../gdb.python/py-framefilter-trampoline.py   | 31 ++++++++
 3 files changed, 119 insertions(+)
 create mode 100644 gdb/testsuite/gdb.python/py-framefilter-trampoline.exp
 create mode 100644 gdb/testsuite/gdb.python/py-framefilter-trampoline.py
  

Patch

diff --git a/gdb/python/py-frame.c b/gdb/python/py-frame.c
index bbb42afb2ed..5c07ba58b23 100644
--- a/gdb/python/py-frame.c
+++ b/gdb/python/py-frame.c
@@ -70,6 +70,17 @@  frame_object_to_frame_info (PyObject *obj)
   if (frame_obj->frame_id_is_next)
     frame = get_prev_frame (frame);
 
+  if (skip_trampoline_functions)
+    {
+      for (int i = 0; (SAFE_TRAMPOLINE_CHAIN (i, frame)
+		       && in_trampoline_frame (frame)); ++i)
+	{
+	  frame = get_prev_frame (frame);
+	  if (frame ==  nullptr)
+	    return nullptr;
+	}
+    }
+
   return frame;
 }
 
diff --git a/gdb/testsuite/gdb.python/py-framefilter-trampoline.exp b/gdb/testsuite/gdb.python/py-framefilter-trampoline.exp
new file mode 100644
index 00000000000..76dbdaa970a
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-framefilter-trampoline.exp
@@ -0,0 +1,77 @@ 
+# Copyright (C) 2024 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 skipping of trampolines
+# in the backtrace command in case Python-based frame-filters are enabled.
+
+load_lib gdb-python.exp
+
+require allow_python_tests allow_fortran_tests
+
+if {![test_compiler_info {ifx-*} f90]} {
+    untested "This test is only applicable for IFX, which emits the\
+	trampoline DIE in Dwarf."
+    return -1
+}
+
+load_lib fortran.exp
+
+set testfile py-framefilter-trampoline
+set srcfile "${srcdir}/gdb.fortran/func-trampoline.f90"
+set binfile [standard_output_file $testfile]
+
+if {[build_executable $testfile.exp $testfile $srcfile {debug f90}] == -1} {
+    return -1
+}
+
+# Start with a fresh gdb.
+gdb_exit
+gdb_start
+
+gdb_test "info frame-filter" \
+    "No frame filters\\." \
+    "info frame filter before loading filters"
+
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+if {![runto_main]} {
+    return
+}
+gdb_test_no_output "set python print-stack full" \
+    "set python print-stack to full"
+
+# Load frame-filters
+set remote_python_file [gdb_remote_download host \
+			    ${srcdir}/${subdir}/${testfile}.py]
+gdb_test_no_output "source ${remote_python_file}" "load python file"
+
+set inner_loc [gdb_get_line_number "second-breakpt"]
+set middle_loc [gdb_get_line_number "first-breakpt"]
+set outer_loc [gdb_get_line_number "main-outer-loc"]
+set fill "\[^\r\n\]*"
+
+set inner_desc  "second \\(x=20, y=9\\) at ${fill}$srcfile:$inner_loc"
+set middle_desc "first \\(num1=16, num2=3\\) at ${fill}$srcfile:$middle_loc"
+set outer_desc  ".* at .*$srcfile:$outer_loc"
+
+# Set breakpoint inside the innermost function 'second'.
+gdb_breakpoint "$srcfile:$inner_loc"
+gdb_continue_to_breakpoint "innermost-body" ".*$srcfile:$inner_loc.*"
+
+# Test with frame filter.
+gdb_test "bt" [multi_line \
+    "#$decimal.* $middle_desc" \
+    "#$decimal.* $outer_desc.*"]
diff --git a/gdb/testsuite/gdb.python/py-framefilter-trampoline.py b/gdb/testsuite/gdb.python/py-framefilter-trampoline.py
new file mode 100644
index 00000000000..0e9dfd7af2a
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-framefilter-trampoline.py
@@ -0,0 +1,31 @@ 
+# Copyright (C) 2024 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 if trampolines are
+# skipped when Python-based frame-filters are enabled.
+
+import gdb
+
+class TestTrampolineFrameFilter():
+    def __init__(self):
+        self.name = "TestTrampolineFrameFilter"
+        self.priority = 100
+        self.enabled = True
+        gdb.frame_filters[self.name] = self
+
+    def filter(self, frame_iter):
+        return frame_iter
+
+TestTrampolineFrameFilter()