]>
Commit | Line | Data |
---|---|---|
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 | ||
19 | load_lib gdb-python.exp | |
20 | ||
d82e5429 | 21 | require allow_python_tests |
79749205 | 22 | |
15e15b2d AB |
23 | standard_testfile |
24 | ||
25 | if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} "debug"] } { | |
26 | return -1 | |
27 | } | |
28 | ||
b0e16ca5 | 29 | if {![runto_main]} { |
15e15b2d AB |
30 | fail "can't run to main" |
31 | return 0 | |
32 | } | |
33 | ||
34 | set pyfile [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py] | |
35 | ||
36 | gdb_test "source ${pyfile}" "Python script imported" \ | |
37 | "import python scripts" | |
38 | ||
39 | gdb_breakpoint [gdb_get_line_number "Break here."] | |
40 | gdb_continue_to_breakpoint "Break here." | |
41 | ||
42 | set curr_pc [get_valueof "/x" "\$pc" "*unknown*"] | |
43 | ||
44 | gdb_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. | |
50 | set 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. | |
54 | set curr_arch [get_python_valueof "gdb.selected_inferior().architecture().name()" "*unknown*"] | |
55 | ||
56 | # Helper proc that removes all registered disassemblers. | |
57 | proc 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 | 68 | set nop "(nop|nop\t0)" |
15e15b2d AB |
69 | set unknown_error_pattern "unknown disassembler error \\(error = -1\\)" |
70 | set addr_pattern "\r\n=> ${curr_pc_pattern} <\[^>\]+>:\\s+" | |
fa59ab98 | 71 | set base_pattern "${addr_pattern}${nop}" |
4de4e485 AB |
72 | |
73 | # Helper proc to format a Python exception of TYPE with MSG. | |
74 | proc 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. | |
82 | proc 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 |
88 | set 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. | |
145 | foreach 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. | |
164 | with_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. | |
183 | with_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. | |
191 | with_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. | |
198 | with_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. | |
205 | with_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. | |
213 | with_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. | |
226 | with_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. | |
253 | with_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. | |
264 | with_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. | |
284 | gdb_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 | ||
290 | foreach 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. | |
300 | foreach 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 | } |