diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py @@ -257,6 +257,19 @@ "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, diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py @@ -135,6 +135,7 @@ 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 @@ 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 @@ "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) diff --git a/lldb/test/API/tools/lldb-vscode/coreFile/TestVSCode_coreFile.py b/lldb/test/API/tools/lldb-vscode/coreFile/TestVSCode_coreFile.py --- a/lldb/test/API/tools/lldb-vscode/coreFile/TestVSCode_coreFile.py +++ b/lldb/test/API/tools/lldb-vscode/coreFile/TestVSCode_coreFile.py @@ -25,25 +25,25 @@ 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", }, ] diff --git a/lldb/test/API/tools/lldb-vscode/disassemble/Makefile b/lldb/test/API/tools/lldb-vscode/disassemble/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/tools/lldb-vscode/disassemble/Makefile @@ -0,0 +1,3 @@ +C_SOURCES := main.c + +include Makefile.rules diff --git a/lldb/test/API/tools/lldb-vscode/disassemble/TestVSCode_disassemble.py b/lldb/test/API/tools/lldb-vscode/disassemble/TestVSCode_disassemble.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/tools/lldb-vscode/disassemble/TestVSCode_disassemble.py @@ -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.") diff --git a/lldb/test/API/tools/lldb-vscode/disassemble/main.c b/lldb/test/API/tools/lldb-vscode/disassemble/main.c new file mode 100644 --- /dev/null +++ b/lldb/test/API/tools/lldb-vscode/disassemble/main.c @@ -0,0 +1,30 @@ +#include +#include +#include + +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; +} \ No newline at end of file diff --git a/lldb/tools/lldb-vscode/JSONUtils.h b/lldb/tools/lldb-vscode/JSONUtils.h --- a/lldb/tools/lldb-vscode/JSONUtils.h +++ b/lldb/tools/lldb-vscode/JSONUtils.h @@ -324,31 +324,6 @@ /// 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 diff --git a/lldb/tools/lldb-vscode/JSONUtils.cpp b/lldb/tools/lldb-vscode/JSONUtils.cpp --- a/lldb/tools/lldb-vscode/JSONUtils.cpp +++ b/lldb/tools/lldb-vscode/JSONUtils.cpp @@ -342,6 +342,9 @@ 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 @@ 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 @@ return llvm::json::Value(std::move(source)); } -llvm::json::Value CreateSource(lldb::SBFrame &frame, int64_t &disasm_line) { - disasm_line = 0; +std::optional 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", ""); - } 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 @@ // "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 @@ 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 = ""; 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"); + } + + const auto pc = frame.GetPC(); + if (pc != LLDB_INVALID_ADDRESS) { + std::string formatted_addr = "0x" + llvm::utohexstr(pc); + object.try_emplace("instructionPointerReference", formatted_addr); } - object.try_emplace("column", line_entry.GetColumn()); + return llvm::json::Value(std::move(object)); } diff --git a/lldb/tools/lldb-vscode/SourceReference.h b/lldb/tools/lldb-vscode/SourceReference.h deleted file mode 100644 --- a/lldb/tools/lldb-vscode/SourceReference.h +++ /dev/null @@ -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 - -namespace lldb_vscode { - -struct SourceReference { - std::string content; - llvm::DenseMap 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 diff --git a/lldb/tools/lldb-vscode/VSCode.h b/lldb/tools/lldb-vscode/VSCode.h --- a/lldb/tools/lldb-vscode/VSCode.h +++ b/lldb/tools/lldb-vscode/VSCode.h @@ -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 @@ std::thread event_thread; std::thread progress_event_thread; std::unique_ptr log; - llvm::DenseMap addr_to_source_ref; - llvm::DenseMap source_map; llvm::StringMap source_breakpoints; FunctionBreakpointMap function_breakpoints; std::vector exception_breakpoints; @@ -194,7 +191,6 @@ ~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); diff --git a/lldb/tools/lldb-vscode/VSCode.cpp b/lldb/tools/lldb-vscode/VSCode.cpp --- a/lldb/tools/lldb-vscode/VSCode.cpp +++ b/lldb/tools/lldb-vscode/VSCode.cpp @@ -64,13 +64,6 @@ 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 @@ o, llvm::StringRef(buffer, std::min(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(); diff --git a/lldb/tools/lldb-vscode/VSCodeForward.h b/lldb/tools/lldb-vscode/VSCodeForward.h --- a/lldb/tools/lldb-vscode/VSCodeForward.h +++ b/lldb/tools/lldb-vscode/VSCodeForward.h @@ -14,7 +14,6 @@ struct ExceptionBreakpoint; struct FunctionBreakpoint; struct SourceBreakpoint; -struct SourceReference; } // namespace lldb_vscode namespace lldb { diff --git a/lldb/tools/lldb-vscode/lldb-vscode.cpp b/lldb/tools/lldb-vscode/lldb-vscode.cpp --- a/lldb/tools/lldb-vscode/lldb-vscode.cpp +++ b/lldb/tools/lldb-vscode/lldb-vscode.cpp @@ -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 @@ 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_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))); } @@ -3290,6 +3282,211 @@ 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 @@ -3334,6 +3531,7 @@ 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);