
This adds a new Python function, gdb.execute_mi, that can be used to invoke an MI command but get the output as a Python object, rather than a string. This is done by implementing a new ui_out subclass that builds a Python object. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=11688 Reviewed-By: Eli Zaretskii <eliz@gnu.org>
147 lines
4.7 KiB
Python
147 lines
4.7 KiB
Python
# Copyright (C) 2019-2023 Free Software Foundation, Inc.
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import gdb
|
|
|
|
|
|
class BadKey:
|
|
def __repr__(self):
|
|
return "Bad Key"
|
|
|
|
|
|
class ReallyBadKey:
|
|
def __repr__(self):
|
|
return BadKey()
|
|
|
|
|
|
class pycmd1(gdb.MICommand):
|
|
def invoke(self, argv):
|
|
if argv[0] == "int":
|
|
return {"result": 42}
|
|
elif argv[0] == "str":
|
|
return {"result": "Hello world!"}
|
|
elif argv[0] == "ary":
|
|
return {"result": ["Hello", 42]}
|
|
elif argv[0] == "dct":
|
|
return {"result": {"hello": "world", "times": 42}}
|
|
elif argv[0] == "bk1":
|
|
return {"result": {BadKey(): "world"}}
|
|
elif argv[0] == "bk2":
|
|
return {"result": {1: "world"}}
|
|
elif argv[0] == "bk3":
|
|
return {"result": {ReallyBadKey(): "world"}}
|
|
elif argv[0] == "tpl":
|
|
return {"result": (42, "Hello")}
|
|
elif argv[0] == "itr":
|
|
return {"result": iter([1, 2, 3])}
|
|
elif argv[0] == "nn1":
|
|
return None
|
|
elif argv[0] == "nn2":
|
|
return {"result": [None]}
|
|
elif argv[0] == "red":
|
|
pycmd2("-pycmd")
|
|
return None
|
|
elif argv[0] == "nd1":
|
|
return [1, 2, 3]
|
|
elif argv[0] == "nd2":
|
|
return 123
|
|
elif argv[0] == "nd3":
|
|
return "abc"
|
|
elif argv[0] == "ik1":
|
|
return {"xxx yyy": 123}
|
|
elif argv[0] == "ik2":
|
|
return {"result": {"xxx yyy": 123}}
|
|
elif argv[0] == "ik3":
|
|
return {"xxx+yyy": 123}
|
|
elif argv[0] == "ik4":
|
|
return {"xxx.yyy": 123}
|
|
elif argv[0] == "ik5":
|
|
return {"123xxxyyy": 123}
|
|
elif argv[0] == "empty_key":
|
|
return {"": 123}
|
|
elif argv[0] == "dash-key":
|
|
return {"the-key": 123}
|
|
elif argv[0] == "exp":
|
|
raise gdb.GdbError()
|
|
else:
|
|
raise gdb.GdbError("Invalid parameter: %s" % argv[0])
|
|
|
|
|
|
class pycmd2(gdb.MICommand):
|
|
def invoke(self, argv):
|
|
if argv[0] == "str":
|
|
return {"result": "Ciao!"}
|
|
elif argv[0] == "red":
|
|
pycmd1("-pycmd")
|
|
raise gdb.GdbError("Command redefined but we failing anyway")
|
|
elif argv[0] == "new":
|
|
pycmd1("-pycmd-new")
|
|
return None
|
|
else:
|
|
raise gdb.GdbError("Invalid parameter: %s" % argv[0])
|
|
|
|
|
|
# This class creates a command that returns a string, which is passed
|
|
# when the command is created.
|
|
class pycmd3(gdb.MICommand):
|
|
def __init__(self, name, msg, top_level):
|
|
super(pycmd3, self).__init__(name)
|
|
self._msg = msg
|
|
self._top_level = top_level
|
|
|
|
def invoke(self, args):
|
|
return {self._top_level: {"msg": self._msg}}
|
|
|
|
|
|
# A command that is missing it's invoke method.
|
|
class no_invoke(gdb.MICommand):
|
|
def __init__(self, name):
|
|
super(no_invoke, self).__init__(name)
|
|
|
|
|
|
def free_invoke(obj, args):
|
|
return {"result": args}
|
|
|
|
|
|
# Run some test involving catching exceptions. It's easier to write
|
|
# these as a Python function which is then called from the exp script.
|
|
def run_exception_tests():
|
|
print("PASS")
|
|
|
|
|
|
# Run some execute_mi tests. This is easier to do from Python.
|
|
def run_execute_mi_tests():
|
|
# Install the command.
|
|
cmd = pycmd1("-pycmd")
|
|
# Pass in a representative subset of the pycmd1 keys, and then
|
|
# check that the result via MI is the same as the result via a
|
|
# direct Python call. Note that some results won't compare as
|
|
# equal -- for example, a Python MI command can return a tuple,
|
|
# but that will be translated to a Python list.
|
|
for name in ("int", "str", "dct"):
|
|
expect = cmd.invoke([name])
|
|
got = gdb.execute_mi("-pycmd", name)
|
|
if expect != got:
|
|
print("FAIL: saw " + repr(got) + ", but expected " + repr(expect))
|
|
return
|
|
ok = False
|
|
try:
|
|
gdb.execute_mi("-pycmd", "exp")
|
|
# Due to the "denaturation" problem, we have to expect a gdb.error
|
|
# here and not a gdb.GdbError.
|
|
except gdb.error:
|
|
ok = True
|
|
if not ok:
|
|
print("FAIL: did not throw exception")
|
|
print("PASS")
|