gdb/python: implement support for sending custom MI async notifications

This commit adds a new Python function, gdb.notify_mi, that can be used
to emit custom async notification to MI channel.  This can be used, among
other things, to implement notifications about events MI does not support,
such as remote connection closed or register change.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
Approved-By: Andrew Burgess <aburgess@redhat.com>
This commit is contained in:
Jan Vrany
2023-10-10 11:22:56 +01:00
parent 80a3485f81
commit 4825fd2d35
6 changed files with 204 additions and 0 deletions

View File

@@ -19,8 +19,14 @@
#include "defs.h"
#include "python-internal.h"
#include "utils.h"
#include "ui.h"
#include "ui-out.h"
#include "interps.h"
#include "target.h"
#include "mi/mi-parse.h"
#include "mi/mi-console.h"
#include "mi/mi-interp.h"
/* A ui_out subclass that creates a Python object based on the data
that is passed in. */
@@ -455,3 +461,71 @@ serialize_mi_results (PyObject *results)
serialize_mi_result_1 (value, key_string.get ());
}
}
/* See python-internal.h. */
PyObject *
gdbpy_notify_mi (PyObject *self, PyObject *args, PyObject *kwargs)
{
static const char *keywords[] = { "name", "data", nullptr };
char *name = nullptr;
PyObject *data = Py_None;
if (!gdb_PyArg_ParseTupleAndKeywords (args, kwargs, "s|O", keywords,
&name, &data))
return nullptr;
/* Validate notification name. */
const int name_len = strlen (name);
if (name_len == 0)
{
PyErr_SetString (PyExc_ValueError, _("MI notification name is empty."));
return nullptr;
}
for (int i = 0; i < name_len; i++)
{
if (!isalnum (name[i]) && name[i] != '-')
{
PyErr_Format
(PyExc_ValueError,
_("MI notification name contains invalid character: %c."),
name[i]);
return nullptr;
}
}
/* Validate additional data. */
if (!(data == Py_None || PyDict_Check (data)))
{
PyErr_Format
(PyExc_ValueError,
_("MI notification data must be either None or a dictionary, not %s"),
Py_TYPE (data)->tp_name);
return nullptr;
}
SWITCH_THRU_ALL_UIS ()
{
struct mi_interp *mi = as_mi_interp (top_level_interpreter ());
if (mi == nullptr)
continue;
target_terminal::scoped_restore_terminal_state term_state;
target_terminal::ours_for_output ();
gdb_printf (mi->event_channel, "%s", name);
if (data != Py_None)
{
ui_out *mi_uiout = mi->interp_ui_out ();
ui_out_redirect_pop redir (mi_uiout, mi->event_channel);
scoped_restore restore_uiout
= make_scoped_restore (&current_uiout, mi_uiout);
serialize_mi_results (data);
}
gdb_flush (mi->event_channel);
}
Py_RETURN_NONE;
}

View File

@@ -499,6 +499,11 @@ extern PyObject *gdbpy_execute_mi_command (PyObject *self, PyObject *args,
extern void serialize_mi_results (PyObject *results);
/* Implementation of the gdb.notify_mi function. */
extern PyObject *gdbpy_notify_mi (PyObject *self, PyObject *args,
PyObject *kw);
/* Convert Python object OBJ to a program_space pointer. OBJ must be a
gdb.Progspace reference. Return nullptr if the gdb.Progspace is not
valid (see gdb.Progspace.is_valid), otherwise return the program_space

View File

@@ -2669,6 +2669,10 @@ Return the name of the currently selected language." },
"print_options () -> dict\n\
Return the current print options." },
{ "notify_mi", (PyCFunction) gdbpy_notify_mi,
METH_VARARGS | METH_KEYWORDS,
"notify_mi (name, data) -> None\n\
Output async record to MI channels if any." },
{NULL, NULL, 0, NULL}
};