]>
Commit | Line | Data |
---|---|---|
4a94e368 | 1 | # Copyright 2021-2022 Free Software Foundation, Inc. |
9b8efa2c PA |
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 | if { [build_executable "failed to prepare" $testfile $srcfile] } { | |
25 | return -1 | |
26 | } | |
27 | ||
28 | # Unwind to each function in FRAMES, and compare "info frame" output | |
29 | # to what was saved in the 'info_frame_before' array. | |
30 | proc compare_frames {frames} { | |
31 | foreach_with_prefix compare_frame $frames { | |
32 | if {[gdb_test \ | |
33 | "frame function $compare_frame" \ | |
34 | " $compare_frame .*"] != 0} { | |
35 | continue | |
36 | } | |
37 | set info_frame_after "" | |
38 | gdb_test_multiple "info frame" "" { | |
39 | -re "(.*\r\n$::gdb_prompt $)" { | |
40 | set info_frame_after $expect_out(1,string) | |
41 | pass $gdb_test_name | |
42 | } | |
43 | } | |
44 | ||
45 | # Nuke the PC address, since it'll be different. The | |
46 | # first time it's the actual PC before the call, the | |
47 | # second time it's the resume address after the call | |
48 | # returns. | |
49 | # E.g., on x86-64: | |
50 | # rip = 0x555555555168 in main (gdb.base/backtrace.c:41); saved rip = 0x7ffff7dd90b3 | |
51 | # vs | |
52 | # rip = 0x555555555172 in main (gdb.base/backtrace.c:41); saved rip = 0x7ffff7dd90b3 | |
53 | # | |
54 | set from \ | |
55 | "= $::hex in $compare_frame " | |
56 | set to \ | |
57 | "= \$hex in $compare_frame " | |
58 | regsub $from $::info_frame_before($compare_frame) $to \ | |
59 | ::info_frame_before($compare_frame) | |
60 | regsub $from $info_frame_after $to \ | |
61 | info_frame_after | |
62 | ||
63 | # Remove the "caller of frame at" line, which didn't | |
64 | # appear the first time, since the frame hadn't called any | |
65 | # other function yet then. | |
66 | regsub "\r\n caller of frame at $::hex\r\n" \ | |
67 | $info_frame_after "\r\n" \ | |
68 | info_frame_after | |
69 | regsub ", caller of frame at $::hex" \ | |
70 | $info_frame_after "" \ | |
71 | info_frame_after | |
72 | ||
73 | # "Stack level 0/1/2/3" -> "Stack level N" | |
74 | set from \ | |
75 | "Stack level $::decimal" | |
76 | set to \ | |
77 | "Stack level N" | |
78 | regsub $from $::info_frame_before($compare_frame) $to \ | |
79 | ::info_frame_before($compare_frame) | |
80 | regsub $from $info_frame_after $to \ | |
81 | info_frame_after | |
82 | ||
83 | # For debugging. | |
84 | verbose -log "BEFORE:\n$::info_frame_before($compare_frame)" | |
85 | verbose -log "AFTER:\n$info_frame_after" | |
86 | ||
87 | gdb_assert {[string match \ | |
88 | $::info_frame_before($compare_frame)\ | |
89 | $info_frame_after]} \ | |
90 | "info frame before/after match" | |
91 | } | |
92 | } | |
93 | ||
94 | proc test {dwarf_unwinder} { | |
95 | ||
96 | clean_restart $::binfile | |
97 | ||
98 | gdb_test_no_output "maint set dwarf unwinder $dwarf_unwinder" | |
99 | ||
100 | if ![runto_main] then { | |
9b8efa2c PA |
101 | return 0 |
102 | } | |
103 | ||
104 | array unset ::info_frame_before | |
105 | ||
106 | # Run to each function, and record "info frame" output in the | |
107 | # 'info_frame_before' array. At each stop, unwind to each | |
108 | # already-recorded function, and compare "info frame" output to | |
109 | # what was saved in the 'info_frame_before' array. | |
110 | set funcs {"main" "foo" "bar" "baz"} | |
111 | set idx_funcs 0 | |
112 | foreach_with_prefix stop_func $funcs { | |
113 | if {$idx_funcs != 0} { | |
114 | gdb_breakpoint $stop_func | |
115 | gdb_continue_to_breakpoint ".*$stop_func \(\).*" | |
116 | } | |
117 | ||
118 | set ::info_frame_before($stop_func) "" | |
119 | gdb_test_multiple "info frame" "" { | |
120 | -re "(.*\r\n$::gdb_prompt $)" { | |
121 | set ::info_frame_before($stop_func) $expect_out(1,string) | |
122 | pass $gdb_test_name | |
123 | } | |
124 | } | |
125 | ||
126 | if {$idx_funcs != 0} { | |
127 | compare_frames [lreverse [lrange $funcs 0 $idx_funcs-1]] | |
128 | } | |
129 | incr idx_funcs | |
130 | } | |
131 | } | |
132 | ||
133 | foreach_with_prefix dwarf {"off" "on"} { | |
134 | test $dwarf | |
135 | } |