Home | History | Annotate | Line # | Download | only in gdb.python
      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