1 # Copyright (C) 2015-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 # This file is part of the GDB testsuite. It verifies that frame 17 # unwinders can be implemented in Python. 18 19 load_lib gdb-python.exp 20 21 require allow_python_tests 22 23 standard_testfile 24 25 # Stack protection can make the stack look a bit different, breaking the 26 # assumptions this test has about its layout. 27 28 set flags "additional_flags=-fno-stack-protector" 29 30 if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} "debug $flags"] } { 31 return -1 32 } 33 34 # This test runs on a specific platform. 35 require is_x86_64_m64_target 36 37 # The following tests require execution. 38 39 if {![runto_main]} { 40 return 0 41 } 42 43 # Check for the corrupt backtrace. 44 proc check_for_broken_backtrace {testname} { 45 gdb_test_sequence "where" $testname { 46 "\\r\\n#0 .* corrupt_frame_inner \\(\\) at " 47 "\\r\\n#1 .* corrupt_frame_outer \\(\\) at " 48 "Backtrace stopped: frame did not save the PC" 49 } 50 } 51 52 # Check for the correct backtrace. 53 proc check_for_fixed_backtrace {testname} { 54 gdb_test_sequence "where" $testname { 55 "\\r\\n#0 .* corrupt_frame_inner \\(\\) at " 56 "\\r\\n#1 .* corrupt_frame_outer \\(\\) at " 57 "\\r\\n#2 .* main \\(.*\\) at" 58 } 59 } 60 61 # Check the 'info unwinder' output. 62 proc check_info_unwinder {testname enabled} { 63 if {$enabled} { 64 set suffix "" 65 } else { 66 set suffix " \\\[disabled\\\]" 67 } 68 69 gdb_test_sequence "info unwinder" $testname \ 70 [list \ 71 "Global:" \ 72 " test unwinder${suffix}"] 73 } 74 75 set pyfile [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py] 76 77 gdb_breakpoint [gdb_get_line_number "break backtrace-broken"] 78 79 gdb_continue_to_breakpoint "break backtrace-broken" 80 81 check_for_broken_backtrace "Backtrace is initially broken" 82 83 gdb_test "source ${pyfile}" "Python script imported" \ 84 "import python scripts" 85 86 check_info_unwinder "info unwinder after loading script" on 87 88 check_for_fixed_backtrace "check backtrace after loading unwinder" 89 90 # Check that the Python unwinder frames can be flushed / released. 91 gdb_test "maint flush register-cache" "Register cache flushed\\." "flush frames" 92 93 check_for_fixed_backtrace "check backtrace after flush" 94 95 # Try to disable the unwinder but instead set the enabled field to a 96 # non boolean value. This should fail. Check the 'info unwinder' 97 # output to be sure. 98 gdb_test "python global_test_unwinder.enabled = \"off\"" \ 99 [multi_line \ 100 "TypeError.*: incorrect type for enabled attribute: <class 'str'>" \ 101 "Error occurred in Python.*"] 102 check_info_unwinder "info unwinder after failed disable" on 103 104 # While we're doing silly stuff, lets try to change the name of this 105 # unwider. Doing this is bad as the new name might clash with an 106 # already registered name, which violates the promises made during 107 # 'register_unwinder'. 108 set pattern_1 "can't set attribute(?: 'name')?" 109 set pattern_2 "property 'name' of 'TestUnwinder' object has no setter" 110 gdb_test "python global_test_unwinder.name = \"foo\"" \ 111 [multi_line \ 112 "AttributeError.*: (?:${pattern_1}|${pattern_2})" \ 113 "Error occurred in Python.*"] 114 check_info_unwinder "info unwinder after failed name change" on 115 116 # Now actually disable the unwinder by manually adjusting the 117 # 'enabled' attribute. Check that the stack is once again broken, and 118 # that the unwinder shows as disabled in the 'info unwinder' output. 119 gdb_test_no_output "python global_test_unwinder.enabled = False" 120 check_for_broken_backtrace "stack is broken after disabling" 121 check_info_unwinder "info unwinder after manually disabling" off 122 123 # Now enable the unwinder using the 'enable unwinder' command. 124 gdb_test "enable unwinder global \"test unwinder\"" \ 125 "1 unwinder enabled" 126 check_for_fixed_backtrace "check backtrace after enabling with command" 127 check_info_unwinder "info unwinder after command enabled" on 128 129 # And now disable using the command and check the stack is once again 130 # broken, and that the 'info unwinder' output updates correctly. 131 gdb_test "disable unwinder global \"test unwinder\"" \ 132 "1 unwinder disabled" 133 check_for_broken_backtrace "stack is broken after command disabling" 134 check_info_unwinder "info unwinder after command disabling" off 135 136 # Check that invalid register names and values cause errors. 137 gdb_test "python print(add_saved_register_errors\[\"unknown_name\"\])" \ 138 "Bad register" \ 139 "add_saved_register error when an unknown register name is used" 140 gdb_test "python print(add_saved_register_errors\[\"unknown_number\"\])" \ 141 "Bad register" \ 142 "add_saved_register error when an unknown register number is used" 143 gdb_test "python print(add_saved_register_errors\[\"bad_value\"\])" \ 144 "argument 2 must be gdb.Value, not int" \ 145 "add_saved_register error when invalid register value is used" 146 gdb_test "python print(read_register_error)" "Bad register" \ 147 "read_register error" 148 149 # Try to create an unwinder object with a non-string name. 150 gdb_test "python obj = simple_unwinder(True)" \ 151 [multi_line \ 152 "TypeError.*: incorrect type for name: <class 'bool'>" \ 153 "Error occurred in Python.*"] 154 155 # Now register the simple_unwinder with a valid name, and use the 156 # unwinder to capture a PendingFrame object. 157 gdb_test_no_output "python obj = simple_unwinder(\"simple\")" 158 gdb_test_no_output "python gdb.unwinder.register_unwinder(None, obj)" 159 check_for_broken_backtrace "backtrace to capture a PendingFrame object" 160 161 # Check the captured PendingFrame is not valid. 162 gdb_test "python print(captured_pending_frame.is_valid())" "False" 163 164 # Check the __repr__ of an invalid PendingFrame. 165 gdb_test "python print(repr(captured_pending_frame))" \ 166 "<gdb.PendingFrame \\(invalid\\)>" 167 168 # Check the __repr__ of an UnwindInfo for an invalid PendingFrame. 169 gdb_test "python print(captured_unwind_info)" 170 gdb_test "python print(repr(captured_unwind_info))" \ 171 "<gdb.UnwindInfo for an invalid frame>" 172 173 # Check the repr of a PendingFrame that was copied (as a string) at a 174 # time the PendingFrame was valid. 175 gdb_test "python print(captured_pending_frame_repr)" \ 176 "<gdb.PendingFrame level=0, sp=$hex, pc=$hex>" 177 178 # Check the repr of an UnwindInfo that was copied (as a string) at a 179 # time the UnwindInfo was valid. 180 gdb_test "python print(captured_unwind_info_repr)" \ 181 "<gdb.UnwindInfo frame #0, saved_regs=\\(rip, rbp, rsp\\)>" 182 183 # Call methods on the captured gdb.PendingFrame and check we see the 184 # expected error. 185 gdb_test_no_output "python pf = captured_pending_frame" 186 foreach cmd {"pf.read_register(\"pc\")" \ 187 "pf.create_unwind_info(None)" \ 188 "pf.architecture()" \ 189 "pf.level()" \ 190 "pf.name()" \ 191 "pf.pc()" \ 192 "pf.language()" \ 193 "pf.find_sal()" \ 194 "pf.block()" \ 195 "pf.function()" } { 196 gdb_test "python $cmd" \ 197 [multi_line \ 198 "ValueError.*: gdb\\.PendingFrame is invalid\\." \ 199 "Error occurred in Python.*"] 200 } 201 202 # Turn on the useful unwinder so we have the full backtrace again, and 203 # disable the simple unwinder -- because we can! 204 gdb_test "enable unwinder global \"test unwinder\"" \ 205 "1 unwinder enabled" \ 206 "re-enable 'test unwinder' so we can check PendingFrame methods" 207 gdb_test "disable unwinder global \"simple\"" \ 208 "1 unwinder disabled" 209 check_for_fixed_backtrace \ 210 "check backtrace before testing PendingFrame methods" 211 212 # Turn the 'simple' unwinder back on. 213 gdb_test "enable unwinder global \"simple\"" \ 214 "1 unwinder enabled" 215 216 # Replace the "simple" unwinder with a new version that doesn't set 217 # the 'sp' attribute. Also the 'pc' attribute is invalid, but we'll 218 # hit the missing 'sp' error first. 219 with_test_prefix "frame-id 'sp' is None" { 220 gdb_test_no_output "python obj = simple_unwinder(\"simple\", None, \"xyz\")" 221 gdb_test_no_output "python gdb.unwinder.register_unwinder(None, obj, replace=True)" 222 gdb_test_no_output "python captured_pending_frame = None" 223 gdb_test "backtrace" \ 224 "Python Exception <class 'ValueError'>: frame_id should have 'sp' attribute\\.\r\n.*" 225 } 226 227 # Replace the "simple" unwinder with a new version that sets the 'sp' 228 # attribute to an invalid value. Also the 'pc' attribute is invalid, but we'll 229 # hit the invalid 'sp' error first. 230 with_test_prefix "frame-id 'sp' is invalid" { 231 gdb_test_no_output "python obj = simple_unwinder(\"simple\", \"jkl\", \"xyz\")" 232 gdb_test_no_output "python gdb.unwinder.register_unwinder(None, obj, replace=True)" 233 gdb_test_no_output "python captured_pending_frame = None" 234 gdb_test "backtrace" \ 235 "Python Exception <class 'ValueError'>: invalid literal for int\\(\\) with base 10: 'jkl'\r\n.*" 236 } 237 238 # Replace the "simple" unwinder with a new version that sets the 'sp' 239 # to a valid value, but set the 'pc' attribute to an invalid value. 240 with_test_prefix "frame-id 'pc' is invalid" { 241 gdb_test_no_output "python obj = simple_unwinder(\"simple\", 0x123, \"xyz\")" 242 gdb_test_no_output "python gdb.unwinder.register_unwinder(None, obj, replace=True)" 243 gdb_test_no_output "python captured_pending_frame = None" 244 gdb_test "backtrace" \ 245 "Python Exception <class 'ValueError'>: invalid literal for int\\(\\) with base 10: 'xyz'\r\n.*" 246 } 247 248 # Gather information about every frame. 249 gdb_test_no_output "python capture_all_frame_information()" 250 gdb_test_no_output "python gdb.newest_frame().select()" 251 gdb_test_no_output "python pspace = gdb.selected_inferior().progspace" 252 gdb_test_no_output "python obj = validating_unwinder()" 253 gdb_test_no_output "python gdb.unwinder.register_unwinder(pspace, obj)" 254 255 check_for_fixed_backtrace \ 256 "check backtrace to validate all information" 257 258 gdb_test_no_output "python check_all_frame_information_matched()" 259 260 # Check we can't sub-class from gdb.UnwindInfo. 261 gdb_test_multiline "Sub-class gdb.UnwindInfo " \ 262 "python" "" \ 263 "class my_unwind_info(gdb.UnwindInfo):" "" \ 264 " def __init__(self):" "" \ 265 " pass" "" \ 266 "end" \ 267 [multi_line \ 268 "TypeError.*: type 'gdb\\.UnwindInfo' is not an acceptable base type" \ 269 "Error occurred in Python.*"] 270 271 # Check we can't directly instantiate a gdb.UnwindInfo. 272 gdb_test "python uw = gdb.UnwindInfo()" \ 273 [multi_line \ 274 "TypeError.*: cannot create 'gdb\\.UnwindInfo' instances" \ 275 "Error occurred in Python.*"] 276