import gdb
import os
+import re
# These are deprecated in 3.9, but required in older versions.
from typing import Optional, Sequence
-from .server import request, capability
+from .server import request, capability, send_event
from .startup import send_gdb_with_response, in_gdb_thread, log_stack
from .typecheck import type_check
return result
-# Helper function to set odinary breakpoints according to a list of
+class _PrintBreakpoint(gdb.Breakpoint):
+ def __init__(self, logMessage, **args):
+ super().__init__(**args)
+ # Split the message up for easier processing.
+ self.message = re.split("{(.*?)}", logMessage)
+
+ def stop(self):
+ output = ""
+ for idx, item in enumerate(self.message):
+ if idx % 2 == 0:
+ # Even indices are plain text.
+ output += item
+ else:
+ # Odd indices are expressions to substitute. The {}
+ # have already been stripped by the placement of the
+ # regex capture in the 'split' call.
+ try:
+ val = gdb.parse_and_eval(item)
+ output += str(val)
+ except Exception as e:
+ output += "<" + str(e) + ">"
+ send_event(
+ "output",
+ {
+ "category": "console",
+ "output": output,
+ },
+ )
+ # Do not stop.
+ return False
+
+
+# Set a single breakpoint or a log point. Returns the new breakpoint.
+# Note that not every spec will pass logMessage, so here we use a
+# default.
+@in_gdb_thread
+def _set_one_breakpoint(*, logMessage=None, **args):
+ if logMessage is not None:
+ return _PrintBreakpoint(logMessage, **args)
+ else:
+ return gdb.Breakpoint(**args)
+
+
+# Helper function to set ordinary breakpoints according to a list of
# specifications.
@in_gdb_thread
def _set_breakpoints(kind, specs):
- return _set_breakpoints_callback(kind, specs, gdb.Breakpoint)
+ return _set_breakpoints_callback(kind, specs, _set_one_breakpoint)
# A helper function that rewrites a SourceBreakpoint into the internal
line: int,
condition: Optional[str] = None,
hitCondition: Optional[str] = None,
+ logMessage: Optional[str] = None,
**args,
):
return {
"line": line,
"condition": condition,
"hitCondition": hitCondition,
+ "logMessage": logMessage,
}
@request("setBreakpoints")
@capability("supportsHitConditionalBreakpoints")
@capability("supportsConditionalBreakpoints")
+@capability("supportsLogPoints")
def set_breakpoint(*, source, breakpoints: Sequence = (), **args):
if "path" not in source:
result = []
--- /dev/null
+/* Copyright 2023 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/>. */
+
+int global_variable;
+
+int
+func (int local)
+{
+ return global_variable - local; /* HERE */
+}
+
+int
+main ()
+{
+ global_variable = 23;
+ return func (23);
+}
--- /dev/null
+# Copyright 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/>.
+
+# Test DAP logging breakpoints.
+
+require allow_dap_tests
+
+load_lib dap-support.exp
+
+standard_testfile
+
+if {[build_executable ${testfile}.exp $testfile] == -1} {
+ return
+}
+
+if {[dap_launch $testfile] == ""} {
+ return
+}
+
+set line [gdb_get_line_number "HERE"]
+set obj [dap_check_request_and_response "set breakpoint" \
+ setBreakpoints \
+ [format {o source [o path [%s]] \
+ breakpoints [a [o line [i %d] \
+ logMessage [s "got {global_variable} - {local} = {global_variable - local}"]]]} \
+ [list s $srcfile] $line]]
+set fn_bpno [dap_get_breakpoint_number $obj]
+
+dap_check_request_and_response "start inferior" configurationDone
+dap_wait_for_event_and_check "inferior started" thread "body reason" started
+
+dap_wait_for_event_and_check "logging output" output \
+ {body category} console \
+ {body output} "got 23 - 23 = 0"
+
+# Check that the breakpoint did not cause a stop.
+dap_wait_for_event_and_check "inferior exited" exited
+
+dap_shutdown