[Lldb-commits] [lldb] [llvm] [lldb][lldb-dap] Implement jump to cursor (PR #130503)

Ebuka Ezike via lldb-commits lldb-commits at lists.llvm.org
Tue Mar 18 03:53:37 PDT 2025


https://github.com/da-viper updated https://github.com/llvm/llvm-project/pull/130503

>From 63c0d5071146893b485dd4d2665e55fc697e1352 Mon Sep 17 00:00:00 2001
From: Ezike Ebuka <yerimyah1 at gmail.com>
Date: Sun, 9 Mar 2025 12:46:54 +0000
Subject: [PATCH 01/19] [lldb-dap] implement jump to cursor.

---
 lldb/cmake/modules/LLDBConfig.cmake           |   2 +-
 lldb/tools/lldb-dap/CMakeLists.txt            |   2 +
 lldb/tools/lldb-dap/DAP.cpp                   |  23 +++-
 lldb/tools/lldb-dap/DAP.h                     |  27 +++-
 .../lldb-dap/Handler/GoToRequestHandler.cpp   | 103 +++++++++++++++
 .../Handler/GoToTargetsRequestHandler.cpp     | 120 ++++++++++++++++++
 .../Handler/InitializeRequestHandler.cpp      |   2 +-
 lldb/tools/lldb-dap/Handler/RequestHandler.h  |  14 ++
 lldb/tools/lldb-dap/lldb-dap.cpp              |   2 +
 9 files changed, 291 insertions(+), 4 deletions(-)
 create mode 100644 lldb/tools/lldb-dap/Handler/GoToRequestHandler.cpp
 create mode 100644 lldb/tools/lldb-dap/Handler/GoToTargetsRequestHandler.cpp

diff --git a/lldb/cmake/modules/LLDBConfig.cmake b/lldb/cmake/modules/LLDBConfig.cmake
index 747f7e6038181..8d02088548634 100644
--- a/lldb/cmake/modules/LLDBConfig.cmake
+++ b/lldb/cmake/modules/LLDBConfig.cmake
@@ -57,7 +57,7 @@ add_optional_dependency(LLDB_ENABLE_CURSES "Enable curses support in LLDB" Curse
 add_optional_dependency(LLDB_ENABLE_LZMA "Enable LZMA compression support in LLDB" LibLZMA LIBLZMA_FOUND)
 add_optional_dependency(LLDB_ENABLE_LUA "Enable Lua scripting support in LLDB" LuaAndSwig LUAANDSWIG_FOUND)
 add_optional_dependency(LLDB_ENABLE_PYTHON "Enable Python scripting support in LLDB" PythonAndSwig PYTHONANDSWIG_FOUND)
-add_optional_dependency(LLDB_ENABLE_LIBXML2 "Enable Libxml 2 support in LLDB" LibXml2 LIBXML2_FOUND VERSION 2.8)
+add_optional_dependency(LLDB_ENABLE_LIBXML2 "Enable Libxml 2 support in LLDB" LibXml2 LIBXML2_FOUND VERSION)
 add_optional_dependency(LLDB_ENABLE_FBSDVMCORE "Enable libfbsdvmcore support in LLDB" FBSDVMCore FBSDVMCore_FOUND QUIET)
 
 option(LLDB_USE_ENTITLEMENTS "When codesigning, use entitlements if available" ON)
diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt
index 9a2d604f4d573..ff7e413c4bb1c 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -50,6 +50,8 @@ add_lldb_tool(lldb-dap
   Handler/DisconnectRequestHandler.cpp
   Handler/EvaluateRequestHandler.cpp
   Handler/ExceptionInfoRequestHandler.cpp
+        Handler/GoToRequestHandler.cpp
+        Handler/GoToTargetsRequestHandler.cpp
   Handler/InitializeRequestHandler.cpp
   Handler/LaunchRequestHandler.cpp
   Handler/LocationsRequestHandler.cpp
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index 1f7b25e7c5bcc..f72bc34d52b53 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -76,7 +76,7 @@ DAP::DAP(std::string name, llvm::StringRef path, std::ofstream *log,
       configuration_done_sent(false), waiting_for_run_in_terminal(false),
       progress_event_reporter(
           [&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }),
-      reverse_request_seq(0), repl_mode(repl_mode) {}
+      reverse_request_seq(0), repl_mode(repl_mode), goto_id_map() {}
 
 DAP::~DAP() = default;
 
@@ -899,6 +899,27 @@ lldb::SBError DAP::WaitForProcessToStop(uint32_t seconds) {
   return error;
 }
 
+std::optional<lldb::SBLineEntry> Gotos::GetLineEntry(uint64_t id) const {
+  const auto iter = line_entries.find(id);
+  if (iter != line_entries.end())
+    return iter->second;
+
+  return std::nullopt;
+}
+
+uint64_t Gotos::InsertLineEntry(lldb::SBLineEntry line_entry) {
+  const auto spec_id = this->NewSpecId();
+  line_entries.insert(std::make_pair(spec_id, line_entry));
+  return spec_id;
+}
+
+void Gotos::Clear() {
+  new_id = 0UL;
+  line_entries.clear();
+}
+
+uint64_t Gotos::NewSpecId() { return new_id++; }
+
 void Variables::Clear() {
   locals.Clear();
   globals.Clear();
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index 8b2e498a28c95..693908016fdc9 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -79,6 +79,27 @@ enum class PacketStatus {
 
 enum class ReplMode { Variable = 0, Command, Auto };
 
+class Gotos {
+public:
+  /// \return the line_entry corresponding with \p id
+  ///
+  /// If \p id is invalid std::nullopt is returned.
+  std::optional<lldb::SBLineEntry> GetLineEntry(uint64_t id) const;
+
+  /// Insert a new \p line_entry.
+  /// \return id assigned to this line_entry.
+  uint64_t InsertLineEntry(lldb::SBLineEntry line_entry);
+
+  /// clears all line entries and reset the generated ids.
+  void Clear();
+
+private:
+  uint64_t NewSpecId();
+
+  llvm::DenseMap<uint64_t, lldb::SBLineEntry> line_entries;
+  uint64_t new_id = 0ul;
+};
+
 struct Variables {
   /// Variable_reference start index of permanent expandable variable.
   static constexpr int64_t PermanentVariableStartIndex = (1ll << 32);
@@ -209,6 +230,7 @@ struct DAP {
   // empty; if the previous expression was a variable expression, this string
   // will contain that expression.
   std::string last_nonempty_var_expression;
+  Gotos goto_id_map;
 
   DAP(std::string name, llvm::StringRef path, std::ofstream *log,
       lldb::IOObjectSP input, lldb::IOObjectSP output, ReplMode repl_mode,
@@ -352,7 +374,10 @@ struct DAP {
   }
 
   /// Debuggee will continue from stopped state.
-  void WillContinue() { variables.Clear(); }
+  void WillContinue() {
+    variables.Clear();
+    goto_id_map.Clear();
+  }
 
   /// Poll the process to wait for it to reach the eStateStopped state.
   ///
diff --git a/lldb/tools/lldb-dap/Handler/GoToRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/GoToRequestHandler.cpp
new file mode 100644
index 0000000000000..06a50eb939828
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/GoToRequestHandler.cpp
@@ -0,0 +1,103 @@
+//===-- GoToRequestHandler.cpp --------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+#include "EventHelper.h"
+#include "JSONUtils.h"
+
+namespace lldb_dap {
+
+// "GotoRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "The request sets the location where the debuggee will
+//     continue to run.\nThis makes it possible to skip the execution of code or
+//     to execute code again.\nThe code between the current location and the
+//     goto target is not executed but skipped.\nThe debug adapter first sends
+//     the response and then a `stopped` event with reason `goto`.\nClients
+//     should only call this request if the corresponding capability
+//     `supportsGotoTargetsRequest` is true (because only then goto targets
+//     exist that can be passed as arguments).", "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "goto" ]
+//       },
+//       "arguments": {
+//         "$ref": "#/definitions/GotoArguments"
+//       }
+//     },
+//     "required": [ "command", "arguments"  ]
+//   }]
+// }
+// "GotoArguments": {
+//   "type": "object",
+//   "description": "Arguments for `goto` request.",
+//   "properties": {
+//     "threadId": {
+//       "type": "integer",
+//       "description": "Set the goto target for this thread."
+//     },
+//     "targetId": {
+//       "type": "integer",
+//       "description": "The location where the debuggee will continue to run."
+//     }
+//   },
+//   "required": [ "threadId", "targetId" ]
+// }
+// "GotoResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to `goto` request. This is just an
+//     acknowledgement, so no body field is required."
+//   }]
+// }
+void GoToRequestHandler::operator()(const llvm::json::Object &request) const {
+  llvm::json::Object response;
+  FillResponse(request, response);
+
+  auto SendError = [&](auto &&message) {
+    response["success"] = false;
+    response["message"] = message;
+    dap.SendJSON(llvm::json::Value(std::move(response)));
+  };
+
+  const auto *goto_arguments = request.getObject("arguments");
+  if (goto_arguments == nullptr) {
+    SendError("Arguments is empty");
+    return;
+  }
+
+  lldb::SBThread current_thread = dap.GetLLDBThread(*goto_arguments);
+  if (!current_thread.IsValid()) {
+    SendError(llvm::formatv("Thread id `{0}` is not valid",
+                            current_thread.GetThreadID()));
+    return;
+  }
+
+  const auto target_id = GetInteger<uint64_t>(goto_arguments, "targetId");
+  const auto line_entry = dap.goto_id_map.GetLineEntry(target_id.value());
+  if (!target_id || !line_entry) {
+    SendError(llvm::formatv("Target id `{0}` is not valid",
+                            current_thread.GetThreadID()));
+    return;
+  }
+
+  auto file_spec = line_entry->GetFileSpec();
+  const auto error =
+      current_thread.JumpToLine(file_spec, line_entry->GetLine());
+  if (error.Fail()) {
+    SendError(error.GetCString());
+    return;
+  }
+
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+
+  SendThreadStoppedEvent(dap);
+}
+
+} // namespace lldb_dap
\ No newline at end of file
diff --git a/lldb/tools/lldb-dap/Handler/GoToTargetsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/GoToTargetsRequestHandler.cpp
new file mode 100644
index 0000000000000..9481055ee0119
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/GoToTargetsRequestHandler.cpp
@@ -0,0 +1,120 @@
+//===-- GoToTargetsRequestHandler.cpp
+//--------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DAP.h"
+
+#include "JSONUtils.h"
+
+#include <lldb/API/SBStream.h>
+
+namespace lldb_dap {
+
+//  "GotoTargetsRequest": {
+//    "allOf": [ { "$ref": "#/definitions/Request" }, {
+//      "type": "object",
+//      "description": "This request retrieves the possible goto targets for the
+//      specified source location.\nThese targets can be used in the `goto`
+//      request.\nClients should only call this request if the corresponding
+//      capability `supportsGotoTargetsRequest` is true.", "properties": {
+//        "command": {
+//          "type": "string",
+//          "enum": [ "gotoTargets" ]
+//        },
+//        "arguments": {
+//          "$ref": "#/definitions/GotoTargetsArguments"
+//        }
+//      },
+//      "required": [ "command", "arguments"  ]
+//    }]
+//  },
+//  "GotoTargetsArguments": {
+//    "type": "object",
+//    "description": "Arguments for `gotoTargets` request.",
+//    "properties": {
+//      "source": {
+//        "$ref": "#/definitions/Source",
+//        "description": "The source location for which the goto targets are
+//        determined."
+//      },
+//      "line": {
+//        "type": "integer",
+//        "description": "The line location for which the goto targets are
+//        determined."
+//      },
+//      "column": {
+//        "type": "integer",
+//        "description": "The position within `line` for which the goto targets
+//        are determined. It is measured in UTF-16 code units and the client
+//        capability `columnsStartAt1` determines whether it is 0- or 1-based."
+//      }
+//    },
+//    "required": [ "source", "line" ]
+//  },
+//  "GotoTargetsResponse": {
+//    "allOf": [ { "$ref": "#/definitions/Response" }, {
+//      "type": "object",
+//      "description": "Response to `gotoTargets` request.",
+//      "properties": {
+//        "body": {
+//          "type": "object",
+//          "properties": {
+//            "targets": {
+//              "type": "array",
+//              "items": {
+//                "$ref": "#/definitions/GotoTarget"
+//              },
+//              "description": "The possible goto targets of the specified
+//              location."
+//            }
+//          },
+//          "required": [ "targets" ]
+//        }
+//      },
+//      "required": [ "body" ]
+//    }]
+//  },
+void GoToTargetsRequestHandler::operator()(
+    const llvm::json::Object &request) const {
+  llvm::json::Object response;
+  FillResponse(request, response);
+  const auto *arguments = request.getObject("arguments");
+  const auto *source = arguments->getObject("source");
+  const std::string path = GetString(source, "path").str();
+
+  const auto goto_line = GetInteger<uint64_t>(arguments, "line").value_or(0u);
+  const auto goto_column =
+      GetInteger<uint64_t>(arguments, "column").value_or(0u);
+
+  lldb::SBLineEntry line_entry{};
+  const lldb::SBFileSpec file_spec(path.c_str(), true);
+  line_entry.SetFileSpec(file_spec);
+  line_entry.SetLine(goto_line);
+  line_entry.SetColumn(goto_column);
+
+  const auto target_id = dap.goto_id_map.InsertLineEntry(line_entry);
+  llvm::json::Array response_targets;
+  const auto target_line = line_entry.GetLine();
+  const auto target_column = line_entry.GetColumn();
+  auto target = llvm::json::Object();
+  target.try_emplace("id", target_id);
+
+  lldb::SBStream stream;
+  line_entry.GetDescription(stream);
+  target.try_emplace("label",
+                     llvm::StringRef(stream.GetData(), stream.GetSize()));
+  target.try_emplace("column", target_column);
+  target.try_emplace("line", target_line);
+
+  response_targets.push_back(std::move(target));
+  llvm::json::Object body;
+  body.try_emplace("targets", std::move(response_targets));
+  response.try_emplace("body", std::move(body));
+  dap.SendJSON(llvm::json::Value(std::move(response)));
+}
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp
index 5bb73a7ec0d85..12f292a612e6c 100644
--- a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp
@@ -406,7 +406,7 @@ void InitializeRequestHandler::operator()(
   // The debug adapter supports restarting a frame.
   body.try_emplace("supportsRestartFrame", false);
   // The debug adapter supports the gotoTargetsRequest.
-  body.try_emplace("supportsGotoTargetsRequest", false);
+  body.try_emplace("supportsGotoTargetsRequest", true);
   // The debug adapter supports the stepInTargetsRequest.
   body.try_emplace("supportsStepInTargetsRequest", true);
   // The debug adapter supports the completions request.
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index b44367518bcb9..c0f33684da7c4 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -113,6 +113,20 @@ class ExceptionInfoRequestHandler : public RequestHandler {
   void operator()(const llvm::json::Object &request) const override;
 };
 
+class GoToRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "goto"; }
+  void operator()(const llvm::json::Object &request) const override;
+};
+
+class GoToTargetsRequestHandler : public RequestHandler {
+public:
+  using RequestHandler::RequestHandler;
+  static llvm::StringLiteral getCommand() { return "gotoTargets"; }
+  void operator()(const llvm::json::Object &request) const override;
+};
+
 class InitializeRequestHandler : public RequestHandler {
 public:
   using RequestHandler::RequestHandler;
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index a5d9978e30248..ef2a1d92010ca 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -124,6 +124,8 @@ static void RegisterRequestCallbacks(DAP &dap) {
   dap.RegisterRequest<EvaluateRequestHandler>();
   dap.RegisterRequest<ExceptionInfoRequestHandler>();
   dap.RegisterRequest<InitializeRequestHandler>();
+  dap.RegisterRequest<GoToRequestHandler>();
+  dap.RegisterRequest<GoToTargetsRequestHandler>();
   dap.RegisterRequest<LaunchRequestHandler>();
   dap.RegisterRequest<LocationsRequestHandler>();
   dap.RegisterRequest<NextRequestHandler>();

>From 3038036fdfeffbbfbbec1a6583babb0aa2e9f697 Mon Sep 17 00:00:00 2001
From: Ezike Ebuka <yerimyah1 at gmail.com>
Date: Sun, 9 Mar 2025 18:30:24 +0000
Subject: [PATCH 02/19] [lldb][lldb-dap] add jump to cursor tests

---
 .../test/tools/lldb-dap/dap_server.py         | 27 +++++++++
 .../API/tools/lldb-dap/gotoTarget/Makefile    |  3 +
 .../lldb-dap/gotoTarget/TestDAP_gotoTarget.py | 60 +++++++++++++++++++
 .../test/API/tools/lldb-dap/gotoTarget/main.c | 11 ++++
 4 files changed, 101 insertions(+)
 create mode 100644 lldb/test/API/tools/lldb-dap/gotoTarget/Makefile
 create mode 100644 lldb/test/API/tools/lldb-dap/gotoTarget/TestDAP_gotoTarget.py
 create mode 100644 lldb/test/API/tools/lldb-dap/gotoTarget/main.c

diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
index 9471594b66012..d6c3bd0551cd7 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
@@ -753,6 +753,33 @@ def request_exceptionInfo(self, threadId=None):
         }
         return self.send_recv(command_dict)
 
+    def request_goto(self, threadId: int, targetId: int):
+        command_dict = {
+            "command": "goto",
+            "type": "request",
+            "arguments": {
+                "threadId": threadId,
+                "targetId": targetId,
+            },
+        }
+        return self.send_recv(command_dict)
+
+    def request_gotoTargets(self, filename: str, path: str, line: int, column: int):
+        arguments = {
+            "source": {
+                "name": filename,
+                "path": path,
+            },
+            "line": line,
+            "column": column,
+        }
+        command_dict = {
+            "command": "gotoTargets",
+            "type": "request",
+            "arguments": arguments,
+        }
+        return self.send_recv(command_dict)
+
     def request_initialize(self, sourceInitFile):
         command_dict = {
             "command": "initialize",
diff --git a/lldb/test/API/tools/lldb-dap/gotoTarget/Makefile b/lldb/test/API/tools/lldb-dap/gotoTarget/Makefile
new file mode 100644
index 0000000000000..10495940055b6
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/gotoTarget/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
diff --git a/lldb/test/API/tools/lldb-dap/gotoTarget/TestDAP_gotoTarget.py b/lldb/test/API/tools/lldb-dap/gotoTarget/TestDAP_gotoTarget.py
new file mode 100644
index 0000000000000..6d0f9ae478f33
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/gotoTarget/TestDAP_gotoTarget.py
@@ -0,0 +1,60 @@
+"""
+Test lldb-dap gotoTarget request
+"""
+
+from lldbsuite.test.lldbtest import line_number
+import lldbdap_testcase
+import os
+
+
+class TestDAP_gotoTarget(lldbdap_testcase.DAPTestCaseBase):
+
+    def test_default(self):
+        """
+        Tests the jump to cursor of a simple program. No arguments,
+        environment, or anything else is specified.
+        This does not run any statement between the current breakpoint
+        and the jump line location.
+        """
+        program = self.getBuildArtifact("a.out")
+        self.build_and_launch(program)
+
+        source_file = "main.c"
+        self.source_path = os.path.join(os.getcwd(), source_file)
+        self.set_source_breakpoints(
+            source_file, [line_number(source_file, "// breakpoint 1")]
+        )
+        self.continue_to_next_stop()
+
+        first_var_1_object = self.dap_server.get_local_variable("var_1")
+        self.assertEqual(first_var_1_object["value"], "10")
+
+        goto_line = line_number(source_file, "// goto 1")
+        goto_column = 1
+        response = self.dap_server.request_gotoTargets(
+            source_file, self.source_path, goto_line, goto_column
+        )
+
+        self.assertEqual(
+            response["success"], True, "expects success when request for targets"
+        )
+        target = response["body"]["targets"][0]
+        self.assertGreaterEqual(
+            target["id"], 0, "targetId should be greater than or equal to zero"
+        )
+
+        target_id = target["id"]
+        thread_id = self.dap_server.get_thread_id()
+        self.assertIsNotNone(thread_id, "thread Id should not be none")
+
+        response = self.dap_server.request_goto(thread_id, target_id)
+
+        self.assertEqual(
+            response["success"], True, "expects success to go to a target id"
+        )
+
+        var_1_object = self.dap_server.get_local_variable("var_1")
+        self.assertEqual(first_var_1_object["value"], var_1_object["value"])
+
+        self.continue_to_next_stop()  # a stop event is sent after a successful goto response
+        self.continue_to_exit()
diff --git a/lldb/test/API/tools/lldb-dap/gotoTarget/main.c b/lldb/test/API/tools/lldb-dap/gotoTarget/main.c
new file mode 100644
index 0000000000000..74210e5877369
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/gotoTarget/main.c
@@ -0,0 +1,11 @@
+
+int main() {
+
+  int var_1 = 10;
+
+  var_1 = 20; // breakpoint 1
+
+  int var_2 = 40; // goto 1
+
+  return 0;
+}
\ No newline at end of file

>From 1397e5fb983edc560ab00187ee52f50d0b176fe5 Mon Sep 17 00:00:00 2001
From: Ezike Ebuka <yerimyah1 at gmail.com>
Date: Sun, 9 Mar 2025 18:47:44 +0000
Subject: [PATCH 03/19] [lldb-dap] Rever removing libXML version

---
 lldb/cmake/modules/LLDBConfig.cmake | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lldb/cmake/modules/LLDBConfig.cmake b/lldb/cmake/modules/LLDBConfig.cmake
index 8d02088548634..747f7e6038181 100644
--- a/lldb/cmake/modules/LLDBConfig.cmake
+++ b/lldb/cmake/modules/LLDBConfig.cmake
@@ -57,7 +57,7 @@ add_optional_dependency(LLDB_ENABLE_CURSES "Enable curses support in LLDB" Curse
 add_optional_dependency(LLDB_ENABLE_LZMA "Enable LZMA compression support in LLDB" LibLZMA LIBLZMA_FOUND)
 add_optional_dependency(LLDB_ENABLE_LUA "Enable Lua scripting support in LLDB" LuaAndSwig LUAANDSWIG_FOUND)
 add_optional_dependency(LLDB_ENABLE_PYTHON "Enable Python scripting support in LLDB" PythonAndSwig PYTHONANDSWIG_FOUND)
-add_optional_dependency(LLDB_ENABLE_LIBXML2 "Enable Libxml 2 support in LLDB" LibXml2 LIBXML2_FOUND VERSION)
+add_optional_dependency(LLDB_ENABLE_LIBXML2 "Enable Libxml 2 support in LLDB" LibXml2 LIBXML2_FOUND VERSION 2.8)
 add_optional_dependency(LLDB_ENABLE_FBSDVMCORE "Enable libfbsdvmcore support in LLDB" FBSDVMCore FBSDVMCore_FOUND QUIET)
 
 option(LLDB_USE_ENTITLEMENTS "When codesigning, use entitlements if available" ON)

>From 07ec2d67e50698923ab2098e51ff2c250c6d7eb2 Mon Sep 17 00:00:00 2001
From: Ezike Ebuka <yerimyah1 at gmail.com>
Date: Sun, 9 Mar 2025 19:05:32 +0000
Subject: [PATCH 04/19] [lldb][lldb-dap] fix code format

---
 lldb/test/API/tools/lldb-dap/gotoTarget/TestDAP_gotoTarget.py | 1 -
 1 file changed, 1 deletion(-)

diff --git a/lldb/test/API/tools/lldb-dap/gotoTarget/TestDAP_gotoTarget.py b/lldb/test/API/tools/lldb-dap/gotoTarget/TestDAP_gotoTarget.py
index 6d0f9ae478f33..9eb6d7b836d34 100644
--- a/lldb/test/API/tools/lldb-dap/gotoTarget/TestDAP_gotoTarget.py
+++ b/lldb/test/API/tools/lldb-dap/gotoTarget/TestDAP_gotoTarget.py
@@ -8,7 +8,6 @@
 
 
 class TestDAP_gotoTarget(lldbdap_testcase.DAPTestCaseBase):
-
     def test_default(self):
         """
         Tests the jump to cursor of a simple program. No arguments,

>From faa6d730a043a6649d428ccb3ef2bd59fa426ac5 Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <57949090+da-viper at users.noreply.github.com>
Date: Tue, 11 Mar 2025 13:19:21 +0000
Subject: [PATCH 05/19] Update
 lldb/tools/lldb-dap/Handler/GoToTargetsRequestHandler.cpp

Co-authored-by: Adrian Vogelsgesang <adrian.vogelsgesang at tum.de>
---
 lldb/tools/lldb-dap/Handler/GoToTargetsRequestHandler.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lldb/tools/lldb-dap/Handler/GoToTargetsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/GoToTargetsRequestHandler.cpp
index 9481055ee0119..db676385a3d39 100644
--- a/lldb/tools/lldb-dap/Handler/GoToTargetsRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/GoToTargetsRequestHandler.cpp
@@ -21,7 +21,8 @@ namespace lldb_dap {
 //      "description": "This request retrieves the possible goto targets for the
 //      specified source location.\nThese targets can be used in the `goto`
 //      request.\nClients should only call this request if the corresponding
-//      capability `supportsGotoTargetsRequest` is true.", "properties": {
+//      capability `supportsGotoTargetsRequest` is true.",
+//.     "properties": {
 //        "command": {
 //          "type": "string",
 //          "enum": [ "gotoTargets" ]

>From 1d873e528701e93cbcc1458196ba31b95562c4d3 Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <57949090+da-viper at users.noreply.github.com>
Date: Tue, 11 Mar 2025 13:19:31 +0000
Subject: [PATCH 06/19] Update lldb/tools/lldb-dap/DAP.h

Co-authored-by: Adrian Vogelsgesang <adrian.vogelsgesang at tum.de>
---
 lldb/tools/lldb-dap/DAP.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index 693908016fdc9..84a4e09167786 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -90,7 +90,7 @@ class Gotos {
   /// \return id assigned to this line_entry.
   uint64_t InsertLineEntry(lldb::SBLineEntry line_entry);
 
-  /// clears all line entries and reset the generated ids.
+  /// Clears all line entries and reset the generated ids.
   void Clear();
 
 private:

>From 05a01c956a9946059288c3dc50f64aa28a8a640d Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <57949090+da-viper at users.noreply.github.com>
Date: Tue, 11 Mar 2025 13:34:07 +0000
Subject: [PATCH 07/19] Update
 lldb/tools/lldb-dap/Handler/GoToRequestHandler.cpp

Co-authored-by: Adrian Vogelsgesang <adrian.vogelsgesang at tum.de>
---
 lldb/tools/lldb-dap/Handler/GoToRequestHandler.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lldb/tools/lldb-dap/Handler/GoToRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/GoToRequestHandler.cpp
index 06a50eb939828..f36c0f483d1db 100644
--- a/lldb/tools/lldb-dap/Handler/GoToRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/GoToRequestHandler.cpp
@@ -22,7 +22,8 @@ namespace lldb_dap {
 //     the response and then a `stopped` event with reason `goto`.\nClients
 //     should only call this request if the corresponding capability
 //     `supportsGotoTargetsRequest` is true (because only then goto targets
-//     exist that can be passed as arguments).", "properties": {
+//     exist that can be passed as arguments).",
+//.    "properties": {
 //       "command": {
 //         "type": "string",
 //         "enum": [ "goto" ]

>From b974f8249d4372aed55d89c619a35ebc726e42e6 Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Tue, 11 Mar 2025 13:36:51 +0000
Subject: [PATCH 08/19] [lldb][lldb-dap] add review commits

---
 lldb/test/API/tools/lldb-dap/gotoTarget/main.c            | 2 +-
 lldb/tools/lldb-dap/Handler/GoToRequestHandler.cpp        | 2 +-
 lldb/tools/lldb-dap/Handler/GoToTargetsRequestHandler.cpp | 4 ++--
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/lldb/test/API/tools/lldb-dap/gotoTarget/main.c b/lldb/test/API/tools/lldb-dap/gotoTarget/main.c
index 74210e5877369..8c0a2d4770f17 100644
--- a/lldb/test/API/tools/lldb-dap/gotoTarget/main.c
+++ b/lldb/test/API/tools/lldb-dap/gotoTarget/main.c
@@ -8,4 +8,4 @@ int main() {
   int var_2 = 40; // goto 1
 
   return 0;
-}
\ No newline at end of file
+}
diff --git a/lldb/tools/lldb-dap/Handler/GoToRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/GoToRequestHandler.cpp
index f36c0f483d1db..6a312ffef031e 100644
--- a/lldb/tools/lldb-dap/Handler/GoToRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/GoToRequestHandler.cpp
@@ -101,4 +101,4 @@ void GoToRequestHandler::operator()(const llvm::json::Object &request) const {
   SendThreadStoppedEvent(dap);
 }
 
-} // namespace lldb_dap
\ No newline at end of file
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/GoToTargetsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/GoToTargetsRequestHandler.cpp
index db676385a3d39..5fba6e65c28c7 100644
--- a/lldb/tools/lldb-dap/Handler/GoToTargetsRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/GoToTargetsRequestHandler.cpp
@@ -1,5 +1,4 @@
-//===-- GoToTargetsRequestHandler.cpp
-//--------------------------------------===//
+//===-- GoToTargetsRequestHandler.cpp -------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -118,4 +117,5 @@ void GoToTargetsRequestHandler::operator()(
   response.try_emplace("body", std::move(body));
   dap.SendJSON(llvm::json::Value(std::move(response)));
 }
+
 } // namespace lldb_dap

>From 4aa4a46282570b88d9ca040f8fc2cb41271e74ee Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Tue, 11 Mar 2025 13:40:19 +0000
Subject: [PATCH 09/19] [lldb][lldb-dap] add review changes

---
 lldb/tools/lldb-dap/CMakeLists.txt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt
index ff7e413c4bb1c..6cd38259d7fc1 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -50,8 +50,8 @@ add_lldb_tool(lldb-dap
   Handler/DisconnectRequestHandler.cpp
   Handler/EvaluateRequestHandler.cpp
   Handler/ExceptionInfoRequestHandler.cpp
-        Handler/GoToRequestHandler.cpp
-        Handler/GoToTargetsRequestHandler.cpp
+  Handler/GoToRequestHandler.cpp
+  Handler/GoToTargetsRequestHandler.cpp
   Handler/InitializeRequestHandler.cpp
   Handler/LaunchRequestHandler.cpp
   Handler/LocationsRequestHandler.cpp

>From 32b519d5ff2841f7b263b0ce0e050425fa56f4ca Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Tue, 11 Mar 2025 14:26:59 +0000
Subject: [PATCH 10/19] [lldb][lldb-dap] Update jump to cursor test

---
 .../lldb-dap/gotoTarget/TestDAP_gotoTarget.py | 38 +++++++++++++++----
 1 file changed, 31 insertions(+), 7 deletions(-)

diff --git a/lldb/test/API/tools/lldb-dap/gotoTarget/TestDAP_gotoTarget.py b/lldb/test/API/tools/lldb-dap/gotoTarget/TestDAP_gotoTarget.py
index 9eb6d7b836d34..764949931c0cb 100644
--- a/lldb/test/API/tools/lldb-dap/gotoTarget/TestDAP_gotoTarget.py
+++ b/lldb/test/API/tools/lldb-dap/gotoTarget/TestDAP_gotoTarget.py
@@ -44,16 +44,40 @@ def test_default(self):
 
         target_id = target["id"]
         thread_id = self.dap_server.get_thread_id()
-        self.assertIsNotNone(thread_id, "thread Id should not be none")
+        self.assertIsNotNone(thread_id, "threadId should not be none")
 
         response = self.dap_server.request_goto(thread_id, target_id)
 
-        self.assertEqual(
-            response["success"], True, "expects success to go to a target id"
-        )
+        self.assertEqual(response["success"], True, "expects success to go to targetId")
+
+        self.dap_server.request_next(thread_id)
+        self.continue_to_next_stop()
+
+        local_variables = self.dap_server.get_local_variables()
+        verify_variables = {
+            "var_1": {
+                "name": "var_1",
+                "type": "int",
+                "value": "10",
+                "variablesReference": 0,
+            },
+            "var_2": {
+                "name": "var_2",
+                "type": "int",
+                "value": "40",
+                "variablesReference": 0,
+            },
+        }
 
-        var_1_object = self.dap_server.get_local_variable("var_1")
-        self.assertEqual(first_var_1_object["value"], var_1_object["value"])
+        for variable in local_variables:
+            name = variable["name"]
+            verify_variable = verify_variables[name]
 
-        self.continue_to_next_stop()  # a stop event is sent after a successful goto response
+            for key, value in verify_variable.items():
+                actual_value = variable[key]
+                self.assertEqual(
+                    actual_value,
+                    value,
+                    f"values does not match for key: `{key}` expected_value: `{value}`, actual_value: `{actual_value}`",
+                )
         self.continue_to_exit()

>From 53b6263671556605237c2275c207a3671009f8b8 Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Tue, 11 Mar 2025 15:56:42 +0000
Subject: [PATCH 11/19] [lldb][lldb-dap] add new test for goto execute again.

---
 .../lldb-dap/gotoTarget/TestDAP_gotoTarget.py | 95 ++++++++++++++-----
 .../test/API/tools/lldb-dap/gotoTarget/main.c |  9 ++
 2 files changed, 79 insertions(+), 25 deletions(-)

diff --git a/lldb/test/API/tools/lldb-dap/gotoTarget/TestDAP_gotoTarget.py b/lldb/test/API/tools/lldb-dap/gotoTarget/TestDAP_gotoTarget.py
index 764949931c0cb..efb9b3d4ec04b 100644
--- a/lldb/test/API/tools/lldb-dap/gotoTarget/TestDAP_gotoTarget.py
+++ b/lldb/test/API/tools/lldb-dap/gotoTarget/TestDAP_gotoTarget.py
@@ -2,12 +2,26 @@
 Test lldb-dap gotoTarget request
 """
 
+from typing import Dict, Any
+from unittest import SkipTest
+
 from lldbsuite.test.lldbtest import line_number
 import lldbdap_testcase
 import os
 
 
 class TestDAP_gotoTarget(lldbdap_testcase.DAPTestCaseBase):
+    def verify_variable(
+        self, actual_dict: Dict[str, Any], expected_dict: Dict[str, Any]
+    ):
+        for key, value in expected_dict.items():
+            actual_value = actual_dict[key]
+            self.assertEqual(
+                actual_value,
+                value,
+                f"values does not match for key: `{key}` expected_value: `{value}`, actual_value: `{actual_value}`",
+            )
+
     def test_default(self):
         """
         Tests the jump to cursor of a simple program. No arguments,
@@ -53,31 +67,62 @@ def test_default(self):
         self.dap_server.request_next(thread_id)
         self.continue_to_next_stop()
 
-        local_variables = self.dap_server.get_local_variables()
-        verify_variables = {
-            "var_1": {
-                "name": "var_1",
-                "type": "int",
-                "value": "10",
-                "variablesReference": 0,
-            },
-            "var_2": {
-                "name": "var_2",
-                "type": "int",
-                "value": "40",
-                "variablesReference": 0,
-            },
+        var1_variable = self.dap_server.get_local_variable("var_1")
+        var_1_expected = {
+            "name": "var_1",
+            "type": "int",
+            "value": "10",
+            "variablesReference": 0,
         }
+        self.verify_variable(var1_variable, var_1_expected)
+
+        var2_variable = self.dap_server.get_local_variable("var_2")
+        var_2_expected = {
+            "name": "var_2",
+            "type": "int",
+            "value": "40",
+            "variablesReference": 0,
+        }
+        self.verify_variable(var2_variable, var_2_expected)
+
+        self.continue_to_exit()
 
-        for variable in local_variables:
-            name = variable["name"]
-            verify_variable = verify_variables[name]
-
-            for key, value in verify_variable.items():
-                actual_value = variable[key]
-                self.assertEqual(
-                    actual_value,
-                    value,
-                    f"values does not match for key: `{key}` expected_value: `{value}`, actual_value: `{actual_value}`",
-                )
+    def test_execute_again(self):
+        program = self.getBuildArtifact("a.out")
+        self.build_and_launch(program)
+
+        source_file = "main.c"
+        self.source_path = os.path.join(os.getcwd(), source_file)
+        self.set_source_breakpoints(
+            source_file, [line_number(source_file, "// breakpoint 2")]
+        )
+        self.continue_to_next_stop()
+
+        end_var_3_value = self.dap_server.get_local_variable_value("var_3")
+        self.assertEqual(end_var_3_value, "99")
+
+        goto_line = line_number(source_file, "// goto 2")
+        goto_column = 1
+        response = self.dap_server.request_gotoTargets(
+            source_file, self.source_path, goto_line, goto_column
+        )
+
+        target = response["body"]["targets"][0]
+        self.assertGreaterEqual(
+            target["id"], 0, "targetId should be greater than or equal to zero"
+        )
+
+        target_id = target["id"]
+        thread_id = self.dap_server.get_thread_id()
+        self.assertIsNotNone(thread_id, "threadId should not be none")
+
+        response = self.dap_server.request_goto(thread_id, target_id)
+        self.assertEqual(response["success"], True, "expects success to go to targetId")
+        self.dap_server.request_next(thread_id)
+        self.continue_to_next_stop()
+
+        goto_var_3_value = self.dap_server.get_local_variable_value("var_3")
+        self.assertEqual(goto_var_3_value, "10")
+
+        self.continue_to_next_stop()
         self.continue_to_exit()
diff --git a/lldb/test/API/tools/lldb-dap/gotoTarget/main.c b/lldb/test/API/tools/lldb-dap/gotoTarget/main.c
index 8c0a2d4770f17..d67aeed25d411 100644
--- a/lldb/test/API/tools/lldb-dap/gotoTarget/main.c
+++ b/lldb/test/API/tools/lldb-dap/gotoTarget/main.c
@@ -1,4 +1,12 @@
 
+int test_execute_again() {
+  int var_3 = 10; // goto 2
+
+  var_3 = 99;
+
+  return var_3; // breakpoint 2
+}
+
 int main() {
 
   int var_1 = 10;
@@ -7,5 +15,6 @@ int main() {
 
   int var_2 = 40; // goto 1
 
+  int result = test_execute_again();
   return 0;
 }

>From 97751c085429d0dc4d38a17580508a5d907bbf63 Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Wed, 12 Mar 2025 22:00:36 +0000
Subject: [PATCH 12/19] [lldb][lldb-dap] add helper function
 `SendThreadGotoEvent`

LLDB currently does not have a `StopReason` for goto so I cannot use `SendThreadStoppedEvent` as I need to put the goto reason.

Signed-off-by: Ebuka Ezike <yerimyah1 at gmail.com>
---
 .../lldb-dap/Handler/GoToRequestHandler.cpp     | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/lldb/tools/lldb-dap/Handler/GoToRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/GoToRequestHandler.cpp
index 6a312ffef031e..bbbb48f0e0630 100644
--- a/lldb/tools/lldb-dap/Handler/GoToRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/GoToRequestHandler.cpp
@@ -12,6 +12,21 @@
 
 namespace lldb_dap {
 
+/// Creates an \p StoppedEvent with the reason \a goto
+static void SendThreadGotoEvent(DAP &dap, lldb::tid_t thread_id) {
+
+  llvm::json::Object event(CreateEventObject("stopped"));
+  llvm::json::Object body;
+  body.try_emplace("reason", "goto");
+  body.try_emplace("description", "Paused on Jump To Cursor");
+  body.try_emplace("threadId", thread_id);
+  body.try_emplace("preserveFocusHint", false);
+  body.try_emplace("allThreadsStopped", true);
+
+  event.try_emplace("body", std::move(body));
+  dap.SendJSON(llvm::json::Value(std::move(event)));
+}
+
 // "GotoRequest": {
 //   "allOf": [ { "$ref": "#/definitions/Request" }, {
 //     "type": "object",
@@ -98,7 +113,7 @@ void GoToRequestHandler::operator()(const llvm::json::Object &request) const {
 
   dap.SendJSON(llvm::json::Value(std::move(response)));
 
-  SendThreadStoppedEvent(dap);
+  SendThreadGotoEvent(dap, current_thread.GetThreadID());
 }
 
 } // namespace lldb_dap

>From 8b0a81ecde0fb5837eca6897b1c3b865647874b7 Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Wed, 12 Mar 2025 22:01:50 +0000
Subject: [PATCH 13/19] [lldb][lldb-dap] Rename `gotoTarget` to `gotoTargets`
 and improve validation.

Renamed test files, directories, and references from `gotoTarget` to `gotoTargets` for consistency. Added validation to confirm stopped events include a "goto" reason after goto requests in the test suite.

Signed-off-by: Ebuka Ezike <yerimyah1 at gmail.com>
---
 .../lldb-dap/{gotoTarget => gotoTargets}/Makefile  |  0
 .../TestDAP_gotoTargets.py}                        | 14 ++++++++++++--
 .../lldb-dap/{gotoTarget => gotoTargets}/main.c    |  0
 3 files changed, 12 insertions(+), 2 deletions(-)
 rename lldb/test/API/tools/lldb-dap/{gotoTarget => gotoTargets}/Makefile (100%)
 rename lldb/test/API/tools/lldb-dap/{gotoTarget/TestDAP_gotoTarget.py => gotoTargets/TestDAP_gotoTargets.py} (87%)
 rename lldb/test/API/tools/lldb-dap/{gotoTarget => gotoTargets}/main.c (100%)

diff --git a/lldb/test/API/tools/lldb-dap/gotoTarget/Makefile b/lldb/test/API/tools/lldb-dap/gotoTargets/Makefile
similarity index 100%
rename from lldb/test/API/tools/lldb-dap/gotoTarget/Makefile
rename to lldb/test/API/tools/lldb-dap/gotoTargets/Makefile
diff --git a/lldb/test/API/tools/lldb-dap/gotoTarget/TestDAP_gotoTarget.py b/lldb/test/API/tools/lldb-dap/gotoTargets/TestDAP_gotoTargets.py
similarity index 87%
rename from lldb/test/API/tools/lldb-dap/gotoTarget/TestDAP_gotoTarget.py
rename to lldb/test/API/tools/lldb-dap/gotoTargets/TestDAP_gotoTargets.py
index efb9b3d4ec04b..95f1a417e8884 100644
--- a/lldb/test/API/tools/lldb-dap/gotoTarget/TestDAP_gotoTarget.py
+++ b/lldb/test/API/tools/lldb-dap/gotoTargets/TestDAP_gotoTargets.py
@@ -10,7 +10,7 @@
 import os
 
 
-class TestDAP_gotoTarget(lldbdap_testcase.DAPTestCaseBase):
+class TestDAP_gotoTargets(lldbdap_testcase.DAPTestCaseBase):
     def verify_variable(
         self, actual_dict: Dict[str, Any], expected_dict: Dict[str, Any]
     ):
@@ -61,9 +61,13 @@ def test_default(self):
         self.assertIsNotNone(thread_id, "threadId should not be none")
 
         response = self.dap_server.request_goto(thread_id, target_id)
-
         self.assertEqual(response["success"], True, "expects success to go to targetId")
 
+        stopped_events = self.dap_server.wait_for_stopped()
+        is_goto = lambda event: event["body"]["reason"] == "goto"
+        has_goto_event = any(map(is_goto, stopped_events))
+        self.assertEqual(has_goto_event, True, "expects stopped event with reason goto")
+
         self.dap_server.request_next(thread_id)
         self.continue_to_next_stop()
 
@@ -118,6 +122,12 @@ def test_execute_again(self):
 
         response = self.dap_server.request_goto(thread_id, target_id)
         self.assertEqual(response["success"], True, "expects success to go to targetId")
+
+        stopped_events = self.dap_server.wait_for_stopped()
+        is_goto = lambda event: event["body"]["reason"] == "goto"
+        has_goto_event = any(map(is_goto, stopped_events))
+        self.assertEqual(has_goto_event, True, "expects stopped event with reason goto")
+
         self.dap_server.request_next(thread_id)
         self.continue_to_next_stop()
 
diff --git a/lldb/test/API/tools/lldb-dap/gotoTarget/main.c b/lldb/test/API/tools/lldb-dap/gotoTargets/main.c
similarity index 100%
rename from lldb/test/API/tools/lldb-dap/gotoTarget/main.c
rename to lldb/test/API/tools/lldb-dap/gotoTargets/main.c

>From 1c5e1ac48ba07fbbcdfd2a4c430051b3554420da Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Thu, 13 Mar 2025 19:54:07 +0000
Subject: [PATCH 14/19] Update
 lldb/test/API/tools/lldb-dap/gotoTargets/TestDAP_gotoTargets.py

Co-authored-by: Adrian Vogelsgesang <adrian.vogelsgesang at tum.de>
---
 lldb/test/API/tools/lldb-dap/gotoTargets/TestDAP_gotoTargets.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lldb/test/API/tools/lldb-dap/gotoTargets/TestDAP_gotoTargets.py b/lldb/test/API/tools/lldb-dap/gotoTargets/TestDAP_gotoTargets.py
index 95f1a417e8884..83e0fe2911dcd 100644
--- a/lldb/test/API/tools/lldb-dap/gotoTargets/TestDAP_gotoTargets.py
+++ b/lldb/test/API/tools/lldb-dap/gotoTargets/TestDAP_gotoTargets.py
@@ -49,7 +49,7 @@ def test_default(self):
         )
 
         self.assertEqual(
-            response["success"], True, "expects success when request for targets"
+            response["success"], True, "request for gotoTargets should be successful"
         )
         target = response["body"]["targets"][0]
         self.assertGreaterEqual(

>From 3b9520ae880b3d17f95f183d260f177938360440 Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Thu, 13 Mar 2025 19:54:16 +0000
Subject: [PATCH 15/19] Update
 lldb/test/API/tools/lldb-dap/gotoTargets/TestDAP_gotoTargets.py

Co-authored-by: Adrian Vogelsgesang <adrian.vogelsgesang at tum.de>
---
 lldb/test/API/tools/lldb-dap/gotoTargets/TestDAP_gotoTargets.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lldb/test/API/tools/lldb-dap/gotoTargets/TestDAP_gotoTargets.py b/lldb/test/API/tools/lldb-dap/gotoTargets/TestDAP_gotoTargets.py
index 83e0fe2911dcd..9d4eea5732015 100644
--- a/lldb/test/API/tools/lldb-dap/gotoTargets/TestDAP_gotoTargets.py
+++ b/lldb/test/API/tools/lldb-dap/gotoTargets/TestDAP_gotoTargets.py
@@ -61,7 +61,7 @@ def test_default(self):
         self.assertIsNotNone(thread_id, "threadId should not be none")
 
         response = self.dap_server.request_goto(thread_id, target_id)
-        self.assertEqual(response["success"], True, "expects success to go to targetId")
+        self.assertEqual(response["success"], True, "goto request with targetId should be successful")
 
         stopped_events = self.dap_server.wait_for_stopped()
         is_goto = lambda event: event["body"]["reason"] == "goto"

>From f0f49079886ca1a05b54c74386e6be969c13704c Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Thu, 13 Mar 2025 19:54:47 +0000
Subject: [PATCH 16/19] Update
 lldb/test/API/tools/lldb-dap/gotoTargets/TestDAP_gotoTargets.py

Co-authored-by: Adrian Vogelsgesang <adrian.vogelsgesang at tum.de>
---
 lldb/test/API/tools/lldb-dap/gotoTargets/TestDAP_gotoTargets.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lldb/test/API/tools/lldb-dap/gotoTargets/TestDAP_gotoTargets.py b/lldb/test/API/tools/lldb-dap/gotoTargets/TestDAP_gotoTargets.py
index 9d4eea5732015..5e21ac3b8c325 100644
--- a/lldb/test/API/tools/lldb-dap/gotoTargets/TestDAP_gotoTargets.py
+++ b/lldb/test/API/tools/lldb-dap/gotoTargets/TestDAP_gotoTargets.py
@@ -66,7 +66,7 @@ def test_default(self):
         stopped_events = self.dap_server.wait_for_stopped()
         is_goto = lambda event: event["body"]["reason"] == "goto"
         has_goto_event = any(map(is_goto, stopped_events))
-        self.assertEqual(has_goto_event, True, "expects stopped event with reason goto")
+        self.assertEqual(has_goto_event, True, "expected a stopped event with reason `goto`")
 
         self.dap_server.request_next(thread_id)
         self.continue_to_next_stop()

>From 71d1c210ce2c049f8ff85daff92da05f92bce190 Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Thu, 13 Mar 2025 19:55:42 +0000
Subject: [PATCH 17/19] Update
 lldb/test/API/tools/lldb-dap/gotoTargets/TestDAP_gotoTargets.py

Co-authored-by: Adrian Vogelsgesang <adrian.vogelsgesang at tum.de>
---
 .../test/API/tools/lldb-dap/gotoTargets/TestDAP_gotoTargets.py | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/lldb/test/API/tools/lldb-dap/gotoTargets/TestDAP_gotoTargets.py b/lldb/test/API/tools/lldb-dap/gotoTargets/TestDAP_gotoTargets.py
index 5e21ac3b8c325..4ce54f4f85df8 100644
--- a/lldb/test/API/tools/lldb-dap/gotoTargets/TestDAP_gotoTargets.py
+++ b/lldb/test/API/tools/lldb-dap/gotoTargets/TestDAP_gotoTargets.py
@@ -71,6 +71,9 @@ def test_default(self):
         self.dap_server.request_next(thread_id)
         self.continue_to_next_stop()
 
+        # Verify that `var_1=10` and `var_2=40`. This combination is only possible by
+        # skipping execution of a line from the original program. Observing this combination
+        # hence proves that our `goto` request actually skipped execution of the code line.
         var1_variable = self.dap_server.get_local_variable("var_1")
         var_1_expected = {
             "name": "var_1",

>From 749a4c05be8e4afd6504cef689c258f7e089a993 Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Fri, 14 Mar 2025 18:18:55 +0000
Subject: [PATCH 18/19] [lldb][lldb-dap] use breakpoints to verify goto
 location.

---
 .../lldb-dap/Handler/GoToRequestHandler.cpp   |   1 -
 .../Handler/GoToTargetsRequestHandler.cpp     | 100 +++++++++++++-----
 2 files changed, 72 insertions(+), 29 deletions(-)

diff --git a/lldb/tools/lldb-dap/Handler/GoToRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/GoToRequestHandler.cpp
index bbbb48f0e0630..72448498d9cdd 100644
--- a/lldb/tools/lldb-dap/Handler/GoToRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/GoToRequestHandler.cpp
@@ -14,7 +14,6 @@ namespace lldb_dap {
 
 /// Creates an \p StoppedEvent with the reason \a goto
 static void SendThreadGotoEvent(DAP &dap, lldb::tid_t thread_id) {
-
   llvm::json::Object event(CreateEventObject("stopped"));
   llvm::json::Object body;
   body.try_emplace("reason", "goto");
diff --git a/lldb/tools/lldb-dap/Handler/GoToTargetsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/GoToTargetsRequestHandler.cpp
index 5fba6e65c28c7..0dbe4379b636c 100644
--- a/lldb/tools/lldb-dap/Handler/GoToTargetsRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/GoToTargetsRequestHandler.cpp
@@ -10,10 +10,51 @@
 
 #include "JSONUtils.h"
 
+#include <lldb/API/SBBreakpointLocation.h>
+#include <lldb/API/SBListener.h>
 #include <lldb/API/SBStream.h>
 
 namespace lldb_dap {
 
+static llvm::SmallVector<lldb::SBLineEntry>
+GetLineValidEntry(DAP &dap, const lldb::SBFileSpec &file_spec, uint32_t line) {
+  // disable breakpoint listeners so they do not send events to the DAP client.
+  lldb::SBListener listener = dap.debugger.GetListener();
+  lldb::SBBroadcaster broadcaster = dap.target.GetBroadcaster();
+  constexpr auto event_mask = lldb::SBTarget::eBroadcastBitBreakpointChanged;
+  listener.StopListeningForEvents(broadcaster, event_mask);
+
+  // create a breakpoint to resolve the line if it is on an empty line.
+  lldb::SBBreakpoint goto_bp =
+      dap.target.BreakpointCreateByLocation(file_spec, line);
+  if (!goto_bp.IsValid())
+    return {};
+
+  llvm::SmallVector<lldb::SBLineEntry> entry_locations{};
+  const size_t resolved_count = goto_bp.GetNumResolvedLocations();
+  for (size_t idx = 0; idx < resolved_count; ++idx) {
+    lldb::SBBreakpointLocation location = goto_bp.GetLocationAtIndex(idx);
+    if (!location.IsValid())
+      continue;
+
+    lldb::SBAddress addr = location.GetAddress();
+    if (!addr.IsValid())
+      continue;
+
+    lldb::SBLineEntry line_entry = addr.GetLineEntry();
+    if (!line_entry.IsValid())
+      continue;
+
+    entry_locations.push_back(line_entry);
+  }
+
+  // clean up;
+  dap.target.BreakpointDelete(goto_bp.GetID());
+  listener.StartListeningForEvents(broadcaster, event_mask);
+
+  return entry_locations;
+}
+
 //  "GotoTargetsRequest": {
 //    "allOf": [ { "$ref": "#/definitions/Request" }, {
 //      "type": "object",
@@ -83,37 +124,40 @@ void GoToTargetsRequestHandler::operator()(
     const llvm::json::Object &request) const {
   llvm::json::Object response;
   FillResponse(request, response);
-  const auto *arguments = request.getObject("arguments");
-  const auto *source = arguments->getObject("source");
-  const std::string path = GetString(source, "path").str();
-
-  const auto goto_line = GetInteger<uint64_t>(arguments, "line").value_or(0u);
-  const auto goto_column =
-      GetInteger<uint64_t>(arguments, "column").value_or(0u);
 
-  lldb::SBLineEntry line_entry{};
+  const llvm::json::Object *arguments = request.getObject("arguments");
+  const llvm::json::Object *source = arguments->getObject("source");
+  const std::string path = GetString(source, "path").str();
   const lldb::SBFileSpec file_spec(path.c_str(), true);
-  line_entry.SetFileSpec(file_spec);
-  line_entry.SetLine(goto_line);
-  line_entry.SetColumn(goto_column);
-
-  const auto target_id = dap.goto_id_map.InsertLineEntry(line_entry);
-  llvm::json::Array response_targets;
-  const auto target_line = line_entry.GetLine();
-  const auto target_column = line_entry.GetColumn();
-  auto target = llvm::json::Object();
-  target.try_emplace("id", target_id);
-
-  lldb::SBStream stream;
-  line_entry.GetDescription(stream);
-  target.try_emplace("label",
-                     llvm::StringRef(stream.GetData(), stream.GetSize()));
-  target.try_emplace("column", target_column);
-  target.try_emplace("line", target_line);
-
-  response_targets.push_back(std::move(target));
+  const uint64_t goto_line =
+      GetInteger<uint64_t>(arguments, "line").value_or(1U);
+
   llvm::json::Object body;
-  body.try_emplace("targets", std::move(response_targets));
+
+  llvm::SmallVector<lldb::SBLineEntry> goto_locations =
+      GetLineValidEntry(dap, file_spec, goto_line);
+  if (goto_locations.empty()) {
+    response["success"] = false;
+    response["message"] = "Invalid jump location";
+  } else {
+    llvm::json::Array response_targets;
+    for (lldb::SBLineEntry &line_entry : goto_locations) {
+      const uint64_t target_id = dap.goto_id_map.InsertLineEntry(line_entry);
+      const uint32_t target_line = line_entry.GetLine();
+      auto target = llvm::json::Object();
+      target.try_emplace("id", target_id);
+
+      lldb::SBStream stream;
+      line_entry.GetDescription(stream);
+      target.try_emplace("label",
+                         llvm::StringRef(stream.GetData(), stream.GetSize()));
+      target.try_emplace("line", target_line);
+      response_targets.push_back(std::move(target));
+    }
+
+    body.try_emplace("targets", std::move(response_targets));
+  }
+
   response.try_emplace("body", std::move(body));
   dap.SendJSON(llvm::json::Value(std::move(response)));
 }

>From a2a2cb85e5f17a7468970c6014947918d9805e80 Mon Sep 17 00:00:00 2001
From: Ebuka Ezike <yerimyah1 at gmail.com>
Date: Tue, 18 Mar 2025 10:52:28 +0000
Subject: [PATCH 19/19] Add Release notes

---
 llvm/docs/ReleaseNotes.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index d7a80ae93aa34..ff80e19c495ac 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -177,6 +177,8 @@ Changes to LLDB
 
 * Breakpoints can now be set for specific columns within a line.
 * Function return value is now displayed on step-out.
+* Jump to cursor is supported implementing the debug adapter
+  protocol's capability `supportsGotoTargetsRequest`.
 
 Changes to BOLT
 ---------------------------------



More information about the lldb-commits mailing list