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