Home | History | Annotate | Line # | Download | only in gdb.python
      1 # Copyright (C) 2019-2024 Free Software Foundation, Inc.
      2 # This program is free software; you can redistribute it and/or modify
      3 # it under the terms of the GNU General Public License as published by
      4 # the Free Software Foundation; either version 3 of the License, or
      5 # (at your option) any later version.
      6 #
      7 # This program is distributed in the hope that it will be useful,
      8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
      9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     10 # GNU General Public License for more details.
     11 #
     12 # You should have received a copy of the GNU General Public License
     13 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
     14 
     15 # Test custom MI commands implemented in Python.
     16 
     17 load_lib gdb-python.exp
     18 load_lib mi-support.exp
     19 set MIFLAGS "-i=mi"
     20 
     21 gdb_exit
     22 if {[mi_gdb_start]} {
     23     return
     24 }
     25 
     26 if {[lsearch -exact [mi_get_features] python] < 0} {
     27     unsupported "python support is disabled"
     28     return -1
     29 }
     30 
     31 standard_testfile
     32 
     33 mi_gdb_test "set python print-stack full" \
     34     ".*\\^done" \
     35     "set python print-stack full"
     36 
     37 mi_gdb_test "source ${srcdir}/${subdir}/${testfile}.py" \
     38     ".*\\^done" \
     39     "load python file"
     40 
     41 mi_gdb_test "python pycmd1('-pycmd')" \
     42     ".*\\^done" \
     43     "define -pycmd MI command"
     44 
     45 mi_gdb_test "-pycmd int" \
     46     "\\^done,result=\"42\"" \
     47     "-pycmd int"
     48 
     49 mi_gdb_test "-pycmd str" \
     50     "\\^done,result=\"Hello world!\"" \
     51     "-pycmd str"
     52 
     53 mi_gdb_test "-pycmd ary" \
     54     "\\^done,result=\\\[\"Hello\",\"42\"\\\]" \
     55     "-pycmd ary"
     56 
     57 set re_order1 "\\^done,result={hello=\"world\",times=\"42\"}"
     58 set re_order2 "\\^done,result={times=\"42\",hello=\"world\"}"
     59 mi_gdb_test "-pycmd dct" \
     60     "($re_order1|$re_order2)" \
     61     "-pycmd dct"
     62 
     63 mi_gdb_test "-pycmd bk1" \
     64     "\\^error,msg=\"Error occurred in Python: non-string object used as key: Bad Key\"" \
     65     "-pycmd bk1"
     66 
     67 mi_gdb_test "-pycmd bk2" \
     68     "\\^error,msg=\"Error occurred in Python: non-string object used as key: 1\"" \
     69     "-pycmd bk2"
     70 
     71 mi_gdb_test "-pycmd bk3" \
     72     [multi_line \
     73 	 "&\"TypeError.*: __repr__ returned non-string \\(type BadKey\\)..\"" \
     74 	 "\\^error,msg=\"Error occurred in Python: __repr__ returned non-string \\(type BadKey\\)\""] \
     75     "-pycmd bk3"
     76 
     77 mi_gdb_test "-pycmd tpl" \
     78     "\\^done,result=\\\[\"42\",\"Hello\"\\\]" \
     79     "-pycmd tpl"
     80 
     81 mi_gdb_test "-pycmd itr" \
     82     "\\^done,result=\\\[\"1\",\"2\",\"3\"\\\]" \
     83     "-pycmd itr"
     84 
     85 mi_gdb_test "-pycmd nn1" \
     86     "\\^done" \
     87     "-pycmd nn1"
     88 
     89 mi_gdb_test "-pycmd nn2" \
     90     "\\^done,result=\\\[\"None\"\\\]" \
     91     "-pycmd nn2"
     92 
     93 mi_gdb_test "-pycmd bogus" \
     94     "\\^error,msg=\"Invalid parameter: bogus\"" \
     95     "-pycmd bogus"
     96 
     97 # Check that the top-level result from 'invoke' must be a dictionary.
     98 foreach test_name { nd1 nd2 nd3 } {
     99     mi_gdb_test "-pycmd ${test_name}" \
    100 	"\\^error,msg=\"Error occurred in Python: Result from invoke must be a dictionary\""
    101 }
    102 
    103 # Check for invalid strings in the result.
    104 foreach test_desc { {ik1 "xxx yyy"} {ik2 "xxx yyy"} {ik3 "xxx\\+yyy"} \
    105 			{ik4 "xxx\\.yyy"} {ik5 "123xxxyyy"} } {
    106     lassign $test_desc name pattern
    107 
    108     mi_gdb_test "-pycmd ${name}" \
    109 	"\\^error,msg=\"Error occurred in Python: Invalid key in MI result: ${pattern}\""
    110 }
    111 
    112 mi_gdb_test "-pycmd empty_key" \
    113     "\\^error,msg=\"Error occurred in Python: Invalid empty key in MI result\""
    114 
    115 # Check that a dash ('-') can be used in a key name.
    116 mi_gdb_test "-pycmd dash-key" \
    117     "\\^done,the-key=\"123\""
    118 
    119 # With this argument the command raises a gdb.GdbError with no message
    120 # string.  GDB considers this a bug in the user program, so prints a
    121 # backtrace, and a generic error message.
    122 
    123 set line1 \
    124     [string_to_regexp {Traceback (most recent call last):\n}]
    125 set line2 \
    126     [string cat \
    127 	 [string_to_regexp {  File \"}] \
    128 	 "\[^\r\n\]+" \
    129 	 [string_to_regexp ${testfile}.py] \
    130 	 [string_to_regexp {\", line }] \
    131 	 $decimal \
    132 	 [string_to_regexp {, in invoke\n}]]
    133 set line3 \
    134     [string_to_regexp {    raise gdb.GdbError()\n}]
    135 set line4 \
    136     [string_to_regexp {gdb.GdbError\n}]
    137 set errline \
    138     [string_to_regexp {^error,msg="Error occurred in Python."}]
    139 
    140 set start_line \
    141     [string_to_regexp {&"}]
    142 set end_line \
    143     [string_to_regexp {"}]
    144 
    145 # With python <= 3.12.
    146 set re1 \
    147     [multi_line \
    148 	 $start_line$line1$end_line \
    149 	 $start_line$line2$end_line \
    150 	 $start_line$line3$end_line \
    151 	 $start_line$line4$end_line \
    152 	 $errline]
    153 
    154 # With python >= 3.13.
    155 set re2 \
    156     [multi_line \
    157 	 $start_line$line1$end_line \
    158 	 $start_line$line2$line3$end_line \
    159 	 $start_line$line4$end_line \
    160 	 $errline]
    161 
    162 mi_gdb_test "-pycmd exp" ($re1|$re2)
    163 
    164 mi_gdb_test "python pycmd2('-pycmd')" \
    165     ".*\\^done" \
    166     "redefine -pycmd MI command from CLI command"
    167 
    168 mi_gdb_test "-pycmd str" \
    169     "\\^done,result=\"Ciao!\"" \
    170     "-pycmd str - redefined from CLI"
    171 
    172 mi_gdb_test "-pycmd int" \
    173     "\\^error,msg=\"Invalid parameter: int\"" \
    174     "-pycmd int - redefined from CLI"
    175 
    176 mi_gdb_test "-pycmd new" \
    177     "\\^done" \
    178     "Define new command -pycmd-new MI command from Python MI command"
    179 
    180 mi_gdb_test "-pycmd red" \
    181     "\\^error,msg=\"Command redefined but we failing anyway\"" \
    182     "redefine -pycmd MI command from Python MI command"
    183 
    184 mi_gdb_test "-pycmd int" \
    185     "\\^done,result=\"42\"" \
    186     "-pycmd int - redefined from MI"
    187 
    188 mi_gdb_test "-pycmd-new int" \
    189     "\\^done,result=\"42\"" \
    190     "-pycmd-new int - defined from MI"
    191 
    192 mi_gdb_test "python pycmd1('')" \
    193     ".*&\"ValueError.*: MI command name is empty\\...\".*\\^error,msg=\"Error occurred in Python.*\"" \
    194     "empty MI command name"
    195 
    196 mi_gdb_test "python pycmd1('-')" \
    197     [multi_line \
    198 	 ".*" \
    199 	 "&\"ValueError.*: MI command name does not start with '-' followed by at least one letter or digit\\...\"" \
    200 	 "&\"Error occurred in Python.*..\"" \
    201 	 "\\^error,msg=\"Error occurred in Python.*\""] \
    202     "invalid MI command name"
    203 
    204 mi_gdb_test "python pycmd1('-bad-character-@')" \
    205     [multi_line \
    206 	 ".*" \
    207 	 "&\"ValueError.*: MI command name contains invalid character: @\\...\"" \
    208 	 "&\"Error occurred in Python.*..\"" \
    209 	 "\\^error,msg=\"Error occurred in Python.*\""] \
    210     "invalid character in MI command name"
    211 
    212 mi_gdb_test "python cmd=pycmd1('-abc')" \
    213     ".*\\^done" \
    214     "create command -abc, stored in a python variable"
    215 
    216 mi_gdb_test "python print(cmd.name)" \
    217     ".*\r\n~\"-abc\\\\n\"\r\n\\^done" \
    218     "print the name of the stored mi command"
    219 
    220 mi_gdb_test "python print(cmd.installed)" \
    221     ".*\r\n~\"True\\\\n\"\r\n\\^done" \
    222     "print the installed status of the stored mi command"
    223 
    224 mi_gdb_test "-abc str" \
    225     "\\^done,result=\"Hello world!\"" \
    226     "-abc str"
    227 
    228 mi_gdb_test "python cmd.installed = False" \
    229     ".*\\^done" \
    230     "uninstall the mi command"
    231 
    232 mi_gdb_test "-abc str" \
    233     "\\^error,msg=\"Undefined MI command: abc\",code=\"undefined-command\"" \
    234     "-abc str, but now the command is gone"
    235 
    236 mi_gdb_test "python cmd.installed = None" \
    237     ".*\r\n\\^error,msg=\"Error occurred in Python: gdb\\.MICommand\\.installed must be set to a bool, not None\"" \
    238     "re-install the mi command using value None"
    239 
    240 mi_gdb_test "python cmd.installed = 1" \
    241     ".*\r\n\\^error,msg=\"Error occurred in Python: gdb\\.MICommand\\.installed must be set to a bool, not int\"" \
    242     "re-install the mi command using an int value"
    243 
    244 mi_gdb_test "python print(cmd.installed)" \
    245     [multi_line \
    246 	 ".*" \
    247 	 "~\"False\\\\n\"" \
    248 	 "\\^done"] \
    249     "cmd is still not installed"
    250 
    251 mi_gdb_test "python cmd.installed = True" \
    252     ".*\\^done" \
    253     "re-install the mi command"
    254 
    255 mi_gdb_test "-abc str" \
    256     "\\^done,result=\"Hello world!\"" \
    257     "-abc str, the command is back again"
    258 
    259 mi_gdb_test "python other=pycmd2('-abc')" \
    260     ".*\\^done" \
    261     "create another command called -abc, stored in a separate python variable"
    262 
    263 mi_gdb_test "python print(other.installed)" \
    264     ".*\r\n~\"True\\\\n\"\r\n\\^done" \
    265     "print the installed status of the other stored mi command"
    266 
    267 mi_gdb_test "python print(cmd.installed)" \
    268     ".*\r\n~\"False\\\\n\"\r\n\\^done" \
    269     "print the installed status of the original stored mi command"
    270 
    271 mi_gdb_test "-abc str" \
    272     "\\^done,result=\"Ciao!\"" \
    273     "-abc str, when the other command is in place"
    274 
    275 mi_gdb_test "python cmd.installed = True" \
    276     ".*\\^done" \
    277     "re-install the original mi command"
    278 
    279 mi_gdb_test "-abc str" \
    280     "\\^done,result=\"Hello world!\"" \
    281     "-abc str, the original command is back again"
    282 
    283 mi_gdb_test "python print(other.installed)" \
    284     ".*\r\n~\"False\\\\n\"\r\n\\^done" \
    285     "the other command is now not installed"
    286 
    287 mi_gdb_test "python print(cmd.installed)" \
    288     ".*\r\n~\"True\\\\n\"\r\n\\^done" \
    289     "the original command is now installed"
    290 
    291 mi_gdb_test "python aa = pycmd3('-aa', 'message one', 'xxx')" \
    292     ".*\\^done" \
    293     "created a new -aa command"
    294 
    295 mi_gdb_test "-aa" \
    296     ".*\\^done,xxx={msg=\"message one\"}" \
    297     "call the -aa command"
    298 
    299 mi_gdb_test "python aa.__init__('-aa', 'message two', 'yyy')" \
    300     ".*\\^done" \
    301     "reinitialise -aa command with a new message"
    302 
    303 mi_gdb_test "-aa" \
    304     ".*\\^done,yyy={msg=\"message two\"}" \
    305     "call the -aa command, get the new message"
    306 
    307 mi_gdb_test "python aa.__init__('-bb', 'message three', 'zzz')" \
    308     [multi_line \
    309 	 ".*" \
    310 	 "&\"ValueError.*: can't reinitialize object with a different command name..\"" \
    311 	 "&\"Error occurred in Python.*..\"" \
    312 	 "\\^error,msg=\"Error occurred in Python.*\""] \
    313     "attempt to reinitialise aa variable to a new command name"
    314 
    315 mi_gdb_test "-aa" \
    316     ".*\\^done,yyy={msg=\"message two\"}" \
    317     "check the aa object has not changed after failed initialization"
    318 
    319 mi_gdb_test "python aa.installed = False" \
    320     ".*\\^done" \
    321     "uninstall the -aa command"
    322 
    323 mi_gdb_test "python aa.__init__('-bb', 'message three', 'zzz')" \
    324     [multi_line \
    325 	 ".*" \
    326 	 "&\"ValueError.*: can't reinitialize object with a different command name..\"" \
    327 	 "&\"Error occurred in Python.*..\"" \
    328 	 "\\^error,msg=\"Error occurred in Python.*\""] \
    329     "attempt to reinitialise aa variable to a new command name while uninstalled"
    330 
    331 mi_gdb_test "python aa.__init__('-aa', 'message three', 'zzz')" \
    332     ".*\\^done" \
    333     "reinitialise -aa command with a new message while uninstalled"
    334 
    335 mi_gdb_test "python aa.installed = True" \
    336     ".*\\^done" \
    337     "install the -aa command"
    338 
    339 mi_gdb_test "-aa" \
    340     ".*\\^done,zzz={msg=\"message three\"}" \
    341     "call the -aa command looking for message three"
    342 
    343 # Try to register a command object that is missing an invoke method.
    344 # This is accepted, but will give an error when the user tries to run
    345 # the command.
    346 mi_gdb_test "python no_invoke('-no-invoke')" ".*\\^done" \
    347     "attempt to register command with no invoke method"
    348 mi_gdb_test "-no-invoke" \
    349     [multi_line \
    350 	 ".*" \
    351 	 "&\"AttributeError.*: 'no_invoke' object has no attribute 'invoke'..\"" \
    352 	 "\\^error,msg=\"Error occurred in Python: 'no_invoke' object has no attribute 'invoke'\""] \
    353     "execute -no-invoke command, which is missing the invoke method"
    354 
    355 # Register a command, then delete its invoke method.  What is the user thinking!!
    356 mi_gdb_test "python setattr(no_invoke, 'invoke', free_invoke)" ".*\\^done"
    357 mi_gdb_test "python cmd = no_invoke('-hello')" ".*\\^done"
    358 mi_gdb_test "-hello" ".*\\^done,result=\\\[\\\]" \
    359     "execute no_invoke command, while it still has an invoke attribute"
    360 mi_gdb_test "python delattr(no_invoke, 'invoke')" ".*\\^done"
    361 mi_gdb_test "-hello" \
    362     [multi_line \
    363 	 ".*" \
    364 	 "&\"AttributeError.*: 'no_invoke' object has no attribute 'invoke'..\"" \
    365 	 "\\^error,msg=\"Error occurred in Python: 'no_invoke' object has no attribute 'invoke'\""] \
    366     "execute -hello command, that had its invoke method removed"
    367 mi_gdb_test "python cmd.invoke = 'string'" ".*\\^done"
    368 mi_gdb_test "-hello" \
    369     [multi_line \
    370 	 ".*" \
    371 	 "&\"TypeError.*: 'str' object is not callable..\"" \
    372 	 "\\^error,msg=\"Error occurred in Python: 'str' object is not callable\""] \
    373     "execute command with invoke set to a string"
    374 
    375 # Try to create a new MI command that uses the name of a builtin MI command.
    376 mi_gdb_test "python cmd = pycmd2('-data-disassemble')" \
    377     [multi_line \
    378 	 ".*" \
    379 	 "&\"RuntimeError.*: unable to add command, name is already in use..\"" \
    380 	 "&\"Error occurred in Python.*..\"" \
    381 	 "\\^error,msg=\"Error occurred in Python.*\""] \
    382     "try to register a command that replaces -data-disassemble"
    383 
    384 
    385 
    386 mi_gdb_test "python run_exception_tests()" \
    387     [multi_line \
    388 	 ".*" \
    389 	 "~\"PASS..\"" \
    390 	 "\\^done"]
    391