llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-lldb Author: Jonas Devlieghere (JDevlieghere) <details> <summary>Changes</summary> Continuation of the work started in https://github.com/llvm/llvm-project/pull/128262. Builds on top of https://github.com/llvm/llvm-project/pull/128550. --- Patch is 230.81 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/128551.diff 27 Files Affected: - (modified) lldb/tools/lldb-dap/CMakeLists.txt (+23) - (added) lldb/tools/lldb-dap/Handler/CompileUnitsRequestHandler.cpp (+80) - (added) lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp (+190) - (added) lldb/tools/lldb-dap/Handler/DisassembleRequestHandler.cpp (+222) - (added) lldb/tools/lldb-dap/Handler/LocationsRequestHandler.cpp (+160) - (added) lldb/tools/lldb-dap/Handler/ModulesRequestHandler.cpp (+58) - (added) lldb/tools/lldb-dap/Handler/NextRequestHandler.cpp (+79) - (added) lldb/tools/lldb-dap/Handler/PauseRequestHandler.cpp (+60) - (added) lldb/tools/lldb-dap/Handler/ReadMemoryRequestHandler.cpp (+139) - (modified) lldb/tools/lldb-dap/Handler/RequestHandler.cpp (+62) - (modified) lldb/tools/lldb-dap/Handler/RequestHandler.h (+176-1) - (added) lldb/tools/lldb-dap/Handler/ScopesRequestHandler.cpp (+106) - (added) lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp (+182) - (added) lldb/tools/lldb-dap/Handler/SetDataBreakpointsRequestHandler.cpp (+114) - (added) lldb/tools/lldb-dap/Handler/SetExceptionBreakpointsRequestHandler.cpp (+93) - (added) lldb/tools/lldb-dap/Handler/SetFunctionBreakpointsRequestHandler.cpp (+139) - (added) lldb/tools/lldb-dap/Handler/SetInstructionBreakpointsRequestHandler.cpp (+249) - (added) lldb/tools/lldb-dap/Handler/SetVariableRequestHandler.cpp (+177) - (added) lldb/tools/lldb-dap/Handler/SourceRequestHandler.cpp (+82) - (added) lldb/tools/lldb-dap/Handler/StackTraceRequestHandler.cpp (+197) - (added) lldb/tools/lldb-dap/Handler/StepInRequestHandler.cpp (+96) - (added) lldb/tools/lldb-dap/Handler/StepInTargetsRequestHandler.cpp (+149) - (added) lldb/tools/lldb-dap/Handler/StepOutRequestHandler.cpp (+68) - (added) lldb/tools/lldb-dap/Handler/TestGetTargetBreakpointsRequestHandler.cpp (+31) - (added) lldb/tools/lldb-dap/Handler/ThreadsRequestHandler.cpp (+71) - (added) lldb/tools/lldb-dap/Handler/VariablesRequestHandler.cpp (+217) - (modified) lldb/tools/lldb-dap/lldb-dap.cpp (+29-2676) ``````````diff diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt index 73762af5c2fd7..804dd8e4cc2a0 100644 --- a/lldb/tools/lldb-dap/CMakeLists.txt +++ b/lldb/tools/lldb-dap/CMakeLists.txt @@ -39,16 +39,39 @@ add_lldb_tool(lldb-dap Handler/AttachRequestHandler.cpp Handler/BreakpointLocationsHandler.cpp + Handler/CompileUnitsRequestHandler.cpp Handler/CompletionsHandler.cpp Handler/ConfigurationDoneRequestHandler.cpp Handler/ContinueRequestHandler.cpp + Handler/DataBreakpointInfoRequestHandler.cpp + Handler/DisassembleRequestHandler.cpp Handler/DisconnectRequestHandler.cpp Handler/EvaluateRequestHandler.cpp Handler/ExceptionInfoRequestHandler.cpp Handler/InitializeRequestHandler.cpp Handler/LaunchRequestHandler.cpp + Handler/LocationsRequestHandler.cpp + Handler/ModulesRequestHandler.cpp + Handler/NextRequestHandler.cpp + Handler/PauseRequestHandler.cpp + Handler/ReadMemoryRequestHandler.cpp Handler/RequestHandler.cpp Handler/RestartRequestHandler.cpp + Handler/ScopesRequestHandler.cpp + Handler/SetBreakpointsRequestHandler.cpp + Handler/SetDataBreakpointsRequestHandler.cpp + Handler/SetExceptionBreakpointsRequestHandler.cpp + Handler/SetFunctionBreakpointsRequestHandler.cpp + Handler/SetInstructionBreakpointsRequestHandler.cpp + Handler/SetVariableRequestHandler.cpp + Handler/SourceRequestHandler.cpp + Handler/StackTraceRequestHandler.cpp + Handler/StepInRequestHandler.cpp + Handler/StepInTargetsRequestHandler.cpp + Handler/StepOutRequestHandler.cpp + Handler/TestGetTargetBreakpointsRequestHandler.cpp + Handler/ThreadsRequestHandler.cpp + Handler/VariablesRequestHandler.cpp LINK_LIBS liblldb diff --git a/lldb/tools/lldb-dap/Handler/CompileUnitsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/CompileUnitsRequestHandler.cpp new file mode 100644 index 0000000000000..c541d1cd039c8 --- /dev/null +++ b/lldb/tools/lldb-dap/Handler/CompileUnitsRequestHandler.cpp @@ -0,0 +1,80 @@ +//===-- CompileUnitsRequestHandler.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" +#include "RequestHandler.h" + +namespace lldb_dap { + +// "compileUnitsRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "Compile Unit request; value of command field is +// 'compileUnits'.", +// "properties": { +// "command": { +// "type": "string", +// "enum": [ "compileUnits" ] +// }, +// "arguments": { +// "$ref": "#/definitions/compileUnitRequestArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "compileUnitsRequestArguments": { +// "type": "object", +// "description": "Arguments for 'compileUnits' request.", +// "properties": { +// "moduleId": { +// "type": "string", +// "description": "The ID of the module." +// } +// }, +// "required": [ "moduleId" ] +// }, +// "compileUnitsResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to 'compileUnits' request.", +// "properties": { +// "body": { +// "description": "Response to 'compileUnits' request. Array of +// paths of compile units." +// } +// } +// }] +// } +void CompileUnitsRequestHandler::operator()(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + llvm::json::Object body; + llvm::json::Array units; + const auto *arguments = request.getObject("arguments"); + std::string module_id = std::string(GetString(arguments, "moduleId")); + int num_modules = dap.target.GetNumModules(); + for (int i = 0; i < num_modules; i++) { + auto curr_module = dap.target.GetModuleAtIndex(i); + if (module_id == curr_module.GetUUIDString()) { + int num_units = curr_module.GetNumCompileUnits(); + for (int j = 0; j < num_units; j++) { + auto curr_unit = curr_module.GetCompileUnitAtIndex(j); + units.emplace_back(CreateCompileUnit(curr_unit)); + } + body.try_emplace("compileUnits", std::move(units)); + break; + } + } + 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/DataBreakpointInfoRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp new file mode 100644 index 0000000000000..519a9c728e4b3 --- /dev/null +++ b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp @@ -0,0 +1,190 @@ +//===-- DataBreakpointInfoRequestHandler.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" +#include "RequestHandler.h" +#include "lldb/API/SBMemoryRegionInfo.h" +#include "llvm/ADT/StringExtras.h" + +namespace lldb_dap { + +// "DataBreakpointInfoRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "Obtains information on a possible data breakpoint that +// could be set on an expression or variable.\nClients should only call this +// request if the corresponding capability `supportsDataBreakpoints` is +// true.", "properties": { +// "command": { +// "type": "string", +// "enum": [ "dataBreakpointInfo" ] +// }, +// "arguments": { +// "$ref": "#/definitions/DataBreakpointInfoArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "DataBreakpointInfoArguments": { +// "type": "object", +// "description": "Arguments for `dataBreakpointInfo` request.", +// "properties": { +// "variablesReference": { +// "type": "integer", +// "description": "Reference to the variable container if the data +// breakpoint is requested for a child of the container. The +// `variablesReference` must have been obtained in the current suspended +// state. See 'Lifetime of Object References' in the Overview section for +// details." +// }, +// "name": { +// "type": "string", +// "description": "The name of the variable's child to obtain data +// breakpoint information for.\nIf `variablesReference` isn't specified, +// this can be an expression." +// }, +// "frameId": { +// "type": "integer", +// "description": "When `name` is an expression, evaluate it in the scope +// of this stack frame. If not specified, the expression is evaluated in +// the global scope. When `variablesReference` is specified, this property +// has no effect." +// } +// }, +// "required": [ "name" ] +// }, +// "DataBreakpointInfoResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to `dataBreakpointInfo` request.", +// "properties": { +// "body": { +// "type": "object", +// "properties": { +// "dataId": { +// "type": [ "string", "null" ], +// "description": "An identifier for the data on which a data +// breakpoint can be registered with the `setDataBreakpoints` +// request or null if no data breakpoint is available. If a +// `variablesReference` or `frameId` is passed, the `dataId` is +// valid in the current suspended state, otherwise it's valid +// indefinitely. See 'Lifetime of Object References' in the Overview +// section for details. Breakpoints set using the `dataId` in the +// `setDataBreakpoints` request may outlive the lifetime of the +// associated `dataId`." +// }, +// "description": { +// "type": "string", +// "description": "UI string that describes on what data the +// breakpoint is set on or why a data breakpoint is not available." +// }, +// "accessTypes": { +// "type": "array", +// "items": { +// "$ref": "#/definitions/DataBreakpointAccessType" +// }, +// "description": "Attribute lists the available access types for a +// potential data breakpoint. A UI client could surface this +// information." +// }, +// "canPersist": { +// "type": "boolean", +// "description": "Attribute indicates that a potential data +// breakpoint could be persisted across sessions." +// } +// }, +// "required": [ "dataId", "description" ] +// } +// }, +// "required": [ "body" ] +// }] +// } +void DataBreakpointInfoRequestHandler::operator()( + const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + llvm::json::Object body; + lldb::SBError error; + llvm::json::Array accessTypes{"read", "write", "readWrite"}; + const auto *arguments = request.getObject("arguments"); + const auto variablesReference = + GetUnsigned(arguments, "variablesReference", 0); + llvm::StringRef name = GetString(arguments, "name"); + lldb::SBFrame frame = dap.GetLLDBFrame(*arguments); + lldb::SBValue variable = FindVariable(variablesReference, name); + std::string addr, size; + + if (variable.IsValid()) { + lldb::addr_t load_addr = variable.GetLoadAddress(); + size_t byte_size = variable.GetByteSize(); + if (load_addr == LLDB_INVALID_ADDRESS) { + body.try_emplace("dataId", nullptr); + body.try_emplace("description", + "does not exist in memory, its location is " + + std::string(variable.GetLocation())); + } else if (byte_size == 0) { + body.try_emplace("dataId", nullptr); + body.try_emplace("description", "variable size is 0"); + } else { + addr = llvm::utohexstr(load_addr); + size = llvm::utostr(byte_size); + } + } else if (variablesReference == 0 && frame.IsValid()) { + lldb::SBValue value = frame.EvaluateExpression(name.data()); + if (value.GetError().Fail()) { + lldb::SBError error = value.GetError(); + const char *error_cstr = error.GetCString(); + body.try_emplace("dataId", nullptr); + body.try_emplace("description", error_cstr && error_cstr[0] + ? std::string(error_cstr) + : "evaluation failed"); + } else { + uint64_t load_addr = value.GetValueAsUnsigned(); + lldb::SBData data = value.GetPointeeData(); + if (data.IsValid()) { + size = llvm::utostr(data.GetByteSize()); + addr = llvm::utohexstr(load_addr); + lldb::SBMemoryRegionInfo region; + lldb::SBError err = + dap.target.GetProcess().GetMemoryRegionInfo(load_addr, region); + // Only lldb-server supports "qMemoryRegionInfo". So, don't fail this + // request if SBProcess::GetMemoryRegionInfo returns error. + if (err.Success()) { + if (!(region.IsReadable() || region.IsWritable())) { + body.try_emplace("dataId", nullptr); + body.try_emplace("description", + "memory region for address " + addr + + " has no read or write permissions"); + } + } + } else { + body.try_emplace("dataId", nullptr); + body.try_emplace("description", + "unable to get byte size for expression: " + + name.str()); + } + } + } else { + body.try_emplace("dataId", nullptr); + body.try_emplace("description", "variable not found: " + name.str()); + } + + if (!body.getObject("dataId")) { + body.try_emplace("dataId", addr + "/" + size); + body.try_emplace("accessTypes", std::move(accessTypes)); + body.try_emplace("description", + size + " bytes at " + addr + " " + name.str()); + } + 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/DisassembleRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/DisassembleRequestHandler.cpp new file mode 100644 index 0000000000000..6d25ef0fc5d74 --- /dev/null +++ b/lldb/tools/lldb-dap/Handler/DisassembleRequestHandler.cpp @@ -0,0 +1,222 @@ +//===-- DisassembleRequestHandler.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" +#include "RequestHandler.h" +#include "lldb/API/SBInstruction.h" +#include "llvm/ADT/StringExtras.h" + +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) { + llvm::json::Object response; + FillResponse(request, response); + auto *arguments = request.getObject("arguments"); + + llvm::StringRef memoryReference = GetString(arguments, "memoryReference"); + 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; + + addr_ptr += GetSigned(arguments, "instructionOffset", 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 = GetUnsigned(arguments, "instructionCount", 0); + lldb::SBInstructionList insts = dap.target.ReadInstructions(addr, inst_count); + + if (!insts.IsValid()) { + response["success"] = false; + response["message"] = "Failed to find instructions for memory address."; + dap.SendJSON(llvm::json::Value(std::move(response))); + return; + } + + const bool resolveSymbols = GetBoolean(arguments, "resolveSymbols", false); + llvm::json::Array instructions; + const auto num_insts = insts.GetSize(); + for (size_t i = 0; i < num_insts; ++i) { + lldb::SBInstruction inst = insts.GetInstructionAtIndex(i); + auto addr = inst.GetAddress(); + const auto inst_addr = addr.GetLoadAddress(dap.target); + const char *m = inst.GetMnemonic(dap.target); + const char *o = inst.GetOperands(dap.target); + const char *c = inst.GetComment(dap.target); + auto d = inst.GetData(dap.target); + + std::string bytes; + llvm::raw_string_ostream sb(bytes); + for (unsigned i = 0; i < inst.GetByteSize(); i++) { + lldb::SBError error; + uint8_t b = d.GetUnsignedInt8(error, i); + if (error.Success()) { + sb << llvm::format("%2.2x ", b); + } + } + + llvm::json::Object disassembled_inst{ + {"address", "0x" + llvm::utohexstr(inst_addr)}, + {"instructionBytes", + bytes.size() > 0 ? bytes.substr(0, bytes.size() - 1) : ""}, + }; + + std::string instruction; + llvm::raw_string_ostream si(instruction); + + lldb::SBSymbol symbol = addr.GetSymbol(); + // Only add the symbol on the first line of the function. + if (symbol.IsValid() && symbol.GetStartAddress() == addr) { + // If we have a valid symbol, append it as a label prefix for the first + // instruction. This is so you can see the start of a function/callsite + // in the assembly, at the moment VS Code (1.80) does not visualize the + // symbol associated with the assembly instruction. + si << (symbol.GetMangledName() != nullptr ? symbol.GetMangledName() + : symbol.GetName()) + << ": "; + + if (resolveSymbols) { + disassembled_inst.try_emplace("symbol", symbol.GetDisplayName()); + } + } + + si << llvm::formatv("{0,7} {1,12}", m, o); + if (c && c[0]) { + si << " ; " << c; + } + + disassembled_inst.try_emplace("instruction", instruction); + + auto line_entry = addr.GetLineEntry(); + // If the line number is 0 then the entry represents a compiler generated + // location. + 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); + + const auto line = line_entry.GetLine(); + if (line && line != LLDB_INVALID_LINE_NUMBER) { + disassembled_inst.try_emplace("line", line); + } + const auto column = line_entry.GetColumn(); + if (column && column != LLDB_INVALID_COLUMN_NUMBER) { + disassembled_inst.try_emplace("column", column);... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/128551 _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits