diff options
Diffstat (limited to 'lldb')
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; } |
