]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - 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
CommitLineData
1d506c26 1# Copyright (C) 2021-2024 Free Software Foundation, Inc.
15e15b2d
AB
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
19load_lib gdb-python.exp
20
d82e5429 21require allow_python_tests
79749205 22
15e15b2d
AB
23standard_testfile
24
25if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} "debug"] } {
26 return -1
27}
28
b0e16ca5 29if {![runto_main]} {
15e15b2d
AB
30 fail "can't run to main"
31 return 0
32}
33
34set pyfile [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
35
36gdb_test "source ${pyfile}" "Python script imported" \
37 "import python scripts"
38
39gdb_breakpoint [gdb_get_line_number "Break here."]
40gdb_continue_to_breakpoint "Break here."
41
42set curr_pc [get_valueof "/x" "\$pc" "*unknown*"]
43
44gdb_test_no_output "python current_pc = ${curr_pc}"
45
46# The current pc will be something like 0x1234 with no leading zeros.
47# However, in the disassembler output addresses are padded with zeros.
48# This substitution changes 0x1234 to 0x0*1234, which can then be used
49# as a regexp in the disassembler output matching.
50set curr_pc_pattern [string replace ${curr_pc} 0 1 "0x0*"]
51
52# Grab the name of the current architecture, this is used in the tests
53# patterns below.
54set curr_arch [get_python_valueof "gdb.selected_inferior().architecture().name()" "*unknown*"]
55
56# Helper proc that removes all registered disassemblers.
57proc py_remove_all_disassemblers {} {
58 gdb_test_no_output "python remove_all_python_disassemblers()"
59}
60
61# A list of test plans. Each plan is a list of two elements, the
62# first element is the name of a class in py-disasm.py, this is a
63# disassembler class. The second element is a pattern that should be
64# matched in the disassembler output.
65#
66# Each different disassembler tests some different feature of the
67# Python disassembler API.
fa59ab98 68set nop "(nop|nop\t0)"
15e15b2d
AB
69set unknown_error_pattern "unknown disassembler error \\(error = -1\\)"
70set addr_pattern "\r\n=> ${curr_pc_pattern} <\[^>\]+>:\\s+"
fa59ab98 71set base_pattern "${addr_pattern}${nop}"
4de4e485
AB
72
73# Helper proc to format a Python exception of TYPE with MSG.
74proc make_exception_pattern { type msg } {
75 return "${::addr_pattern}Python Exception <class '$type'>: $msg\r\n\r\n${::unknown_error_pattern}"
76}
77
637969a7
AB
78# Helper proc to build a pattern for the text Python emits when a
79# function argument is missing. This string changed in Python 3.7 and
80# later. NAME is the parameter name, and POS is its integer position
81# in the argument list.
82proc missing_arg_pattern { name pos } {
83 set pattern_1 "function missing required argument '$name' \\(pos $pos\\)"
84 set pattern_2 "Required argument '$name' \\(pos $pos\\) not found"
85 return "(?:${pattern_1}|${pattern_2})"
86}
87
15e15b2d
AB
88set test_plans \
89 [list \
90 [list "" "${base_pattern}\r\n.*"] \
91 [list "GlobalNullDisassembler" "${base_pattern}\r\n.*"] \
15ccb5e3
AB
92 [list "ShowInfoRepr" "${base_pattern}\\s+## <gdb.disassembler.DisassembleInfo address=$hex architecture=\[^>\]+>\r\n.*"] \
93 [list "ShowInfoSubClassRepr" "${base_pattern}\\s+## <MyInfo address=$hex architecture=\[^>\]+>\r\n.*"] \
94 [list "ShowResultRepr" "${base_pattern}\\s+## <gdb.disassembler.DisassemblerResult length=$decimal string=\"\[^\r\n\]+\">\r\n.*"] \
6a667807 95 [list "ShowResultStr" "${base_pattern}\\s+## ${nop}\r\n.*"] \
15e15b2d
AB
96 [list "GlobalPreInfoDisassembler" "${base_pattern}\\s+## ad = $hex, ar = ${curr_arch}\r\n.*"] \
97 [list "GlobalPostInfoDisassembler" "${base_pattern}\\s+## ad = $hex, ar = ${curr_arch}\r\n.*"] \
98 [list "GlobalReadDisassembler" "${base_pattern}\\s+## bytes =( $hex)+\r\n.*"] \
99 [list "GlobalAddrDisassembler" "${base_pattern}\\s+## addr = ${curr_pc_pattern} <\[^>\]+>\r\n.*"] \
100 [list "GdbErrorEarlyDisassembler" "${addr_pattern}GdbError instead of a result\r\n${unknown_error_pattern}"] \
101 [list "RuntimeErrorEarlyDisassembler" "${addr_pattern}Python Exception <class 'RuntimeError'>: RuntimeError instead of a result\r\n\r\n${unknown_error_pattern}"] \
102 [list "GdbErrorLateDisassembler" "${addr_pattern}GdbError after builtin disassembler\r\n${unknown_error_pattern}"] \
103 [list "RuntimeErrorLateDisassembler" "${addr_pattern}Python Exception <class 'RuntimeError'>: RuntimeError after builtin disassembler\r\n\r\n${unknown_error_pattern}"] \
104 [list "MemoryErrorEarlyDisassembler" "${base_pattern}\\s+## AFTER ERROR\r\n.*"] \
105 [list "MemoryErrorLateDisassembler" "${addr_pattern}Cannot access memory at address ${curr_pc_pattern}"] \
106 [list "RethrowMemoryErrorDisassembler" "${addr_pattern}Cannot access memory at address $hex"] \
107 [list "ReadMemoryMemoryErrorDisassembler" "${addr_pattern}Cannot access memory at address ${curr_pc_pattern}"] \
108 [list "ReadMemoryGdbErrorDisassembler" "${addr_pattern}read_memory raised GdbError\r\n${unknown_error_pattern}"] \
4de4e485
AB
109 [list "ReadMemoryRuntimeErrorDisassembler" \
110 [make_exception_pattern "RuntimeError" \
111 "read_memory raised RuntimeError"]] \
fa59ab98
TV
112 [list "ReadMemoryCaughtMemoryErrorDisassembler" "${addr_pattern}${nop}\r\n.*"] \
113 [list "ReadMemoryCaughtGdbErrorDisassembler" "${addr_pattern}${nop}\r\n.*"] \
114 [list "ReadMemoryCaughtRuntimeErrorDisassembler" "${addr_pattern}${nop}\r\n.*"] \
4de4e485
AB
115 [list "MemorySourceNotABufferDisassembler" \
116 [make_exception_pattern "TypeError" \
117 "Result from read_memory is not a buffer"]] \
118 [list "MemorySourceBufferTooLongDisassembler" \
119 [make_exception_pattern "ValueError" \
120 "Buffer returned from read_memory is sized $decimal instead of the expected $decimal"]] \
121 [list "ResultOfWrongType" \
122 [make_exception_pattern "TypeError" \
123 "Result is not a DisassemblerResult."]] \
124 [list "ErrorCreatingTextPart_NoArgs" \
125 [make_exception_pattern "TypeError" \
637969a7 126 [missing_arg_pattern "style" 1]]] \
4de4e485
AB
127 [list "ErrorCreatingAddressPart_NoArgs" \
128 [make_exception_pattern "TypeError" \
637969a7 129 [missing_arg_pattern "address" 1]]] \
4de4e485
AB
130 [list "ErrorCreatingTextPart_NoString" \
131 [make_exception_pattern "TypeError" \
637969a7 132 [missing_arg_pattern "string" 2]]] \
4de4e485
AB
133 [list "ErrorCreatingTextPart_NoStyle" \
134 [make_exception_pattern "TypeError" \
637969a7 135 [missing_arg_pattern "style" 1]]] \
4de4e485
AB
136 [list "All_Text_Part_Styles" "${addr_pattern}p1p2p3p4p5p6p7p8p9p10\r\n.*"] \
137 [list "ErrorCreatingTextPart_StringAndParts" \
138 [make_exception_pattern "ValueError" \
139 "Cannot use 'string' and 'parts' when creating gdb\\.disassembler\\.DisassemblerResult\\."]] \
140 [list "Build_Result_Using_All_Parts" \
141 "${addr_pattern}fake\treg, ${curr_pc_pattern}(?: <\[^>\]+>)?, 123\r\n.*"] \
142 ]
15e15b2d
AB
143
144# Now execute each test plan.
145foreach plan $test_plans {
146 set global_disassembler_name [lindex $plan 0]
147 set expected_pattern [lindex $plan 1]
148
149 with_test_prefix "global_disassembler=${global_disassembler_name}" {
150 # Remove all existing disassemblers.
151 py_remove_all_disassemblers
152
153 # If we have a disassembler to load, do it now.
154 if { $global_disassembler_name != "" } {
155 gdb_test_no_output "python add_global_disassembler($global_disassembler_name)"
156 }
157
23aa2bef
AB
158 # Disassemble test, and check the disassembler output.
159 gdb_test "disassemble test" $expected_pattern
15e15b2d
AB
160 }
161}
162
163# Check some errors relating to DisassemblerResult creation.
164with_test_prefix "DisassemblerResult errors" {
165 gdb_test "python gdb.disassembler.DisassemblerResult(0, 'abc')" \
166 [multi_line \
167 "ValueError: Length must be greater than 0." \
168 "Error while executing Python code."]
169 gdb_test "python gdb.disassembler.DisassemblerResult(-1, 'abc')" \
170 [multi_line \
171 "ValueError: Length must be greater than 0." \
172 "Error while executing Python code."]
173 gdb_test "python gdb.disassembler.DisassemblerResult(1, '')" \
174 [multi_line \
175 "ValueError: String must not be empty." \
176 "Error while executing Python code."]
177}
178
179# Check that the architecture specific disassemblers can override the
180# global disassembler.
181#
182# First, register a global disassembler, and check it is in place.
183with_test_prefix "GLOBAL tagging disassembler" {
184 py_remove_all_disassemblers
185 gdb_test_no_output "python gdb.disassembler.register_disassembler(TaggingDisassembler(\"GLOBAL\"), None)"
23aa2bef 186 gdb_test "disassemble test" "${base_pattern}\\s+## tag = GLOBAL\r\n.*"
15e15b2d
AB
187}
188
189# Now register an architecture specific disassembler, and check it
190# overrides the global disassembler.
191with_test_prefix "LOCAL tagging disassembler" {
192 gdb_test_no_output "python gdb.disassembler.register_disassembler(TaggingDisassembler(\"LOCAL\"), \"${curr_arch}\")"
23aa2bef 193 gdb_test "disassemble test" "${base_pattern}\\s+## tag = LOCAL\r\n.*"
15e15b2d
AB
194}
195
196# Now remove the architecture specific disassembler, and check that
197# the global disassembler kicks back in.
198with_test_prefix "GLOBAL tagging disassembler again" {
199 gdb_test_no_output "python gdb.disassembler.register_disassembler(None, \"${curr_arch}\")"
23aa2bef 200 gdb_test "disassemble test" "${base_pattern}\\s+## tag = GLOBAL\r\n.*"
15e15b2d
AB
201}
202
203# Check that a DisassembleInfo becomes invalid after the call into the
204# disassembler.
205with_test_prefix "DisassembleInfo becomes invalid" {
206 py_remove_all_disassemblers
207 gdb_test_no_output "python add_global_disassembler(GlobalCachingDisassembler)"
23aa2bef 208 gdb_test "disassemble test" "${base_pattern}\\s+## CACHED\r\n.*"
15e15b2d
AB
209 gdb_test "python GlobalCachingDisassembler.check()" "PASS"
210}
211
212# Test the memory source aspect of the builtin disassembler.
213with_test_prefix "memory source api" {
214 py_remove_all_disassemblers
215 gdb_test_no_output "python analyzing_disassembler = add_global_disassembler(AnalyzingDisassembler)"
23aa2bef 216 gdb_test "disassemble test" "${base_pattern}\r\n.*"
15e15b2d
AB
217 gdb_test "python analyzing_disassembler.find_replacement_candidate()" \
218 "Replace from $hex to $hex with NOP"
23aa2bef 219 gdb_test "disassemble test" "${base_pattern}\r\n.*" \
15e15b2d
AB
220 "second disassembler pass"
221 gdb_test "python analyzing_disassembler.check()" \
222 "PASS"
223}
224
225# Test the 'maint info python-disassemblers command.
226with_test_prefix "maint info python-disassemblers" {
227 py_remove_all_disassemblers
228 gdb_test "maint info python-disassemblers" "No Python disassemblers registered\\." \
229 "list disassemblers, none registered"
230 gdb_test_no_output "python disasm = add_global_disassembler(BuiltinDisassembler)"
231 gdb_test "maint info python-disassemblers" \
232 [multi_line \
233 "Architecture\\s+Disassember Name" \
234 "GLOBAL\\s+BuiltinDisassembler\\s+\\(Matches current architecture\\)"] \
235 "list disassemblers, single global disassembler"
236 gdb_test_no_output "python arch = gdb.selected_inferior().architecture().name()"
237 gdb_test_no_output "python gdb.disassembler.register_disassembler(disasm, arch)"
238 gdb_test "maint info python-disassemblers" \
239 [multi_line \
240 "Architecture\\s+Disassember Name" \
241 "\[^\r\n\]+BuiltinDisassembler\\s+\\(Matches current architecture\\)" \
242 "GLOBAL\\s+BuiltinDisassembler"] \
243 "list disassemblers, multiple disassemblers registered"
23aa2bef
AB
244
245 # Check that disassembling main (with the BuiltinDisassembler in
246 # place) doesn't cause GDB to crash. The hope is that
247 # disassembling main will result in a call to print_address, which
248 # is where the problem was.
249 gdb_test "disassemble main" ".*"
15e15b2d
AB
250}
251
252# Check the attempt to create a "new" DisassembleInfo object fails.
253with_test_prefix "Bad DisassembleInfo creation" {
254 gdb_test_no_output "python my_info = InvalidDisassembleInfo()"
255 gdb_test "python print(my_info.is_valid())" "True"
256 gdb_test "python gdb.disassembler.builtin_disassemble(my_info)" \
257 [multi_line \
258 "RuntimeError: DisassembleInfo is no longer valid\\." \
259 "Error while executing Python code\\."]
260}
0af2f233 261
4de4e485
AB
262# Some of the disassembler related types should not be sub-typed,
263# check these now.
264with_test_prefix "check inheritance" {
265 foreach_with_prefix type {gdb.disassembler.DisassemblerResult \
266 gdb.disassembler.DisassemblerPart
267 gdb.disassembler.DisassemblerTextPart \
268 gdb.disassembler.DisassemblerAddressPart} {
269 set type_ptn [string_to_regexp $type]
270 gdb_test_multiline "Sub-class a breakpoint" \
271 "python" "" \
272 "class InvalidResultType($type):" "" \
273 " def __init__(self):" "" \
274 " pass" "" \
275 "end" \
276 [multi_line \
277 "TypeError: type '${type_ptn}' is not an acceptable base type" \
278 "Error while executing Python code\\."]
279 }
280}
281
282
283# Test some error conditions when creating a DisassemblerResult object.
284gdb_test "python result = gdb.disassembler.DisassemblerResult()" \
0af2f233 285 [multi_line \
637969a7 286 "TypeError: [missing_arg_pattern length 1]" \
4de4e485
AB
287 "Error while executing Python code\\."] \
288 "try to create a DisassemblerResult without a length argument"
289
290foreach len {0 -1} {
291 gdb_test "python result = gdb.disassembler.DisassemblerResult($len)" \
292 [multi_line \
293 "ValueError: Length must be greater than 0\\." \
294 "Error while executing Python code\\."] \
295 "try to create a DisassemblerResult with length $len"
296}
297
298# Check we can't directly create DisassemblerTextPart or
299# DisassemblerAddressPart objects.
300foreach type {DisassemblerTextPart DisassemblerAddressPart} {
301 gdb_test "python result = gdb.disassembler.${type}()" \
302 [multi_line \
303 "RuntimeError: Cannot create instances of DisassemblerPart\\." \
304 "Error while executing Python code\\."] \
305 "try to create an instance of ${type}"
306}