[lldb-vscode] Adding support for the "disassemble" request.

Instead of creating psuedo source files for each stack frame this change adopts the new DAP “disassemble” request, allowing clients to inspect assembly instructions of files with debug info in addition to files without debug info.

[[ https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Disassemble | spec ]]

See attached screenshot of the disassembly view. {F28473848}

Reviewed By: wallace

Differential Revision: https://reviews.llvm.org/D156493
This commit is contained in:
John Harrison 2023-07-31 12:44:06 -04:00 committed by David Goldman
parent bae14bcd1d
commit ca71dc1b54
13 changed files with 354 additions and 193 deletions

View File

@ -257,6 +257,19 @@ class VSCodeTestCaseBase(TestBase):
"exitCode == %i" % (exitCode),
)
def disassemble(self, threadId=None, frameIndex=None):
stackFrames = self.get_stackFrames(
threadId=threadId, startFrame=frameIndex, levels=1
)
self.assertIsNotNone(stackFrames)
memoryReference = stackFrames[0]["instructionPointerReference"]
self.assertIsNotNone(memoryReference)
if memoryReference not in self.vscode.disassembled_instructions:
self.vscode.request_disassemble(memoryReference=memoryReference)
return self.vscode.disassembled_instructions[memoryReference]
def attach(
self,
program=None,

View File

@ -135,6 +135,7 @@ class DebugCommunication(object):
self.configuration_done_sent = False
self.frame_scopes = {}
self.init_commands = init_commands
self.disassembled_instructions = {}
@classmethod
def encode_content(cls, s):
@ -427,7 +428,7 @@ class DebugCommunication(object):
def get_stackFrame(self, frameIndex=0, threadId=None):
"""Get a single "StackFrame" object from a "stackTrace" request and
return the "StackFrame as a python dictionary, or None on failure
return the "StackFrame" as a python dictionary, or None on failure
"""
if threadId is None:
threadId = self.get_thread_id()
@ -647,6 +648,22 @@ class DebugCommunication(object):
"arguments": args_dict,
}
return self.send_recv(command_dict)
def request_disassemble(self, memoryReference, offset=-50, instructionCount=200, resolveSymbols=True):
args_dict = {
"memoryReference": memoryReference,
"offset": offset,
"instructionCount": instructionCount,
"resolveSymbols": resolveSymbols
}
command_dict = {
"command": "disassemble",
"type": "request",
"arguments": args_dict,
}
instructions = self.send_recv(command_dict)["body"]["instructions"]
for inst in instructions:
self.disassembled_instructions[inst["address"]] = inst
def request_evaluate(self, expression, frameIndex=0, threadId=None, context=None):
stackFrame = self.get_stackFrame(frameIndex=frameIndex, threadId=threadId)

View File

@ -25,25 +25,25 @@ class TestVSCode_coreFile(lldbvscode_testcase.VSCodeTestCaseBase):
expected_frames = [
{
"column": 0,
"id": 524288,
"line": 4,
"name": "bar",
"source": {"name": "main.c", "path": "/home/labath/test/main.c"},
"instructionPointerReference": "0x40011C",
},
{
"column": 0,
"id": 524289,
"line": 10,
"name": "foo",
"source": {"name": "main.c", "path": "/home/labath/test/main.c"},
"instructionPointerReference": "0x400142",
},
{
"column": 0,
"id": 524290,
"line": 16,
"name": "_start",
"source": {"name": "main.c", "path": "/home/labath/test/main.c"},
"instructionPointerReference": "0x40015F",
},
]

View File

@ -0,0 +1,3 @@
C_SOURCES := main.c
include Makefile.rules

View File

@ -0,0 +1,41 @@
"""
Test lldb-vscode disassemble request
"""
import vscode
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
import lldbvscode_testcase
import os
class TestVSCode_disassemble(lldbvscode_testcase.VSCodeTestCaseBase):
@skipIfWindows
@skipIfRemote
def test_disassemble(self):
"""
Tests the 'disassemble' request.
"""
program = self.getBuildArtifact("a.out")
self.build_and_launch(program)
source = "main.c"
self.source_path = os.path.join(os.getcwd(), source)
self.set_source_breakpoints(
source,
[
line_number(source, "// breakpoint 1"),
],
)
self.continue_to_next_stop()
pc_assembly = self.disassemble(frameIndex=0)
self.assertTrue("location" in pc_assembly, "Source location missing.")
self.assertTrue("instruction" in pc_assembly, "Assembly instruction missing.")
# The calling frame (qsort) is coming from a system library, as a result
# we should not have a source location.
qsort_assembly = self.disassemble(frameIndex=1)
self.assertFalse("location" in qsort_assembly, "Source location not expected.")
self.assertTrue("instruction" in pc_assembly, "Assembly instruction missing.")

