mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-27 03:54:41 +08:00
1ccb6f106a
Result of: ... $ search="GDB_PY_HANDLE_EXCEPTION (" $ replace="return gdbpy_handle_gdb_exception (nullptr, " $ sed -i \ "s/$search/$replace/" \ gdb/python/*.c ... Also remove the now unused GDB_PY_HANDLE_EXCEPTION. No functional changes. Tested on x86_64-linux. Approved-By: Tom Tromey <tom@tromey.com>
563 lines
17 KiB
C
563 lines
17 KiB
C
/* Python interface to inferiors.
|
|
|
|
Copyright (C) 2009-2024 Free Software Foundation, Inc.
|
|
|
|
This file is part of GDB.
|
|
|
|
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/>. */
|
|
|
|
#include "python-internal.h"
|
|
#include "process-stratum-target.h"
|
|
#include "inferior.h"
|
|
#include "observable.h"
|
|
#include "target-connection.h"
|
|
#include "py-events.h"
|
|
#include "py-event.h"
|
|
#include "arch-utils.h"
|
|
#include "remote.h"
|
|
#include "charset.h"
|
|
|
|
#include <map>
|
|
|
|
/* The Python object that represents a connection. */
|
|
|
|
struct connection_object
|
|
{
|
|
PyObject_HEAD
|
|
|
|
/* The process target that represents this connection. When a
|
|
connection_object is created this field will always point at a valid
|
|
target. Later, if GDB stops using this target (the target is popped
|
|
from all target stacks) then this field is set to nullptr, which
|
|
indicates that this Python object is now in the invalid state (see
|
|
the is_valid() method below). */
|
|
struct process_stratum_target *target;
|
|
};
|
|
|
|
extern PyTypeObject connection_object_type
|
|
CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("connection_object");
|
|
|
|
extern PyTypeObject remote_connection_object_type
|
|
CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("remote_connection_object");
|
|
|
|
/* Require that CONNECTION be valid. */
|
|
#define CONNPY_REQUIRE_VALID(connection) \
|
|
do { \
|
|
if (connection->target == nullptr) \
|
|
{ \
|
|
PyErr_SetString (PyExc_RuntimeError, \
|
|
_("Connection no longer exists.")); \
|
|
return nullptr; \
|
|
} \
|
|
} while (0)
|
|
|
|
/* A map between process_stratum targets and the Python object representing
|
|
them. We actually hold a gdbpy_ref around the Python object so that
|
|
reference counts are handled correctly when entries are deleted. */
|
|
static std::map<process_stratum_target *,
|
|
gdbpy_ref<connection_object>> all_connection_objects;
|
|
|
|
/* Return a reference to a gdb.TargetConnection object for TARGET. If
|
|
TARGET is nullptr then a reference to None is returned.
|
|
|
|
Previously created gdb.TargetConnection objects are cached, and
|
|
additional references to the same connection object can be returned with
|
|
later calls to this function. */
|
|
|
|
gdbpy_ref<>
|
|
target_to_connection_object (process_stratum_target *target)
|
|
{
|
|
if (target == nullptr)
|
|
return gdbpy_ref<>::new_reference (Py_None);
|
|
|
|
gdbpy_ref <connection_object> conn_obj;
|
|
auto conn_obj_iter = all_connection_objects.find (target);
|
|
if (conn_obj_iter == all_connection_objects.end ())
|
|
{
|
|
PyTypeObject *type;
|
|
|
|
if (is_remote_target (target))
|
|
type = &remote_connection_object_type;
|
|
else
|
|
type = &connection_object_type;
|
|
|
|
conn_obj.reset (PyObject_New (connection_object, type));
|
|
if (conn_obj == nullptr)
|
|
return nullptr;
|
|
conn_obj->target = target;
|
|
all_connection_objects.emplace (target, conn_obj);
|
|
}
|
|
else
|
|
conn_obj = conn_obj_iter->second;
|
|
|
|
gdb_assert (conn_obj != nullptr);
|
|
|
|
/* Repackage the result as a PyObject reference. */
|
|
return gdbpy_ref<> ((PyObject *) conn_obj.release ());
|
|
}
|
|
|
|
/* Return a list of gdb.TargetConnection objects, one for each currently
|
|
active connection. The returned list is in no particular order. */
|
|
|
|
PyObject *
|
|
gdbpy_connections (PyObject *self, PyObject *args)
|
|
{
|
|
gdbpy_ref<> list (PyList_New (0));
|
|
if (list == nullptr)
|
|
return nullptr;
|
|
|
|
for (process_stratum_target *target : all_non_exited_process_targets ())
|
|
{
|
|
gdb_assert (target != nullptr);
|
|
|
|
gdbpy_ref<> conn = target_to_connection_object (target);
|
|
if (conn == nullptr)
|
|
return nullptr;
|
|
gdb_assert (conn.get () != Py_None);
|
|
|
|
if (PyList_Append (list.get (), conn.get ()) < 0)
|
|
return nullptr;
|
|
}
|
|
|
|
return list.release ();
|
|
}
|
|
|
|
/* Emit a connection event for TARGET to REGISTRY. Return 0 on success, or
|
|
a negative value on error. */
|
|
|
|
static int
|
|
emit_connection_event (process_stratum_target *target,
|
|
eventregistry_object *registry)
|
|
{
|
|
gdbpy_ref<> event_obj
|
|
= create_event_object (&connection_event_object_type);
|
|
if (event_obj == nullptr)
|
|
return -1;
|
|
|
|
gdbpy_ref<> conn = target_to_connection_object (target);
|
|
if (evpy_add_attribute (event_obj.get (), "connection", conn.get ()) < 0)
|
|
return -1;
|
|
|
|
return evpy_emit_event (event_obj.get (), registry);
|
|
}
|
|
|
|
/* Callback for the connection_removed observer. */
|
|
|
|
static void
|
|
connpy_connection_removed (process_stratum_target *target)
|
|
{
|
|
if (!gdb_python_initialized)
|
|
return;
|
|
|
|
gdbpy_enter enter_py;
|
|
|
|
if (!evregpy_no_listeners_p (gdb_py_events.connection_removed))
|
|
if (emit_connection_event (target, gdb_py_events.connection_removed) < 0)
|
|
gdbpy_print_stack ();
|
|
|
|
auto conn_obj_iter = all_connection_objects.find (target);
|
|
if (conn_obj_iter != all_connection_objects.end ())
|
|
{
|
|
gdbpy_ref <connection_object> conn_obj = conn_obj_iter->second;
|
|
conn_obj->target = nullptr;
|
|
all_connection_objects.erase (target);
|
|
}
|
|
}
|
|
|
|
/* Called when a gdb.TargetConnection object is deallocated. */
|
|
|
|
static void
|
|
connpy_connection_dealloc (PyObject *obj)
|
|
{
|
|
connection_object *conn_obj = (connection_object *) obj;
|
|
|
|
/* As the all_connection_objects map holds a reference to each connection
|
|
object we can only enter the dealloc function when the reference in
|
|
all_connection_objects has been erased.
|
|
|
|
As we always set the target pointer back to nullptr before we erase
|
|
items from all_connection_objects then, when we get here, the target
|
|
pointer must be nullptr. */
|
|
gdb_assert (conn_obj->target == nullptr);
|
|
|
|
Py_TYPE (obj)->tp_free (obj);
|
|
}
|
|
|
|
/* Implement repr() for gdb.TargetConnection. */
|
|
|
|
static PyObject *
|
|
connpy_repr (PyObject *obj)
|
|
{
|
|
connection_object *self = (connection_object *) obj;
|
|
process_stratum_target *target = self->target;
|
|
|
|
if (target == nullptr)
|
|
return gdb_py_invalid_object_repr (obj);
|
|
|
|
return PyUnicode_FromFormat ("<%s num=%d, what=\"%s\">",
|
|
Py_TYPE (obj)->tp_name,
|
|
target->connection_number,
|
|
make_target_connection_string (target).c_str ());
|
|
}
|
|
|
|
/* Implementation of gdb.TargetConnection.is_valid() -> Boolean. Returns
|
|
True if this connection object is still associated with a
|
|
process_stratum_target, otherwise, returns False. */
|
|
|
|
static PyObject *
|
|
connpy_is_valid (PyObject *self, PyObject *args)
|
|
{
|
|
connection_object *conn = (connection_object *) self;
|
|
|
|
if (conn->target == nullptr)
|
|
Py_RETURN_FALSE;
|
|
|
|
Py_RETURN_TRUE;
|
|
}
|
|
|
|
/* Return the id number of this connection. */
|
|
|
|
static PyObject *
|
|
connpy_get_connection_num (PyObject *self, void *closure)
|
|
{
|
|
connection_object *conn = (connection_object *) self;
|
|
|
|
CONNPY_REQUIRE_VALID (conn);
|
|
|
|
auto num = conn->target->connection_number;
|
|
return gdb_py_object_from_longest (num).release ();
|
|
}
|
|
|
|
/* Return a string that gives the short name for this connection type. */
|
|
|
|
static PyObject *
|
|
connpy_get_connection_type (PyObject *self, void *closure)
|
|
{
|
|
connection_object *conn = (connection_object *) self;
|
|
|
|
CONNPY_REQUIRE_VALID (conn);
|
|
|
|
const char *shortname = conn->target->shortname ();
|
|
return host_string_to_python_string (shortname).release ();
|
|
}
|
|
|
|
/* Return a string that gives a longer description of this connection type. */
|
|
|
|
static PyObject *
|
|
connpy_get_description (PyObject *self, void *closure)
|
|
{
|
|
connection_object *conn = (connection_object *) self;
|
|
|
|
CONNPY_REQUIRE_VALID (conn);
|
|
|
|
const char *longname = conn->target->longname ();
|
|
return host_string_to_python_string (longname).release ();
|
|
}
|
|
|
|
/* Return a string that gives additional details about this connection, or
|
|
None, if there are no additional details for this connection type. */
|
|
|
|
static PyObject *
|
|
connpy_get_connection_details (PyObject *self, void *closure)
|
|
{
|
|
connection_object *conn = (connection_object *) self;
|
|
|
|
CONNPY_REQUIRE_VALID (conn);
|
|
|
|
const char *details = conn->target->connection_string ();
|
|
if (details != nullptr)
|
|
return host_string_to_python_string (details).release ();
|
|
else
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
/* Python specific initialization for this file. */
|
|
|
|
static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
|
|
gdbpy_initialize_connection (void)
|
|
{
|
|
if (gdbpy_type_ready (&connection_object_type) < 0)
|
|
return -1;
|
|
|
|
if (gdbpy_type_ready (&remote_connection_object_type) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Set of callbacks used to implement gdb.send_packet. */
|
|
|
|
struct py_send_packet_callbacks : public send_remote_packet_callbacks
|
|
{
|
|
/* Constructor, initialise the result to nullptr. It is invalid to try
|
|
and read the result before sending a packet and processing the
|
|
reply. */
|
|
|
|
py_send_packet_callbacks ()
|
|
: m_result (nullptr)
|
|
{ /* Nothing. */ }
|
|
|
|
/* There's nothing to do when the packet is sent. */
|
|
|
|
void sending (gdb::array_view<const char> &buf) override
|
|
{ /* Nothing. */ }
|
|
|
|
/* When the result is returned create a Python object and assign this
|
|
into M_RESULT. If for any reason we can't create a Python object to
|
|
represent the result then M_RESULT is set to nullptr, and Python's
|
|
internal error flags will be set. If the result we got back from the
|
|
remote is empty then set the result to None. */
|
|
|
|
void received (gdb::array_view<const char> &buf) override
|
|
{
|
|
if (buf.size () > 0 && buf.data ()[0] != '\0')
|
|
m_result.reset (PyBytes_FromStringAndSize (buf.data (), buf.size ()));
|
|
else
|
|
{
|
|
/* We didn't get back any result data; set the result to None. */
|
|
Py_INCREF (Py_None);
|
|
m_result.reset (Py_None);
|
|
}
|
|
}
|
|
|
|
/* Get a reference to the result as a Python object. It is invalid to
|
|
call this before sending a packet to the remote and processing the
|
|
reply.
|
|
|
|
The result value is setup in the RECEIVED call above. If the RECEIVED
|
|
call causes an error then the result value will be set to nullptr,
|
|
and the error reason is left stored in Python's global error state.
|
|
|
|
It is important that the result is inspected immediately after sending
|
|
a packet to the remote, and any error fetched, calling any other
|
|
Python functions that might clear the error state, or rely on an error
|
|
not being set will cause undefined behaviour. */
|
|
|
|
gdbpy_ref<> result () const
|
|
{
|
|
return m_result;
|
|
}
|
|
|
|
private:
|
|
|
|
/* A reference to the result value. */
|
|
|
|
gdbpy_ref<> m_result;
|
|
};
|
|
|
|
/* Implement RemoteTargetConnection.send_packet function. Send a packet to
|
|
the target identified by SELF. The connection must still be valid, and
|
|
the packet to be sent must be non-empty, otherwise an exception will be
|
|
thrown. */
|
|
|
|
static PyObject *
|
|
connpy_send_packet (PyObject *self, PyObject *args, PyObject *kw)
|
|
{
|
|
connection_object *conn = (connection_object *) self;
|
|
|
|
CONNPY_REQUIRE_VALID (conn);
|
|
|
|
static const char *keywords[] = {"packet", nullptr};
|
|
PyObject *packet_obj;
|
|
|
|
if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "O", keywords,
|
|
&packet_obj))
|
|
return nullptr;
|
|
|
|
/* If the packet is a unicode string then convert it to a bytes object. */
|
|
if (PyUnicode_Check (packet_obj))
|
|
{
|
|
/* We encode the string to bytes using the ascii codec, if this fails
|
|
then a suitable error will have been set. */
|
|
packet_obj = PyUnicode_AsASCIIString (packet_obj);
|
|
if (packet_obj == nullptr)
|
|
return nullptr;
|
|
}
|
|
|
|
/* Check the packet is now a bytes object. */
|
|
if (!PyBytes_Check (packet_obj))
|
|
{
|
|
PyErr_SetString (PyExc_TypeError, _("Packet is not a bytes object"));
|
|
return nullptr;
|
|
}
|
|
|
|
Py_ssize_t packet_len = 0;
|
|
char *packet_str_nonconst = nullptr;
|
|
if (PyBytes_AsStringAndSize (packet_obj, &packet_str_nonconst,
|
|
&packet_len) < 0)
|
|
return nullptr;
|
|
const char *packet_str = packet_str_nonconst;
|
|
gdb_assert (packet_str != nullptr);
|
|
|
|
if (packet_len == 0)
|
|
{
|
|
PyErr_SetString (PyExc_ValueError, _("Packet must not be empty"));
|
|
return nullptr;
|
|
}
|
|
|
|
try
|
|
{
|
|
scoped_restore_current_thread restore_thread;
|
|
switch_to_target_no_thread (conn->target);
|
|
|
|
gdb::array_view<const char> view (packet_str, packet_len);
|
|
py_send_packet_callbacks callbacks;
|
|
send_remote_packet (view, &callbacks);
|
|
PyObject *result = callbacks.result ().release ();
|
|
/* If we encountered an error converting the reply to a Python
|
|
object, then the result here can be nullptr. In that case, Python
|
|
should be aware that an error occurred. */
|
|
gdb_assert ((result == nullptr) == (PyErr_Occurred () != nullptr));
|
|
return result;
|
|
}
|
|
catch (const gdb_exception &except)
|
|
{
|
|
return gdbpy_handle_gdb_exception (nullptr, except);
|
|
}
|
|
}
|
|
|
|
/* Global initialization for this file. */
|
|
|
|
void _initialize_py_connection ();
|
|
void
|
|
_initialize_py_connection ()
|
|
{
|
|
gdb::observers::connection_removed.attach (connpy_connection_removed,
|
|
"py-connection");
|
|
}
|
|
|
|
GDBPY_INITIALIZE_FILE (gdbpy_initialize_connection);
|
|
|
|
/* Methods for the gdb.TargetConnection object type. */
|
|
|
|
static PyMethodDef connection_object_methods[] =
|
|
{
|
|
{ "is_valid", connpy_is_valid, METH_NOARGS,
|
|
"is_valid () -> Boolean.\n\
|
|
Return true if this TargetConnection is valid, false if not." },
|
|
{ NULL }
|
|
};
|
|
|
|
/* Methods for the gdb.RemoteTargetConnection object type. */
|
|
|
|
static PyMethodDef remote_connection_object_methods[] =
|
|
{
|
|
{ "send_packet", (PyCFunction) connpy_send_packet,
|
|
METH_VARARGS | METH_KEYWORDS,
|
|
"send_packet (PACKET) -> Bytes\n\
|
|
Send PACKET to a remote target, return the reply as a bytes array." },
|
|
{ NULL }
|
|
};
|
|
|
|
/* Attributes for the gdb.TargetConnection object type. */
|
|
|
|
static gdb_PyGetSetDef connection_object_getset[] =
|
|
{
|
|
{ "num", connpy_get_connection_num, NULL,
|
|
"ID number of this connection, as assigned by GDB.", NULL },
|
|
{ "type", connpy_get_connection_type, NULL,
|
|
"A short string that is the name for this connection type.", NULL },
|
|
{ "description", connpy_get_description, NULL,
|
|
"A longer string describing this connection type.", NULL },
|
|
{ "details", connpy_get_connection_details, NULL,
|
|
"A string containing additional connection details.", NULL },
|
|
{ NULL }
|
|
};
|
|
|
|
/* Define the gdb.TargetConnection object type. */
|
|
|
|
PyTypeObject connection_object_type =
|
|
{
|
|
PyVarObject_HEAD_INIT (NULL, 0)
|
|
"gdb.TargetConnection", /* tp_name */
|
|
sizeof (connection_object), /* tp_basicsize */
|
|
0, /* tp_itemsize */
|
|
connpy_connection_dealloc, /* tp_dealloc */
|
|
0, /* tp_print */
|
|
0, /* tp_getattr */
|
|
0, /* tp_setattr */
|
|
0, /* tp_compare */
|
|
connpy_repr, /* tp_repr */
|
|
0, /* tp_as_number */
|
|
0, /* tp_as_sequence */
|
|
0, /* tp_as_mapping */
|
|
0, /* tp_hash */
|
|
0, /* tp_call */
|
|
0, /* tp_str */
|
|
0, /* tp_getattro */
|
|
0, /* tp_setattro */
|
|
0, /* tp_as_buffer */
|
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
|
|
"GDB target connection object", /* tp_doc */
|
|
0, /* tp_traverse */
|
|
0, /* tp_clear */
|
|
0, /* tp_richcompare */
|
|
0, /* tp_weaklistoffset */
|
|
0, /* tp_iter */
|
|
0, /* tp_iternext */
|
|
connection_object_methods, /* tp_methods */
|
|
0, /* tp_members */
|
|
connection_object_getset, /* tp_getset */
|
|
0, /* tp_base */
|
|
0, /* tp_dict */
|
|
0, /* tp_descr_get */
|
|
0, /* tp_descr_set */
|
|
0, /* tp_dictoffset */
|
|
0, /* tp_init */
|
|
0 /* tp_alloc */
|
|
};
|
|
|
|
/* Define the gdb.RemoteTargetConnection object type. */
|
|
|
|
PyTypeObject remote_connection_object_type =
|
|
{
|
|
PyVarObject_HEAD_INIT (NULL, 0)
|
|
"gdb.RemoteTargetConnection", /* tp_name */
|
|
sizeof (connection_object), /* tp_basicsize */
|
|
0, /* tp_itemsize */
|
|
connpy_connection_dealloc, /* tp_dealloc */
|
|
0, /* tp_print */
|
|
0, /* tp_getattr */
|
|
0, /* tp_setattr */
|
|
0, /* tp_compare */
|
|
connpy_repr, /* tp_repr */
|
|
0, /* tp_as_number */
|
|
0, /* tp_as_sequence */
|
|
0, /* tp_as_mapping */
|
|
0, /* tp_hash */
|
|
0, /* tp_call */
|
|
0, /* tp_str */
|
|
0, /* tp_getattro */
|
|
0, /* tp_setattro */
|
|
0, /* tp_as_buffer */
|
|
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
|
"GDB remote target connection object", /* tp_doc */
|
|
0, /* tp_traverse */
|
|
0, /* tp_clear */
|
|
0, /* tp_richcompare */
|
|
0, /* tp_weaklistoffset */
|
|
0, /* tp_iter */
|
|
0, /* tp_iternext */
|
|
remote_connection_object_methods, /* tp_methods */
|
|
0, /* tp_members */
|
|
0, /* tp_getset */
|
|
&connection_object_type, /* tp_base */
|
|
0, /* tp_dict */
|
|
0, /* tp_descr_get */
|
|
0, /* tp_descr_set */
|
|
0, /* tp_dictoffset */
|
|
0, /* tp_init */
|
|
0 /* tp_alloc */
|
|
};
|