Completion of #129818 Did not want to put `llvm::StringRef()` as the default value instead it was `""`
227 lines
8.0 KiB
C++
227 lines
8.0 KiB
C++
//===-- 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) const {
|
|
llvm::json::Object response;
|
|
FillResponse(request, response);
|
|
auto *arguments = request.getObject("arguments");
|
|
|
|
llvm::StringRef memoryReference =
|
|
GetString(arguments, "memoryReference").value_or("");
|
|
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 += GetInteger<int64_t>(arguments, "instructionOffset").value_or(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 =
|
|
GetInteger<int64_t>(arguments, "instructionCount").value_or(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").value_or(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);
|
|
}
|
|
|
|
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));
|
|
dap.SendJSON(llvm::json::Value(std::move(response)));
|
|
}
|
|
|
|
} // namespace lldb_dap
|