
This is more straight forward refactor of the startup sequence that reverts parts of ba29e60f9a2222bd5e883579bb78db13fc5a7588. Unlike my previous attempt, I ended up removing the pending request queue and not including an `AsyncReqeustHandler` because I don't think we actually need that at the moment. The key is that during the startup flow there are 2 parallel operations happening in the DAP that have different triggers. * The `initialize` request is sent and once the response is received the `launch` or `attach` is sent. * When the `initialized` event is recieved the `setBreakpionts` and other config requests are made followed by the `configurationDone` event. I moved the `initialized` event back to happen in the `PostRun` of the `launch` or `attach` request handlers. This ensures that we have a valid target by the time the configuration calls are made. I added also added a few extra validations that to the `configurationeDone` handler to ensure we're in an expected state. I've also fixed up the tests to match the new flow. With the other additional test fixes in 087a5d2ec7897cd99d3787820711fec76a8e1792 I think we've narrowed down the main source of test instability that motivated the startup sequence change.
147 lines
5.3 KiB
C++
147 lines
5.3 KiB
C++
//===-- 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 "LLDBUtils.h"
|
|
#include "Protocol/ProtocolRequests.h"
|
|
#include "RequestHandler.h"
|
|
#include "lldb/API/SBAttachInfo.h"
|
|
#include "lldb/API/SBListener.h"
|
|
#include "lldb/lldb-defines.h"
|
|
#include "llvm/Support/Error.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
|
|
using namespace llvm;
|
|
using namespace lldb_dap::protocol;
|
|
|
|
namespace lldb_dap {
|
|
|
|
/// The `attach` request is sent from the client to the debug adapter to attach
|
|
/// to a debuggee that is already running.
|
|
///
|
|
/// Since attaching is debugger/runtime specific, the arguments for this request
|
|
/// are not part of this specification.
|
|
Error AttachRequestHandler::Run(const AttachRequestArguments &args) const {
|
|
// Validate that we have a well formed attach request.
|
|
if (args.attachCommands.empty() && args.coreFile.empty() &&
|
|
args.configuration.program.empty() &&
|
|
args.pid == LLDB_INVALID_PROCESS_ID &&
|
|
args.gdbRemotePort == LLDB_DAP_INVALID_PORT)
|
|
return make_error<DAPError>(
|
|
"expected one of 'pid', 'program', 'attachCommands', "
|
|
"'coreFile' or 'gdb-remote-port' to be specified");
|
|
|
|
// Check if we have mutually exclusive arguments.
|
|
if ((args.pid != LLDB_INVALID_PROCESS_ID) &&
|
|
(args.gdbRemotePort != LLDB_DAP_INVALID_PORT))
|
|
return make_error<DAPError>(
|
|
"'pid' and 'gdb-remote-port' are mutually exclusive");
|
|
|
|
dap.SetConfiguration(args.configuration, /*is_attach=*/true);
|
|
if (!args.coreFile.empty())
|
|
dap.stop_at_entry = true;
|
|
|
|
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 (!dap.configuration.debuggerRoot.empty())
|
|
sys::fs::set_current_path(dap.configuration.debuggerRoot);
|
|
|
|
// Run any initialize LLDB commands the user specified in the launch.json
|
|
if (llvm::Error err = dap.RunInitCommands())
|
|
return err;
|
|
|
|
dap.ConfigureSourceMaps();
|
|
|
|
lldb::SBError error;
|
|
lldb::SBTarget target = dap.CreateTarget(error);
|
|
if (error.Fail())
|
|
return ToError(error);
|
|
|
|
dap.SetTarget(target);
|
|
|
|
// Run any pre run LLDB commands the user specified in the launch.json
|
|
if (Error err = dap.RunPreRunCommands())
|
|
return err;
|
|
|
|
if ((args.pid == LLDB_INVALID_PROCESS_ID ||
|
|
args.gdbRemotePort == LLDB_DAP_INVALID_PORT) &&
|
|
args.waitFor) {
|
|
dap.SendOutput(OutputType::Console,
|
|
llvm::formatv("Waiting to attach to \"{0}\"...",
|
|
dap.target.GetExecutable().GetFilename())
|
|
.str());
|
|
}
|
|
|
|
{
|
|
// Perform the launch in synchronous mode so that we don't have to worry
|
|
// about process state changes during the launch.
|
|
ScopeSyncMode scope_sync_mode(dap.debugger);
|
|
|
|
if (!args.attachCommands.empty()) {
|
|
// Run the attach commands, after which we expect the debugger's selected
|
|
// target to contain a valid and stopped process. Otherwise inform the
|
|
// user that their command failed or the debugger is in an unexpected
|
|
// state.
|
|
if (llvm::Error err = dap.RunAttachCommands(args.attachCommands))
|
|
return err;
|
|
|
|
dap.target = dap.debugger.GetSelectedTarget();
|
|
|
|
// Validate the attachCommand results.
|
|
if (!dap.target.GetProcess().IsValid())
|
|
return make_error<DAPError>(
|
|
"attachCommands failed to attach to a process");
|
|
} else if (!args.coreFile.empty()) {
|
|
dap.target.LoadCore(args.coreFile.data(), error);
|
|
} else if (args.gdbRemotePort != LLDB_DAP_INVALID_PORT) {
|
|
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}:", args.gdbRemoteHostname);
|
|
connect_url += std::to_string(args.gdbRemotePort);
|
|
dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote",
|
|
error);
|
|
} else {
|
|
// Attach by pid or process name.
|
|
lldb::SBAttachInfo attach_info;
|
|
if (args.pid != LLDB_INVALID_PROCESS_ID)
|
|
attach_info.SetProcessID(args.pid);
|
|
else if (!dap.configuration.program.empty())
|
|
attach_info.SetExecutable(dap.configuration.program.data());
|
|
attach_info.SetWaitForLaunch(args.waitFor, /*async=*/false);
|
|
dap.target.Attach(attach_info, error);
|
|
}
|
|
}
|
|
|
|
// Make sure the process is attached and stopped.
|
|
error = dap.WaitForProcessToStop(args.configuration.timeout);
|
|
if (error.Fail())
|
|
return ToError(error);
|
|
|
|
if (args.coreFile.empty() && !dap.target.GetProcess().IsValid())
|
|
return make_error<DAPError>("failed to attach to process");
|
|
|
|
dap.RunPostRunCommands();
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
void AttachRequestHandler::PostRun() const {
|
|
dap.SendJSON(CreateEventObject("initialized"));
|
|
}
|
|
|
|
} // namespace lldb_dap
|