| 1 | //===-- RequestHandler.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 "Handler/RequestHandler.h" |
| 10 | #include "DAP.h" |
| 11 | #include "EventHelper.h" |
| 12 | #include "Handler/ResponseHandler.h" |
| 13 | #include "JSONUtils.h" |
| 14 | #include "LLDBUtils.h" |
| 15 | #include "Protocol/ProtocolBase.h" |
| 16 | #include "Protocol/ProtocolRequests.h" |
| 17 | #include "RunInTerminal.h" |
| 18 | #include "lldb/API/SBDefines.h" |
| 19 | #include "lldb/API/SBEnvironment.h" |
| 20 | #include "llvm/Support/Error.h" |
| 21 | #include <mutex> |
| 22 | |
| 23 | #if !defined(_WIN32) |
| 24 | #include <unistd.h> |
| 25 | #endif |
| 26 | |
| 27 | using namespace lldb_dap::protocol; |
| 28 | |
| 29 | namespace lldb_dap { |
| 30 | |
| 31 | static std::vector<const char *> |
| 32 | MakeArgv(const llvm::ArrayRef<std::string> &strs) { |
| 33 | // Create and return an array of "const char *", one for each C string in |
| 34 | // "strs" and terminate the list with a NULL. This can be used for argument |
| 35 | // vectors (argv) or environment vectors (envp) like those passed to the |
| 36 | // "main" function in C programs. |
| 37 | std::vector<const char *> argv; |
| 38 | for (const auto &s : strs) |
| 39 | argv.push_back(x: s.c_str()); |
| 40 | argv.push_back(x: nullptr); |
| 41 | return argv; |
| 42 | } |
| 43 | |
| 44 | static uint32_t SetLaunchFlag(uint32_t flags, bool flag, |
| 45 | lldb::LaunchFlags mask) { |
| 46 | if (flag) |
| 47 | flags |= mask; |
| 48 | else |
| 49 | flags &= ~mask; |
| 50 | |
| 51 | return flags; |
| 52 | } |
| 53 | |
| 54 | static llvm::Error |
| 55 | RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) { |
| 56 | if (!dap.clientFeatures.contains( |
| 57 | V: protocol::eClientFeatureRunInTerminalRequest)) |
| 58 | return llvm::make_error<DAPError>(Args: "Cannot use runInTerminal, feature is " |
| 59 | "not supported by the connected client" ); |
| 60 | |
| 61 | if (arguments.configuration.program.empty()) |
| 62 | return llvm::make_error<DAPError>( |
| 63 | Args: "program must be set to when using runInTerminal" ); |
| 64 | |
| 65 | dap.is_attach = true; |
| 66 | lldb::SBAttachInfo attach_info; |
| 67 | |
| 68 | llvm::Expected<std::shared_ptr<FifoFile>> comm_file_or_err = |
| 69 | CreateRunInTerminalCommFile(); |
| 70 | if (!comm_file_or_err) |
| 71 | return comm_file_or_err.takeError(); |
| 72 | FifoFile &comm_file = *comm_file_or_err.get(); |
| 73 | |
| 74 | RunInTerminalDebugAdapterCommChannel comm_channel(comm_file.m_path); |
| 75 | |
| 76 | lldb::pid_t debugger_pid = LLDB_INVALID_PROCESS_ID; |
| 77 | #if !defined(_WIN32) |
| 78 | debugger_pid = getpid(); |
| 79 | #endif |
| 80 | |
| 81 | llvm::json::Object reverse_request = CreateRunInTerminalReverseRequest( |
| 82 | program: arguments.configuration.program, args: arguments.args, env: arguments.env, |
| 83 | cwd: arguments.cwd, comm_file: comm_file.m_path, debugger_pid); |
| 84 | dap.SendReverseRequest<LogFailureResponseHandler>(command: "runInTerminal" , |
| 85 | arguments: std::move(reverse_request)); |
| 86 | |
| 87 | if (llvm::Expected<lldb::pid_t> pid = comm_channel.GetLauncherPid()) |
| 88 | attach_info.SetProcessID(*pid); |
| 89 | else |
| 90 | return pid.takeError(); |
| 91 | |
| 92 | std::optional<ScopeSyncMode> scope_sync_mode(dap.debugger); |
| 93 | lldb::SBError error; |
| 94 | dap.target.Attach(attach_info, error); |
| 95 | |
| 96 | if (error.Fail()) |
| 97 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), |
| 98 | Fmt: "Failed to attach to the target process. %s" , |
| 99 | Vals: comm_channel.GetLauncherError().c_str()); |
| 100 | // This will notify the runInTerminal launcher that we attached. |
| 101 | // We have to make this async, as the function won't return until the launcher |
| 102 | // resumes and reads the data. |
| 103 | std::future<lldb::SBError> did_attach_message_success = |
| 104 | comm_channel.NotifyDidAttach(); |
| 105 | |
| 106 | // We just attached to the runInTerminal launcher, which was waiting to be |
| 107 | // attached. We now resume it, so it can receive the didAttach notification |
| 108 | // and then perform the exec. Upon continuing, the debugger will stop the |
| 109 | // process right in the middle of the exec. To the user, what we are doing is |
| 110 | // transparent, as they will only be able to see the process since the exec, |
| 111 | // completely unaware of the preparatory work. |
| 112 | dap.target.GetProcess().Continue(); |
| 113 | |
| 114 | // Now that the actual target is just starting (i.e. exec was just invoked), |
| 115 | // we return the debugger to its sync state. |
| 116 | scope_sync_mode.reset(); |
| 117 | |
| 118 | // If sending the notification failed, the launcher should be dead by now and |
| 119 | // the async didAttach notification should have an error message, so we |
| 120 | // return it. Otherwise, everything was a success. |
| 121 | did_attach_message_success.wait(); |
| 122 | error = did_attach_message_success.get(); |
| 123 | if (error.Success()) |
| 124 | return llvm::Error::success(); |
| 125 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), |
| 126 | S: error.GetCString()); |
| 127 | } |
| 128 | |
| 129 | void BaseRequestHandler::Run(const Request &request) { |
| 130 | // If this request was cancelled, send a cancelled response. |
| 131 | if (dap.IsCancelled(request)) { |
| 132 | Response cancelled{/*request_seq=*/request.seq, |
| 133 | /*command=*/request.command, |
| 134 | /*success=*/false, |
| 135 | /*message=*/eResponseMessageCancelled, |
| 136 | /*body=*/std::nullopt}; |
| 137 | dap.Send(message: cancelled); |
| 138 | return; |
| 139 | } |
| 140 | |
| 141 | lldb::SBMutex lock = dap.GetAPIMutex(); |
| 142 | std::lock_guard<lldb::SBMutex> guard(lock); |
| 143 | |
| 144 | // FIXME: After all the requests have migrated from LegacyRequestHandler > |
| 145 | // RequestHandler<> we should be able to move this into |
| 146 | // RequestHandler<>::operator(). |
| 147 | operator()(request); |
| 148 | |
| 149 | // FIXME: After all the requests have migrated from LegacyRequestHandler > |
| 150 | // RequestHandler<> we should be able to check `debugger.InterruptRequest` and |
| 151 | // mark the response as cancelled. |
| 152 | } |
| 153 | |
| 154 | llvm::Error BaseRequestHandler::LaunchProcess( |
| 155 | const protocol::LaunchRequestArguments &arguments) const { |
| 156 | auto launchCommands = arguments.launchCommands; |
| 157 | |
| 158 | // Instantiate a launch info instance for the target. |
| 159 | auto launch_info = dap.target.GetLaunchInfo(); |
| 160 | |
| 161 | // Grab the current working directory if there is one and set it in the |
| 162 | // launch info. |
| 163 | if (!arguments.cwd.empty()) |
| 164 | launch_info.SetWorkingDirectory(arguments.cwd.data()); |
| 165 | |
| 166 | // Extract any extra arguments and append them to our program arguments for |
| 167 | // when we launch |
| 168 | if (!arguments.args.empty()) |
| 169 | launch_info.SetArguments(argv: MakeArgv(strs: arguments.args).data(), append: true); |
| 170 | |
| 171 | // Pass any environment variables along that the user specified. |
| 172 | if (!arguments.env.empty()) { |
| 173 | lldb::SBEnvironment env; |
| 174 | for (const auto &kv : arguments.env) |
| 175 | env.Set(name: kv.first().data(), value: kv.second.c_str(), overwrite: true); |
| 176 | launch_info.SetEnvironment(env, append: true); |
| 177 | } |
| 178 | |
| 179 | launch_info.SetDetachOnError(arguments.detachOnError); |
| 180 | launch_info.SetShellExpandArguments(arguments.shellExpandArguments); |
| 181 | |
| 182 | auto flags = launch_info.GetLaunchFlags(); |
| 183 | flags = |
| 184 | SetLaunchFlag(flags, flag: arguments.disableASLR, mask: lldb::eLaunchFlagDisableASLR); |
| 185 | flags = SetLaunchFlag(flags, flag: arguments.disableSTDIO, |
| 186 | mask: lldb::eLaunchFlagDisableSTDIO); |
| 187 | launch_info.SetLaunchFlags(flags | lldb::eLaunchFlagDebug | |
| 188 | lldb::eLaunchFlagStopAtEntry); |
| 189 | |
| 190 | { |
| 191 | // Perform the launch in synchronous mode so that we don't have to worry |
| 192 | // about process state changes during the launch. |
| 193 | ScopeSyncMode scope_sync_mode(dap.debugger); |
| 194 | |
| 195 | if (arguments.runInTerminal) { |
| 196 | if (llvm::Error err = RunInTerminal(dap, arguments)) |
| 197 | return err; |
| 198 | } else if (launchCommands.empty()) { |
| 199 | lldb::SBError error; |
| 200 | dap.target.Launch(launch_info, error); |
| 201 | if (error.Fail()) |
| 202 | return ToError(error); |
| 203 | } else { |
| 204 | // Set the launch info so that run commands can access the configured |
| 205 | // launch details. |
| 206 | dap.target.SetLaunchInfo(launch_info); |
| 207 | if (llvm::Error err = dap.RunLaunchCommands(launch_commands: launchCommands)) |
| 208 | return err; |
| 209 | |
| 210 | // The custom commands might have created a new target so we should use |
| 211 | // the selected target after these commands are run. |
| 212 | dap.target = dap.debugger.GetSelectedTarget(); |
| 213 | } |
| 214 | } |
| 215 | |
| 216 | // Make sure the process is launched and stopped at the entry point before |
| 217 | // proceeding. |
| 218 | lldb::SBError error = |
| 219 | dap.WaitForProcessToStop(seconds: arguments.configuration.timeout); |
| 220 | if (error.Fail()) |
| 221 | return ToError(error); |
| 222 | |
| 223 | return llvm::Error::success(); |
| 224 | } |
| 225 | |
| 226 | void BaseRequestHandler::PrintWelcomeMessage() const { |
| 227 | #ifdef LLDB_DAP_WELCOME_MESSAGE |
| 228 | dap.SendOutput(OutputType::Console, LLDB_DAP_WELCOME_MESSAGE); |
| 229 | #endif |
| 230 | } |
| 231 | |
| 232 | bool BaseRequestHandler::HasInstructionGranularity( |
| 233 | const llvm::json::Object &arguments) const { |
| 234 | if (std::optional<llvm::StringRef> value = arguments.getString(K: "granularity" )) |
| 235 | return value == "instruction" ; |
| 236 | return false; |
| 237 | } |
| 238 | |
| 239 | } // namespace lldb_dap |
| 240 | |