[Lldb-commits] [lldb] [lldb-dap] Fix logpoint to breakpoint conversion (PR #222906)

Sergei Druzhkov via lldb-commits lldb-commits at lists.llvm.org
Fri Sep 11 08:54:38 PDT 2026


https://github.com/DrSergei updated https://github.com/llvm/llvm-project/pull/222906

>From e588ddf8a2cf448bec6eb4f5a76115e4ab214d39 Mon Sep 17 00:00:00 2001
From: Sergei Druzhkov <serzhdruzhok at gmail.com>
Date: Fri, 11 Sep 2026 10:30:43 +0300
Subject: [PATCH 1/2] [lldb-dap] Fix logpoint to breakpoint conversion

---
 .../lldb-dap/breakpoint/TestDAP_logpoints.py  | 63 +++++++++++++++++++
 lldb/tools/lldb-dap/SourceBreakpoint.cpp      |  5 ++
 2 files changed, 68 insertions(+)

diff --git a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_logpoints.py b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_logpoints.py
index 635387f593cf7..527b6d90314ee 100644
--- a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_logpoints.py
+++ b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_logpoints.py
@@ -78,6 +78,69 @@ def test_logMessage_basic(self):
             )
         session.continue_to_exit()
 
+    def check_logpoint_to_breakpoint_conversion(self, log_message):
+        session = self.build_and_create_session()
+        initial_stop = self.stop_at_before_loop_line(session)
+        loop_line = line_number("main.cpp", "// break loop")
+        after_loop_line = line_number("main.cpp", "// after loop")
+        log_prefix = "This is log message for "
+
+        [logpoint_id, post_loop_breakpoint_id] = session.resolve_source_breakpoints(
+            self.main_path,
+            [
+                SourceBreakpoint(loop_line, logMessage=log_prefix + "{i}"),
+                SourceBreakpoint(after_loop_line),
+            ],
+        )
+
+        # Change logpoint to usual breakpoint and check that id preserved.
+        [breakpoint_id, _] = session.resolve_source_breakpoints(
+            self.main_path,
+            [
+                SourceBreakpoint(loop_line, logMessage=log_message),
+                SourceBreakpoint(after_loop_line),
+            ],
+        )
+        self.assertEqual(breakpoint_id, logpoint_id)
+
+        # Check that stop works as expected for updated logpoint.
+        loop_stop = session.continue_to_breakpoint(breakpoint_id)
+        frame = session.top_frame_from(loop_stop)
+        self.assertEqual(frame.locals["i"].value_as_int, 0)
+        captured = session.collect_console(after=initial_stop, until=loop_stop)
+        self.assertNotIn(log_prefix, captured.seen_texts)
+
+        # Change breakpoint to logpoint back.
+        [restored_id, _] = session.resolve_source_breakpoints(
+            self.main_path,
+            [
+                SourceBreakpoint(loop_line, logMessage=log_prefix + "{i}"),
+                SourceBreakpoint(after_loop_line),
+            ],
+        )
+        self.assertEqual(restored_id, logpoint_id)
+
+        # Check that logpoint prints messages.
+        post_loop_stop = session.continue_to_breakpoint(post_loop_breakpoint_id)
+        captured = session.collect_console(after=loop_stop, until=post_loop_stop)
+        messages = [
+            line
+            for line in captured.seen_texts.splitlines()
+            if line.startswith(log_prefix)
+        ]
+        self.assertEqual(messages, [log_prefix + str(i) for i in range(1, 10)])
+        session.continue_to_exit()
+
+    @skipIfWindows
+    def test_logmessage_none(self):
+        """Tests removing logMessage restores a stopping breakpoint."""
+        self.check_logpoint_to_breakpoint_conversion(None)
+
+    @skipIfWindows
+    def test_logmessage_empty(self):
+        """Tests clearing logMessage restores a stopping breakpoint."""
+        self.check_logpoint_to_breakpoint_conversion("")
+
     @skipIfWindows
     def test_logmessage_advanced(self):
         """Tests breakpoint logmessage functionality for complex expression."""
