]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Simplify DAP stop-reason code
authorTom Tromey <tromey@adacore.com>
Fri, 3 Nov 2023 19:59:10 +0000 (13:59 -0600)
committerTom Tromey <tromey@adacore.com>
Mon, 11 Dec 2023 18:35:07 +0000 (11:35 -0700)
Now that gdb adds stop-reason details to stop events, we can simplify
the DAP code to emit correct stop reasons in its own events.  For the
most part a simple renaming of gdb reasons is sufficient; however,
"pause" must still be handled specially.

gdb/python/lib/gdb/dap/events.py
gdb/python/lib/gdb/dap/launch.py
gdb/python/lib/gdb/dap/next.py
gdb/python/lib/gdb/dap/pause.py

index b759ba43b5f0949541227cc604af56fcb1d1ae56..cbefe90e4ca4db6786492f787ec53c22b5e14b76 100644 (file)
@@ -13,7 +13,6 @@
 # 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
@@ -148,48 +147,77 @@ 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
+_expected_pause = False
 
 
 @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
-    if reason != StopKinds.PAUSE:
-        global _suppress_cont
-        _suppress_cont = True
+def exec_and_expect_stop(cmd, expected_pause=False):
+    """A wrapper for exec_and_log that sets the continue-suppression flag.
+
+    When EXPECTED_PAUSE is True, a stop that looks like a pause (e.g.,
+    a SIGINT) will be reported as "pause" instead.
+    """
+    global _expected_pause
+    _expected_pause = expected_pause
+    global _suppress_cont
+    # If we're expecting a pause, then we're definitely not
+    # continuing.
+    _suppress_cont = not expected_pause
     # FIXME if the call fails should we clear _suppress_cont?
     exec_and_log(cmd)
 
 
+# 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):
     global inferior_running
     inferior_running = False
     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]
+    _expected_pause = False
     send_event("stopped", obj)
 
 
@@ -204,13 +232,26 @@ _infcall_was_running = False
 @in_gdb_thread
 def _on_inferior_call(event):
     global _infcall_was_running
+    global inferior_running
     if isinstance(event, gdb.InferiorCallPreEvent):
         _infcall_was_running = inferior_running
         if not _infcall_was_running:
             _cont(None)
     else:
-        if not _infcall_was_running:
-            _on_stop(None)
+        # If the inferior is already marked as stopped here, then that
+        # means that the call caused some other stop, and we don't
+        # want to double-report it.
+        if not _infcall_was_running and inferior_running:
+            inferior_running = False
+            obj = {
+                "threadId": gdb.selected_thread().global_num,
+                "allThreadsStopped": True,
+                # DAP says any string is ok.
+                "reason": "function call",
+            }
+            global _expected_pause
+            _expected_pause = False
+            send_event("stopped", obj)
 
 
 gdb.events.stop.connect(_on_stop)
index 995641bd94545d6b6a5ce77e2d7312bb40df92e6..7014047ff51c298e4bdddab880d043c2d7748cf4 100644 (file)
@@ -80,6 +80,4 @@ def config_done(**args):
     global _program
     if _program is not None:
         expect_process("process")
-        # Suppress the continue event, but don't set any particular
-        # expected stop.
-        exec_and_expect_stop("run", None)
+        exec_and_expect_stop("run")
index eedc26f28a5d91cb41c97761d89ad3e3c2ee7c06..c06093e74ed475f487367fe601862b9cfae54caf 100644 (file)
@@ -15,7 +15,7 @@
 
 import gdb
 
-from .events import StopKinds, exec_and_expect_stop
+from .events import exec_and_expect_stop
 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"
-    exec_and_expect_stop(cmd, StopKinds.STEP)
+    exec_and_expect_stop(cmd)
 
 
 @capability("supportsSteppingGranularity")
@@ -70,13 +70,13 @@ def step_in(
     cmd = "step"
     if granularity == "instruction":
         cmd += "i"
-    exec_and_expect_stop(cmd, StopKinds.STEP)
+    exec_and_expect_stop(cmd)
 
 
 @request("stepOut", response=False)
 def step_out(*, threadId: int, singleThread: bool = False, **args):
     _handle_thread_step(threadId, singleThread, True)
-    exec_and_expect_stop("finish", StopKinds.STEP)
+    exec_and_expect_stop("finish")
 
 
 # This is a server-side request because it is funny: it wants to
@@ -87,5 +87,5 @@ def step_out(*, threadId: int, singleThread: bool = False, **args):
 @request("continue", on_dap_thread=True)
 def continue_request(*, threadId: int, singleThread: bool = False, **args):
     locked = send_gdb_with_response(lambda: _handle_thread_step(threadId, singleThread))
-    send_gdb(lambda: exec_and_expect_stop("continue", None))
+    send_gdb(lambda: exec_and_expect_stop("continue"))
     return {"allThreadsContinued": not locked}
index b7e21452d691ca323bfa64b00b18d45edde162a4..19ff17d511b54fee5d0cd85da1c4ec8c29512e14 100644 (file)
 # 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, exec_and_expect_stop
+from .events import exec_and_expect_stop
 from .server import request
 
 
 @request("pause", response=False, expect_stopped=False)
 def pause(**args):
-    exec_and_expect_stop("interrupt -a", StopKinds.PAUSE)
+    exec_and_expect_stop("interrupt -a", True)