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