aboutsummaryrefslogtreecommitdiff
path: root/gdb/python/py-connection.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/python/py-connection.c')
-rw-r--r--gdb/python/py-connection.c210
1 files changed, 207 insertions, 3 deletions
diff --git a/gdb/python/py-connection.c b/gdb/python/py-connection.c
index f1dfa26..48e14fe 100644
--- a/gdb/python/py-connection.c
+++ b/gdb/python/py-connection.c
@@ -26,6 +26,8 @@
#include "py-events.h"
#include "py-event.h"
#include "arch-utils.h"
+#include "remote.h"
+#include "charset.h"
#include <map>
@@ -47,6 +49,9 @@ struct connection_object
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 { \
@@ -81,8 +86,14 @@ target_to_connection_object (process_stratum_target *target)
auto conn_obj_iter = all_connection_objects.find (target);
if (conn_obj_iter == all_connection_objects.end ())
{
- conn_obj.reset (PyObject_New (connection_object,
- &connection_object_type));
+ 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;
@@ -284,9 +295,148 @@ gdbpy_initialize_connection (void)
(PyObject *) &connection_object_type) < 0)
return -1;
+ if (PyType_Ready (&remote_connection_object_type) < 0)
+ return -1;
+
+ if (gdb_pymodule_addobject (gdb_module, "RemoteTargetConnection",
+ (PyObject *) &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)
+ {
+ gdbpy_convert_exception (except);
+ return nullptr;
+ }
+}
+
/* Global initialization for this file. */
void _initialize_py_connection ();
@@ -307,6 +457,17 @@ 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[] =
@@ -345,7 +506,7 @@ PyTypeObject connection_object_type =
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT, /* tp_flags */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
"GDB target connection object", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
@@ -364,3 +525,46 @@ PyTypeObject connection_object_type =
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 */
+};