View File

@ -0,0 +1,30 @@
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
int compare_ints(const void* a, const void* b)
{
int arg1 = *(const int*)a;
int arg2 = *(const int*)b;
// breakpoint 1
if (arg1 < arg2) return -1;
if (arg1 > arg2) return 1;
return 0;
}
int main(void)
{
int ints[] = { -2, 99, 0, -743, 2, INT_MIN, 4 };
int size = sizeof ints / sizeof *ints;
qsort(ints, size, sizeof(int), compare_ints);
for (int i = 0; i < size; i++) {
printf("%d ", ints[i]);
}
printf("\n");
return 0;
}

View File

@ -342,6 +342,9 @@ llvm::json::Value CreateBreakpoint(lldb::SBBreakpoint &bp,
object.try_emplace("source", CreateSource(*request_path));
if (bp_addr.IsValid()) {
std::string formatted_addr =
"0x" + llvm::utohexstr(bp_addr.GetLoadAddress(g_vsc.target));
object.try_emplace("instructionReference", formatted_addr);
auto line_entry = bp_addr.GetLineEntry();
const auto line = line_entry.GetLine();
if (line != UINT32_MAX)
@ -600,8 +603,8 @@ llvm::json::Value CreateSource(lldb::SBLineEntry &line_entry) {
if (name)
EmplaceSafeString(object, "name", name);
char path[PATH_MAX] = "";
file.GetPath(path, sizeof(path));
if (path[0]) {
if (file.GetPath(path, sizeof(path)) &&
lldb::SBFileSpec::ResolvePath(path, path, PATH_MAX)) {
EmplaceSafeString(object, "path", std::string(path));
}
}
@ -616,97 +619,14 @@ llvm::json::Value CreateSource(llvm::StringRef source_path) {
return llvm::json::Value(std::move(source));
}
llvm::json::Value CreateSource(lldb::SBFrame &frame, int64_t &disasm_line) {
disasm_line = 0;
std::optional<llvm::json::Value> CreateSource(lldb::SBFrame &frame) {
auto line_entry = frame.GetLineEntry();
// A line entry of 0 indicates the line is compiler generated i.e. no source
// file so don't return early with the line entry.
// file is associated with the frame.
if (line_entry.GetFileSpec().IsValid() && line_entry.GetLine() != 0)
return CreateSource(line_entry);
llvm::json::Object object;
const auto pc = frame.GetPC();
lldb::SBInstructionList insts;
lldb::SBFunction function = frame.GetFunction();
lldb::addr_t low_pc = LLDB_INVALID_ADDRESS;
if (function.IsValid()) {
low_pc = function.GetStartAddress().GetLoadAddress(g_vsc.target);
auto addr_srcref = g_vsc.addr_to_source_ref.find(low_pc);
if (addr_srcref != g_vsc.addr_to_source_ref.end()) {
// We have this disassembly cached already, return the existing
// sourceReference
object.try_emplace("sourceReference", addr_srcref->second);
disasm_line = g_vsc.GetLineForPC(addr_srcref->second, pc);
} else {
insts = function.GetInstructions(g_vsc.target);
}
} else {
lldb::SBSymbol symbol = frame.GetSymbol();
if (symbol.IsValid()) {
low_pc = symbol.GetStartAddress().GetLoadAddress(g_vsc.target);
auto addr_srcref = g_vsc.addr_to_source_ref.find(low_pc);
if (addr_srcref != g_vsc.addr_to_source_ref.end()) {
// We have this disassembly cached already, return the existing
// sourceReference
object.try_emplace("sourceReference", addr_srcref->second);
disasm_line = g_vsc.GetLineForPC(addr_srcref->second, pc);
} else {
insts = symbol.GetInstructions(g_vsc.target);
}
}
}
const auto num_insts = insts.GetSize();
if (low_pc != LLDB_INVALID_ADDRESS && num_insts > 0) {
if (line_entry.GetLine() == 0) {
EmplaceSafeString(object, "name", "<compiler-generated>");
} else {
EmplaceSafeString(object, "name", frame.GetDisplayFunctionName());
}
SourceReference source;
llvm::raw_string_ostream src_strm(source.content);
std::string line;
for (size_t i = 0; i < num_insts; ++i) {
lldb::SBInstruction inst = insts.GetInstructionAtIndex(i);
const auto inst_addr = inst.GetAddress().GetLoadAddress(g_vsc.target);
const char *m = inst.GetMnemonic(g_vsc.target);
const char *o = inst.GetOperands(g_vsc.target);
const char *c = inst.GetComment(g_vsc.target);
if (pc == inst_addr)
disasm_line = i + 1;
const auto inst_offset = inst_addr - low_pc;
int spaces = 0;
if (inst_offset < 10)
spaces = 3;
else if (inst_offset < 100)
spaces = 2;
else if (inst_offset < 1000)
spaces = 1;
line.clear();
llvm::raw_string_ostream line_strm(line);
line_strm << llvm::formatv("{0:X+}: <{1}> {2} {3,12} {4}", inst_addr,
inst_offset, llvm::fmt_repeat(' ', spaces), m,
o);
// If there is a comment append it starting at column 60 or after one
// space past the last char
const uint32_t comment_row = std::max(line_strm.str().size(), (size_t)60);
if (c && c[0]) {
if (line.size() < comment_row)
line_strm.indent(comment_row - line_strm.str().size());
line_strm << " # " << c;
}
src_strm << line_strm.str() << "\n";
source.addr_to_line[inst_addr] = i + 1;
}
// Flush the source stream
src_strm.str();
auto sourceReference = VSCode::GetNextSourceReference();
g_vsc.source_map[sourceReference] = std::move(source);
g_vsc.addr_to_source_ref[low_pc] = sourceReference;
object.try_emplace("sourceReference", sourceReference);
}
return llvm::json::Value(std::move(object));
return {};
}
// "StackFrame": {
@ -748,6 +668,12 @@ llvm::json::Value CreateSource(lldb::SBFrame &frame, int64_t &disasm_line) {
// "description": "An optional end column of the range covered by the
// stack frame."
// },
// "instructionPointerReference": {
// "type": "string",
// "description": "A memory reference for the current instruction
// pointer
// in this frame."
// },
// "moduleId": {
// "type": ["integer", "string"],
// "description": "The module associated with this frame, if any."
@ -770,30 +696,37 @@ llvm::json::Value CreateStackFrame(lldb::SBFrame &frame) {
int64_t frame_id = MakeVSCodeFrameID(frame);
object.try_emplace("id", frame_id);
std::string frame_name;
const char *func_name = frame.GetFunctionName();
if (func_name)
frame_name = func_name;
else
std::string frame_name = frame.GetDisplayFunctionName();
if (frame_name.empty())
frame_name = "<unknown>";
bool is_optimized = frame.GetFunction().GetIsOptimized();
if (is_optimized)
frame_name += " [opt]";
EmplaceSafeString(object, "name", frame_name);
int64_t disasm_line = 0;
object.try_emplace("source", CreateSource(frame, disasm_line));
auto source = CreateSource(frame);
auto line_entry = frame.GetLineEntry();
if (disasm_line > 0) {
object.try_emplace("line", disasm_line);
} else {
if (source) {
object.try_emplace("source", *source);
auto line_entry = frame.GetLineEntry();
auto line = line_entry.GetLine();
if (line == UINT32_MAX)
line = 0;
object.try_emplace("line", line);
if (line && line != LLDB_INVALID_LINE_NUMBER)
object.try_emplace("line", line);
auto column = line_entry.GetColumn();
if (column && column != LLDB_INVALID_COLUMN_NUMBER)
object.try_emplace("column", column);
} else {
object.try_emplace("line", 0);
object.try_emplace("column", 0);
object.try_emplace("presentationHint", "subtle");
}
object.try_emplace("column", line_entry.GetColumn());
const auto pc = frame.GetPC();
if (pc != LLDB_INVALID_ADDRESS) {
std::string formatted_addr = "0x" + llvm::utohexstr(pc);
object.try_emplace("instructionPointerReference", formatted_addr);
}
return llvm::json::Value(std::move(object));
}

View File

@ -324,31 +324,6 @@ llvm::json::Value CreateSource(lldb::SBLineEntry &line_entry);
/// definition outlined by Microsoft.
llvm::json::Value CreateSource(llvm::StringRef source_path);
/// Create a "Source" object for a given frame.
///
/// When there is no source file information for a stack frame, we will
/// create disassembly for a function and store a permanent
/// "sourceReference" that contains the textual disassembly for a
/// function along with address to line information. The "Source" object
/// that is created will contain a "sourceReference" that the VSCode
/// protocol can later fetch as text in order to display disassembly.
/// The PC will be extracted from the frame and the disassembly line
/// within the source referred to by "sourceReference" will be filled
/// in.
///
/// \param[in] frame
/// The LLDB stack frame to use when populating out the "Source"
/// object.
///
/// \param[out] disasm_line
/// The line within the "sourceReference" file that the PC from
/// \a frame matches.
///
/// \return
/// A "Source" JSON object with that follows the formal JSON
/// definition outlined by Microsoft.
llvm::json::Value CreateSource(lldb::SBFrame &frame, int64_t &disasm_line);
/// Create a "StackFrame" object for a LLDB frame object.
///
/// This function will fill in the following keys in the returned

View File

@ -1,32 +0,0 @@
//===-- SourceReference.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_VSCODE_SOURCEREFERENCE_H
#define LLDB_TOOLS_LLDB_VSCODE_SOURCEREFERENCE_H
#include "lldb/lldb-types.h"
#include "llvm/ADT/DenseMap.h"
#include <string>
namespace lldb_vscode {
struct SourceReference {
std::string content;
llvm::DenseMap<lldb::addr_t, int64_t> addr_to_line;
int64_t GetLineForPC(lldb::addr_t pc) const {
auto addr_line = addr_to_line.find(pc);
if (addr_line != addr_to_line.end())
return addr_line->second;
return 0;
}
};
} // namespace lldb_vscode
#endif

View File

@ -64,13 +64,6 @@ VSCode::VSCode()
VSCode::~VSCode() = default;
int64_t VSCode::GetLineForPC(int64_t sourceReference, lldb::addr_t pc) const {
auto pos = source_map.find(sourceReference);
if (pos != source_map.end())
return pos->second.GetLineForPC(pc);
return 0;
}
ExceptionBreakpoint *VSCode::GetExceptionBreakpoint(const std::string &filter) {
for (auto &bp : exception_breakpoints) {
if (bp.filter == filter)
@ -341,11 +334,6 @@ VSCode::SendFormattedOutput(OutputType o, const char *format, ...) {
o, llvm::StringRef(buffer, std::min<int>(actual_length, sizeof(buffer))));
}
int64_t VSCode::GetNextSourceReference() {
static int64_t ref = 0;
return ++ref;
}
ExceptionBreakpoint *
VSCode::GetExceptionBPFromStopReason(lldb::SBThread &thread) {
const auto num = thread.GetStopReasonDataCount();

View File

@ -55,7 +55,6 @@
#include "ProgressEvent.h"
#include "RunInTerminal.h"
#include "SourceBreakpoint.h"
#include "SourceReference.h"
#define VARREF_LOCALS (int64_t)1
#define VARREF_GLOBALS (int64_t)2
@ -153,8 +152,6 @@ struct VSCode {
std::thread event_thread;
std::thread progress_event_thread;
std::unique_ptr<std::ofstream> log;
llvm::DenseMap<lldb::addr_t, int64_t> addr_to_source_ref;
llvm::DenseMap<int64_t, SourceReference> source_map;
llvm::StringMap<SourceBreakpointMap> source_breakpoints;
FunctionBreakpointMap function_breakpoints;
std::vector<ExceptionBreakpoint> exception_breakpoints;
@ -194,7 +191,6 @@ struct VSCode {
~VSCode();
VSCode(const VSCode &rhs) = delete;
void operator=(const VSCode &rhs) = delete;
int64_t GetLineForPC(int64_t sourceReference, lldb::addr_t pc) const;
ExceptionBreakpoint *GetExceptionBreakpoint(const std::string &filter);
ExceptionBreakpoint *GetExceptionBreakpoint(const lldb::break_id_t bp_id);

View File

@ -14,7 +14,6 @@ struct BreakpointBase;
struct ExceptionBreakpoint;
struct FunctionBreakpoint;
struct SourceBreakpoint;
struct SourceReference;
} // namespace lldb_vscode
namespace lldb {

View File

@ -50,6 +50,7 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/Option.h"
@ -1563,6 +1564,8 @@ void request_initialize(const llvm::json::Object &request) {
body.try_emplace("supportsStepInTargetsRequest", false);
// The debug adapter supports the completions request.
body.try_emplace("supportsCompletionsRequest", true);
// The debug adapter supports the disassembly request.
body.try_emplace("supportsDisassembleRequest", true);
llvm::json::Array completion_characters;
completion_characters.emplace_back(".");
@ -2588,18 +2591,7 @@ void request_setFunctionBreakpoints(const llvm::json::Object &request) {
void request_source(const llvm::json::Object &request) {
llvm::json::Object response;
FillResponse(request, response);
llvm::json::Object body;
auto arguments = request.getObject("arguments");
auto source = arguments->getObject("source");
auto sourceReference = GetSigned(source, "sourceReference", -1);
auto pos = g_vsc.source_map.find((lldb::addr_t)sourceReference);
if (pos != g_vsc.source_map.end()) {
EmplaceSafeString(body, "content", pos->second.content);
} else {
response["success"] = llvm::json::Value(false);
}
EmplaceSafeString(body, "mimeType", "text/x-lldb.disassembly");
llvm::json::Object body{{"content", ""}};
response.try_emplace("body", std::move(body));
g_vsc.SendJSON(llvm::json::Value(std::move(response)));
}
@ -3301,6 +3293,211 @@ void request_variables(const llvm::json::Object &request) {
g_vsc.SendJSON(llvm::json::Value(std::move(response)));
}
// "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 request_disassemble(const llvm::json::Object &request) {
llvm::json::Object response;
FillResponse(request, response);
auto arguments = request.getObject("arguments");
auto memoryReference = GetString(arguments, "memoryReference");
lldb::addr_t addr_ptr;
if (memoryReference.consumeInteger(0, addr_ptr)) {
response["success"] = false;
response["message"] =
"Malformed memory reference: " + memoryReference.str();
g_vsc.SendJSON(llvm::json::Value(std::move(response)));
return;
}
addr_ptr += GetSigned(arguments, "instructionOffset", 0);
lldb::SBAddress addr(addr_ptr, g_vsc.target);
if (!addr.IsValid()) {
response["success"] = false;
response["message"] = "Memory reference not found in the current binary.";
g_vsc.SendJSON(llvm::json::Value(std::move(response)));
return;
}
const auto inst_count = GetUnsigned(arguments, "instructionCount", 0);
lldb::SBInstructionList insts =
g_vsc.target.ReadInstructions(addr, inst_count);
if (!insts.IsValid()) {
response["success"] = false;
response["message"] = "Failed to find instructions for memory address.";
g_vsc.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(g_vsc.target);
const char *m = inst.GetMnemonic(g_vsc.target);
const char *o = inst.GetOperands(g_vsc.target);
const char *c = inst.GetComment(g_vsc.target);
auto d = inst.GetData(g_vsc.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);
}
}
sb.flush();
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;
}
si.flush();
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);
}
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);
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);
}
}
}
}
instructions.emplace_back(std::move(disassembled_inst));
}
llvm::json::Object body;
body.try_emplace("instructions", std::move(instructions));
response.try_emplace("body", std::move(body));
g_vsc.SendJSON(llvm::json::Value(std::move(response)));
}
// A request used in testing to get the details on all breakpoints that are
// currently set in the target. This helps us to test "setBreakpoints" and
// "setFunctionBreakpoints" requests to verify we have the correct set of
@ -3345,6 +3542,7 @@ void RegisterRequestCallbacks() {
g_vsc.RegisterRequestCallback("stepOut", request_stepOut);
g_vsc.RegisterRequestCallback("threads", request_threads);
g_vsc.RegisterRequestCallback("variables", request_variables);
g_vsc.RegisterRequestCallback("disassemble", request_disassemble);
// Custom requests
g_vsc.RegisterRequestCallback("compileUnits", request_compileUnits);
g_vsc.RegisterRequestCallback("modules", request_modules);