]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - gdb/testsuite/gdb.python/py-disasm.exp
Update copyright year range in header of all files managed by GDB
[thirdparty/binutils-gdb.git] / gdb / testsuite / gdb.python / py-disasm.exp
1 # Copyright (C) 2021-2023 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 validates the Python
17 # disassembler API.
18
19 load_lib gdb-python.exp
20
21 standard_testfile
22
23 if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} "debug"] } {
24 return -1
25 }
26
27 # Skip all tests if Python scripting is not enabled.
28 if { [skip_python_tests] } { continue }
29
30 if {![runto_main]} {
31 fail "can't run to main"
32 return 0
33 }
34
35 set pyfile [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
36
37 gdb_test "source ${pyfile}" "Python script imported" \
38 "import python scripts"
39
40 gdb_breakpoint [gdb_get_line_number "Break here."]
41 gdb_continue_to_breakpoint "Break here."
42
43 set curr_pc [get_valueof "/x" "\$pc" "*unknown*"]
44
45 gdb_test_no_output "python current_pc = ${curr_pc}"
46
47 # The current pc will be something like 0x1234 with no leading zeros.
48 # However, in the disassembler output addresses are padded with zeros.
49 # This substitution changes 0x1234 to 0x0*1234, which can then be used
50 # as a regexp in the disassembler output matching.
51 set curr_pc_pattern [string replace ${curr_pc} 0 1 "0x0*"]
52
53 # Grab the name of the current architecture, this is used in the tests
54 # patterns below.
55 set curr_arch [get_python_valueof "gdb.selected_inferior().architecture().name()" "*unknown*"]
56
57 # Helper proc that removes all registered disassemblers.
58 proc py_remove_all_disassemblers {} {
59 gdb_test_no_output "python remove_all_python_disassemblers()"
60 }
61
62 # A list of test plans. Each plan is a list of two elements, the
63 # first element is the name of a class in py-disasm.py, this is a
64 # disassembler class. The second element is a pattern that should be
65 # matched in the disassembler output.
66 #
67 # Each different disassembler tests some different feature of the
68 # Python disassembler API.
69 set nop "(nop|nop\t0)"
70 set unknown_error_pattern "unknown disassembler error \\(error = -1\\)"
71 set addr_pattern "\r\n=> ${curr_pc_pattern} <\[^>\]+>:\\s+"
72 set base_pattern "${addr_pattern}${nop}"
73 set test_plans \
74 [list \
75 [list "" "${base_pattern}\r\n.*"] \
76 [list "GlobalNullDisassembler" "${base_pattern}\r\n.*"] \
77 [list "GlobalPreInfoDisassembler" "${base_pattern}\\s+## ad = $hex, ar = ${curr_arch}\r\n.*"] \
78 [list "GlobalPostInfoDisassembler" "${base_pattern}\\s+## ad = $hex, ar = ${curr_arch}\r\n.*"] \
79 [list "GlobalReadDisassembler" "${base_pattern}\\s+## bytes =( $hex)+\r\n.*"] \
80 [list "GlobalAddrDisassembler" "${base_pattern}\\s+## addr = ${curr_pc_pattern} <\[^>\]+>\r\n.*"] \
81 [list "GdbErrorEarlyDisassembler" "${addr_pattern}GdbError instead of a result\r\n${unknown_error_pattern}"] \
82 [list "RuntimeErrorEarlyDisassembler" "${addr_pattern}Python Exception <class 'RuntimeError'>: RuntimeError instead of a result\r\n\r\n${unknown_error_pattern}"] \
83 [list "GdbErrorLateDisassembler" "${addr_pattern}GdbError after builtin disassembler\r\n${unknown_error_pattern}"] \
84 [list "RuntimeErrorLateDisassembler" "${addr_pattern}Python Exception <class 'RuntimeError'>: RuntimeError after builtin disassembler\r\n\r\n${unknown_error_pattern}"] \
85 [list "MemoryErrorEarlyDisassembler" "${base_pattern}\\s+## AFTER ERROR\r\n.*"] \
86 [list "MemoryErrorLateDisassembler" "${addr_pattern}Cannot access memory at address ${curr_pc_pattern}"] \
87 [list "RethrowMemoryErrorDisassembler" "${addr_pattern}Cannot access memory at address $hex"] \
88 [list "ReadMemoryMemoryErrorDisassembler" "${addr_pattern}Cannot access memory at address ${curr_pc_pattern}"] \
89 [list "ReadMemoryGdbErrorDisassembler" "${addr_pattern}read_memory raised GdbError\r\n${unknown_error_pattern}"] \
90 [list "ReadMemoryRuntimeErrorDisassembler" "${addr_pattern}Python Exception <class 'RuntimeError'>: read_memory raised RuntimeError\r\n\r\n${unknown_error_pattern}"] \
91 [list "ReadMemoryCaughtMemoryErrorDisassembler" "${addr_pattern}${nop}\r\n.*"] \
92 [list "ReadMemoryCaughtGdbErrorDisassembler" "${addr_pattern}${nop}\r\n.*"] \
93 [list "ReadMemoryCaughtRuntimeErrorDisassembler" "${addr_pattern}${nop}\r\n.*"] \
94 [list "MemorySourceNotABufferDisassembler" "${addr_pattern}Python Exception <class 'TypeError'>: Result from read_memory is not a buffer\r\n\r\n${unknown_error_pattern}"] \
95 [list "MemorySourceBufferTooLongDisassembler" "${addr_pattern}Python Exception <class 'ValueError'>: Buffer returned from read_memory is sized $decimal instead of the expected $decimal\r\n\r\n${unknown_error_pattern}"] \
96 [list "ResultOfWrongType" "${addr_pattern}Python Exception <class 'TypeError'>: Result is not a DisassemblerResult.\r\n.*"] \
97 [list "ResultWithInvalidLength" "${addr_pattern}Python Exception <class 'ValueError'>: Invalid length attribute: length must be greater than 0.\r\n.*"] \
98 [list "ResultWithInvalidString" "${addr_pattern}Python Exception <class 'ValueError'>: String attribute must not be empty.\r\n.*"]]
99
100 # Now execute each test plan.
101 foreach plan $test_plans {
102 set global_disassembler_name [lindex $plan 0]
103 set expected_pattern [lindex $plan 1]
104
105 with_test_prefix "global_disassembler=${global_disassembler_name}" {
106 # Remove all existing disassemblers.
107 py_remove_all_disassemblers
108
109 # If we have a disassembler to load, do it now.
110 if { $global_disassembler_name != "" } {
111 gdb_test_no_output "python add_global_disassembler($global_disassembler_name)"
112 }
113
114 # Disassemble test, and check the disassembler output.
115 gdb_test "disassemble test" $expected_pattern
116 }
117 }
118
119 # Check some errors relating to DisassemblerResult creation.
120 with_test_prefix "DisassemblerResult errors" {
121 gdb_test "python gdb.disassembler.DisassemblerResult(0, 'abc')" \
122 [multi_line \
123 "ValueError: Length must be greater than 0." \
124 "Error while executing Python code."]
125 gdb_test "python gdb.disassembler.DisassemblerResult(-1, 'abc')" \
126 [multi_line \
127 "ValueError: Length must be greater than 0." \
128 "Error while executing Python code."]
129 gdb_test "python gdb.disassembler.DisassemblerResult(1, '')" \
130 [multi_line \
131 "ValueError: String must not be empty." \
132 "Error while executing Python code."]
133 }
134
135 # Check that the architecture specific disassemblers can override the
136 # global disassembler.
137 #
138 # First, register a global disassembler, and check it is in place.
139 with_test_prefix "GLOBAL tagging disassembler" {
140 py_remove_all_disassemblers
141 gdb_test_no_output "python gdb.disassembler.register_disassembler(TaggingDisassembler(\"GLOBAL\"), None)"
142 gdb_test "disassemble test" "${base_pattern}\\s+## tag = GLOBAL\r\n.*"
143 }
144
145 # Now register an architecture specific disassembler, and check it
146 # overrides the global disassembler.
147 with_test_prefix "LOCAL tagging disassembler" {
148 gdb_test_no_output "python gdb.disassembler.register_disassembler(TaggingDisassembler(\"LOCAL\"), \"${curr_arch}\")"
149 gdb_test "disassemble test" "${base_pattern}\\s+## tag = LOCAL\r\n.*"
150 }
151
152 # Now remove the architecture specific disassembler, and check that
153 # the global disassembler kicks back in.
154 with_test_prefix "GLOBAL tagging disassembler again" {
155 gdb_test_no_output "python gdb.disassembler.register_disassembler(None, \"${curr_arch}\")"
156 gdb_test "disassemble test" "${base_pattern}\\s+## tag = GLOBAL\r\n.*"
157 }
158
159 # Check that a DisassembleInfo becomes invalid after the call into the
160 # disassembler.
161 with_test_prefix "DisassembleInfo becomes invalid" {
162 py_remove_all_disassemblers
163 gdb_test_no_output "python add_global_disassembler(GlobalCachingDisassembler)"
164 gdb_test "disassemble test" "${base_pattern}\\s+## CACHED\r\n.*"
165 gdb_test "python GlobalCachingDisassembler.check()" "PASS"
166 }
167
168 # Test the memory source aspect of the builtin disassembler.
169 with_test_prefix "memory source api" {
170 py_remove_all_disassemblers
171 gdb_test_no_output "python analyzing_disassembler = add_global_disassembler(AnalyzingDisassembler)"
172 gdb_test "disassemble test" "${base_pattern}\r\n.*"
173 gdb_test "python analyzing_disassembler.find_replacement_candidate()" \
174 "Replace from $hex to $hex with NOP"
175 gdb_test "disassemble test" "${base_pattern}\r\n.*" \
176 "second disassembler pass"
177 gdb_test "python analyzing_disassembler.check()" \
178 "PASS"
179 }
180
181 # Test the 'maint info python-disassemblers command.
182 with_test_prefix "maint info python-disassemblers" {
183 py_remove_all_disassemblers
184 gdb_test "maint info python-disassemblers" "No Python disassemblers registered\\." \
185 "list disassemblers, none registered"
186 gdb_test_no_output "python disasm = add_global_disassembler(BuiltinDisassembler)"
187 gdb_test "maint info python-disassemblers" \
188 [multi_line \
189 "Architecture\\s+Disassember Name" \
190 "GLOBAL\\s+BuiltinDisassembler\\s+\\(Matches current architecture\\)"] \
191 "list disassemblers, single global disassembler"
192 gdb_test_no_output "python arch = gdb.selected_inferior().architecture().name()"
193 gdb_test_no_output "python gdb.disassembler.register_disassembler(disasm, arch)"
194 gdb_test "maint info python-disassemblers" \
195 [multi_line \
196 "Architecture\\s+Disassember Name" \
197 "\[^\r\n\]+BuiltinDisassembler\\s+\\(Matches current architecture\\)" \
198 "GLOBAL\\s+BuiltinDisassembler"] \
199 "list disassemblers, multiple disassemblers registered"
200
201 # Check that disassembling main (with the BuiltinDisassembler in
202 # place) doesn't cause GDB to crash. The hope is that
203 # disassembling main will result in a call to print_address, which
204 # is where the problem was.
205 gdb_test "disassemble main" ".*"
206 }
207
208 # Check the attempt to create a "new" DisassembleInfo object fails.
209 with_test_prefix "Bad DisassembleInfo creation" {
210 gdb_test_no_output "python my_info = InvalidDisassembleInfo()"
211 gdb_test "python print(my_info.is_valid())" "True"
212 gdb_test "python gdb.disassembler.builtin_disassemble(my_info)" \
213 [multi_line \
214 "RuntimeError: DisassembleInfo is no longer valid\\." \
215 "Error while executing Python code\\."]
216 }