blob: 5e622f3d3dcd40b974b866edc6298d9bc9226274 [file] [log] [blame]
//===-- AttachRequestHandler.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/SBListener.h"
#include "llvm/Support/FileSystem.h"
namespace lldb_dap {
// "AttachRequest": {
// "allOf": [ { "$ref": "#/definitions/Request" }, {
// "type": "object",
// "description": "Attach request; value of command field is 'attach'.",
// "properties": {
// "command": {
// "type": "string",
// "enum": [ "attach" ]
// },
// "arguments": {
// "$ref": "#/definitions/AttachRequestArguments"
// }
// },
// "required": [ "command", "arguments" ]
// }]
// },
// "AttachRequestArguments": {
// "type": "object",
// "description": "Arguments for 'attach' request.\nThe attach request has no
// standardized attributes."
// },
// "AttachResponse": {
// "allOf": [ { "$ref": "#/definitions/Response" }, {
// "type": "object",
// "description": "Response to 'attach' request. This is just an
// acknowledgement, so no body field is required."
// }]
// }
void AttachRequestHandler::operator()(const llvm::json::Object &request) const {
dap.is_attach = true;
dap.last_launch_or_attach_request = request;
llvm::json::Object response;
lldb::SBError error;
FillResponse(request, response);
lldb::SBAttachInfo attach_info;
const int invalid_port = 0;
const auto *arguments = request.getObject("arguments");
const lldb::pid_t pid =
GetInteger<uint64_t>(arguments, "pid").value_or(LLDB_INVALID_PROCESS_ID);
const auto gdb_remote_port =
GetInteger<uint64_t>(arguments, "gdb-remote-port").value_or(invalid_port);
const auto gdb_remote_hostname =
GetString(arguments, "gdb-remote-hostname").value_or("localhost");
if (pid != LLDB_INVALID_PROCESS_ID)
attach_info.SetProcessID(pid);
const auto wait_for = GetBoolean(arguments, "waitFor").value_or(false);
attach_info.SetWaitForLaunch(wait_for, false /*async*/);
dap.configuration.initCommands = GetStrings(arguments, "initCommands");
dap.configuration.preRunCommands = GetStrings(arguments, "preRunCommands");
dap.configuration.stopCommands = GetStrings(arguments, "stopCommands");
dap.configuration.exitCommands = GetStrings(arguments, "exitCommands");
dap.configuration.terminateCommands =
GetStrings(arguments, "terminateCommands");
auto attachCommands = GetStrings(arguments, "attachCommands");
llvm::StringRef core_file = GetString(arguments, "coreFile").value_or("");
const uint64_t timeout_seconds =
GetInteger<uint64_t>(arguments, "timeout").value_or(30);
dap.stop_at_entry = core_file.empty()
? GetBoolean(arguments, "stopOnEntry").value_or(false)
: true;
dap.configuration.postRunCommands = GetStrings(arguments, "postRunCommands");
const llvm::StringRef debuggerRoot =
GetString(arguments, "debuggerRoot").value_or("");
dap.configuration.enableAutoVariableSummaries =
GetBoolean(arguments, "enableAutoVariableSummaries").value_or(false);
dap.configuration.enableSyntheticChildDebugging =
GetBoolean(arguments, "enableSyntheticChildDebugging").value_or(false);
dap.configuration.displayExtendedBacktrace =
GetBoolean(arguments, "displayExtendedBacktrace").value_or(false);
dap.configuration.commandEscapePrefix =
GetString(arguments, "commandEscapePrefix").value_or("`");
dap.SetFrameFormat(GetString(arguments, "customFrameFormat").value_or(""));
dap.SetThreadFormat(GetString(arguments, "customThreadFormat").value_or(""));
PrintWelcomeMessage();
// This is a hack for loading DWARF in .o files on Mac where the .o files
// in the debug map of the main executable have relative paths which require
// the lldb-dap binary to have its working directory set to that relative
// root for the .o files in order to be able to load debug info.
if (!debuggerRoot.empty())
llvm::sys::fs::set_current_path(debuggerRoot);
// Run any initialize LLDB commands the user specified in the launch.json
if (llvm::Error err = dap.RunInitCommands()) {
response["success"] = false;
EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
dap.SendJSON(llvm::json::Value(std::move(response)));
return;
}
SetSourceMapFromArguments(*arguments);
lldb::SBError status;
dap.SetTarget(dap.CreateTargetFromArguments(*arguments, status));
if (status.Fail()) {
response["success"] = llvm::json::Value(false);
EmplaceSafeString(response, "message", status.GetCString());
dap.SendJSON(llvm::json::Value(std::move(response)));
return;
}
// Run any pre run LLDB commands the user specified in the launch.json
if (llvm::Error err = dap.RunPreRunCommands()) {
response["success"] = false;
EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
dap.SendJSON(llvm::json::Value(std::move(response)));
return;
}
if ((pid == LLDB_INVALID_PROCESS_ID || gdb_remote_port == invalid_port) &&
wait_for) {
char attach_msg[256];
auto attach_msg_len = snprintf(attach_msg, sizeof(attach_msg),
"Waiting to attach to \"%s\"...",
dap.target.GetExecutable().GetFilename());
dap.SendOutput(OutputType::Console,
llvm::StringRef(attach_msg, attach_msg_len));
}
if (attachCommands.empty()) {
// No "attachCommands", just attach normally.
// Disable async events so the attach will be successful when we return from
// the launch call and the launch will happen synchronously
dap.debugger.SetAsync(false);
if (core_file.empty()) {
if ((pid != LLDB_INVALID_PROCESS_ID) &&
(gdb_remote_port != invalid_port)) {
// If both pid and port numbers are specified.
error.SetErrorString("The user can't specify both pid and port");
} else if (gdb_remote_port != invalid_port) {
// If port is specified and pid is not.
lldb::SBListener listener = dap.debugger.GetListener();
// If the user hasn't provided the hostname property, default localhost
// being used.
std::string connect_url =
llvm::formatv("connect://{0}:", gdb_remote_hostname);
connect_url += std::to_string(gdb_remote_port);
dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote",
error);
} else {
// Attach by process name or id.
dap.target.Attach(attach_info, error);
}
} else
dap.target.LoadCore(core_file.data(), error);
// Reenable async events
dap.debugger.SetAsync(true);
} else {
// We have "attachCommands" that are a set of commands that are expected
// to execute the commands after which a process should be created. If there
// is no valid process after running these commands, we have failed.
if (llvm::Error err = dap.RunAttachCommands(attachCommands)) {
response["success"] = false;
EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
dap.SendJSON(llvm::json::Value(std::move(response)));
return;
}
// The custom commands might have created a new target so we should use the
// selected target after these commands are run.
dap.target = dap.debugger.GetSelectedTarget();
// Make sure the process is attached and stopped before proceeding as the
// the launch commands are not run using the synchronous mode.
error = dap.WaitForProcessToStop(timeout_seconds);
}
if (error.Success() && core_file.empty()) {
auto attached_pid = dap.target.GetProcess().GetProcessID();
if (attached_pid == LLDB_INVALID_PROCESS_ID) {
if (attachCommands.empty())
error.SetErrorString("failed to attach to a process");
else
error.SetErrorString("attachCommands failed to attach to a process");
}
}
if (error.Fail()) {
response["success"] = llvm::json::Value(false);
EmplaceSafeString(response, "message", std::string(error.GetCString()));
} else {
dap.RunPostRunCommands();
}
dap.SendJSON(llvm::json::Value(std::move(response)));
if (error.Success()) {
SendProcessEvent(dap, Attach);
dap.SendJSON(CreateEventObject("initialized"));
}
}
} // namespace lldb_dap