aboutsummaryrefslogtreecommitdiff
path: root/lldb
diff options
context:
space:
mode:
Diffstat (limited to 'lldb')
-rw-r--r--lldb/bindings/interface/SBPlatformExtensions.i7
-rw-r--r--lldb/bindings/interfaces.swig1
-rw-r--r--lldb/include/lldb/API/SBPlatform.h3
-rw-r--r--lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py6
-rw-r--r--lldb/source/API/SBPlatform.cpp5
-rw-r--r--lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp1
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp103
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h16
-rw-r--r--lldb/test/API/CMakeLists.txt10
-rw-r--r--lldb/test/API/functionalities/thread/finish-from-empty-func/TestEmptyFuncThreadStepOut.py28
-rw-r--r--lldb/test/API/functionalities/thread/finish-from-empty-func/main.c1
-rw-r--r--lldb/test/API/lang/objc/foundation/TestObjCMethodsNSError.py27
-rw-r--r--lldb/tools/lldb-dap/DAP.cpp52
-rw-r--r--lldb/tools/lldb-dap/DAP.h24
-rw-r--r--lldb/tools/lldb-dap/EventHelper.cpp40
-rw-r--r--lldb/tools/lldb-dap/Handler/RequestHandler.cpp11
-rw-r--r--lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp2
-rw-r--r--lldb/tools/lldb-dap/JSONUtils.cpp5
-rw-r--r--lldb/tools/lldb-dap/Protocol/ProtocolBase.cpp35
-rw-r--r--lldb/tools/lldb-dap/Protocol/ProtocolBase.h54
-rw-r--r--lldb/unittests/Target/MemoryTest.cpp2
21 files changed, 333 insertions, 100 deletions
diff --git a/lldb/bindings/interface/SBPlatformExtensions.i b/lldb/bindings/interface/SBPlatformExtensions.i
new file mode 100644
index 0000000..86759bd
--- /dev/null
+++ b/lldb/bindings/interface/SBPlatformExtensions.i
@@ -0,0 +1,7 @@
+%extend lldb::SBPlatform {
+#ifdef SWIGPYTHON
+ %pythoncode %{
+ is_host = property(IsHost, None, doc='''A read only property that returns a boolean value that indiciates if this platform is the host platform.''')
+ %}
+#endif
+}
diff --git a/lldb/bindings/interfaces.swig b/lldb/bindings/interfaces.swig
index e71ed13..b3d4497 100644
--- a/lldb/bindings/interfaces.swig
+++ b/lldb/bindings/interfaces.swig
@@ -53,6 +53,7 @@
%include "./interface/SBModuleSpecDocstrings.i"
%include "./interface/SBMutexExtensions.i"
%include "./interface/SBPlatformDocstrings.i"
+%include "./interface/SBPlatformExtensions.i"
%include "./interface/SBProcessDocstrings.i"
%include "./interface/SBProcessInfoDocstrings.i"
%include "./interface/SBProgressDocstrings.i"
diff --git a/lldb/include/lldb/API/SBPlatform.h b/lldb/include/lldb/API/SBPlatform.h
index d63d2ed..7cb19b3 100644
--- a/lldb/include/lldb/API/SBPlatform.h
+++ b/lldb/include/lldb/API/SBPlatform.h
@@ -112,6 +112,9 @@ public:
bool IsValid() const;
+ /// Returns true if this platform is the host platform, otherwise false.
+ bool IsHost() const;
+
void Clear();
const char *GetWorkingDirectory();
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 a3d924d..d892c01 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
@@ -11,6 +11,7 @@ import subprocess
import signal
import sys
import threading
+import warnings
import time
from typing import (
Any,
@@ -383,6 +384,10 @@ class DebugCommunication(object):
"""Process received packets, updating the session state."""
with self._recv_condition:
for packet in self._recv_packets:
+ if packet and ("seq" not in packet or packet["seq"] == 0):
+ warnings.warn(
+ f"received a malformed packet, expected 'seq != 0' for {packet!r}"
+ )
# Handle events that may modify any stateful properties of
# the DAP session.
if packet and packet["type"] == "event":
@@ -576,6 +581,7 @@ class DebugCommunication(object):
# If we exited, then we are done
if stopped_event["event"] == "exited":
break
+
# Otherwise we stopped and there might be one or more 'stopped'
# events for each thread that stopped with a reason, so keep
# checking for more 'stopped' events and return all of them
diff --git a/lldb/source/API/SBPlatform.cpp b/lldb/source/API/SBPlatform.cpp
index ec59e27..9a0b47c 100644
--- a/lldb/source/API/SBPlatform.cpp
+++ b/lldb/source/API/SBPlatform.cpp
@@ -331,6 +331,11 @@ SBPlatform::operator bool() const {
return m_opaque_sp.get() != nullptr;
}
+bool SBPlatform::IsHost() const {
+ LLDB_INSTRUMENT_VA(this);
+ return m_opaque_sp.get() != nullptr && m_opaque_sp->IsHost();
+}
+
void SBPlatform::Clear() {
LLDB_INSTRUMENT_VA(this);
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
index 6b121c9..9900745 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
@@ -754,7 +754,6 @@ ClangExpressionParser::ClangExpressionParser(
// Make sure clang uses the same VFS as LLDB.
m_compiler->setVirtualFileSystem(
FileSystem::Instance().GetVirtualFileSystem());
- m_compiler->createFileManager();
// 2. Configure the compiler with a set of default options that are
// appropriate for most situations.
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index 91f3a6c..b4422a7 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -86,6 +86,7 @@
#include "lldb/Host/Host.h"
#include "lldb/Utility/StringExtractorGDBRemote.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringSwitch.h"
@@ -2762,6 +2763,108 @@ size_t ProcessGDBRemote::DoReadMemory(addr_t addr, void *buf, size_t size,
return 0;
}
+llvm::SmallVector<llvm::MutableArrayRef<uint8_t>>
+ProcessGDBRemote::ReadMemoryRanges(
+ llvm::ArrayRef<Range<lldb::addr_t, size_t>> ranges,
+ llvm::MutableArrayRef<uint8_t> buffer) {
+ if (!m_gdb_comm.GetMultiMemReadSupported())
+ return Process::ReadMemoryRanges(ranges, buffer);
+
+ llvm::Expected<StringExtractorGDBRemote> response =
+ SendMultiMemReadPacket(ranges);
+ if (!response) {
+ LLDB_LOG_ERROR(GetLog(GDBRLog::Process), response.takeError(),
+ "MultiMemRead error response: {0}");
+ return Process::ReadMemoryRanges(ranges, buffer);
+ }
+
+ llvm::StringRef response_str = response->GetStringRef();
+ const unsigned expected_num_ranges = ranges.size();
+ llvm::Expected<llvm::SmallVector<llvm::MutableArrayRef<uint8_t>>>
+ parsed_response =
+ ParseMultiMemReadPacket(response_str, buffer, expected_num_ranges);
+ if (!parsed_response) {
+ LLDB_LOG_ERROR(GetLog(GDBRLog::Process), parsed_response.takeError(),
+ "MultiMemRead error parsing response: {0}");
+ return Process::ReadMemoryRanges(ranges, buffer);
+ }
+ return std::move(*parsed_response);
+}
+
+llvm::Expected<StringExtractorGDBRemote>
+ProcessGDBRemote::SendMultiMemReadPacket(
+ llvm::ArrayRef<Range<lldb::addr_t, size_t>> ranges) {
+ std::string packet_str;
+ llvm::raw_string_ostream stream(packet_str);
+ stream << "MultiMemRead:ranges:";
+
+ auto range_to_stream = [&](auto range) {
+ // the "-" marker omits the '0x' prefix.
+ stream << llvm::formatv("{0:x-},{1:x-}", range.base, range.size);
+ };
+ llvm::interleave(ranges, stream, range_to_stream, ",");
+ stream << ";";
+
+ StringExtractorGDBRemote response;
+ GDBRemoteCommunication::PacketResult packet_result =
+ m_gdb_comm.SendPacketAndWaitForResponse(packet_str.data(), response,
+ GetInterruptTimeout());
+ if (packet_result != GDBRemoteCommunication::PacketResult::Success)
+ return llvm::createStringError(
+ llvm::formatv("MultiMemRead failed to send packet: '{0}'", packet_str));
+
+ if (response.IsErrorResponse())
+ return llvm::createStringError(
+ llvm::formatv("MultiMemRead failed: '{0}'", response.GetStringRef()));
+
+ if (!response.IsNormalResponse())
+ return llvm::createStringError(llvm::formatv(
+ "MultiMemRead unexpected response: '{0}'", response.GetStringRef()));
+
+ return response;
+}
+
+llvm::Expected<llvm::SmallVector<llvm::MutableArrayRef<uint8_t>>>
+ProcessGDBRemote::ParseMultiMemReadPacket(llvm::StringRef response_str,
+ llvm::MutableArrayRef<uint8_t> buffer,
+ unsigned expected_num_ranges) {
+ // The sizes and the data are separated by a `;`.
+ auto [sizes_str, memory_data] = response_str.split(';');
+ if (sizes_str.size() == response_str.size())
+ return llvm::createStringError(llvm::formatv(
+ "MultiMemRead response missing field separator ';' in: '{0}'",
+ response_str));
+
+ llvm::SmallVector<llvm::MutableArrayRef<uint8_t>> read_results;
+
+ // Sizes are separated by a `,`.
+ for (llvm::StringRef size_str : llvm::split(sizes_str, ',')) {
+ uint64_t read_size;
+ if (size_str.getAsInteger(16, read_size))
+ return llvm::createStringError(llvm::formatv(
+ "MultiMemRead response has invalid size string: {0}", size_str));
+
+ if (memory_data.size() < read_size)
+ return llvm::createStringError(
+ llvm::formatv("MultiMemRead response did not have enough data, "
+ "requested sizes: {0}",
+ sizes_str));
+
+ llvm::StringRef region_to_read = memory_data.take_front(read_size);
+ memory_data = memory_data.drop_front(read_size);
+
+ assert(buffer.size() >= read_size);
+ llvm::MutableArrayRef<uint8_t> region_to_write =
+ buffer.take_front(read_size);
+ buffer = buffer.drop_front(read_size);
+
+ memcpy(region_to_write.data(), region_to_read.data(), read_size);
+ read_results.push_back(region_to_write);
+ }
+
+ return read_results;
+}
+
bool ProcessGDBRemote::SupportsMemoryTagging() {
return m_gdb_comm.GetMemoryTaggingSupported();
}
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
index 7c3dfb1..eb33b52 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -137,6 +137,22 @@ public:
size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
Status &error) override;
+ /// Override of ReadMemoryRanges that uses MultiMemRead to optimize this
+ /// operation.
+ llvm::SmallVector<llvm::MutableArrayRef<uint8_t>>
+ ReadMemoryRanges(llvm::ArrayRef<Range<lldb::addr_t, size_t>> ranges,
+ llvm::MutableArrayRef<uint8_t> buf) override;
+
+private:
+ llvm::Expected<StringExtractorGDBRemote>
+ SendMultiMemReadPacket(llvm::ArrayRef<Range<lldb::addr_t, size_t>> ranges);
+
+ llvm::Expected<llvm::SmallVector<llvm::MutableArrayRef<uint8_t>>>
+ ParseMultiMemReadPacket(llvm::StringRef response_str,
+ llvm::MutableArrayRef<uint8_t> buffer,
+ unsigned expected_num_ranges);
+
+public:
Status
WriteObjectFile(std::vector<ObjectFile::LoadableData> entries) override;
diff --git a/lldb/test/API/CMakeLists.txt b/lldb/test/API/CMakeLists.txt
index e3bffbc..c719ac3 100644
--- a/lldb/test/API/CMakeLists.txt
+++ b/lldb/test/API/CMakeLists.txt
@@ -74,6 +74,16 @@ else()
endif()
endif()
+find_program(LLDB_DIRNAME_PATH dirname)
+if(LLDB_DIRNAME_PATH)
+ message(STATUS "Found dirname: ${LLDB_DIRNAME_PATH}")
+else()
+ message(STATUS "Could NOT find 'dirname'")
+ message(WARNING
+ "Many LLDB API tests require the GNU coreutils tools. Please make "
+ "sure they are installed and in PATH.")
+endif()
+
if (TARGET clang)
set(LLDB_DEFAULT_TEST_COMPILER "${LLVM_TOOLS_BINARY_DIR}/clang${CMAKE_EXECUTABLE_SUFFIX}")
else()
diff --git a/lldb/test/API/functionalities/thread/finish-from-empty-func/TestEmptyFuncThreadStepOut.py b/lldb/test/API/functionalities/thread/finish-from-empty-func/TestEmptyFuncThreadStepOut.py
index f5d3da53..c95a57f 100644
--- a/lldb/test/API/functionalities/thread/finish-from-empty-func/TestEmptyFuncThreadStepOut.py
+++ b/lldb/test/API/functionalities/thread/finish-from-empty-func/TestEmptyFuncThreadStepOut.py
@@ -13,12 +13,31 @@ class FinishFromEmptyFunctionTestCase(TestBase):
@skipIf(compiler="clang", compiler_version=['<', '17.0'])
def test_finish_from_empty_function(self):
- """Test that when stopped at a breakpoint in an empty function, finish leaves it correctly."""
+ """Test that when stopped at a breakpoint located at the last instruction
+ of a function, finish leaves it correctly."""
self.build()
- exe = self.getBuildArtifact("a.out")
- target, process, thread, _ = lldbutil.run_to_name_breakpoint(
- self, "done", exe_name=exe
+ target, _, thread, _ = lldbutil.run_to_source_breakpoint(
+ self, "// Set breakpoint here", lldb.SBFileSpec("main.c")
)
+ # Find the address of the last instruction of 'done()' and set a breakpoint there.
+ # Even though 'done()' is empty, it may contain prologue and epilogue code, so
+ # simply setting a breakpoint at the function can place it before 'ret'.
+ error = lldb.SBError()
+ ret_bp_addr = lldb.SBAddress()
+ while True:
+ thread.StepInstruction(False, error)
+ self.assertTrue(error.Success())
+ frame = thread.GetSelectedFrame()
+ if "done" in frame.GetFunctionName():
+ ret_bp_addr = frame.GetPCAddress()
+ elif ret_bp_addr.IsValid():
+ # The entire function 'done()' has been stepped through, so 'ret_bp_addr'
+ # now contains the address of its last instruction, i.e. 'ret'.
+ break
+ ret_bp = target.BreakpointCreateByAddress(ret_bp_addr.GetLoadAddress(target))
+ self.assertTrue(ret_bp.IsValid())
+ # Resume the execution and hit the new breakpoint.
+ self.runCmd("cont")
if self.TraceOn():
self.runCmd("bt")
@@ -29,7 +48,6 @@ class FinishFromEmptyFunctionTestCase(TestBase):
)
self.assertTrue(safety_bp.IsValid())
- error = lldb.SBError()
thread.StepOut(error)
self.assertTrue(error.Success())
diff --git a/lldb/test/API/functionalities/thread/finish-from-empty-func/main.c b/lldb/test/API/functionalities/thread/finish-from-empty-func/main.c
index bc66a548..b3f90db5 100644
--- a/lldb/test/API/functionalities/thread/finish-from-empty-func/main.c
+++ b/lldb/test/API/functionalities/thread/finish-from-empty-func/main.c
@@ -2,6 +2,7 @@
void done() {}
int main() {
puts("in main");
+ done(); // Set breakpoint here
done();
puts("leaving main");
return 0;
diff --git a/lldb/test/API/lang/objc/foundation/TestObjCMethodsNSError.py b/lldb/test/API/lang/objc/foundation/TestObjCMethodsNSError.py
index a14035d..a9fbe54 100644
--- a/lldb/test/API/lang/objc/foundation/TestObjCMethodsNSError.py
+++ b/lldb/test/API/lang/objc/foundation/TestObjCMethodsNSError.py
@@ -45,3 +45,30 @@ class FoundationTestCaseNSError(TestBase):
],
)
self.runCmd("process continue")
+
+ @skipIfOutOfTreeDebugserver
+ def test_runtime_types_efficient_memreads(self):
+ # Test that we use an efficient reading of memory when reading
+ # Objective-C method descriptions.
+ logfile = os.path.join(self.getBuildDir(), "log.txt")
+ self.runCmd(f"log enable -f {logfile} gdb-remote packets process")
+ self.addTearDownHook(lambda: self.runCmd("log disable gdb-remote packets"))
+
+ self.build()
+ self.target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
+ self, "// Break here for NSString tests", lldb.SBFileSpec("main.m", False)
+ )
+
+ self.runCmd(f"proc plugin packet send StartTesting", check=False)
+ self.expect('expression str = [NSString stringWithCString: "new"]')
+ self.runCmd(f"proc plugin packet send EndTesting", check=False)
+
+ self.assertTrue(os.path.exists(logfile))
+ log_text = open(logfile).read()
+ log_text = log_text.split("StartTesting", 1)[-1].split("EndTesting", 1)[0]
+
+ # This test is only checking that the packet it used at all (and that
+ # no errors are produced). It doesn't check that the packet is being
+ # used to solve a problem in an optimal way.
+ self.assertIn("MultiMemRead:", log_text)
+ self.assertNotIn("MultiMemRead error", log_text)
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index 61226cc..3c4f225 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -26,6 +26,7 @@
#include "lldb/API/SBEvent.h"
#include "lldb/API/SBLanguageRuntime.h"
#include "lldb/API/SBListener.h"
+#include "lldb/API/SBMutex.h"
#include "lldb/API/SBProcess.h"
#include "lldb/API/SBStream.h"
#include "lldb/Host/JSONTransport.h"
@@ -266,40 +267,49 @@ void DAP::SendJSON(const llvm::json::Value &json) {
Send(message);
}
-void DAP::Send(const Message &message) {
- if (const protocol::Event *event = std::get_if<protocol::Event>(&message)) {
+Id DAP::Send(const Message &message) {
+ std::lock_guard<std::mutex> guard(call_mutex);
+ Message msg = std::visit(
+ [this](auto &&msg) -> Message {
+ if (msg.seq == kCalculateSeq)
+ msg.seq = seq++;
+ return msg;
+ },
+ Message(message));
+
+ if (const protocol::Event *event = std::get_if<protocol::Event>(&msg)) {
if (llvm::Error err = transport.Send(*event))
DAP_LOG_ERROR(log, std::move(err), "({0}) sending event failed",
m_client_name);
- return;
+ return event->seq;
}
- if (const Request *req = std::get_if<Request>(&message)) {
+ if (const Request *req = std::get_if<Request>(&msg)) {
if (llvm::Error err = transport.Send(*req))
DAP_LOG_ERROR(log, std::move(err), "({0}) sending request failed",
m_client_name);
- return;
+ return req->seq;
}
- if (const Response *resp = std::get_if<Response>(&message)) {
+ if (const Response *resp = std::get_if<Response>(&msg)) {
// FIXME: After all the requests have migrated from LegacyRequestHandler >
// RequestHandler<> this should be handled in RequestHandler<>::operator().
// If the debugger was interrupted, convert this response into a
// 'cancelled' response because we might have a partial result.
- llvm::Error err =
- (debugger.InterruptRequested())
- ? transport.Send({/*request_seq=*/resp->request_seq,
- /*command=*/resp->command,
- /*success=*/false,
- /*message=*/eResponseMessageCancelled,
- /*body=*/std::nullopt})
- : transport.Send(*resp);
- if (err) {
+ llvm::Error err = (debugger.InterruptRequested())
+ ? transport.Send({
+ /*request_seq=*/resp->request_seq,
+ /*command=*/resp->command,
+ /*success=*/false,
+ /*message=*/eResponseMessageCancelled,
+ /*body=*/std::nullopt,
+ /*seq=*/resp->seq,
+ })
+ : transport.Send(*resp);
+ if (err)
DAP_LOG_ERROR(log, std::move(err), "({0}) sending response failed",
m_client_name);
- return;
- }
- return;
+ return resp->seq;
}
llvm_unreachable("Unexpected message type");
@@ -1443,7 +1453,11 @@ void DAP::EventThread() {
const bool remove_module =
event_mask & lldb::SBTarget::eBroadcastBitModulesUnloaded;
- std::lock_guard<std::mutex> guard(modules_mutex);
+ // NOTE: Both mutexes must be acquired to prevent deadlock when
+ // handling `modules_request`, which also requires both locks.
+ lldb::SBMutex api_mutex = GetAPIMutex();
+ const std::scoped_lock<lldb::SBMutex, std::mutex> guard(
+ api_mutex, modules_mutex);
for (uint32_t i = 0; i < num_modules; ++i) {
lldb::SBModule module =
lldb::SBTarget::GetModuleAtIndexFromEvent(i, event);
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index bf2c3f1..b4f111e 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -136,7 +136,7 @@ struct DAP final : public DAPTransport::MessageHandler {
/// unless we send a "thread" event to indicate the thread exited.
llvm::DenseSet<lldb::tid_t> thread_ids;
- uint32_t reverse_request_seq = 0;
+ protocol::Id seq = 0;
std::mutex call_mutex;
llvm::SmallDenseMap<int64_t, std::unique_ptr<ResponseHandler>>
inflight_reverse_requests;
@@ -220,8 +220,8 @@ struct DAP final : public DAPTransport::MessageHandler {
/// Serialize the JSON value into a string and send the JSON packet to the
/// "out" stream.
void SendJSON(const llvm::json::Value &json);
- /// Send the given message to the client
- void Send(const protocol::Message &message);
+ /// Send the given message to the client.
+ protocol::Id Send(const protocol::Message &message);
void SendOutput(OutputType o, const llvm::StringRef output);
@@ -353,19 +353,13 @@ struct DAP final : public DAPTransport::MessageHandler {
template <typename Handler>
void SendReverseRequest(llvm::StringRef command,
llvm::json::Value arguments) {
- int64_t id;
- {
- std::lock_guard<std::mutex> locker(call_mutex);
- id = ++reverse_request_seq;
- inflight_reverse_requests[id] = std::make_unique<Handler>(command, id);
- }
-
- SendJSON(llvm::json::Object{
- {"type", "request"},
- {"seq", id},
- {"command", command},
- {"arguments", std::move(arguments)},
+ protocol::Id id = Send(protocol::Request{
+ command.str(),
+ std::move(arguments),
});
+
+ std::lock_guard<std::mutex> locker(call_mutex);
+ inflight_reverse_requests[id] = std::make_unique<Handler>(command, id);
}
/// The set of capabilities supported by this adapter.
diff --git a/lldb/tools/lldb-dap/EventHelper.cpp b/lldb/tools/lldb-dap/EventHelper.cpp
index 3042d32..c5d5f2b 100644
--- a/lldb/tools/lldb-dap/EventHelper.cpp
+++ b/lldb/tools/lldb-dap/EventHelper.cpp
@@ -15,6 +15,7 @@
#include "Protocol/ProtocolRequests.h"
#include "Protocol/ProtocolTypes.h"
#include "lldb/API/SBFileSpec.h"
+#include "lldb/API/SBPlatform.h"
#include "llvm/Support/Error.h"
#include <utility>
@@ -78,11 +79,9 @@ void SendExtraCapabilities(DAP &dap) {
// { "$ref": "#/definitions/Event" },
// {
// "type": "object",
-// "description": "Event message for 'process' event type. The event
-// indicates that the debugger has begun debugging a
-// new process. Either one that it has launched, or one
-// that it has attached to.",
-// "properties": {
+// "description": "The event indicates that the debugger has begun
+// debugging a new process. Either one that it has launched, or one that
+// it has attached to.", "properties": {
// "event": {
// "type": "string",
// "enum": [ "process" ]
@@ -93,31 +92,37 @@ void SendExtraCapabilities(DAP &dap) {
// "name": {
// "type": "string",
// "description": "The logical name of the process. This is
-// usually the full path to process's executable
-// file. Example: /home/myproj/program.js."
+// usually the full path to process's executable file. Example:
+// /home/example/myproj/program.js."
// },
// "systemProcessId": {
// "type": "integer",
-// "description": "The system process id of the debugged process.
-// This property will be missing for non-system
-// processes."
+// "description": "The process ID of the debugged process, as
+// assigned by the operating system. This property should be
+// omitted for logical processes that do not map to operating
+// system processes on the machine."
// },
// "isLocalProcess": {
// "type": "boolean",
// "description": "If true, the process is running on the same
-// computer as the debug adapter."
+// computer as the debug adapter."
// },
// "startMethod": {
// "type": "string",
// "enum": [ "launch", "attach", "attachForSuspendedLaunch" ],
// "description": "Describes how the debug engine started
-// debugging this process.",
-// "enumDescriptions": [
+// debugging this process.", "enumDescriptions": [
// "Process was launched under the debugger.",
// "Debugger attached to an existing process.",
-// "A project launcher component has launched a new process in
-// a suspended state and then asked the debugger to attach."
+// "A project launcher component has launched a new process in a
+// suspended state and then asked the debugger to attach."
// ]
+// },
+// "pointerSize": {
+// "type": "integer",
+// "description": "The size of a pointer or address for this
+// process, in bits. This value may be used by clients when
+// formatting addresses for display."
// }
// },
// "required": [ "name" ]
@@ -126,7 +131,7 @@ void SendExtraCapabilities(DAP &dap) {
// "required": [ "event", "body" ]
// }
// ]
-// }
+// },
void SendProcessEvent(DAP &dap, LaunchMethod launch_method) {
lldb::SBFileSpec exe_fspec = dap.target.GetExecutable();
char exe_path[PATH_MAX];
@@ -136,7 +141,8 @@ void SendProcessEvent(DAP &dap, LaunchMethod launch_method) {
EmplaceSafeString(body, "name", exe_path);
const auto pid = dap.target.GetProcess().GetProcessID();
body.try_emplace("systemProcessId", (int64_t)pid);
- body.try_emplace("isLocalProcess", true);
+ body.try_emplace("isLocalProcess", dap.target.GetPlatform().IsHost());
+ body.try_emplace("pointerSize", dap.target.GetAddressByteSize() * 8);
const char *startMethod = nullptr;
switch (launch_method) {
case Launch:
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
index de63c9d..d67437a 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
@@ -157,11 +157,12 @@ RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) {
void BaseRequestHandler::Run(const Request &request) {
// If this request was cancelled, send a cancelled response.
if (dap.IsCancelled(request)) {
- Response cancelled{/*request_seq=*/request.seq,
- /*command=*/request.command,
- /*success=*/false,
- /*message=*/eResponseMessageCancelled,
- /*body=*/std::nullopt};
+ Response cancelled{
+ /*request_seq=*/request.seq,
+ /*command=*/request.command,
+ /*success=*/false,
+ /*message=*/eResponseMessageCancelled,
+ };
dap.Send(cancelled);
return;
}
diff --git a/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp
index 45dd7dd..100173b 100644
--- a/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/RestartRequestHandler.cpp
@@ -124,6 +124,8 @@ void RestartRequestHandler::operator()(
return;
}
+ SendProcessEvent(dap, Launch);
+
// This is normally done after receiving a "configuration done" request.
// Because we're restarting, configuration has already happened so we can
// continue the process right away.
diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index 71e91f8..2780a5b 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -10,6 +10,7 @@
#include "DAP.h"
#include "ExceptionBreakpoint.h"
#include "LLDBUtils.h"
+#include "Protocol/ProtocolBase.h"
#include "ProtocolUtils.h"
#include "lldb/API/SBAddress.h"
#include "lldb/API/SBCompileUnit.h"
@@ -284,7 +285,7 @@ void FillResponse(const llvm::json::Object &request,
// Fill in all of the needed response fields to a "request" and set "success"
// to true by default.
response.try_emplace("type", "response");
- response.try_emplace("seq", (int64_t)0);
+ response.try_emplace("seq", protocol::kCalculateSeq);
EmplaceSafeString(response, "command",
GetString(request, "command").value_or(""));
const uint64_t seq = GetInteger<uint64_t>(request, "seq").value_or(0);
@@ -417,7 +418,7 @@ llvm::json::Value CreateScope(const llvm::StringRef name,
// }
llvm::json::Object CreateEventObject(const llvm::StringRef event_name) {
llvm::json::Object event;
- event.try_emplace("seq", 0);
+ event.try_emplace("seq", protocol::kCalculateSeq);
event.try_emplace("type", "event");
EmplaceSafeString(event, "event", event_name);
return event;
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolBase.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolBase.cpp
index 9cd9028..7235921 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolBase.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolBase.cpp
@@ -58,6 +58,8 @@ bool fromJSON(const json::Value &Params, MessageType &M, json::Path P) {
}
json::Value toJSON(const Request &R) {
+ assert(R.seq != kCalculateSeq && "invalid seq");
+
json::Object Result{
{"type", "request"},
{"seq", R.seq},
@@ -103,8 +105,10 @@ bool operator==(const Request &a, const Request &b) {
}
json::Value toJSON(const Response &R) {
+ assert(R.seq != kCalculateSeq && "invalid seq");
+
json::Object Result{{"type", "response"},
- {"seq", 0},
+ {"seq", R.seq},
{"command", R.command},
{"request_seq", R.request_seq},
{"success", R.success}};
@@ -132,8 +136,9 @@ json::Value toJSON(const Response &R) {
return std::move(Result);
}
-bool fromJSON(json::Value const &Params,
- std::variant<ResponseMessage, std::string> &M, json::Path P) {
+static bool fromJSON(json::Value const &Params,
+ std::variant<ResponseMessage, std::string> &M,
+ json::Path P) {
auto rawMessage = Params.getAsString();
if (!rawMessage) {
P.report("expected a string");
@@ -157,8 +162,7 @@ bool fromJSON(json::Value const &Params, Response &R, json::Path P) {
return false;
MessageType type;
- int64_t seq;
- if (!O.map("type", type) || !O.map("seq", seq) ||
+ if (!O.map("type", type) || !O.map("seq", R.seq) ||
!O.map("command", R.command) || !O.map("request_seq", R.request_seq))
return false;
@@ -168,12 +172,7 @@ bool fromJSON(json::Value const &Params, Response &R, json::Path P) {
}
if (R.command.empty()) {
- P.field("command").report("expected to not be ''");
- return false;
- }
-
- if (R.request_seq == 0) {
- P.field("request_seq").report("expected to not be '0'");
+ P.field("command").report("expected to not be empty");
return false;
}
@@ -217,9 +216,11 @@ bool fromJSON(json::Value const &Params, ErrorMessage &EM, json::Path P) {
}
json::Value toJSON(const Event &E) {
+ assert(E.seq != kCalculateSeq && "invalid seq");
+
json::Object Result{
{"type", "event"},
- {"seq", 0},
+ {"seq", E.seq},
{"event", E.event},
};
@@ -235,8 +236,7 @@ bool fromJSON(json::Value const &Params, Event &E, json::Path P) {
return false;
MessageType type;
- int64_t seq;
- if (!O.map("type", type) || !O.map("seq", seq) || !O.map("event", E.event))
+ if (!O.map("type", type) || !O.map("seq", E.seq) || !O.map("event", E.event))
return false;
if (type != eMessageTypeEvent) {
@@ -244,13 +244,8 @@ bool fromJSON(json::Value const &Params, Event &E, json::Path P) {
return false;
}
- if (seq != 0) {
- P.field("seq").report("expected to be '0'");
- return false;
- }
-
if (E.event.empty()) {
- P.field("event").report("expected to not be ''");
+ P.field("event").report("expected to not be empty");
return false;
}
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolBase.h b/lldb/tools/lldb-dap/Protocol/ProtocolBase.h
index 92e41b1..42c6c88 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolBase.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolBase.h
@@ -30,19 +30,15 @@ namespace lldb_dap::protocol {
// MARK: Base Protocol
+/// Message unique identifier type.
using Id = int64_t;
+/// A unique identifier that indicates the `seq` field should be calculated by
+/// the current session.
+static constexpr Id kCalculateSeq = INT64_MAX;
+
/// A client or debug adapter initiated request.
struct Request {
- /// Sequence number of the message (also known as message ID). The `seq` for
- /// the first message sent by a client or debug adapter is 1, and for each
- /// subsequent message is 1 greater than the previous message sent by that
- /// actor. `seq` can be used to order requests, responses, and events, and to
- /// associate requests with their corresponding responses. For protocol
- /// messages of type `request` the sequence number can be used to cancel the
- /// request.
- Id seq;
-
/// The command to execute.
std::string command;
@@ -50,7 +46,16 @@ struct Request {
///
/// Request handlers are expected to validate the arguments, which is handled
/// by `RequestHandler`.
- std::optional<llvm::json::Value> arguments;
+ std::optional<llvm::json::Value> arguments = std::nullopt;
+
+ /// Sequence number of the message (also known as message ID). The `seq` for
+ /// the first message sent by a client or debug adapter is 1, and for each
+ /// subsequent message is 1 greater than the previous message sent by that
+ /// actor. `seq` can be used to order requests, responses, and events, and to
+ /// associate requests with their corresponding responses. For protocol
+ /// messages of type `request` the sequence number can be used to cancel the
+ /// request.
+ Id seq = kCalculateSeq;
};
llvm::json::Value toJSON(const Request &);
bool fromJSON(const llvm::json::Value &, Request &, llvm::json::Path);
@@ -62,7 +67,16 @@ struct Event {
std::string event;
/// Event-specific information.
- std::optional<llvm::json::Value> body;
+ std::optional<llvm::json::Value> body = std::nullopt;
+
+ /// Sequence number of the message (also known as message ID). The `seq` for
+ /// the first message sent by a client or debug adapter is 1, and for each
+ /// subsequent message is 1 greater than the previous message sent by that
+ /// actor. `seq` can be used to order requests, responses, and events, and to
+ /// associate requests with their corresponding responses. For protocol
+ /// messages of type `request` the sequence number can be used to cancel the
+ /// request.
+ Id seq = kCalculateSeq;
};
llvm::json::Value toJSON(const Event &);
bool fromJSON(const llvm::json::Value &, Event &, llvm::json::Path);
@@ -78,7 +92,7 @@ enum ResponseMessage : unsigned {
/// Response for a request.
struct Response {
/// Sequence number of the corresponding request.
- Id request_seq;
+ Id request_seq = 0;
/// The command requested.
std::string command;
@@ -87,21 +101,31 @@ struct Response {
/// attribute may contain the result of the request. If the value is false,
/// the attribute `message` contains the error in short form and the `body`
/// may contain additional information (see `ErrorMessage`).
- bool success;
+ bool success = false;
// FIXME: Migrate usage of fallback string to ErrorMessage
/// Contains the raw error in short form if `success` is false. This raw error
/// might be interpreted by the client and is not shown in the UI. Some
/// predefined values exist.
- std::optional<std::variant<ResponseMessage, std::string>> message;
+ std::optional<std::variant<ResponseMessage, std::string>> message =
+ std::nullopt;
/// Contains request result if success is true and error details if success is
/// false.
///
/// Request handlers are expected to build an appropriate body, see
/// `RequestHandler`.
- std::optional<llvm::json::Value> body;
+ std::optional<llvm::json::Value> body = std::nullopt;
+
+ /// Sequence number of the message (also known as message ID). The `seq` for
+ /// the first message sent by a client or debug adapter is 1, and for each
+ /// subsequent message is 1 greater than the previous message sent by that
+ /// actor. `seq` can be used to order requests, responses, and events, and to
+ /// associate requests with their corresponding responses. For protocol
+ /// messages of type `request` the sequence number can be used to cancel the
+ /// request.
+ Id seq = kCalculateSeq;
};
bool fromJSON(const llvm::json::Value &, Response &, llvm::json::Path);
llvm::json::Value toJSON(const Response &);
diff --git a/lldb/unittests/Target/MemoryTest.cpp b/lldb/unittests/Target/MemoryTest.cpp
index f7b4e97..e444f68 100644
--- a/lldb/unittests/Target/MemoryTest.cpp
+++ b/lldb/unittests/Target/MemoryTest.cpp
@@ -245,7 +245,7 @@ public:
if (read_more_than_requested)
size *= 2;
uint8_t *buffer = static_cast<uint8_t *>(buf);
- for (size_t addr = vm_addr; addr < vm_addr + size; addr++)
+ for (lldb::addr_t addr = vm_addr; addr < vm_addr + size; addr++)
buffer[addr - vm_addr] = static_cast<uint8_t>(addr); // LSB of addr.
return size;
}