[v9,08/10] gdb, mi: Skip trampoline functions for the -stack-list-frames command.

Message ID 20241124220353.3465-9-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 Build passed
linaro-tcwg-bot/tcwg_gdb_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gdb_check--master-arm success Test passed
linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 success Test passed

Commit Message

Abdul Basit Ijaz Nov. 24, 2024, 10:03 p.m. UTC
  From: "Ijaz, Abdul B" <abdul.b.ijaz@intel.com>

This change skips trampoline functions for the '-stack-list-frames' command
when the option 'skip-trampoline-functions' is set to 'on'.  Before this
change, GDB prints the frames indicated by the compiler with DIE
"DW_AT_trampoline" in the backtrace for mi command stack-list-frames, but
for better user experience, all such frames can be hidden from the user.

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-backtrace
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_")

Before this change, the '-stack-list-frames' command output looks like:

'''
(gdb) -stack-list-frames
-stack-list-frames
^done,
stack=[
frame={level="0",addr="0x0000000000405194",func="second",
file="test.f90",fullname="/home/test.f90",line="4",
arch="i386:x86-64"},
frame={level="1",addr="0x0000000000405209",
func="second_.t74p.t75p",
file="test.f90",fullname="/home/test.f90",line="12",
arch="i386:x86-64"},
frame={level="2",addr="0x00000000004051e3",func="first",
file="test.f90",fullname="/home/test.f90",line="10",
arch="i386:x86-64"},
frame={level="3",addr="0x0000000000405309",
func="first_.t95p.t96p",file="test.f90",
fullname="/home/test.f90",line="21",arch="i386:x86-64"},
frame={level="4",addr="0x0000000000405234",
func="func_trampoline",file="test.f90",
fullname="/home/test.f90",line="17",arch="i386:x86-64"}]
'''

After this change:

'''
(gdb) -stack-list-frames
-stack-list-frames
^done,
stack=[frame={level="0",addr="0x0000000000405194",func="second",
file="test.f90",fullname="/home/test.f90",line="4",
arch="i386:x86-64"},
frame={level="2",addr="0x00000000004051e3",func="first",
file="test.f90",fullname="/home/test.f90",line="10",
arch="i386:x86-64"},
frame={level="4",addr="0x0000000000405234",
func="func_trampoline",file="test.f90",fullname="/home/test.f90",
line="17",arch="i386:x86-64"}]
'''

New test gdb.mi/mi-func-trampoline.exp is added for testing the change.

2024-11-24 Ijaz, Abdul B <abdul.b.ijaz@intel.com>
---
 gdb/doc/gdb.texinfo                         |  4 +-
 gdb/mi/mi-cmd-stack.c                       |  7 +++
 gdb/testsuite/gdb.mi/mi-func-trampoline.exp | 68 +++++++++++++++++++++
 3 files changed, 78 insertions(+), 1 deletion(-)
 create mode 100644 gdb/testsuite/gdb.mi/mi-func-trampoline.exp
  

Patch

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 65568568890..efa05922a4b 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -34920,7 +34920,9 @@  an error if @var{low-frame} is larger than the actual number of
 frames.  On the other hand, @var{high-frame} may be larger than the
 actual number of frames, in which case only existing frames will be
 returned.  If the option @code{--no-frame-filters} is supplied, then
-Python frame filters will not be executed.
+Python frame filters will not be executed.  If the option
+@code{skip-trampoline-functions} is set, @value{GDBN} will skip the trampoline
+frames while printing a backtrace.
 
 @subsubheading @value{GDBN} Command
 
diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c
index 5e504283fcf..88a8af8adcf 100644
--- a/gdb/mi/mi-cmd-stack.c
+++ b/gdb/mi/mi-cmd-stack.c
@@ -178,6 +178,13 @@  mi_cmd_stack_list_frames (const char *command, const char *const *argv,
 	   i++, fi = get_prev_frame (fi))
 	{
 	  QUIT;
+	  if (skip_trampoline_functions)
+	    {
+	      for (int j = 0; (SAFE_TRAMPOLINE_CHAIN (j, fi)
+			       && in_trampoline_frame (fi)); ++j)
+		fi = get_prev_frame (fi);
+	    }
+
 	  /* Print the location and the address always, even for level 0.
 	     If args is 0, don't print the arguments.  */
 	  print_frame_info (user_frame_print_options,
diff --git a/gdb/testsuite/gdb.mi/mi-func-trampoline.exp b/gdb/testsuite/gdb.mi/mi-func-trampoline.exp
new file mode 100644
index 00000000000..34a2c464f21
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-func-trampoline.exp
@@ -0,0 +1,68 @@ 
+# Copyright 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/> .
+
+# Test -stack-list-frames command for functions with trampoline
+# calls.  Also checks if trampoline frames are filtered while printing
+# stack.
+
+require 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
+load_lib mi-support.exp
+
+set MIFLAGS "-i=mi"
+
+if {[mi_clean_restart]} {
+    return
+}
+
+set testfile mi-func-trampoline
+set srcfile "${srcdir}/gdb.fortran/func-trampoline.f90"
+set binfile [standard_output_file $testfile]
+
+if { [gdb_compile "${srcfile}" "${binfile}" executable \
+	  {debug f90} ] != "" } {
+    untested "failed to compile"
+    return -1
+}
+
+mi_delete_breakpoints
+mi_gdb_reinitialize_dir $srcdir/$subdir
+mi_gdb_load ${binfile}
+
+set inner_loc [gdb_get_line_number "second-breakpt"]
+set fill "\[^\r\n\]*"
+
+set frame1_regx "\{level=\"0\",addr=\"$hex\",func=\"second\",file=\".*func-trampoline.f90\",fullname=\"${fullname_syntax}func-trampoline.f90\",line=\"$inner_loc\",arch=\".*\"\}"
+set frame2_regx "\{level=\"2\",addr=\"$hex\",func=\"first\",.*\}"
+set frame3_regx "\{level=\"4\",addr=\"$hex\",func=\"func_trampoline\",.*\}"
+
+# Set breakpoint inside the innermost function 'second'.
+mi_create_breakpoint "-t $srcfile:$inner_loc" \
+    "insert breakpoint at line $inner_loc " \
+    -number 1 -disp del -func second ".*trampoline.f90" $inner_loc $hex
+mi_run_cmd
+mi_expect_stop \
+    "breakpoint-hit" "second" ".*" ".*trampoline.f90" "\[0-9\]+" { "" "disp=\"del\"" } \
+    "run to breakpoint at line $inner_loc"
+
+mi_gdb_test "100-stack-list-frames" \
+    "100\\^done,stack=\\\[frame=${frame1_regx},frame=${frame2_regx},frame=${frame3_regx}\\\]"