| //===-- 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 |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "RunInTerminal.h" |
| |
| #if !defined(_WIN32) |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| #endif |
| |
| #include <chrono> |
| #include <fstream> |
| #include <future> |
| #include <thread> |
| |
| #include "llvm/Support/FileSystem.h" |
| |
| #include "lldb/lldb-defines.h" |
| |
| using namespace llvm; |
| |
| namespace lldb_vscode { |
| |
| const RunInTerminalMessagePid *RunInTerminalMessage::GetAsPidMessage() const { |
| return static_cast<const RunInTerminalMessagePid *>(this); |
| } |
| |
| const RunInTerminalMessageError * |
| RunInTerminalMessage::GetAsErrorMessage() const { |
| return static_cast<const RunInTerminalMessageError *>(this); |
| } |
| |
| RunInTerminalMessage::RunInTerminalMessage(RunInTerminalMessageKind kind) |
| : kind(kind) {} |
| |
| RunInTerminalMessagePid::RunInTerminalMessagePid(lldb::pid_t pid) |
| : RunInTerminalMessage(eRunInTerminalMessageKindPID), pid(pid) {} |
| |
| json::Value RunInTerminalMessagePid::ToJSON() const { |
| return json::Object{{"kind", "pid"}, {"pid", static_cast<int64_t>(pid)}}; |
| } |
| |
| RunInTerminalMessageError::RunInTerminalMessageError(StringRef error) |
| : RunInTerminalMessage(eRunInTerminalMessageKindError), error(error) {} |
| |
| json::Value RunInTerminalMessageError::ToJSON() const { |
| return json::Object{{"kind", "error"}, {"value", error}}; |
| } |
| |
| RunInTerminalMessageDidAttach::RunInTerminalMessageDidAttach() |
| : RunInTerminalMessage(eRunInTerminalMessageKindDidAttach) {} |
| |
| json::Value RunInTerminalMessageDidAttach::ToJSON() const { |
| return json::Object{{"kind", "didAttach"}}; |
| } |
| |
| static Expected<RunInTerminalMessageUP> |
| ParseJSONMessage(const json::Value &json) { |
| if (const json::Object *obj = json.getAsObject()) { |
| if (Optional<StringRef> kind = obj->getString("kind")) { |
| if (*kind == "pid") { |
| if (Optional<int64_t> pid = obj->getInteger("pid")) |
| return std::make_unique<RunInTerminalMessagePid>( |
| static_cast<lldb::pid_t>(*pid)); |
| } else if (*kind == "error") { |
| if (Optional<StringRef> error = obj->getString("error")) |
| return std::make_unique<RunInTerminalMessageError>(*error); |
| } else if (*kind == "didAttach") { |
| return std::make_unique<RunInTerminalMessageDidAttach>(); |
| } |
| } |
| } |
| |
| return createStringError(inconvertibleErrorCode(), |
| "Incorrect JSON message: " + JSONToString(json)); |
| } |
| |
| static Expected<RunInTerminalMessageUP> |
| GetNextMessage(FifoFileIO &io, std::chrono::milliseconds timeout) { |
| if (Expected<json::Value> json = io.ReadJSON(timeout)) |
| return ParseJSONMessage(*json); |
| else |
| return json.takeError(); |
| } |
| |
| static Error ToError(const RunInTerminalMessage &message) { |
| if (message.kind == eRunInTerminalMessageKindError) |
| return createStringError(inconvertibleErrorCode(), |
| message.GetAsErrorMessage()->error); |
| return createStringError(inconvertibleErrorCode(), |
| "Unexpected JSON message: " + |
| JSONToString(message.ToJSON())); |
| } |
| |
| RunInTerminalLauncherCommChannel::RunInTerminalLauncherCommChannel( |
| StringRef comm_file) |
| : m_io(comm_file, "debug adaptor") {} |
| |
| Error RunInTerminalLauncherCommChannel::WaitUntilDebugAdaptorAttaches( |
| std::chrono::milliseconds timeout) { |
| if (Expected<RunInTerminalMessageUP> message = |
| GetNextMessage(m_io, timeout)) { |
| if (message.get()->kind == eRunInTerminalMessageKindDidAttach) |
| return Error::success(); |
| else |
| return ToError(*message.get()); |
| } else |
| return message.takeError(); |
| } |
| |
| Error RunInTerminalLauncherCommChannel::NotifyPid() { |
| return m_io.SendJSON(RunInTerminalMessagePid(getpid()).ToJSON()); |
| } |
| |
| void RunInTerminalLauncherCommChannel::NotifyError(StringRef error) { |
| if (Error err = m_io.SendJSON(RunInTerminalMessageError(error).ToJSON(), |
| std::chrono::seconds(2))) |
| llvm::errs() << llvm::toString(std::move(err)) << "\n"; |
| } |
| |
| RunInTerminalDebugAdapterCommChannel::RunInTerminalDebugAdapterCommChannel( |
| StringRef comm_file) |
| : m_io(comm_file, "runInTerminal launcher") {} |
| |
| // Can't use \a std::future<llvm::Error> because it doesn't compile on Windows |
| std::future<lldb::SBError> |
| RunInTerminalDebugAdapterCommChannel::NotifyDidAttach() { |
| return std::async(std::launch::async, [&]() { |
| lldb::SBError error; |
| if (llvm::Error err = |
| m_io.SendJSON(RunInTerminalMessageDidAttach().ToJSON())) |
| error.SetErrorString(llvm::toString(std::move(err)).c_str()); |
| return error; |
| }); |
| } |
| |
| Expected<lldb::pid_t> RunInTerminalDebugAdapterCommChannel::GetLauncherPid() { |
| if (Expected<RunInTerminalMessageUP> message = |
| GetNextMessage(m_io, std::chrono::seconds(20))) { |
| if (message.get()->kind == eRunInTerminalMessageKindPID) |
| return message.get()->GetAsPidMessage()->pid; |
| return ToError(*message.get()); |
| } else { |
| return message.takeError(); |
| } |
| } |
| |
| std::string RunInTerminalDebugAdapterCommChannel::GetLauncherError() { |
| // We know there's been an error, so a small timeout is enough. |
| if (Expected<RunInTerminalMessageUP> message = |
| GetNextMessage(m_io, std::chrono::seconds(1))) |
| return toString(ToError(*message.get())); |
| else |
| return toString(message.takeError()); |
| } |
| |
| Expected<std::shared_ptr<FifoFile>> CreateRunInTerminalCommFile() { |
| SmallString<256> comm_file; |
| if (std::error_code EC = sys::fs::getPotentiallyUniqueTempFileName( |
| "lldb-vscode-run-in-terminal-comm", "", comm_file)) |
| return createStringError(EC, "Error making unique file name for " |
| "runInTerminal communication files"); |
| |
| return CreateFifoFile(comm_file.str()); |
| } |
| |
| } // namespace lldb_vscode |