Home | History | Annotate | Line # | Download | only in gdb.base
      1 # Copyright 2021-2024 Free Software Foundation, Inc.
      2 
      3 # This program is free software; you can redistribute it and/or modify
      4 # it under the terms of the GNU General Public License as published by
      5 # the Free Software Foundation; either version 3 of the License, or
      6 # (at your option) any later version.
      7 #
      8 # This program is distributed in the hope that it will be useful,
      9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
     10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     11 # GNU General Public License for more details.
     12 #
     13 # You should have received a copy of the GNU General Public License
     14 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
     15 
     16 # Check that the unwinder produces consistent frame info, by making
     17 # sure that "info frame" shows the same result when stopped at a
     18 # function (level == 0), compared to when we find the same frame in
     19 # the stack at a level > 0.  Tests both the DWARF stack unwinder, and
     20 # the fallback heuristic unwinder.
     21 
     22 standard_testfile backtrace.c
     23 
     24 set flags {}
     25 lappend flags debug
     26 lappend_include_file flags $srcdir/lib/attributes.h
     27 
     28 if { [build_executable "failed to prepare" $testfile $srcfile $flags] } {
     29     return -1
     30 }
     31 
     32 # Unwind to each function in FRAMES, and compare "info frame" output
     33 # to what was saved in the 'info_frame_before' array.
     34 proc compare_frames {frames} {
     35     foreach_with_prefix compare_frame $frames {
     36 	if {[gdb_test \
     37 		 "frame function $compare_frame" \
     38 		 " $compare_frame .*"] != 0} {
     39 	    continue
     40 	}
     41 	set info_frame_after ""
     42 	gdb_test_multiple "info frame" "" {
     43 	    -re "(.*\r\n$::gdb_prompt $)" {
     44 		set info_frame_after $expect_out(1,string)
     45 		pass $gdb_test_name
     46 	    }
     47 	}
     48 
     49 	# Nuke the PC address, since it'll be different.  The
     50 	# first time it's the actual PC before the call, the
     51 	# second time it's the resume address after the call
     52 	# returns.
     53 	# E.g., on x86-64:
     54 	#   rip = 0x555555555168 in main (gdb.base/backtrace.c:41); saved rip = 0x7ffff7dd90b3
     55 	# vs
     56 	#   rip = 0x555555555172 in main (gdb.base/backtrace.c:41); saved rip = 0x7ffff7dd90b3
     57 	#
     58 	set from \
     59 	    "= $::hex in $compare_frame "
     60 	set to \
     61 	    "= \$hex in $compare_frame "
     62 	regsub $from $::info_frame_before($compare_frame) $to \
     63 	    ::info_frame_before($compare_frame)
     64 	regsub $from $info_frame_after $to \
     65 	    info_frame_after
     66 
     67 	# Remove the "caller of frame at" line, which didn't
     68 	# appear the first time, since the frame hadn't called any
     69 	# other function yet then.
     70 	regsub "\r\n caller of frame at $::hex\r\n" \
     71 	    $info_frame_after "\r\n" \
     72 	    info_frame_after
     73 	regsub ", caller of frame at $::hex" \
     74 	    $info_frame_after "" \
     75 	    info_frame_after
     76 
     77 	# "Stack level 0/1/2/3" -> "Stack level N"
     78 	set from \
     79 	    "Stack level $::decimal"
     80 	set to \
     81 	    "Stack level N"
     82 	regsub $from $::info_frame_before($compare_frame) $to \
     83 	    ::info_frame_before($compare_frame)
     84 	regsub $from $info_frame_after $to \
     85 	    info_frame_after
     86 
     87 	# For debugging.
     88 	verbose -log "BEFORE:\n$::info_frame_before($compare_frame)"
     89 	verbose -log "AFTER:\n$info_frame_after"
     90 
     91 	gdb_assert {[string match \
     92 			 $::info_frame_before($compare_frame)\
     93 			 $info_frame_after]} \
     94 	    "info frame before/after match"
     95     }
     96 }
     97 
     98 proc test {dwarf_unwinder} {
     99 
    100     clean_restart $::binfile
    101 
    102     gdb_test_no_output "maint set dwarf unwinder $dwarf_unwinder"
    103 
    104     if {![runto_main]} {
    105 	return 0
    106     }
    107 
    108     array unset ::info_frame_before
    109 
    110     # Run to each function, and record "info frame" output in the
    111     # 'info_frame_before' array.  At each stop, unwind to each
    112     # already-recorded function, and compare "info frame" output to
    113     # what was saved in the 'info_frame_before' array.
    114     set funcs {"main" "foo" "bar" "baz"}
    115     set idx_funcs 0
    116     foreach_with_prefix stop_func $funcs {
    117 	if {$idx_funcs != 0} {
    118 	    gdb_breakpoint $stop_func
    119 	    gdb_continue_to_breakpoint ".*$stop_func \(\).*"
    120 	}
    121 
    122 	set ::info_frame_before($stop_func) ""
    123 	gdb_test_multiple "info frame" "" {
    124 	    -re "(.*\r\n$::gdb_prompt $)" {
    125 		set ::info_frame_before($stop_func) $expect_out(1,string)
    126 		pass $gdb_test_name
    127 	    }
    128 	}
    129 
    130 	if {$idx_funcs != 0} {
    131 	    compare_frames [lreverse [lrange $funcs 0 $idx_funcs-1]]
    132 	}
    133 	incr idx_funcs
    134     }
    135 }
    136 
    137 foreach_with_prefix dwarf {"off" "on"} {
    138     test $dwarf
    139 }
    140