@@ -99,60 +99,79 @@ def _cont(event):
)
-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 expect_stop(reason):
- """Indicate that a stop is expected, for the reason given."""
- global _expected_stop
- _expected_stop = reason
+_expected_pause = False
# A wrapper for Invoker that also sets the expected stop.
class ExecutionInvoker(Invoker):
- """A subclass of Invoker that sets the expected stop.
- Note that this assumes that the command will restart the inferior,
- so it will also cause ContinuedEvents to be suppressed."""
+ """A subclass of Invoker that sets the continue-suppression flag.
- def __init__(self, cmd, expected):
+ When EXPECTED_PAUSE is True, a stop that looks like a pause (e.g.,
+ a SIGINT) will be reported as "pause" instead.
+ """
+
+ def __init__(self, cmd, expected_pause=False):
super().__init__(cmd)
- self.expected = expected
+ self.expected_pause = expected_pause
@in_gdb_thread
def __call__(self):
- expect_stop(self.expected)
+ global _expected_pause
+ _expected_pause = self.expected_pause
global _suppress_cont
_suppress_cont = True
# FIXME if the call fails should we clear _suppress_cont?
super().__call__()
+# Map from gdb stop reasons to DAP stop reasons. Some of these can't
+# be seen ordinarily in DAP -- only if the client lets the user toggle
+# some settings (e.g. stop-on-solib-events) or enter commands (e.g.,
+# 'until').
+stop_reason_map = {
+ "breakpoint-hit": "breakpoint",
+ "watchpoint-trigger": "data breakpoint",
+ "read-watchpoint-trigger": "data breakpoint",
+ "access-watchpoint-trigger": "data breakpoint",
+ "function-finished": "step",
+ "location-reached": "step",
+ "watchpoint-scope": "data breakpoint",
+ "end-stepping-range": "step",
+ "exited-signalled": "exited",
+ "exited": "exited",
+ "exited-normally": "exited",
+ "signal-received": "signal",
+ "solib-event": "solib",
+ "fork": "fork",
+ "vfork": "vfork",
+ "syscall-entry": "syscall-entry",
+ "syscall-return": "syscall-return",
+ "exec": "exec",
+ "no-history": "no-history",
+}
+
+
@in_gdb_thread
def _on_stop(event):
log("entering _on_stop: " + repr(event))
- global _expected_stop
+ log(" details: " + repr(event.details))
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
+ global stop_reason_map
+ reason = event.details["reason"]
+ global _expected_pause
+ if (
+ _expected_pause
+ and reason == "signal-received"
+ and event.details["signal-name"] in ("SIGINT", "SIGSTOP")
+ ):
+ obj["reason"] = "pause"
+ else:
+ obj["reason"] = stop_reason_map[reason]
send_event("stopped", obj)
@@ -88,6 +88,4 @@ def attach(*, pid: Optional[int] = None, target: Optional[str] = None, **args):
def config_done(**args):
global _program
if _program is not None:
- # Suppress the continue event, but don't set any particular
- # expected stop.
- send_gdb(ExecutionInvoker("run", None))
+ send_gdb(ExecutionInvoker("run"))
@@ -15,7 +15,7 @@
import gdb
-from .events import StopKinds, ExecutionInvoker
+from .events import ExecutionInvoker
from .server import capability, request
from .startup import in_gdb_thread, send_gdb, send_gdb_with_response
from .state import set_thread
@@ -57,7 +57,7 @@ def next(
cmd = "next"
if granularity == "instruction":
cmd += "i"
- send_gdb(ExecutionInvoker(cmd, StopKinds.STEP))
+ send_gdb(ExecutionInvoker(cmd))
@capability("supportsSteppingGranularity")
@@ -70,17 +70,17 @@ def step_in(
cmd = "step"
if granularity == "instruction":
cmd += "i"
- send_gdb(ExecutionInvoker(cmd, StopKinds.STEP))
+ send_gdb(ExecutionInvoker(cmd))
@request("stepOut")
def step_out(*, threadId: int, singleThread: bool = False, **args):
send_gdb(lambda: _handle_thread_step(threadId, singleThread, True))
- send_gdb(ExecutionInvoker("finish", StopKinds.STEP))
+ send_gdb(ExecutionInvoker("finish"))
@request("continue")
def continue_request(*, threadId: int, singleThread: bool = False, **args):
locked = send_gdb_with_response(lambda: _handle_thread_step(threadId, singleThread))
- send_gdb(ExecutionInvoker("continue", None))
+ send_gdb(ExecutionInvoker("continue"))
return {"allThreadsContinued": not locked}
@@ -13,11 +13,11 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from .events import StopKinds, ExecutionInvoker
+from .events import ExecutionInvoker
from .server import request
from .startup import send_gdb
@request("pause")
def pause(**args):
- send_gdb(ExecutionInvoker("interrupt -a", StopKinds.PAUSE))
+ send_gdb(ExecutionInvoker("interrupt -a", True))