[lldb-dap] Enabling instruction breakpoint support to lldb-dap. by santhoshe447 · Pull Request #105278 · llvm/llvm-project (original) (raw)

@llvm/pr-subscribers-lldb

Author: Santhosh Kumar Ellendula (santhoshe447)

Changes

Added support for "supportsInstructionBreakpoints" capability and now it this command is triggered when we set instruction breakpoint.
We need this support as part of enabling disassembly view debugging. Following features should work as part of this feature enablement:

  1. Settings breakpoints in disassembly view: Unsetting the breakpoint is not happening from the disassembly view. Currently we need to unset breakpoint manually from the breakpoint List. Multiple breakpoints are getting set for the same $
  2. Step over, step into, continue in the disassembly view

The format for DisassembleRequest and DisassembleResponse at https://raw.githubusercontent.com/microsoft/vscode/master/src/vs/workbench/contrib/debug/common/debugProtocol.d.ts .

Ref Images:
Set instruction breakpoint in disassembly view:
image

After issuing continue:
image


Patch is 23.21 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/105278.diff

12 Files Affected:

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 a324af57b61df3..98b599608946c0 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 @@ -1095,6 +1095,20 @@ def terminate(self): self.send.close() # self.recv.close()

class DebugAdaptorServer(DebugCommunication): def init( diff --git a/lldb/test/API/tools/lldb-dap/instruction-breakpoint/Makefile b/lldb/test/API/tools/lldb-dap/instruction-breakpoint/Makefile new file mode 100644 index 00000000000000..714cd70b0bd35c --- /dev/null +++ b/lldb/test/API/tools/lldb-dap/instruction-breakpoint/Makefile @@ -0,0 +1,6 @@ +CXX_SOURCES := main-copy.cpp +CXXFLAGS_EXTRAS := -O1 -g +include Makefile.rules + +main-copy.cpp: main.cpp + cp -f << <@ diff --git a/lldb/test/API/tools/lldb-dap/instruction-breakpoint/TestDAP_instruction_breakpoint.py b/lldb/test/API/tools/lldb-dap/instruction-breakpoint/TestDAP_instruction_breakpoint.py new file mode 100644 index 00000000000000..ed1a6bf250c761 --- /dev/null +++ b/lldb/test/API/tools/lldb-dap/instruction-breakpoint/TestDAP_instruction_breakpoint.py @@ -0,0 +1,97 @@ +import dap_server +import shutil +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil +import lldbdap_testcase +import os +import lldb + + +class TestDAP_InstructionBreakpointTestCase(lldbdap_testcase.DAPTestCaseBase):

diff --git a/lldb/test/API/tools/lldb-dap/instruction-breakpoint/main.cpp b/lldb/test/API/tools/lldb-dap/instruction-breakpoint/main.cpp new file mode 100644 index 00000000000000..3c710d64171570 --- /dev/null +++ b/lldb/test/API/tools/lldb-dap/instruction-breakpoint/main.cpp @@ -0,0 +1,18 @@ +#include <stdio.h> +#include <unistd.h> + +int function(int x) { +

+} + +int main(int argc, char const *argv[]) {

diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index 7828272aa15a7d..8be5a5a95aa38a 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -68,6 +68,8 @@ namespace lldb_dap {

typedef llvm::DenseMap<uint32_t, SourceBreakpoint> SourceBreakpointMap; typedef llvm::StringMap FunctionBreakpointMap; +typedef llvm::DenseMap<lldb::addr_t, InstructionBreakpoint>

enum class OutputType { Console, Stdout, Stderr, Telemetry };

enum DAPBroadcasterBits { diff --git a/lldb/tools/lldb-dap/DAPForward.h b/lldb/tools/lldb-dap/DAPForward.h index 8c79488fae8dbf..159d999a63c820 100644 --- a/lldb/tools/lldb-dap/DAPForward.h +++ b/lldb/tools/lldb-dap/DAPForward.h @@ -15,6 +15,7 @@ struct ExceptionBreakpoint; struct FunctionBreakpoint; struct SourceBreakpoint; struct Watchpoint; +struct InstructionBreakpoint; } // namespace lldb_dap

namespace lldb { diff --git a/lldb/tools/lldb-dap/InstructionBreakpoint.cpp b/lldb/tools/lldb-dap/InstructionBreakpoint.cpp new file mode 100644 index 00000000000000..d4d8db5c51ec15 --- /dev/null +++ b/lldb/tools/lldb-dap/InstructionBreakpoint.cpp @@ -0,0 +1,27 @@ +//===-- InstructionBreakpoint.cpp ------------------------------------- C++ +//--===// +// +// 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 "InstructionBreakpoint.h" +#include "DAP.h" + +namespace lldb_dap { + +// Instruction Breakpoint +InstructionBreakpoint::InstructionBreakpoint(const llvm::json::Object &obj)

+#define LLDB_TOOLS_LLDB_DAP_INSTRUCTIONBREAKPOINT_H + +#include "Breakpoint.h" +#include "llvm/ADT/StringRef.h" + +namespace lldb_dap { + +// Instruction Breakpoint +struct InstructionBreakpoint : public Breakpoint { +

diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp index a8b85f55939e17..5a2a6c044ea4b1 100644 --- a/lldb/tools/lldb-dap/JSONUtils.cpp +++ b/lldb/tools/lldb-dap/JSONUtils.cpp @@ -766,6 +766,102 @@ llvm::json::Value CreateStackFrame(lldb::SBFrame &frame) { return llvm::json::Value(std::move(object)); }

+// Response to setInstructionBreakpoints request. +// "Breakpoint": { +// "type": "object", +// "description": "Response to setInstructionBreakpoints request.", +// "properties": { +// "id": { +// "type": "number", +// "description": "The identifier for the breakpoint. It is needed if +// breakpoint events are used to update or remove breakpoints." +// }, +// "verified": { +// "type": "boolean", +// "description": "If true, the breakpoint could be set (but not +// necessarily at the desired location." +// }, +// "message": { +// "type": "string", +// "description": "A message about the state of the breakpoint. +// This is shown to the user and can be used to explain why a breakpoint +// could not be verified." +// }, +// "source": { +// "type": "Source", +// "description": "The source where the breakpoint is located." +// }, +// "line": { +// "type": "number", +// "description": "The start line of the actual range covered by the +// breakpoint." +// }, +// "column": { +// "type": "number", +// "description": "The start column of the actual range covered by the +// breakpoint." +// }, +// "endLine": { +// "type": "number", +// "description": "The end line of the actual range covered by the +// breakpoint." +// }, +// "endColumn": { +// "type": "number", +// "description": "The end column of the actual range covered by the +// breakpoint. If no end line is given, then the end column is assumed to +// be in the start line." +// }, +// "instructionReference": { +// "type": "string", +// "description": "A memory reference to where the breakpoint is set." +// }, +// "offset": { +// "type": "number", +// "description": "The offset from the instruction reference. +// This can be negative." +// }, +// }, +// "required": [ "id", "verified", "line"] +// } +llvm::json::Value CreateInstructionBreakpoint(lldb::SBBreakpoint &bp) {

+/// Create a "instruction" object for a LLDB disassemble object as described in +/// the Visual Studio Code debug adaptor definition. +/// +/// \param[in] bp +/// The LLDB instruction object used to populate the disassembly +/// instruction. +/// \return +/// A "Scope" JSON object with that follows the formal JSON +/// definition outlined by Microsoft. +llvm::json::Value CreateInstructionBreakpoint(lldb::SBBreakpoint &bp); + /// Create a "Thread" object for a LLDB thread object. /// /// This function will fill in the following keys in the returned diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp index f50a6c17310739..7c0799eaadfb6d 100644 --- a/lldb/tools/lldb-dap/lldb-dap.cpp +++ b/lldb/tools/lldb-dap/lldb-dap.cpp @@ -1718,6 +1718,8 @@ void request_initialize(const llvm::json::Object &request) { body.try_emplace("supportsLogPoints", true); // The debug adapter supports data watchpoints. body.try_emplace("supportsDataBreakpoints", true);

@@ -4046,6 +4048,181 @@ void request__testGetTargetBreakpoints(const llvm::json::Object &request) { g_dap.SendJSON(llvm::json::Value(std::move(response))); }

+// SetInstructionBreakpoints request; value of command field is +// 'setInstructionBreakpoints'. Replaces all existing instruction breakpoints. +// Typically, instruction breakpoints would be set from a disassembly window. To +// clear all instruction breakpoints, specify an empty array. When an +// instruction breakpoint is hit, a stopped event (with reason instruction +// breakpoint) is generated. Clients should only call this request if the +// corresponding capability supportsInstructionBreakpoints is true. interface +// SetInstructionBreakpointsRequest extends Request { +// command: 'setInstructionBreakpoints'; +// arguments: SetInstructionBreakpointsArguments; +// } +// interface SetInstructionBreakpointsArguments { +// The instruction references of the breakpoints +// breakpoints: InstructionBreakpoint[]; +// } +// "InstructionBreakpoint ": { +// "type": "object", +// "description": "Properties of a breakpoint passed to the +// setInstructionBreakpoints request.", "properties": { +// "instructionReference": { +// "type": "string", +// "description": "The instruction reference of the breakpoint. +// This should be a memory or instruction pointer reference from an +// EvaluateResponse, Variable, StackFrame, GotoTarget, or Breakpoint." +// }, +// "offset": { +// "type": "number", +// "description": "The offset from the instruction reference. +// This can be negative." +// }, +// "condition": { +// "type": "string", +// "description": "An expression for conditional breakpoints. +// It is only honored by a debug adapter if the corresponding capability +// supportsConditionalBreakpointsis true." +// }, +// "hitCondition": { +// "type": "string", +// "description": "An expression that controls how many hits of the +// breakpoint are ignored. The debug adapter is expected to interpret the +// expression as needed. The attribute is only honored by a debug adapter +// if the corresponding capabilitysupportsHitConditionalBreakpointsis +// true." +// }, +// } +// interface SetInstructionBreakpointsResponse extends Response { +// body: { +// Information about the breakpoints. The array elements correspond to the +// elements of thebreakpointsarray. +// breakpoints: Breakpoint[]; +// }; +// } +// Response tosetInstructionBreakpointsrequest. +// "Breakpoint": { +// "type": "object", +// "description": "Response tosetInstructionBreakpoints` request.", +// "properties": { +// "id": { +// "type": "number", +// "description": "The identifier for the breakpoint. It is needed if +// breakpoint events are used to update or remove breakpoints." +// }, +// "verified": { +// "type": "boolean", +// "description": "If true, the breakpoint could be set (but not +// necessarily at the desired location." +// }, +// "message": { +// "type": "string", +// "description": "A message about the state of the breakpoint. +// This is shown to the user and can be used to explain why a breakpoint +// could not be verified." +// }, +// "source": { +// "type": "Source", +// "description": "The source where the breakpoint is located." +// }, +// "line": { +// "type": "number", +// "description": "The start line of the actual range covered by the +// breakpoint." +// }, +// "column": { +// "type": "number", +// "description": "The start column of the actual range covered by the +// breakpoint." +// }, +// "endLine": { +// "type": "number", +// "description": "The end line of the actual range covered by the +// breakpoint." +// }, +// "endColumn": { +// "type": "number", +// "description": "The end column of the actual range covered by the +// breakpoint. If no end line is given, then the end column is assumed to +// be in the start line." +// }, +// "instructionReference": { +// "type": "string", +// "description": "A memory reference to where the breakpoint is set." +// }, +// "offset": { +// "type": "number", +// "description": "The offset from the instruction reference. +// This can be negative." +// }, +// }, +// "required": [ "id", "verified", "line"] +// } +void request_setInstructionBreakpoints(const llvm::json::Object &request) {