| //===-- 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 |