diff --git a/lldb/tools/lldb-dap/SourceBreakpoint.cpp b/lldb/tools/lldb-dap/SourceBreakpoint.cpp
index fbc995e8a6e3f..e44ac6bfb1785 100644
--- a/lldb/tools/lldb-dap/SourceBreakpoint.cpp
+++ b/lldb/tools/lldb-dap/SourceBreakpoint.cpp
@@ -281,6 +281,11 @@ lldb::SBError SourceBreakpoint::FormatLogText(llvm::StringRef text,
 void SourceBreakpoint::SetLogMessage() {
   m_log_message_parts.clear();
 
+  if (m_log_message.empty()) {
+    m_bp.SetCallback(nullptr, nullptr);
+    return;
+  }
+
   // Contains unmatched open curly braces indices.
   std::vector<int> unmatched_curly_braces;
 

>From 626ad94f9743efa87761bb5c9c5a0182fcd9bf38 Mon Sep 17 00:00:00 2001
From: Sergei Druzhkov <serzhdruzhok at gmail.com>
Date: Fri, 11 Sep 2026 18:54:22 +0300
Subject: [PATCH 2/2] Fix review comments

---
 .../lldb-dap/breakpoint/TestDAP_logpoints.py     | 16 ++++------------
 lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp   | 11 +++++++----
 lldb/tools/lldb-dap/Protocol/ProtocolTypes.h     |  2 +-
 lldb/tools/lldb-dap/SourceBreakpoint.cpp         |  3 +--
 lldb/unittests/DAP/ProtocolTypesTest.cpp         | 11 +++++++++++
 5 files changed, 24 insertions(+), 19 deletions(-)

diff --git a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_logpoints.py b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_logpoints.py
index 527b6d90314ee..4d968286880db 100644
--- a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_logpoints.py
+++ b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_logpoints.py
@@ -78,7 +78,9 @@ def test_logMessage_basic(self):
             )
         session.continue_to_exit()
 
-    def check_logpoint_to_breakpoint_conversion(self, log_message):
+    @skipIfWindows
+    def test_logmessage_claeared(self):
+        """Tests removing logMessage restores a stopping breakpoint."""
         session = self.build_and_create_session()
         initial_stop = self.stop_at_before_loop_line(session)
         loop_line = line_number("main.cpp", "// break loop")
@@ -97,7 +99,7 @@ def check_logpoint_to_breakpoint_conversion(self, log_message):
         [breakpoint_id, _] = session.resolve_source_breakpoints(
             self.main_path,
             [
-                SourceBreakpoint(loop_line, logMessage=log_message),
+                SourceBreakpoint(loop_line, logMessage=None),
                 SourceBreakpoint(after_loop_line),
             ],
         )
@@ -131,16 +133,6 @@ def check_logpoint_to_breakpoint_conversion(self, log_message):
         self.assertEqual(messages, [log_prefix + str(i) for i in range(1, 10)])
         session.continue_to_exit()
 
-    @skipIfWindows
-    def test_logmessage_none(self):
-        """Tests removing logMessage restores a stopping breakpoint."""
-        self.check_logpoint_to_breakpoint_conversion(None)
-
-    @skipIfWindows
-    def test_logmessage_empty(self):
-        """Tests clearing logMessage restores a stopping breakpoint."""
-        self.check_logpoint_to_breakpoint_conversion("")
-
     @skipIfWindows
     def test_logmessage_advanced(self):
         """Tests breakpoint logmessage functionality for complex expression."""
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
index b70aeb661f3e1..fab46c75a6a4a 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
@@ -855,10 +855,13 @@ bool fromJSON(const llvm::json::Value &Params, Breakpoint &BP,
 bool fromJSON(const llvm::json::Value &Params, SourceBreakpoint &SB,
               llvm::json::Path P) {
   llvm::json::ObjectMapper O(Params, P);
-  return O && O.map("line", SB.line) && O.mapOptional("column", SB.column) &&
+  std::optional<String> log_message;
+  if (!(O && O.mapOptional("logMessage", log_message)))
+    return false;
+  SB.logMessage = log_message.value_or("");
+  return O.map("line", SB.line) && O.mapOptional("column", SB.column) &&
          O.mapOptional("condition", SB.condition) &&
          O.mapOptional("hitCondition", SB.hitCondition) &&
-         O.mapOptional("logMessage", SB.logMessage) &&
          O.mapOptional("mode", SB.mode);
 }
 
@@ -871,8 +874,8 @@ llvm::json::Value toJSON(const SourceBreakpoint &SB) {
     result.insert({"condition", *SB.condition});
   if (SB.hitCondition)
     result.insert({"hitCondition", *SB.hitCondition});
-  if (SB.logMessage)
-    result.insert({"logMessage", *SB.logMessage});
+  if (!SB.logMessage.empty())
+    result.insert({"logMessage", SB.logMessage});
   if (SB.mode)
     result.insert({"mode", *SB.mode});
 
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
index 39eaeb064ccd2..2b4a32e29d809 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
@@ -689,7 +689,7 @@ struct SourceBreakpoint {
   /// capability `supportsLogPoints` is true.
   /// If either `hitCondition` or `condition` is specified, then the message
   /// should only be logged if those conditions are met.
-  std::optional<String> logMessage;
+  String logMessage;
 
   /// The mode of this breakpoint. If defined, this must be one of the
   /// `breakpointModes` the debug adapter advertised in its `Capabilities`.
diff --git a/lldb/tools/lldb-dap/SourceBreakpoint.cpp b/lldb/tools/lldb-dap/SourceBreakpoint.cpp
index e44ac6bfb1785..82a07f1f99552 100644
--- a/lldb/tools/lldb-dap/SourceBreakpoint.cpp
+++ b/lldb/tools/lldb-dap/SourceBreakpoint.cpp
@@ -33,8 +33,7 @@ namespace lldb_dap {
 SourceBreakpoint::SourceBreakpoint(DAP &dap,
                                    const protocol::SourceBreakpoint &breakpoint)
     : Breakpoint(dap, breakpoint.condition, breakpoint.hitCondition),
-      m_log_message(breakpoint.logMessage.value_or("")),
-      m_line(breakpoint.line),
+      m_log_message(breakpoint.logMessage), m_line(breakpoint.line),
       m_column(breakpoint.column.value_or(LLDB_INVALID_COLUMN_NUMBER)) {}
 
 llvm::Error SourceBreakpoint::SetBreakpoint(const protocol::Source &source) {
diff --git a/lldb/unittests/DAP/ProtocolTypesTest.cpp b/lldb/unittests/DAP/ProtocolTypesTest.cpp
index 996b6e3ec6ba5..38d8f554e3984 100644
--- a/lldb/unittests/DAP/ProtocolTypesTest.cpp
+++ b/lldb/unittests/DAP/ProtocolTypesTest.cpp
@@ -163,6 +163,17 @@ TEST(ProtocolTypesTest, SourceBreakpoint) {
   EXPECT_EQ(source_breakpoint.mode, deserialized_source_breakpoint->mode);
 }
 
+TEST(ProtocolTypesTest, SourceBreakpointOptionalLogMessage) {
+  for (StringRef json : {R"({"line": 0, "logMessage": null})", R"({"line": 0})",
+                         R"({"line": 0, "logMessage": ""})"}) {
+    Expected<SourceBreakpoint> source_breakpoint =
+        parse<SourceBreakpoint>(json);
+    ASSERT_THAT_EXPECTED(source_breakpoint, Succeeded());
+    EXPECT_EQ(source_breakpoint->line, 0u);
+    EXPECT_TRUE(source_breakpoint->logMessage.empty());
+  }
+}
+
 TEST(ProtocolTypesTest, FunctionBreakpoint) {
   FunctionBreakpoint function_breakpoint;
   function_breakpoint.name = "myFunction";



More information about the lldb-commits mailing list