wallace updated this revision to Diff 315015.
wallace marked 10 inline comments as done.
wallace added a comment.
Herald added a subscriber: mgorny.
Address all comments.
- Moved all the communication logic between the debug adaptor and the launcher
to classes, simplifying the code in lldb-vscode.
- Added more comments and tests
- Created a file where lldb-vscode can write errors to.
- Now returning any error messages to the IDE, so that the user can see what's
going on. No exit() calls are using in the debug adaptor side.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D93951/new/
https://reviews.llvm.org/D93951
Files:
lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py
lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py
lldb/test/API/tools/lldb-vscode/runInTerminal/TestVSCode_runInTerminal.py
lldb/tools/lldb-vscode/CMakeLists.txt
lldb/tools/lldb-vscode/JSONUtils.cpp
lldb/tools/lldb-vscode/JSONUtils.h
lldb/tools/lldb-vscode/Options.td
lldb/tools/lldb-vscode/RunInTerminal.cpp
lldb/tools/lldb-vscode/RunInTerminal.h
lldb/tools/lldb-vscode/VSCode.h
lldb/tools/lldb-vscode/lldb-vscode.cpp
Index: lldb/tools/lldb-vscode/lldb-vscode.cpp
===================================================================
--- lldb/tools/lldb-vscode/lldb-vscode.cpp
+++ lldb/tools/lldb-vscode/lldb-vscode.cpp
@@ -384,12 +384,7 @@
break;
case lldb::eStateSuspended:
break;
- case lldb::eStateStopped: {
- if (g_vsc.waiting_for_run_in_terminal) {
- g_vsc.waiting_for_run_in_terminal = false;
- g_vsc.request_in_terminal_cv.notify_one();
- }
- }
+ case lldb::eStateStopped:
// Only report a stopped event if the process was not restarted.
if (!lldb::SBProcess::GetRestartedFromEvent(event)) {
SendStdOutStdErr(process);
@@ -461,7 +456,7 @@
}
body.try_emplace("module", module_value);
module_event.try_emplace("body", std::move(body));
- g_vsc.SendJSON(llvm::json::Value(std::move(module_event)));
+ // g_vsc.SendJSON(llvm::json::Value(std::move(module_event)));
}
}
} else if (event.BroadcasterMatchesRef(g_vsc.broadcaster)) {
@@ -1379,9 +1374,11 @@
filters.emplace_back(CreateExceptionBreakpointFilter(exc_bp));
}
body.try_emplace("exceptionBreakpointFilters", std::move(filters));
- // The debug adapter supports launching a debugee in intergrated VSCode
- // terminal.
+#if !defined(_WIN32)
body.try_emplace("supportsRunInTerminalRequest", true);
+#else
+ body.try_emplace("supportsRunInTerminalRequest", false);
+#endif
// The debug adapter supports stepping back via the stepBack and
// reverseContinue requests.
body.try_emplace("supportsStepBack", false);
@@ -1441,47 +1438,61 @@
g_vsc.SendJSON(llvm::json::Value(std::move(response)));
}
-void request_runInTerminal(const llvm::json::Object &launch_request,
- llvm::json::Object &launch_response) {
- // We have already created a target that has a valid "program" path to the
- // executable. We will attach to the next process whose name matches that
- // of the target's.
+llvm::Error request_runInTerminal(const llvm::json::Object &launch_request) {
+#if defined(_WIN32)
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "\"runInTerminal\" is not supported on Windows");
+#else
g_vsc.is_attach = true;
lldb::SBAttachInfo attach_info;
- lldb::SBError error;
- attach_info.SetWaitForLaunch(true, /*async*/ true);
- g_vsc.target.Attach(attach_info, error);
- llvm::json::Object reverse_request =
- CreateRunInTerminalReverseRequest(launch_request);
+ llvm::Expected<RunInTerminalDebugAdaptorCommChannelUP> comm_channel_or_err =
+ RunInTerminalDebugAdaptorCommChannel::Create();
+ if (!comm_channel_or_err)
+ return comm_channel_or_err.takeError();
+ RunInTerminalDebugAdaptorCommChannel &comm_channel =
+ *comm_channel_or_err.get();
+
+ llvm::json::Object reverse_request = CreateRunInTerminalReverseRequest(
+ launch_request, g_vsc.debug_adaptor_path,
+ comm_channel.GetCommFilesPrefix());
llvm::json::Object reverse_response;
lldb_vscode::PacketStatus status =
g_vsc.SendReverseRequest(reverse_request, reverse_response);
if (status != lldb_vscode::PacketStatus::Success)
- error.SetErrorString("Process cannot be launched by IDE.");
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "Process cannot be launched by the IDE. %s",
+ comm_channel.GetLauncherError().c_str());
- if (error.Success()) {
- // Wait for the attach stop event to happen or for a timeout.
- g_vsc.waiting_for_run_in_terminal = true;
- static std::mutex mutex;
- std::unique_lock<std::mutex> locker(mutex);
- g_vsc.request_in_terminal_cv.wait_for(locker, std::chrono::seconds(10));
+ if (llvm::Expected<lldb::pid_t> pid = comm_channel.GetLauncherPid())
+ attach_info.SetProcessID(*pid);
+ else
+ return pid.takeError();
- auto attached_pid = g_vsc.target.GetProcess().GetProcessID();
- if (attached_pid == LLDB_INVALID_PROCESS_ID)
- error.SetErrorString("Failed to attach to a process");
- else
- SendProcessEvent(Attach);
- }
+ g_vsc.debugger.SetAsync(false);
+ lldb::SBError error;
+ g_vsc.target.Attach(attach_info, error);
- if (error.Fail()) {
- launch_response["success"] = llvm::json::Value(false);
- EmplaceSafeString(launch_response, "message",
- std::string(error.GetCString()));
- } else {
- launch_response["success"] = llvm::json::Value(true);
- g_vsc.SendJSON(CreateEventObject("initialized"));
- }
+ if (error.Fail())
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "Failed to attach to the target process. %s",
+ comm_channel.GetLauncherError().c_str());
+ // We just attached to the runInTerminal launcher, which was waiting to be
+ // attached. After resuming, it will exec and become the original target the
+ // user provided.
+
+ // This will notify the runInTerminal launcher that we attached.
+ comm_channel.NotifyDidAttach();
+
+ // Here, we resume the process so that it can exec. It'll be stopped at the
+ // exec point.
+ g_vsc.target.GetProcess().Continue();
+
+ g_vsc.debugger.SetAsync(true);
+
+ return llvm::Error::success();
+#endif
}
// "LaunchRequest": {
@@ -1556,12 +1567,6 @@
return;
}
- if (GetBoolean(arguments, "runInTerminal", false)) {
- request_runInTerminal(request, response);
- g_vsc.SendJSON(llvm::json::Value(std::move(response)));
- return;
- }
-
// Instantiate a launch info instance for the target.
auto launch_info = g_vsc.target.GetLaunchInfo();
@@ -1597,7 +1602,11 @@
// Run any pre run LLDB commands the user specified in the launch.json
g_vsc.RunPreRunCommands();
- if (launchCommands.empty()) {
+
+ if (GetBoolean(arguments, "runInTerminal", false)) {
+ if (llvm::Error err = request_runInTerminal(request))
+ error.SetErrorString(llvm::toString(std::move(err)).c_str());
+ } else if (launchCommands.empty()) {
// Disable async events so the launch will be successful when we return from
// the launch call and the launch will happen synchronously
g_vsc.debugger.SetAsync(false);
@@ -1616,10 +1625,11 @@
}
g_vsc.SendJSON(llvm::json::Value(std::move(response)));
- SendProcessEvent(Launch);
+ if (g_vsc.is_attach)
+ SendProcessEvent(Attach); // this happens when doing runInTerminal
+ else
+ SendProcessEvent(Launch);
g_vsc.SendJSON(llvm::json::Value(CreateEventObject("initialized")));
- // Reenable async events and start the event thread to catch async events.
- // g_vsc.debugger.SetAsync(true);
}
// "NextRequest": {
@@ -2948,7 +2958,81 @@
llvm::outs() << examples;
}
+// If --launch-target is provided, this instance of lldb-vscode becomes a
+// runInTerminal launcher. It will ultimately launch the program specified in
+// the --launch-target argument, which is the original program the user wanted
+// to debug. This is done in such a way that the actual debug adaptor can
+// place breakpoints at the beginning of the program.
+//
+// The launcher will communicate with the debug adaptor using files whose path
+// prefix is specified in the --comm-files-prefix argument. Both this launcher
+// and the debug adaptor should use the same path prefix to guarantee correct
+// communication.
+//
+// Regarding the actual flow, this launcher will first notify the debug adaptor
+// its pid. Then, the launcher will be in a pending state waiting to be attached
+// by the adaptor.
+//
+// Once attached and resumed, the launcher will exec and become the program
+// specified by --launch-target, which is the original target the
+// user wanted to run.
+//
+// In case of errors launching the target, a suitable error message will be
+// emitted to the debug adaptor.
+[[noreturn]] void LaunchRunInTerminalTarget(llvm::opt::Arg &target_arg,
+ llvm::StringRef comm_files_prefix,
+ char *argv[]) {
+#if defined(_WIN32)
+ llvm::errs() << "\"--launch-target\" is not supported on Windows\n";
+ exit(EXIT_FAILURE);
+#else
+ RunInTerminalLauncherCommChannel comm_channel(comm_files_prefix);
+ comm_channel.NotifyPid();
+
+ // Some seconds waiting to be attached. We don't wait indefinitely using a
+ // signal to prevent being paused forever.
+ const char *timeout_env_var = getenv("LLDB_VSCODE_RIT_TIMEOUT_IN_MS");
+ int timeout_in_ms =
+ timeout_env_var != nullptr ? atoi(timeout_env_var) : 10000;
+ for (int i = 0; i < timeout_in_ms / 10; i++) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
+ if (comm_channel.HasDebugAdaptorAttached()) {
+ const char *target = target_arg.getValue();
+ execv(target, argv + target_arg.getIndex() + 2);
+
+ comm_channel.NotifyError(std::strerror(errno));
+ llvm::errs() << std::strerror(errno) << "\n";
+
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ comm_channel.NotifyError("lldb-vscode didn't attach to this process");
+ llvm::errs() << "lldb-vscode didn't attach to this process\n";
+ exit(EXIT_FAILURE);
+#endif
+}
+
int main(int argc, char *argv[]) {
+ llvm::SmallString<256> program_path(argv[0]);
+ llvm::sys::fs::make_absolute(program_path);
+ g_vsc.debug_adaptor_path = program_path.str().str();
+
+ LLDBVSCodeOptTable T;
+ unsigned MAI, MAC;
+ llvm::ArrayRef<const char *> ArgsArr = llvm::makeArrayRef(argv + 1, argc);
+ llvm::opt::InputArgList input_args = T.ParseArgs(ArgsArr, MAI, MAC);
+
+ if (auto *target_arg = input_args.getLastArg(OPT_launch_target)) {
+ if (auto *comm_file_prefix = input_args.getLastArg(OPT_comm_files_prefix))
+ LaunchRunInTerminalTarget(*target_arg, comm_file_prefix->getValue(),
+ argv);
+ else {
+ llvm::errs() << "\"--launch-target\" requires \"--comm-files-prefix\" to "
+ "be specified\n";
+ exit(EXIT_FAILURE);
+ }
+ }
// Initialize LLDB first before we do anything.
lldb::SBDebugger::Initialize();
@@ -2957,11 +3041,6 @@
int portno = -1;
- LLDBVSCodeOptTable T;
- unsigned MAI, MAC;
- llvm::ArrayRef<const char *> ArgsArr = llvm::makeArrayRef(argv + 1, argc);
- llvm::opt::InputArgList input_args = T.ParseArgs(ArgsArr, MAI, MAC);
-
if (input_args.hasArg(OPT_help)) {
printHelp(T, llvm::sys::path::filename(argv[0]));
return 0;
Index: lldb/tools/lldb-vscode/VSCode.h
===================================================================
--- lldb/tools/lldb-vscode/VSCode.h
+++ lldb/tools/lldb-vscode/VSCode.h
@@ -47,6 +47,7 @@
#include "ExceptionBreakpoint.h"
#include "FunctionBreakpoint.h"
#include "IOStream.h"
+#include "RunInTerminal.h"
#include "SourceBreakpoint.h"
#include "SourceReference.h"
@@ -77,6 +78,7 @@
};
struct VSCode {
+ std::string debug_adaptor_path;
InputStream input;
OutputStream output;
lldb::SBDebugger debugger;
@@ -104,7 +106,6 @@
bool is_attach;
uint32_t reverse_request_seq;
std::map<std::string, RequestCallback> request_handlers;
- std::condition_variable request_in_terminal_cv;
bool waiting_for_run_in_terminal;
// Keep track of the last stop thread index IDs as threads won't go away
// unless we send a "thread" event to indicate the thread exited.
Index: lldb/tools/lldb-vscode/RunInTerminal.h
===================================================================
--- /dev/null
+++ lldb/tools/lldb-vscode/RunInTerminal.h
@@ -0,0 +1,92 @@
+//===-- RunInTerminal.h ----------------------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_TOOLS_LLDB_VSCODE_RUNINTERMINAL_H
+#define LLDB_TOOLS_LLDB_VSCODE_RUNINTERMINAL_H
+
+// TODO: In order to implement Windows support for this feature, basically the
+// mkfifo API should be replaced by CreateNamedPipe.
+#if !defined(_WIN32)
+
+#include <string>
+
+#include "llvm/Support/Error.h"
+
+namespace lldb_vscode {
+
+/// Struct holding a list of files used to communicate the runInTerminal
+/// launcher with the debug adaptor. All these files have a common prefix for
+/// convenience.
+struct RunInTerminalCommunicationFiles {
+ RunInTerminalCommunicationFiles(llvm::StringRef file_prefix);
+
+ void DeleteFiles();
+
+ /// Prefix path for all the files in this struct
+ std::string prefix;
+ /// File the runInTerminal launcher can write its pid to
+ std::string pid_file;
+ /// File the runInTerminal launcher can write error messages to
+ std::string err_file;
+ /// File whose existance indicates that the debug adaptor has attached to the
+ /// runInTerminal launcher.
+ std::string did_attach_file;
+};
+
+class RunInTerminalDebugAdaptorCommChannel;
+
+using RunInTerminalDebugAdaptorCommChannelUP =
+ std::unique_ptr<RunInTerminalDebugAdaptorCommChannel>;
+
+class RunInTerminalDebugAdaptorCommChannel {
+public:
+ static llvm::Expected<RunInTerminalDebugAdaptorCommChannelUP> Create();
+
+ ~RunInTerminalDebugAdaptorCommChannel();
+
+ /// Get the common path prefix for all files used for communication.
+ llvm::StringRef GetCommFilesPrefix();
+
+ /// Notify the runInTerminal launcher that it was attached.
+ void NotifyDidAttach();
+
+ /// Fetch the pid of the runInTerminal launcher or return an error
+ /// if a certain timeout if reached.
+ llvm::Expected<lldb::pid_t> GetLauncherPid();
+
+ /// Fetch any errors emitted by the runInTerminal launcher or return a
+ /// default error message if a certain timeout if reached.
+ std::string GetLauncherError();
+
+private:
+ RunInTerminalDebugAdaptorCommChannel(llvm::StringRef prefix);
+
+ RunInTerminalCommunicationFiles m_comm_files;
+};
+
+class RunInTerminalLauncherCommChannel {
+public:
+ RunInTerminalLauncherCommChannel(llvm::StringRef prefix);
+
+ /// Check if the debug adaptor has attach to the current process.
+ bool HasDebugAdaptorAttached();
+
+ /// Notify the debug adaptor this process' pid.
+ void NotifyPid();
+
+ /// Notify the debug adaptor that there's been an error.
+ void NotifyError(llvm::StringRef error);
+
+private:
+ RunInTerminalCommunicationFiles m_comm_files;
+};
+
+} // namespace lldb_vscode
+
+#endif
+#endif // LLDB_TOOLS_LLDB_VSCODE_RUNINTERMINAL_H
Index: lldb/tools/lldb-vscode/RunInTerminal.cpp
===================================================================
--- /dev/null
+++ lldb/tools/lldb-vscode/RunInTerminal.cpp
@@ -0,0 +1,141 @@
+//===-- RunInTerminal.cpp ---------------------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#if !defined(_WIN32)
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <fstream>
+#include <future>
+#include <thread>
+
+#include "llvm/Support/FileSystem.h"
+
+#include "lldb/lldb-defines.h"
+
+#include "RunInTerminal.h"
+
+namespace lldb_vscode {
+
+static llvm::Error CreateFifoFile(const std::string &path) {
+ if (int err = mkfifo(path.c_str(), 0666))
+ return llvm::createStringError(
+ std::error_code(err, std::generic_category()),
+ "Couldn't create fifo file: %s", path.c_str());
+ return llvm::Error::success();
+}
+
+RunInTerminalCommunicationFiles::RunInTerminalCommunicationFiles(
+ llvm::StringRef file_prefix)
+ : prefix(file_prefix.str()) {
+ pid_file = prefix + ".pid";
+ err_file = prefix + ".err";
+ did_attach_file = prefix + ".attached";
+}
+
+void RunInTerminalCommunicationFiles::DeleteFiles() {
+ unlink(pid_file.c_str());
+ unlink(err_file.c_str());
+ unlink(did_attach_file.c_str());
+}
+
+llvm::Expected<RunInTerminalDebugAdaptorCommChannelUP>
+RunInTerminalDebugAdaptorCommChannel::Create() {
+ llvm::SmallString<256> comm_files_prefix;
+ if (std::error_code EC = llvm::sys::fs::getPotentiallyUniqueTempFileName(
+ "lldb-vscode-run-in-terminal-", "", comm_files_prefix))
+ return llvm::createStringError(
+ EC,
+ "Error making unique filename for runInTerminal communication files");
+
+ // Can't use std::make_unique because the constructor is private
+ RunInTerminalDebugAdaptorCommChannelUP comm_channel(
+ new RunInTerminalDebugAdaptorCommChannel(comm_files_prefix.str()));
+ llvm::Error err =
+ llvm::joinErrors(CreateFifoFile(comm_channel->m_comm_files.pid_file),
+ CreateFifoFile(comm_channel->m_comm_files.err_file));
+ if (err)
+ return std::move(err);
+ return comm_channel;
+}
+
+RunInTerminalDebugAdaptorCommChannel::~RunInTerminalDebugAdaptorCommChannel() {
+ m_comm_files.DeleteFiles();
+}
+
+llvm::StringRef RunInTerminalDebugAdaptorCommChannel::GetCommFilesPrefix() {
+ return m_comm_files.prefix;
+}
+
+void RunInTerminalDebugAdaptorCommChannel::NotifyDidAttach() {
+ std::ofstream did_attach_file_write(m_comm_files.did_attach_file);
+}
+
+llvm::Expected<lldb::pid_t>
+RunInTerminalDebugAdaptorCommChannel::GetLauncherPid() {
+ lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
+ std::thread pid_reader([&]() {
+ std::ifstream reader(m_comm_files.pid_file, std::ifstream::in);
+ reader >> pid;
+ });
+ auto future = std::async(std::launch::async, &std::thread::join, &pid_reader);
+ // We try to read with a timeout, in case the launcher is borked.
+ if (future.wait_for(std::chrono::seconds(10)) == std::future_status::timeout)
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "Timed out trying to get the pid from the runInTerminal launcher\n");
+ if (pid == LLDB_INVALID_PROCESS_ID)
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "Couldn't get a valid pid from the runInTerminal launcher\n");
+ return pid;
+}
+
+std::string RunInTerminalDebugAdaptorCommChannel::GetLauncherError() {
+ std::string error_message;
+ std::thread error_reader([&]() {
+ std::ifstream reader(m_comm_files.err_file, std::ifstream::in);
+ std::getline(reader, error_message);
+ });
+ auto future =
+ std::async(std::launch::async, &std::thread::join, &error_reader);
+ // We try to read with a timeout, in case the launcher is borked.
+ // We don't need to wait long, as we already know there's been an error.
+ if (future.wait_for(std::chrono::seconds(2)) == std::future_status::timeout)
+ return "Timed out trying to get error messages from the runInTerminal "
+ "launcher";
+ return error_message;
+}
+
+RunInTerminalDebugAdaptorCommChannel::RunInTerminalDebugAdaptorCommChannel(
+ llvm::StringRef prefix)
+ : m_comm_files(prefix.str()) {}
+
+RunInTerminalLauncherCommChannel::RunInTerminalLauncherCommChannel(
+ llvm::StringRef prefix)
+ : m_comm_files(prefix) {}
+
+bool RunInTerminalLauncherCommChannel::HasDebugAdaptorAttached() {
+ return llvm::sys::fs::exists(m_comm_files.did_attach_file);
+}
+
+void RunInTerminalLauncherCommChannel::NotifyPid() {
+ std::ofstream writer(m_comm_files.pid_file, std::ofstream::out);
+ writer << getpid() << std::endl;
+}
+
+void RunInTerminalLauncherCommChannel::NotifyError(llvm::StringRef error) {
+ std::ofstream writer(m_comm_files.err_file, std::ofstream::out);
+ writer << error.str() << std::endl;
+}
+} // namespace lldb_vscode
+
+#endif
Index: lldb/tools/lldb-vscode/Options.td
===================================================================
--- lldb/tools/lldb-vscode/Options.td
+++ lldb/tools/lldb-vscode/Options.td
@@ -23,3 +23,20 @@
def: Separate<["-"], "p">,
Alias<port>,
HelpText<"Alias for --port">;
+
+def launch_target: Separate<["--", "-"], "launch-target">,
+ MetaVarName<"<target>">,
+ HelpText<"Launch a target for the launchInTerminal request. Any argument "
+ "provided after this one will be passed to the target. The parameter "
+ "--comm-files-prefix must also be specified.">;
+def: Separate<["-"], "t">,
+ Alias<launch_target>,
+ HelpText<"Alias for --launch-target">;
+
+def comm_files_prefix: Separate<["--", "-"], "comm-files-prefix">,
+ MetaVarName<"<path>">,
+ HelpText<"Prefix to files used to communicate with the debug adaptor when "
+ "using --launch-target.">;
+def: Separate<["-"], "f">,
+ Alias<comm_files_prefix>,
+ HelpText<"Alias for --comm-files-prefix">;
Index: lldb/tools/lldb-vscode/JSONUtils.h
===================================================================
--- lldb/tools/lldb-vscode/JSONUtils.h
+++ lldb/tools/lldb-vscode/JSONUtils.h
@@ -449,11 +449,20 @@
/// The original launch_request object whose fields are used to construct
/// the reverse request object.
///
+/// \param[in] debug_adaptor_path
+/// Path to the current debug adaptor. It will be used to delegate the
+/// launch of the target.
+///
+/// \param[in] pid_file
+/// A file that will be used to communicate the pid of the debuggee.
+///
/// \return
/// A "runInTerminal" JSON object that follows the specification outlined by
/// Microsoft.
llvm::json::Object
-CreateRunInTerminalReverseRequest(const llvm::json::Object &launch_request);
+CreateRunInTerminalReverseRequest(const llvm::json::Object &launch_request,
+ llvm::StringRef debug_adaptor_path,
+ llvm::StringRef pid_file);
} // namespace lldb_vscode
Index: lldb/tools/lldb-vscode/JSONUtils.cpp
===================================================================
--- lldb/tools/lldb-vscode/JSONUtils.cpp
+++ lldb/tools/lldb-vscode/JSONUtils.cpp
@@ -1001,7 +1001,9 @@
/// See
/// https://microsoft.github.io/debug-adapter-protocol/specification#Reverse_Requests_RunInTerminal
llvm::json::Object
-CreateRunInTerminalReverseRequest(const llvm::json::Object &launch_request) {
+CreateRunInTerminalReverseRequest(const llvm::json::Object &launch_request,
+ llvm::StringRef debug_adaptor_path,
+ llvm::StringRef pid_file) {
llvm::json::Object reverse_request;
reverse_request.try_emplace("type", "request");
reverse_request.try_emplace("command", "runInTerminal");
@@ -1012,10 +1014,13 @@
run_in_terminal_args.try_emplace("kind", "integrated");
auto launch_request_arguments = launch_request.getObject("arguments");
- std::vector<std::string> args = GetStrings(launch_request_arguments, "args");
// The program path must be the first entry in the "args" field
- args.insert(args.begin(),
- GetString(launch_request_arguments, "program").str());
+ std::vector<std::string> args = {
+ debug_adaptor_path.str(), "-f", pid_file.str(), "-t",
+ GetString(launch_request_arguments, "program").str()};
+ std::vector<std::string> target_args =
+ GetStrings(launch_request_arguments, "args");
+ args.insert(args.end(), target_args.begin(), target_args.end());
run_in_terminal_args.try_emplace("args", args);
const auto cwd = GetString(launch_request_arguments, "cwd");
Index: lldb/tools/lldb-vscode/CMakeLists.txt
===================================================================
--- lldb/tools/lldb-vscode/CMakeLists.txt
+++ lldb/tools/lldb-vscode/CMakeLists.txt
@@ -31,6 +31,7 @@
IOStream.cpp
JSONUtils.cpp
LLDBUtils.cpp
+ RunInTerminal.cpp
SourceBreakpoint.cpp
VSCode.cpp
Index: lldb/test/API/tools/lldb-vscode/runInTerminal/TestVSCode_runInTerminal.py
===================================================================
--- lldb/test/API/tools/lldb-vscode/runInTerminal/TestVSCode_runInTerminal.py
+++ lldb/test/API/tools/lldb-vscode/runInTerminal/TestVSCode_runInTerminal.py
@@ -11,13 +11,14 @@
import lldbvscode_testcase
import time
import os
+import subprocess
class TestVSCode_runInTerminal(lldbvscode_testcase.VSCodeTestCaseBase):
mydir = TestBase.compute_mydir(__file__)
- @skipUnlessDarwin
+ @skipIfWindows
@skipIfRemote
def test_runInTerminal(self):
'''
@@ -46,3 +47,37 @@
# We verify we were able to set the environment
env = self.vscode.request_evaluate('foo')['body']['result']
self.assertIn('bar', env)
+
+ @skipIfWindows
+ @skipIfRemote
+ def test_runInTerminalInvalidTarget(self):
+ self.build_and_create_debug_adaptor()
+ response = self.launch(
+ "INVALIDPROGRAM", stopOnEntry=True, runInTerminal=True, args=["foobar"], env=["FOO=bar"], expectFailure=True)
+ self.assertFalse(response['success'])
+ self.assertIn("Could not create a target for a program 'INVALIDPROGRAM': unable to find executable",
+ response['message'])
+
+ @skipIfWindows
+ @skipIfRemote
+ def test_missingArgInRunInTerminalLauncher(self):
+ proc = subprocess.run([self.lldbVSCodeExec, "--launch-target", "INVALIDPROGRAM"],
+ capture_output=True, universal_newlines=True)
+ self.assertTrue(proc.returncode != 0)
+ self.assertIn('"--launch-target" requires "--comm-files-prefix" to be specified', proc.stderr)
+
+ @skipIfWindows
+ @skipIfRemote
+ def test_NonAttachedRunInTerminalLauncher(self):
+ prefix = "/tmp/lldb-vscode-runInTerminal-test"
+ # We clear the error file
+ with open(prefix + ".err", "w") as f:
+ pass
+
+ proc = subprocess.run([self.lldbVSCodeExec, "--comm-files-prefix", prefix, "--launch-target", "INVALIDPROGRAM"],
+ capture_output=True, universal_newlines=True, env={"LLDB_VSCODE_RIT_TIMEOUT_IN_MS": "1000"})
+
+ # We check the error message in both stderr and the error file
+ self.assertIn("lldb-vscode didn't attach to this process", proc.stderr)
+ with open(prefix + ".err", "r") as f:
+ self.assertIn("lldb-vscode didn't attach to this process", f.readline())
Index: lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py
===================================================================
--- lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py
+++ lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py
@@ -617,7 +617,7 @@
stopCommands=None, exitCommands=None,
terminateCommands=None ,sourcePath=None,
debuggerRoot=None, launchCommands=None, sourceMap=None,
- runInTerminal=False):
+ runInTerminal=False, expectFailure=False):
args_dict = {
'program': program
}
@@ -665,9 +665,10 @@
}
response = self.send_recv(command_dict)
- # Wait for a 'process' and 'initialized' event in any order
- self.wait_for_event(filter=['process', 'initialized'])
- self.wait_for_event(filter=['process', 'initialized'])
+ if not expectFailure:
+ # Wait for a 'process' and 'initialized' event in any order
+ self.wait_for_event(filter=['process', 'initialized'])
+ self.wait_for_event(filter=['process', 'initialized'])
return response
def request_next(self, threadId):
Index: lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py
===================================================================
--- lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py
+++ lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py
@@ -282,7 +282,8 @@
trace=False, initCommands=None, preRunCommands=None,
stopCommands=None, exitCommands=None, terminateCommands=None,
sourcePath=None, debuggerRoot=None, launchCommands=None,
- sourceMap=None, disconnectAutomatically=True, runInTerminal=False):
+ sourceMap=None, disconnectAutomatically=True, runInTerminal=False,
+ expectFailure=False):
'''Sending launch request to vscode
'''
@@ -317,7 +318,12 @@
debuggerRoot=debuggerRoot,
launchCommands=launchCommands,
sourceMap=sourceMap,
- runInTerminal=runInTerminal)
+ runInTerminal=runInTerminal,
+ expectFailure=expectFailure)
+
+ if expectFailure:
+ return response
+
if not (response and response['success']):
self.assertTrue(response['success'],
'launch failed (%s)' % (response['message']))
@@ -325,6 +331,7 @@
# attached a runInTerminal process to finish initialization.
if runInTerminal:
self.vscode.request_configurationDone()
+ return response
def build_and_launch(self, program, args=None, cwd=None, env=None,
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits