1//===-- AttachRequestHandler.cpp ------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "DAP.h"
10#include "EventHelper.h"
11#include "JSONUtils.h"
12#include "LLDBUtils.h"
13#include "Protocol/ProtocolRequests.h"
14#include "RequestHandler.h"
15#include "lldb/API/SBAttachInfo.h"
16#include "lldb/API/SBListener.h"
17#include "lldb/lldb-defines.h"
18#include "llvm/Support/Error.h"
19#include "llvm/Support/FileSystem.h"
20
21using namespace llvm;
22using namespace lldb_dap::protocol;
23
24namespace lldb_dap {
25
26/// The `attach` request is sent from the client to the debug adapter to attach
27/// to a debuggee that is already running.
28///
29/// Since attaching is debugger/runtime specific, the arguments for this request
30/// are not part of this specification.
31Error AttachRequestHandler::Run(const AttachRequestArguments &args) const {
32 // Validate that we have a well formed attach request.
33 if (args.attachCommands.empty() && args.coreFile.empty() &&
34 args.configuration.program.empty() &&
35 args.pid == LLDB_INVALID_PROCESS_ID &&
36 args.gdbRemotePort == LLDB_DAP_INVALID_PORT)
37 return make_error<DAPError>(
38 Args: "expected one of 'pid', 'program', 'attachCommands', "
39 "'coreFile' or 'gdb-remote-port' to be specified");
40
41 // Check if we have mutually exclusive arguments.
42 if ((args.pid != LLDB_INVALID_PROCESS_ID) &&
43 (args.gdbRemotePort != LLDB_DAP_INVALID_PORT))
44 return make_error<DAPError>(
45 Args: "'pid' and 'gdb-remote-port' are mutually exclusive");
46
47 dap.SetConfiguration(confing: args.configuration, /*is_attach=*/true);
48 if (!args.coreFile.empty())
49 dap.stop_at_entry = true;
50
51 PrintWelcomeMessage();
52
53 // This is a hack for loading DWARF in .o files on Mac where the .o files
54 // in the debug map of the main executable have relative paths which
55 // require the lldb-dap binary to have its working directory set to that
56 // relative root for the .o files in order to be able to load debug info.
57 if (!dap.configuration.debuggerRoot.empty())
58 sys::fs::set_current_path(dap.configuration.debuggerRoot);
59
60 // Run any initialize LLDB commands the user specified in the launch.json
61 if (llvm::Error err = dap.RunInitCommands())
62 return err;
63
64 dap.ConfigureSourceMaps();
65
66 lldb::SBError error;
67 lldb::SBTarget target = dap.CreateTarget(error);
68 if (error.Fail())
69 return ToError(error);
70
71 dap.SetTarget(target);
72
73 // Run any pre run LLDB commands the user specified in the launch.json
74 if (Error err = dap.RunPreRunCommands())
75 return err;
76
77 if ((args.pid == LLDB_INVALID_PROCESS_ID ||
78 args.gdbRemotePort == LLDB_DAP_INVALID_PORT) &&
79 args.waitFor) {
80 dap.SendOutput(o: OutputType::Console,
81 output: llvm::formatv(Fmt: "Waiting to attach to \"{0}\"...",
82 Vals: dap.target.GetExecutable().GetFilename())
83 .str());
84 }
85
86 {
87 // Perform the launch in synchronous mode so that we don't have to worry
88 // about process state changes during the launch.
89 ScopeSyncMode scope_sync_mode(dap.debugger);
90
91 if (!args.attachCommands.empty()) {
92 // Run the attach commands, after which we expect the debugger's selected
93 // target to contain a valid and stopped process. Otherwise inform the
94 // user that their command failed or the debugger is in an unexpected
95 // state.
96 if (llvm::Error err = dap.RunAttachCommands(attach_commands: args.attachCommands))
97 return err;
98
99 dap.target = dap.debugger.GetSelectedTarget();
100
101 // Validate the attachCommand results.
102 if (!dap.target.GetProcess().IsValid())
103 return make_error<DAPError>(
104 Args: "attachCommands failed to attach to a process");
105 } else if (!args.coreFile.empty()) {
106 dap.target.LoadCore(core_file: args.coreFile.data(), error);
107 } else if (args.gdbRemotePort != LLDB_DAP_INVALID_PORT) {
108 lldb::SBListener listener = dap.debugger.GetListener();
109
110 // If the user hasn't provided the hostname property, default
111 // localhost being used.
112 std::string connect_url =
113 llvm::formatv(Fmt: "connect://{0}:", Vals: args.gdbRemoteHostname);
114 connect_url += std::to_string(val: args.gdbRemotePort);
115 dap.target.ConnectRemote(listener, url: connect_url.c_str(), plugin_name: "gdb-remote",
116 error);
117 } else {
118 // Attach by pid or process name.
119 lldb::SBAttachInfo attach_info;
120 if (args.pid != LLDB_INVALID_PROCESS_ID)
121 attach_info.SetProcessID(args.pid);
122 else if (!dap.configuration.program.empty())
123 attach_info.SetExecutable(dap.configuration.program.data());
124 attach_info.SetWaitForLaunch(b: args.waitFor, /*async=*/false);
125 dap.target.Attach(attach_info, error);
126 }
127 }
128
129 // Make sure the process is attached and stopped.
130 error = dap.WaitForProcessToStop(seconds: args.configuration.timeout);
131 if (error.Fail())
132 return ToError(error);
133
134 if (args.coreFile.empty() && !dap.target.GetProcess().IsValid())
135 return make_error<DAPError>(Args: "failed to attach to process");
136
137 dap.RunPostRunCommands();
138
139 return Error::success();
140}
141
142void AttachRequestHandler::PostRun() const {
143 dap.SendJSON(json: CreateEventObject(event_name: "initialized"));
144}
145
146} // namespace lldb_dap
147

source code of lldb/tools/lldb-dap/Handler/AttachRequestHandler.cpp