Files
binutils-gdb/gdb/python/lib/gdb/dap/events.py
Tom Tromey c558c8d5f6 Remove ExecutionInvoker
ExecutionInvoker is no longer really needed, due to the previous DAP
refactoring.  This patch removes it in favor of an ordinary function.
One spot (the 'continue' request) could still have used it, but is
more succinctly expressed as a lambda.

Reviewed-by: Kévin Le Gouguec <legouguec@adacore.com>

(cherry picked from commit 68caad9d0b)
2023-11-17 08:43:04 -07:00

151 lines
3.7 KiB
Python

# Copyright 2022-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 enum
import gdb
from .server import send_event
from .startup import exec_and_log, in_gdb_thread, log
from .modules import is_module, make_module
@in_gdb_thread
def _on_exit(event):
code = 0
if hasattr(event, "exit_code"):
code = event.exit_code
send_event(
"exited",
{
"exitCode": code,
},
)
send_event("terminated")
@in_gdb_thread
def thread_event(event, reason):
send_event(
"thread",
{
"reason": reason,
"threadId": event.inferior_thread.global_num,
},
)
@in_gdb_thread
def _new_thread(event):
thread_event(event, "started")
@in_gdb_thread
def _thread_exited(event):
thread_event(event, "exited")
@in_gdb_thread
def _new_objfile(event):
if is_module(event.new_objfile):
send_event(
"module",
{
"reason": "new",
"module": make_module(event.new_objfile),
},
)
@in_gdb_thread
def _objfile_removed(event):
if is_module(event.objfile):
send_event(
"module",
{
"reason": "removed",
"module": make_module(event.objfile),
},
)
_suppress_cont = False
@in_gdb_thread
def _cont(event):
global _suppress_cont
if _suppress_cont:
log("_suppress_cont case")
_suppress_cont = False
else:
send_event(
"continued",
{
"threadId": gdb.selected_thread().global_num,
"allThreadsContinued": True,
},
)
class StopKinds(enum.Enum):
# The values here are chosen to follow the DAP spec.
STEP = "step"
BREAKPOINT = "breakpoint"
PAUSE = "pause"
EXCEPTION = "exception"
_expected_stop = None
@in_gdb_thread
def exec_and_expect_stop(cmd, reason):
"""Indicate that a stop is expected, then execute CMD"""
global _expected_stop
_expected_stop = reason
global _suppress_cont
_suppress_cont = True
# FIXME if the call fails should we clear _suppress_cont?
exec_and_log(cmd)
@in_gdb_thread
def _on_stop(event):
log("entering _on_stop: " + repr(event))
global _expected_stop
obj = {
"threadId": gdb.selected_thread().global_num,
"allThreadsStopped": True,
}
if isinstance(event, gdb.BreakpointEvent):
# Ignore the expected stop, we hit a breakpoint instead.
_expected_stop = StopKinds.BREAKPOINT
obj["hitBreakpointIds"] = [x.number for x in event.breakpoints]
elif _expected_stop is None:
# FIXME what is even correct here
_expected_stop = StopKinds.EXCEPTION
obj["reason"] = _expected_stop.value
_expected_stop = None
send_event("stopped", obj)
gdb.events.stop.connect(_on_stop)
gdb.events.exited.connect(_on_exit)
gdb.events.new_thread.connect(_new_thread)
gdb.events.thread_exited.connect(_thread_exited)
gdb.events.cont.connect(_cont)
gdb.events.new_objfile.connect(_new_objfile)
gdb.events.free_objfile.connect(_objfile_removed)