binutils-gdb/gdb/python/py-dap.c
Tom Tromey 8bb8f83467 Fix gdb.interrupt race
gdb.interrupt was introduced to implement DAP request cancellation.
However, because it can be run from another thread, and because I
didn't look deeply enough at the implementation, it turns out to be
racy.

The fix here is to lock accesses to certain globals in extension.c.

Note that this won't work in the case where configure detects that the
C++ compiler doesn't provide thread support.  This version of the
patch disables DAP entirely in this situation.

Regression tested on x86-64 Fedora 38.  I also ran gdb.dap/pause.exp
in a thread-sanitizer build tree to make sure the reported race is
gone.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31263
2024-02-28 09:08:16 -07:00

123 lines
2.6 KiB
C

/* Python DAP interpreter
Copyright (C) 2022-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 "defs.h"
#include "python-internal.h"
#include "interps.h"
#include "cli-out.h"
#include "ui.h"
class dap_interp final : public interp
{
public:
explicit dap_interp (const char *name)
: interp (name),
m_ui_out (new cli_ui_out (gdb_stdout))
{
}
~dap_interp () override = default;
void init (bool top_level) override;
void suspend () override
{
}
void resume () override
{
}
void exec (const char *command) override
{
/* Just ignore it. */
}
void set_logging (ui_file_up logfile, bool logging_redirect,
bool debug_redirect) override
{
/* Just ignore it. */
}
ui_out *interp_ui_out () override
{
return m_ui_out.get ();
}
void pre_command_loop () override;
private:
std::unique_ptr<ui_out> m_ui_out;
};
/* Call function FN_NAME from module gdb.dap. */
static void
call_dap_fn (const char *fn_name)
{
gdbpy_enter enter_py;
gdbpy_ref<> dap_module (PyImport_ImportModule ("gdb.dap"));
if (dap_module == nullptr)
gdbpy_handle_exception ();
gdbpy_ref<> func (PyObject_GetAttrString (dap_module.get (), fn_name));
if (func == nullptr)
gdbpy_handle_exception ();
gdbpy_ref<> result_obj (PyObject_CallObject (func.get (), nullptr));
if (result_obj == nullptr)
gdbpy_handle_exception ();
}
void
dap_interp::init (bool top_level)
{
#if CXX_STD_THREAD
call_dap_fn ("run");
current_ui->input_fd = -1;
current_ui->m_input_interactive_p = false;
#else
error (_("GDB was compiled without threading, which DAP requires"));
#endif
}
void
dap_interp::pre_command_loop ()
{
call_dap_fn ("pre_command_loop");
}
void _initialize_py_interp ();
void
_initialize_py_interp ()
{
/* The dap code uses module typing, available starting python 3.5. */
#if PY_VERSION_HEX >= 0x03050000
interp_factory_register ("dap", [] (const char *name) -> interp *
{
return new dap_interp (name);
});
#endif
}