llvm-project/lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp
John Harrison 0e0b501bf5
[lldb-dap] Take two at refactoring the startup sequence. (#140331)
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.
2025-05-16 19:28:34 -07:00

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