https://github.com/eronnen created https://github.com/llvm/llvm-project/pull/140482
None >From b187a83605b9c50ea5dbe7674b755bc8706c2936 Mon Sep 17 00:00:00 2001 From: Ely Ronnen <elyron...@gmail.com> Date: Sun, 18 May 2025 23:51:58 +0200 Subject: [PATCH] [lldb-dap] Migrate disassemble request to structured handler --- .../Handler/DisassembleRequestHandler.cpp | 176 ++++-------------- lldb/tools/lldb-dap/Handler/RequestHandler.h | 7 +- .../lldb-dap/Protocol/ProtocolRequests.cpp | 18 ++ .../lldb-dap/Protocol/ProtocolRequests.h | 31 +++ .../tools/lldb-dap/Protocol/ProtocolTypes.cpp | 34 ++++ lldb/tools/lldb-dap/Protocol/ProtocolTypes.h | 53 ++++++ 6 files changed, 179 insertions(+), 140 deletions(-) diff --git a/lldb/tools/lldb-dap/Handler/DisassembleRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/DisassembleRequestHandler.cpp index d738f54ff1a9f..9df831c8b8c61 100644 --- a/lldb/tools/lldb-dap/Handler/DisassembleRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/DisassembleRequestHandler.cpp @@ -9,113 +9,30 @@ #include "DAP.h" #include "EventHelper.h" #include "JSONUtils.h" +#include "Protocol/ProtocolRequests.h" +#include "Protocol/ProtocolTypes.h" #include "RequestHandler.h" #include "lldb/API/SBInstruction.h" #include "llvm/ADT/StringExtras.h" +using namespace lldb_dap::protocol; + namespace lldb_dap { -// "DisassembleRequest": { -// "allOf": [ { "$ref": "#/definitions/Request" }, { -// "type": "object", -// "description": "Disassembles code stored at the provided -// location.\nClients should only call this request if the corresponding -// capability `supportsDisassembleRequest` is true.", "properties": { -// "command": { -// "type": "string", -// "enum": [ "disassemble" ] -// }, -// "arguments": { -// "$ref": "#/definitions/DisassembleArguments" -// } -// }, -// "required": [ "command", "arguments" ] -// }] -// }, -// "DisassembleArguments": { -// "type": "object", -// "description": "Arguments for `disassemble` request.", -// "properties": { -// "memoryReference": { -// "type": "string", -// "description": "Memory reference to the base location containing the -// instructions to disassemble." -// }, -// "offset": { -// "type": "integer", -// "description": "Offset (in bytes) to be applied to the reference -// location before disassembling. Can be negative." -// }, -// "instructionOffset": { -// "type": "integer", -// "description": "Offset (in instructions) to be applied after the byte -// offset (if any) before disassembling. Can be negative." -// }, -// "instructionCount": { -// "type": "integer", -// "description": "Number of instructions to disassemble starting at the -// specified location and offset.\nAn adapter must return exactly this -// number of instructions - any unavailable instructions should be -// replaced with an implementation-defined 'invalid instruction' value." -// }, -// "resolveSymbols": { -// "type": "boolean", -// "description": "If true, the adapter should attempt to resolve memory -// addresses and other values to symbolic names." -// } -// }, -// "required": [ "memoryReference", "instructionCount" ] -// }, -// "DisassembleResponse": { -// "allOf": [ { "$ref": "#/definitions/Response" }, { -// "type": "object", -// "description": "Response to `disassemble` request.", -// "properties": { -// "body": { -// "type": "object", -// "properties": { -// "instructions": { -// "type": "array", -// "items": { -// "$ref": "#/definitions/DisassembledInstruction" -// }, -// "description": "The list of disassembled instructions." -// } -// }, -// "required": [ "instructions" ] -// } -// } -// }] -// } -void DisassembleRequestHandler::operator()( - const llvm::json::Object &request) const { - llvm::json::Object response; - FillResponse(request, response); - auto *arguments = request.getObject("arguments"); - - llvm::StringRef memoryReference = - GetString(arguments, "memoryReference").value_or(""); - auto addr_opt = DecodeMemoryReference(memoryReference); - if (!addr_opt.has_value()) { - response["success"] = false; - response["message"] = - "Malformed memory reference: " + memoryReference.str(); - dap.SendJSON(llvm::json::Value(std::move(response))); - return; - } - lldb::addr_t addr_ptr = *addr_opt; +/// Disassembles code stored at the provided location. +/// Clients should only call this request if the corresponding capability `supportsDisassembleRequest` is true. +llvm::Expected<DisassembleResponseBody> DisassembleRequestHandler::Run(const DisassembleArguments &args) const { + std::vector<DisassembledInstruction> instructions; - addr_ptr += GetInteger<int64_t>(arguments, "instructionOffset").value_or(0); + auto addr_opt = DecodeMemoryReference(args.memoryReference); + if (!addr_opt.has_value()) + return llvm::make_error<DAPError>("Malformed memory reference: " + args.memoryReference); + + lldb::addr_t addr_ptr = *addr_opt; + addr_ptr += args.instructionOffset.value_or(0); lldb::SBAddress addr(addr_ptr, dap.target); - if (!addr.IsValid()) { - response["success"] = false; - response["message"] = "Memory reference not found in the current binary."; - dap.SendJSON(llvm::json::Value(std::move(response))); - return; - } - - const auto inst_count = - GetInteger<int64_t>(arguments, "instructionCount").value_or(0); + if (!addr.IsValid()) + return llvm::make_error<DAPError>("Memory reference not found in the current binary."); std::string flavor_string; const auto target_triple = llvm::StringRef(dap.target.GetTriple()); @@ -133,18 +50,12 @@ void DisassembleRequestHandler::operator()( } lldb::SBInstructionList insts = - dap.target.ReadInstructions(addr, inst_count, flavor_string.c_str()); + dap.target.ReadInstructions(addr, args.instructionCount, flavor_string.c_str()); - if (!insts.IsValid()) { - response["success"] = false; - response["message"] = "Failed to find instructions for memory address."; - dap.SendJSON(llvm::json::Value(std::move(response))); - return; - } + if (!insts.IsValid()) + return llvm::make_error<DAPError>("Failed to find instructions for memory address."); - const bool resolveSymbols = - GetBoolean(arguments, "resolveSymbols").value_or(false); - llvm::json::Array instructions; + const bool resolveSymbols = args.resolveSymbols.value_or(false); const auto num_insts = insts.GetSize(); for (size_t i = 0; i < num_insts; ++i) { lldb::SBInstruction inst = insts.GetInstructionAtIndex(i); @@ -165,11 +76,9 @@ void DisassembleRequestHandler::operator()( } } - llvm::json::Object disassembled_inst{ - {"address", "0x" + llvm::utohexstr(inst_addr)}, - {"instructionBytes", - bytes.size() > 0 ? bytes.substr(0, bytes.size() - 1) : ""}, - }; + DisassembledInstruction disassembled_inst; + disassembled_inst.address = "0x" + llvm::utohexstr(inst_addr); + disassembled_inst.instructionBytes = bytes.size() > 0 ? bytes.substr(0, bytes.size() - 1) : ""; std::string instruction; llvm::raw_string_ostream si(instruction); @@ -185,9 +94,8 @@ void DisassembleRequestHandler::operator()( : symbol.GetName()) << ": "; - if (resolveSymbols) { - disassembled_inst.try_emplace("symbol", symbol.GetDisplayName()); - } + if (resolveSymbols) + disassembled_inst.symbol = symbol.GetDisplayName(); } si << llvm::formatv("{0,7} {1,12}", m, o); @@ -195,7 +103,7 @@ void DisassembleRequestHandler::operator()( si << " ; " << c; } - disassembled_inst.try_emplace("instruction", instruction); + disassembled_inst.instruction = instruction; auto line_entry = addr.GetLineEntry(); // If the line number is 0 then the entry represents a compiler generated @@ -203,41 +111,35 @@ void DisassembleRequestHandler::operator()( if (line_entry.GetStartAddress() == addr && line_entry.IsValid() && line_entry.GetFileSpec().IsValid() && line_entry.GetLine() != 0) { auto source = CreateSource(line_entry); - disassembled_inst.try_emplace("location", source); + disassembled_inst.location = std::move(source); const auto line = line_entry.GetLine(); - if (line && line != LLDB_INVALID_LINE_NUMBER) { - disassembled_inst.try_emplace("line", line); - } + if (line != 0 && line != LLDB_INVALID_LINE_NUMBER) + disassembled_inst.line = line; + const auto column = line_entry.GetColumn(); - if (column && column != LLDB_INVALID_COLUMN_NUMBER) { - disassembled_inst.try_emplace("column", column); - } + if (column != 0 && column != LLDB_INVALID_COLUMN_NUMBER) + disassembled_inst.column = column; auto end_line_entry = line_entry.GetEndAddress().GetLineEntry(); if (end_line_entry.IsValid() && end_line_entry.GetFileSpec() == line_entry.GetFileSpec()) { const auto end_line = end_line_entry.GetLine(); - if (end_line && end_line != LLDB_INVALID_LINE_NUMBER && - end_line != line) { - disassembled_inst.try_emplace("endLine", end_line); + if (end_line != 0 && end_line != LLDB_INVALID_LINE_NUMBER && end_line != line) { + disassembled_inst.endLine = end_line; const auto end_column = end_line_entry.GetColumn(); - if (end_column && end_column != LLDB_INVALID_COLUMN_NUMBER && - end_column != column) { - disassembled_inst.try_emplace("endColumn", end_column - 1); - } + if (end_column != 0 && end_column != LLDB_INVALID_COLUMN_NUMBER && + end_column != column) + disassembled_inst.endColumn = end_column - 1; } } } - instructions.emplace_back(std::move(disassembled_inst)); + instructions.push_back(std::move(disassembled_inst)); } - llvm::json::Object body; - body.try_emplace("instructions", std::move(instructions)); - response.try_emplace("body", std::move(body)); - dap.SendJSON(llvm::json::Value(std::move(response))); + return DisassembleResponseBody{std::move(instructions)}; } } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h index e6bccfe12f402..09bedb88d6b09 100644 --- a/lldb/tools/lldb-dap/Handler/RequestHandler.h +++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h @@ -534,14 +534,15 @@ class LocationsRequestHandler : public LegacyRequestHandler { void operator()(const llvm::json::Object &request) const override; }; -class DisassembleRequestHandler : public LegacyRequestHandler { +class DisassembleRequestHandler final : public RequestHandler<protocol::DisassembleArguments, llvm::Expected<protocol::DisassembleResponseBody>> { public: - using LegacyRequestHandler::LegacyRequestHandler; + using RequestHandler::RequestHandler; static llvm::StringLiteral GetCommand() { return "disassemble"; } FeatureSet GetSupportedFeatures() const override { return {protocol::eAdapterFeatureDisassembleRequest}; } - void operator()(const llvm::json::Object &request) const override; + llvm::Expected<protocol::DisassembleResponseBody> + Run(const protocol::DisassembleArguments &args) const override; }; class ReadMemoryRequestHandler : public LegacyRequestHandler { diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp index 7efab87d39986..4160077d419e1 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp @@ -460,4 +460,22 @@ llvm::json::Value toJSON(const SetDataBreakpointsResponseBody &SDBR) { return result; } +bool fromJSON(const llvm::json::Value &Params, DisassembleArguments &DA, + llvm::json::Path P) { + json::ObjectMapper O(Params, P); + return O && O.map("memoryReference", DA.memoryReference) && + O.mapOptional("offset", DA.offset) && + O.mapOptional("instructionOffset", DA.instructionOffset) && + O.map("instructionCount", DA.instructionCount) && + O.mapOptional("resolveSymbols", DA.resolveSymbols); +} + +llvm::json::Value toJSON(const DisassembleResponseBody &DRB) { + llvm::json::Array instructions; + for (const auto &instruction : DRB.instructions) { + instructions.push_back(toJSON(instruction)); + } + return llvm::json::Object{{"instructions", std::move(instructions)}}; +} + } // namespace lldb_dap::protocol diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h index b421c631344de..d48a3a3a14fb7 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h @@ -726,6 +726,37 @@ struct SetDataBreakpointsResponseBody { }; llvm::json::Value toJSON(const SetDataBreakpointsResponseBody &); +/// Arguments to `disassemble` request. +struct DisassembleArguments { + /// Memory reference to the base location containing the instructions to disassemble. + std::string memoryReference; + + /// Offset (in bytes) to be applied to the reference location before disassembling. Can be negative. + std::optional<int64_t> offset; + + /// Offset (in instructions) to be applied after the byte offset (if any) before disassembling. Can be negative. + std::optional<int64_t> instructionOffset; + + /// Number of instructions to disassemble starting at the specified location + /// and offset. + /// An adapter must return exactly this number of instructions - any + /// unavailable instructions should be replaced with an implementation-defined + /// 'invalid instruction' value. + uint32_t instructionCount; + + /// If true, the adapter should attempt to resolve memory addresses and other values to symbolic names. + std::optional<bool> resolveSymbols; +}; +bool fromJSON(const llvm::json::Value &, DisassembleArguments &, + llvm::json::Path); + +/// Response to `disassemble` request. +struct DisassembleResponseBody { + /// The list of disassembled instructions. + std::vector<DisassembledInstruction> instructions; +}; +llvm::json::Value toJSON(const DisassembleResponseBody &); + } // namespace lldb_dap::protocol #endif diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp index ce7519e3b16b8..94fe236d952a7 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp +++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp @@ -782,4 +782,38 @@ bool fromJSON(const llvm::json::Value &Params, InstructionBreakpoint &IB, O.mapOptional("mode", IB.mode); } +llvm::json::Value toJSON(const DisassembledInstruction::PresentationHint &PH) { + switch (PH) { + case DisassembledInstruction::eSourcePresentationHintNormal: + return "normal"; + case DisassembledInstruction::eSourcePresentationHintInvalid: + return "invalid"; + } + llvm_unreachable("unhandled presentation hint."); +} + +llvm::json::Value toJSON(const DisassembledInstruction &DI) { + llvm::json::Object result{{"address", DI.address}, + {"instruction", DI.instruction}}; + + if (DI.instructionBytes) + result.insert({"instructionBytes", *DI.instructionBytes}); + if (DI.symbol) + result.insert({"symbol", *DI.symbol}); + if (DI.location) + result.insert({"location", *DI.location}); + if (DI.line) + result.insert({"line", *DI.line}); + if (DI.column) + result.insert({"column", *DI.column}); + if (DI.endLine) + result.insert({"endLine", *DI.endLine}); + if (DI.endColumn) + result.insert({"endColumn", *DI.endColumn}); + if (DI.presentationHint) + result.insert({"presentationHint", *DI.presentationHint}); + + return result; +} + } // namespace lldb_dap::protocol diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h index 3df77ee7374a7..262ac240a773f 100644 --- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h +++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h @@ -627,6 +627,59 @@ struct InstructionBreakpoint { bool fromJSON(const llvm::json::Value &, InstructionBreakpoint &, llvm::json::Path); +/// Properties of a single disassembled instruction, returned by `disassemble` request. +struct DisassembledInstruction { + enum PresentationHint : unsigned { + eSourcePresentationHintNormal, + eSourcePresentationHintInvalid, + }; + + /// The address of the instruction. Treated as a hex value if prefixed with + /// `0x`, or as a decimal value otherwise. + std::string address; + + /// Raw bytes representing the instruction and its operands, in an + /// implementation-defined format. + std::optional<std::string> instructionBytes; + + /// Text representing the instruction and its operands, in an + /// implementation-defined format. + std::string instruction; + + /// Name of the symbol that corresponds with the location of this instruction, + /// if any. + std::optional<std::string> symbol; + + /// Source location that corresponds to this instruction, if any. + /// Should always be set (if available) on the first instruction returned, + /// but can be omitted afterwards if this instruction maps to the same source + /// file as the previous instruction. + std::optional<protocol::Source> location; + + /// The line within the source location that corresponds to this instruction, + /// if any. + std::optional<uint32_t> line; + + /// The column within the line that corresponds to this instruction, if any. + std::optional<uint32_t> column; + + /// The end line of the range that corresponds to this instruction, if any. + std::optional<uint32_t> endLine; + + /// The end column of the range that corresponds to this instruction, if any. + std::optional<uint32_t> endColumn; + + /// A hint for how to present the instruction in the UI. + /// + /// A value of `invalid` may be used to indicate this instruction is 'filler' + /// and cannot be reached by the program. For example, unreadable memory + /// addresses may be presented is 'invalid.' + /// Values: 'normal', 'invalid' + std::optional<PresentationHint> presentationHint; +}; +llvm::json::Value toJSON(const DisassembledInstruction::PresentationHint &); +llvm::json::Value toJSON(const DisassembledInstruction &); + } // namespace lldb_dap::protocol #endif _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits