[lldb-dap] Enabling instruction breakpoint support to lldb-dap. (#105278)
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:  After issuing continue:  --------- Co-authored-by: Santhosh Kumar Ellendula <sellendu@hu-sellendu-hyd.qualcomm.com> Co-authored-by: Santhosh Kumar Ellendula <sellendu@hu-sellendu-lv.qualcomm.com>
This commit is contained in:
committed by
GitHub
parent
e1d2251290
commit
89c27d6b07
@@ -1099,6 +1099,20 @@ class DebugCommunication(object):
|
||||
self.send.close()
|
||||
# self.recv.close()
|
||||
|
||||
def request_setInstructionBreakpoints(self, memory_reference=[]):
|
||||
breakpoints = []
|
||||
for i in memory_reference:
|
||||
args_dict = {
|
||||
"instructionReference": i,
|
||||
}
|
||||
breakpoints.append(args_dict)
|
||||
args_dict = {"breakpoints": breakpoints}
|
||||
command_dict = {
|
||||
"command": "setInstructionBreakpoints",
|
||||
"type": "request",
|
||||
"arguments": args_dict,
|
||||
}
|
||||
return self.send_recv(command_dict)
|
||||
|
||||
class DebugAdaptorServer(DebugCommunication):
|
||||
def __init__(
|
||||
|
||||
@@ -81,7 +81,10 @@ class DAPTestCaseBase(TestBase):
|
||||
body = stopped_event["body"]
|
||||
if "reason" not in body:
|
||||
continue
|
||||
if body["reason"] != "breakpoint":
|
||||
if (
|
||||
body["reason"] != "breakpoint"
|
||||
and body["reason"] != "instruction breakpoint"
|
||||
):
|
||||
continue
|
||||
if "description" not in body:
|
||||
continue
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
CXX_SOURCES := main-copy.cpp
|
||||
CXXFLAGS_EXTRAS := -O0 -g
|
||||
include Makefile.rules
|
||||
|
||||
main-copy.cpp: main.cpp
|
||||
cp -f $< $@
|
||||
@@ -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):
|
||||
NO_DEBUG_INFO_TESTCASE = True
|
||||
|
||||
def setUp(self):
|
||||
lldbdap_testcase.DAPTestCaseBase.setUp(self)
|
||||
|
||||
self.main_basename = "main-copy.cpp"
|
||||
self.main_path = os.path.realpath(self.getBuildArtifact(self.main_basename))
|
||||
|
||||
def test_instruction_breakpoint(self):
|
||||
self.build()
|
||||
self.instruction_breakpoint_test()
|
||||
|
||||
def instruction_breakpoint_test(self):
|
||||
"""Sample test to ensure SBFrame::Disassemble produces SOME output"""
|
||||
# Create a target by the debugger.
|
||||
target = self.createTestTarget()
|
||||
|
||||
main_line = line_number("main.cpp", "breakpoint 1")
|
||||
|
||||
program = self.getBuildArtifact("a.out")
|
||||
self.build_and_launch(program)
|
||||
|
||||
# Set source breakpoint 1
|
||||
response = self.dap_server.request_setBreakpoints(self.main_path, [main_line])
|
||||
breakpoints = response["body"]["breakpoints"]
|
||||
self.assertEquals(len(breakpoints), 1)
|
||||
breakpoint = breakpoints[0]
|
||||
self.assertEqual(
|
||||
breakpoint["line"], main_line, "incorrect breakpoint source line"
|
||||
)
|
||||
self.assertTrue(breakpoint["verified"], "breakpoint is not verified")
|
||||
self.assertEqual(
|
||||
self.main_basename, breakpoint["source"]["name"], "incorrect source name"
|
||||
)
|
||||
self.assertEqual(
|
||||
self.main_path, breakpoint["source"]["path"], "incorrect source file path"
|
||||
)
|
||||
other_breakpoint_id = breakpoint["id"]
|
||||
|
||||
# Continue and then verifiy the breakpoint
|
||||
self.dap_server.request_continue()
|
||||
self.verify_breakpoint_hit([other_breakpoint_id])
|
||||
|
||||
# now we check the stack trace making sure that we got mapped source paths
|
||||
frames = self.dap_server.request_stackTrace()["body"]["stackFrames"]
|
||||
intstructionPointerReference = []
|
||||
setIntstructionBreakpoints = []
|
||||
intstructionPointerReference.append(frames[0]["instructionPointerReference"])
|
||||
self.assertEqual(
|
||||
frames[0]["source"]["name"], self.main_basename, "incorrect source name"
|
||||
)
|
||||
self.assertEqual(
|
||||
frames[0]["source"]["path"], self.main_path, "incorrect source file path"
|
||||
)
|
||||
|
||||
# Check disassembly view
|
||||
instruction = self.disassemble(frameIndex=0)
|
||||
self.assertEqual(
|
||||
instruction["address"],
|
||||
intstructionPointerReference[0],
|
||||
"current breakpoint reference is not in the disaasembly view",
|
||||
)
|
||||
|
||||
# Get next instruction address to set instruction breakpoint
|
||||
disassembled_instruction_list = self.dap_server.disassembled_instructions
|
||||
instruction_addr_list = list(disassembled_instruction_list.keys())
|
||||
index = instruction_addr_list.index(intstructionPointerReference[0])
|
||||
if len(instruction_addr_list) >= (index + 1):
|
||||
next_inst_addr = instruction_addr_list[index + 1]
|
||||
if len(next_inst_addr) > 2:
|
||||
setIntstructionBreakpoints.append(next_inst_addr)
|
||||
instruction_breakpoint_response = (
|
||||
self.dap_server.request_setInstructionBreakpoints(
|
||||
setIntstructionBreakpoints
|
||||
)
|
||||
)
|
||||
inst_breakpoints = instruction_breakpoint_response["body"][
|
||||
"breakpoints"
|
||||
]
|
||||
self.assertEqual(
|
||||
inst_breakpoints[0]["instructionReference"],
|
||||
next_inst_addr,
|
||||
"Instruction breakpoint has not been resolved or failed to relocate the instruction breakpoint",
|
||||
)
|
||||
self.dap_server.request_continue()
|
||||
self.verify_breakpoint_hit([inst_breakpoints[0]["id"]])
|
||||
18
lldb/test/API/tools/lldb-dap/instruction-breakpoint/main.cpp
Normal file
18
lldb/test/API/tools/lldb-dap/instruction-breakpoint/main.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int function(int x) {
|
||||
|
||||
if (x == 0) // breakpoint 1
|
||||
return x;
|
||||
|
||||
if ((x % 2) != 0)
|
||||
return x;
|
||||
else
|
||||
return function(x - 1) + x;
|
||||
}
|
||||
|
||||
int main(int argc, char const *argv[]) {
|
||||
int n = function(2);
|
||||
return n;
|
||||
}
|
||||
@@ -38,6 +38,7 @@ add_lldb_tool(lldb-dap
|
||||
SourceBreakpoint.cpp
|
||||
DAP.cpp
|
||||
Watchpoint.cpp
|
||||
InstructionBreakpoint.cpp
|
||||
|
||||
LINK_LIBS
|
||||
liblldb
|
||||
|
||||
@@ -68,7 +68,7 @@ static std::string capitalize(llvm::StringRef str) {
|
||||
|
||||
void DAP::PopulateExceptionBreakpoints() {
|
||||
llvm::call_once(init_exception_breakpoints_flag, [this]() {
|
||||
exception_breakpoints = std::vector<ExceptionBreakpoint> {};
|
||||
exception_breakpoints = std::vector<ExceptionBreakpoint>{};
|
||||
|
||||
if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeC_plus_plus)) {
|
||||
exception_breakpoints->emplace_back("cpp_catch", "C++ Catch",
|
||||
@@ -996,4 +996,32 @@ void DAP::SetThreadFormat(llvm::StringRef format) {
|
||||
}
|
||||
}
|
||||
|
||||
InstructionBreakpoint *
|
||||
DAP::GetInstructionBreakpoint(const lldb::break_id_t bp_id) {
|
||||
for (auto &bp : instruction_breakpoints) {
|
||||
if (bp.second.id == bp_id)
|
||||
return &bp.second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
InstructionBreakpoint *
|
||||
DAP::GetInstructionBPFromStopReason(lldb::SBThread &thread) {
|
||||
const auto num = thread.GetStopReasonDataCount();
|
||||
InstructionBreakpoint *inst_bp = nullptr;
|
||||
for (size_t i = 0; i < num; i += 2) {
|
||||
// thread.GetStopReasonDataAtIndex(i) will return the bp ID and
|
||||
// thread.GetStopReasonDataAtIndex(i+1) will return the location
|
||||
// within that breakpoint. We only care about the bp ID so we can
|
||||
// see if this is an instruction breakpoint that is getting hit.
|
||||
lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(i);
|
||||
inst_bp = GetInstructionBreakpoint(bp_id);
|
||||
// If any breakpoint is not an instruction breakpoint, then stop and
|
||||
// report this as a normal breakpoint
|
||||
if (inst_bp == nullptr)
|
||||
return nullptr;
|
||||
}
|
||||
return inst_bp;
|
||||
}
|
||||
|
||||
} // namespace lldb_dap
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
#include "ExceptionBreakpoint.h"
|
||||
#include "FunctionBreakpoint.h"
|
||||
#include "IOStream.h"
|
||||
#include "InstructionBreakpoint.h"
|
||||
#include "ProgressEvent.h"
|
||||
#include "RunInTerminal.h"
|
||||
#include "SourceBreakpoint.h"
|
||||
@@ -68,6 +69,8 @@ namespace lldb_dap {
|
||||
|
||||
typedef llvm::DenseMap<uint32_t, SourceBreakpoint> SourceBreakpointMap;
|
||||
typedef llvm::StringMap<FunctionBreakpoint> FunctionBreakpointMap;
|
||||
typedef llvm::DenseMap<lldb::addr_t, InstructionBreakpoint>
|
||||
InstructionBreakpointMap;
|
||||
|
||||
enum class OutputType { Console, Stdout, Stderr, Telemetry };
|
||||
|
||||
@@ -160,6 +163,7 @@ struct DAP {
|
||||
std::unique_ptr<std::ofstream> log;
|
||||
llvm::StringMap<SourceBreakpointMap> source_breakpoints;
|
||||
FunctionBreakpointMap function_breakpoints;
|
||||
InstructionBreakpointMap instruction_breakpoints;
|
||||
std::optional<std::vector<ExceptionBreakpoint>> exception_breakpoints;
|
||||
llvm::once_flag init_exception_breakpoints_flag;
|
||||
std::vector<std::string> pre_init_commands;
|
||||
@@ -334,6 +338,10 @@ struct DAP {
|
||||
|
||||
void SetThreadFormat(llvm::StringRef format);
|
||||
|
||||
InstructionBreakpoint *GetInstructionBreakpoint(const lldb::break_id_t bp_id);
|
||||
|
||||
InstructionBreakpoint *GetInstructionBPFromStopReason(lldb::SBThread &thread);
|
||||
|
||||
private:
|
||||
// Send the JSON in "json_str" to the "out" stream. Correctly send the
|
||||
// "Content-Length:" field followed by the length, followed by the raw
|
||||
|
||||
@@ -15,6 +15,7 @@ struct ExceptionBreakpoint;
|
||||
struct FunctionBreakpoint;
|
||||
struct SourceBreakpoint;
|
||||
struct Watchpoint;
|
||||
struct InstructionBreakpoint;
|
||||
} // namespace lldb_dap
|
||||
|
||||
namespace lldb {
|
||||
|
||||
28
lldb/tools/lldb-dap/InstructionBreakpoint.cpp
Normal file
28
lldb/tools/lldb-dap/InstructionBreakpoint.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
//===-- 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)
|
||||
: Breakpoint(obj), instructionAddressReference(LLDB_INVALID_ADDRESS), id(0),
|
||||
offset(GetSigned(obj, "offset", 0)) {
|
||||
GetString(obj, "instructionReference")
|
||||
.getAsInteger(0, instructionAddressReference);
|
||||
instructionAddressReference += offset;
|
||||
}
|
||||
|
||||
void InstructionBreakpoint::SetInstructionBreakpoint() {
|
||||
bp = g_dap.target.BreakpointCreateByAddress(instructionAddressReference);
|
||||
id = bp.GetID();
|
||||
}
|
||||
} // namespace lldb_dap
|
||||
36
lldb/tools/lldb-dap/InstructionBreakpoint.h
Normal file
36
lldb/tools/lldb-dap/InstructionBreakpoint.h
Normal file
@@ -0,0 +1,36 @@
|
||||
//===-- InstructionBreakpoint.h --------------------------------------*- 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLDB_TOOLS_LLDB_DAP_INSTRUCTIONBREAKPOINT_H
|
||||
#define LLDB_TOOLS_LLDB_DAP_INSTRUCTIONBREAKPOINT_H
|
||||
|
||||
#include "Breakpoint.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
|
||||
namespace lldb_dap {
|
||||
|
||||
// Instruction Breakpoint
|
||||
struct InstructionBreakpoint : public Breakpoint {
|
||||
|
||||
lldb::addr_t instructionAddressReference;
|
||||
int32_t id;
|
||||
int32_t offset;
|
||||
|
||||
InstructionBreakpoint()
|
||||
: Breakpoint(), instructionAddressReference(LLDB_INVALID_ADDRESS), id(0),
|
||||
offset(0) {}
|
||||
InstructionBreakpoint(const llvm::json::Object &obj);
|
||||
|
||||
// Set instruction breakpoint in LLDB as a new breakpoint
|
||||
void SetInstructionBreakpoint();
|
||||
};
|
||||
|
||||
} // namespace lldb_dap
|
||||
|
||||
#endif
|
||||
@@ -769,6 +769,70 @@ 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(BreakpointBase *ibp) {
|
||||
llvm::json::Object object;
|
||||
ibp->CreateJsonObject(object);
|
||||
return llvm::json::Value(std::move(object));
|
||||
}
|
||||
|
||||
// "Thread": {
|
||||
// "type": "object",
|
||||
// "description": "A Thread",
|
||||
@@ -893,7 +957,13 @@ llvm::json::Value CreateThreadStopped(lldb::SBThread &thread,
|
||||
body.try_emplace("reason", "exception");
|
||||
EmplaceSafeString(body, "description", exc_bp->label);
|
||||
} else {
|
||||
body.try_emplace("reason", "breakpoint");
|
||||
InstructionBreakpoint *inst_bp =
|
||||
g_dap.GetInstructionBPFromStopReason(thread);
|
||||
if (inst_bp) {
|
||||
body.try_emplace("reason", "instruction breakpoint");
|
||||
} else {
|
||||
body.try_emplace("reason", "breakpoint");
|
||||
}
|
||||
lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(0);
|
||||
lldb::break_id_t bp_loc_id = thread.GetStopReasonDataAtIndex(1);
|
||||
std::string desc_str =
|
||||
|
||||
@@ -322,6 +322,17 @@ llvm::json::Value CreateSource(llvm::StringRef source_path);
|
||||
/// definition outlined by Microsoft.
|
||||
llvm::json::Value CreateStackFrame(lldb::SBFrame &frame);
|
||||
|
||||
/// 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(BreakpointBase *ibp);
|
||||
|
||||
/// Create a "Thread" object for a LLDB thread object.
|
||||
///
|
||||
/// This function will fill in the following keys in the returned
|
||||
|
||||
@@ -1723,6 +1723,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);
|
||||
// The debug adapter support for instruction breakpoint.
|
||||
body.try_emplace("supportsInstructionBreakpoints", true);
|
||||
|
||||
// Put in non-DAP specification lldb specific information.
|
||||
llvm::json::Object lldb_json;
|
||||
@@ -4082,6 +4084,254 @@ void request__testGetTargetBreakpoints(const llvm::json::Object &request) {
|
||||
g_dap.SendJSON(llvm::json::Value(std::move(response)));
|
||||
}
|
||||
|
||||
// "SetInstructionBreakpointsRequest" : {
|
||||
// "allOf" : [
|
||||
// {"$ref" : "#/definitions/Request"}, {
|
||||
// "type" : "object",
|
||||
// "description" :
|
||||
// "Replaces all existing instruction breakpoints. Typically, "
|
||||
// "instruction breakpoints would be set from a disassembly window. "
|
||||
// "\nTo clear all instruction breakpoints, specify an empty "
|
||||
// "array.\nWhen an instruction breakpoint is hit, a `stopped` event "
|
||||
// "(with reason `instruction breakpoint`) is generated.\nClients "
|
||||
// "should only call this request if the corresponding capability "
|
||||
// "`supportsInstructionBreakpoints` is true.",
|
||||
// "properties" : {
|
||||
// "command" : {"type" : "string", "enum" :
|
||||
// ["setInstructionBreakpoints"]}, "arguments" :
|
||||
// {"$ref" : "#/definitions/SetInstructionBreakpointsArguments"}
|
||||
// },
|
||||
// "required" : [ "command", "arguments" ]
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// "SetInstructionBreakpointsArguments"
|
||||
// : {
|
||||
// "type" : "object",
|
||||
// "description" : "Arguments for `setInstructionBreakpoints` request",
|
||||
// "properties" : {
|
||||
// "breakpoints" : {
|
||||
// "type" : "array",
|
||||
// "items" : {"$ref" : "#/definitions/InstructionBreakpoint"},
|
||||
// "description" : "The instruction references of the breakpoints"
|
||||
// }
|
||||
// },
|
||||
// "required" : ["breakpoints"]
|
||||
// },
|
||||
// "SetInstructionBreakpointsResponse"
|
||||
// : {
|
||||
// "allOf" : [
|
||||
// {"$ref" : "#/definitions/Response"}, {
|
||||
// "type" : "object",
|
||||
// "description" : "Response to `setInstructionBreakpoints` request",
|
||||
// "properties" : {
|
||||
// "body" : {
|
||||
// "type" : "object",
|
||||
// "properties" : {
|
||||
// "breakpoints" : {
|
||||
// "type" : "array",
|
||||
// "items" : {"$ref" : "#/definitions/Breakpoint"},
|
||||
// "description" :
|
||||
// "Information about the breakpoints. The array elements
|
||||
// " "correspond to the elements of the `breakpoints`
|
||||
// array."
|
||||
// }
|
||||
// },
|
||||
// "required" : ["breakpoints"]
|
||||
// }
|
||||
// },
|
||||
// "required" : ["body"]
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// "InstructionBreakpoint" : {
|
||||
// "type" : "object",
|
||||
// "description" : "Properties of a breakpoint passed to the "
|
||||
// "`setInstructionBreakpoints` request",
|
||||
// "properties" : {
|
||||
// "instructionReference" : {
|
||||
// "type" : "string",
|
||||
// "description" :
|
||||
// "The instruction reference of the breakpoint.\nThis should be a "
|
||||
// "memory or instruction pointer reference from an
|
||||
// `EvaluateResponse`, "
|
||||
// "`Variable`, `StackFrame`, `GotoTarget`, or `Breakpoint`."
|
||||
// },
|
||||
// "offset" : {
|
||||
// "type" : "integer",
|
||||
// "description" : "The offset from the instruction reference in "
|
||||
// "bytes.\nThis can be negative."
|
||||
// },
|
||||
// "condition" : {
|
||||
// "type" : "string",
|
||||
// "description" : "An expression for conditional breakpoints.\nIt is only
|
||||
// "
|
||||
// "honored by a debug adapter if the corresponding "
|
||||
// "capability `supportsConditionalBreakpoints` is true."
|
||||
// },
|
||||
// "hitCondition" : {
|
||||
// "type" : "string",
|
||||
// "description" : "An expression that controls how many hits of the "
|
||||
// "breakpoint are ignored.\nThe debug adapter is expected
|
||||
// " "to interpret the expression as needed.\nThe
|
||||
// attribute " "is only honored by a debug adapter if the
|
||||
// corresponding " "capability
|
||||
// `supportsHitConditionalBreakpoints` is true."
|
||||
// },
|
||||
// "mode" : {
|
||||
// "type" : "string",
|
||||
// "description" : "The mode of this breakpoint. If defined, this must be
|
||||
// "
|
||||
// "one of the `breakpointModes` the debug adapter "
|
||||
// "advertised in its `Capabilities`."
|
||||
// }
|
||||
// },
|
||||
// "required" : ["instructionReference"]
|
||||
// },
|
||||
// "Breakpoint"
|
||||
// : {
|
||||
// "type" : "object",
|
||||
// "description" :
|
||||
// "Information about a breakpoint created in `setBreakpoints`, "
|
||||
// "`setFunctionBreakpoints`, `setInstructionBreakpoints`, or "
|
||||
// "`setDataBreakpoints` requests.",
|
||||
// "properties" : {
|
||||
// "id" : {
|
||||
// "type" : "integer",
|
||||
// "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.\nThis
|
||||
// "
|
||||
// "is shown to the user and can be used to explain
|
||||
// why " "a breakpoint could not be verified."
|
||||
// },
|
||||
// "source" : {
|
||||
// "$ref" : "#/definitions/Source",
|
||||
// "description" : "The source where the breakpoint is located."
|
||||
// },
|
||||
// "line" : {
|
||||
// "type" : "integer",
|
||||
// "description" :
|
||||
// "The start line of the actual range covered by the breakpoint."
|
||||
// },
|
||||
// "column" : {
|
||||
// "type" : "integer",
|
||||
// "description" :
|
||||
// "Start position of the source range covered by the breakpoint.
|
||||
// " "It is measured in UTF-16 code units and the client
|
||||
// capability "
|
||||
// "`columnsStartAt1` determines whether it is 0- or 1-based."
|
||||
// },
|
||||
// "endLine" : {
|
||||
// "type" : "integer",
|
||||
// "description" :
|
||||
// "The end line of the actual range covered by the breakpoint."
|
||||
// },
|
||||
// "endColumn" : {
|
||||
// "type" : "integer",
|
||||
// "description" :
|
||||
// "End position of the source range covered by the breakpoint. It
|
||||
// " "is measured in UTF-16 code units and the client capability "
|
||||
// "`columnsStartAt1` determines whether it is 0- or 1-based.\nIf
|
||||
// " "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" : "integer",
|
||||
// "description" : "The offset from the instruction reference.\nThis "
|
||||
// "can be negative."
|
||||
// },
|
||||
// "reason" : {
|
||||
// "type" : "string",
|
||||
// "description" :
|
||||
// "A machine-readable explanation of why a breakpoint may not be
|
||||
// " "verified. If a breakpoint is verified or a specific reason
|
||||
// is " "not known, the adapter should omit this property.
|
||||
// Possible " "values include:\n\n- `pending`: Indicates a
|
||||
// breakpoint might be " "verified in the future, but the adapter
|
||||
// cannot verify it in the " "current state.\n - `failed`:
|
||||
// Indicates a breakpoint was not " "able to be verified, and the
|
||||
// adapter does not believe it can be " "verified without
|
||||
// intervention.",
|
||||
// "enum" : [ "pending", "failed" ]
|
||||
// }
|
||||
// },
|
||||
// "required" : ["verified"]
|
||||
// },
|
||||
|
||||
void request_setInstructionBreakpoints(const llvm::json::Object &request) {
|
||||
llvm::json::Object response;
|
||||
llvm::json::Array response_breakpoints;
|
||||
llvm::json::Object body;
|
||||
FillResponse(request, response);
|
||||
|
||||
auto arguments = request.getObject("arguments");
|
||||
auto breakpoints = arguments->getArray("breakpoints");
|
||||
|
||||
// It holds active instruction breakpoint list received from DAP.
|
||||
InstructionBreakpointMap request_ibp;
|
||||
if (breakpoints) {
|
||||
for (const auto &bp : *breakpoints) {
|
||||
auto bp_obj = bp.getAsObject();
|
||||
if (bp_obj) {
|
||||
// Read instruction breakpoint request.
|
||||
InstructionBreakpoint inst_bp(*bp_obj);
|
||||
// Store them into map for reference.
|
||||
request_ibp[inst_bp.instructionAddressReference] = std::move(inst_bp);
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate previous active instruction breakpoint list.
|
||||
for (auto &prev_ibp : g_dap.instruction_breakpoints) {
|
||||
// Find previous instruction breakpoint reference address in newly
|
||||
// received instruction breakpoint list.
|
||||
auto inst_reference = request_ibp.find(prev_ibp.first);
|
||||
// Request for remove and delete the breakpoint, if the prev instruction
|
||||
// breakpoint ID is not available in active instrcation breakpoint list.
|
||||
// Means delete removed breakpoint instance.
|
||||
if (inst_reference == request_ibp.end()) {
|
||||
g_dap.target.BreakpointDelete(prev_ibp.second.id);
|
||||
// Update Prev instruction breakpoint list.
|
||||
g_dap.instruction_breakpoints.erase(prev_ibp.first);
|
||||
} else {
|
||||
// Instead of recreating breakpoint instance, update the breakpoint if
|
||||
// there are any conditional changes.
|
||||
prev_ibp.second.UpdateBreakpoint(inst_reference->second);
|
||||
request_ibp.erase(inst_reference);
|
||||
response_breakpoints.emplace_back(
|
||||
CreateInstructionBreakpoint(&prev_ibp.second));
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &req_bpi : request_ibp) {
|
||||
// Add this breakpoint info to the response
|
||||
g_dap.instruction_breakpoints[req_bpi.first] = std::move(req_bpi.second);
|
||||
InstructionBreakpoint &new_bp =
|
||||
g_dap.instruction_breakpoints[req_bpi.first];
|
||||
new_bp.SetInstructionBreakpoint();
|
||||
response_breakpoints.emplace_back(CreateInstructionBreakpoint(&new_bp));
|
||||
}
|
||||
}
|
||||
|
||||
body.try_emplace("breakpoints", std::move(response_breakpoints));
|
||||
response.try_emplace("body", std::move(body));
|
||||
g_dap.SendJSON(llvm::json::Value(std::move(response)));
|
||||
}
|
||||
|
||||
void RegisterRequestCallbacks() {
|
||||
g_dap.RegisterRequestCallback("attach", request_attach);
|
||||
g_dap.RegisterRequestCallback("completions", request_completions);
|
||||
@@ -4114,6 +4364,9 @@ void RegisterRequestCallbacks() {
|
||||
g_dap.RegisterRequestCallback("threads", request_threads);
|
||||
g_dap.RegisterRequestCallback("variables", request_variables);
|
||||
g_dap.RegisterRequestCallback("disassemble", request_disassemble);
|
||||
// Instruction breakpoint request
|
||||
g_dap.RegisterRequestCallback("setInstructionBreakpoints",
|
||||
request_setInstructionBreakpoints);
|
||||
// Custom requests
|
||||
g_dap.RegisterRequestCallback("compileUnits", request_compileUnits);
|
||||
g_dap.RegisterRequestCallback("modules", request_modules);
|
||||
|
||||
Reference in New Issue
Block a user