1 //===-- RunInTerminal.cpp ---------------------------------------*- C++ -*-===// 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 "RunInTerminal.h" 10 #include "JSONUtils.h" 11 12 #if !defined(_WIN32) 13 #include <sys/stat.h> 14 #include <sys/types.h> 15 #include <unistd.h> 16 #endif 17 18 #include <chrono> 19 #include <future> 20 21 #include "llvm/Support/FileSystem.h" 22 23 using namespace llvm; 24 25 namespace lldb_dap { 26 27 const RunInTerminalMessagePid *RunInTerminalMessage::GetAsPidMessage() const { 28 return static_cast<const RunInTerminalMessagePid *>(this); 29 } 30 31 const RunInTerminalMessageError * 32 RunInTerminalMessage::GetAsErrorMessage() const { 33 return static_cast<const RunInTerminalMessageError *>(this); 34 } 35 36 RunInTerminalMessage::RunInTerminalMessage(RunInTerminalMessageKind kind) 37 : kind(kind) {} 38 39 RunInTerminalMessagePid::RunInTerminalMessagePid(lldb::pid_t pid) 40 : RunInTerminalMessage(eRunInTerminalMessageKindPID), pid(pid) {} 41 42 json::Value RunInTerminalMessagePid::ToJSON() const { 43 return json::Object{{"kind", "pid"}, {"pid", static_cast<int64_t>(pid)}}; 44 } 45 46 RunInTerminalMessageError::RunInTerminalMessageError(StringRef error) 47 : RunInTerminalMessage(eRunInTerminalMessageKindError), error(error) {} 48 49 json::Value RunInTerminalMessageError::ToJSON() const { 50 return json::Object{{"kind", "error"}, {"value", error}}; 51 } 52 53 RunInTerminalMessageDidAttach::RunInTerminalMessageDidAttach() 54 : RunInTerminalMessage(eRunInTerminalMessageKindDidAttach) {} 55 56 json::Value RunInTerminalMessageDidAttach::ToJSON() const { 57 return json::Object{{"kind", "didAttach"}}; 58 } 59 60 static Expected<RunInTerminalMessageUP> 61 ParseJSONMessage(const json::Value &json) { 62 if (const json::Object *obj = json.getAsObject()) { 63 if (std::optional<StringRef> kind = obj->getString("kind")) { 64 if (*kind == "pid") { 65 if (std::optional<int64_t> pid = obj->getInteger("pid")) 66 return std::make_unique<RunInTerminalMessagePid>( 67 static_cast<lldb::pid_t>(*pid)); 68 } else if (*kind == "error") { 69 if (std::optional<StringRef> error = obj->getString("error")) 70 return std::make_unique<RunInTerminalMessageError>(*error); 71 } else if (*kind == "didAttach") { 72 return std::make_unique<RunInTerminalMessageDidAttach>(); 73 } 74 } 75 } 76 77 return createStringError(inconvertibleErrorCode(), 78 "Incorrect JSON message: " + JSONToString(json)); 79 } 80 81 static Expected<RunInTerminalMessageUP> 82 GetNextMessage(FifoFileIO &io, std::chrono::milliseconds timeout) { 83 if (Expected<json::Value> json = io.ReadJSON(timeout)) 84 return ParseJSONMessage(*json); 85 else 86 return json.takeError(); 87 } 88 89 static Error ToError(const RunInTerminalMessage &message) { 90 if (message.kind == eRunInTerminalMessageKindError) 91 return createStringError(inconvertibleErrorCode(), 92 message.GetAsErrorMessage()->error); 93 return createStringError(inconvertibleErrorCode(), 94 "Unexpected JSON message: " + 95 JSONToString(message.ToJSON())); 96 } 97 98 RunInTerminalLauncherCommChannel::RunInTerminalLauncherCommChannel( 99 StringRef comm_file) 100 : m_io(comm_file, "debug adaptor") {} 101 102 Error RunInTerminalLauncherCommChannel::WaitUntilDebugAdaptorAttaches( 103 std::chrono::milliseconds timeout) { 104 if (Expected<RunInTerminalMessageUP> message = 105 GetNextMessage(m_io, timeout)) { 106 if (message.get()->kind == eRunInTerminalMessageKindDidAttach) 107 return Error::success(); 108 else 109 return ToError(*message.get()); 110 } else 111 return message.takeError(); 112 } 113 114 Error RunInTerminalLauncherCommChannel::NotifyPid() { 115 return m_io.SendJSON(RunInTerminalMessagePid(getpid()).ToJSON()); 116 } 117 118 void RunInTerminalLauncherCommChannel::NotifyError(StringRef error) { 119 if (Error err = m_io.SendJSON(RunInTerminalMessageError(error).ToJSON(), 120 std::chrono::seconds(2))) 121 llvm::errs() << llvm::toString(std::move(err)) << "\n"; 122 } 123 124 RunInTerminalDebugAdapterCommChannel::RunInTerminalDebugAdapterCommChannel( 125 StringRef comm_file) 126 : m_io(comm_file, "runInTerminal launcher") {} 127 128 // Can't use \a std::future<llvm::Error> because it doesn't compile on Windows 129 std::future<lldb::SBError> 130 RunInTerminalDebugAdapterCommChannel::NotifyDidAttach() { 131 return std::async(std::launch::async, [&]() { 132 lldb::SBError error; 133 if (llvm::Error err = 134 m_io.SendJSON(RunInTerminalMessageDidAttach().ToJSON())) 135 error.SetErrorString(llvm::toString(std::move(err)).c_str()); 136 return error; 137 }); 138 } 139 140 Expected<lldb::pid_t> RunInTerminalDebugAdapterCommChannel::GetLauncherPid() { 141 if (Expected<RunInTerminalMessageUP> message = 142 GetNextMessage(m_io, std::chrono::seconds(20))) { 143 if (message.get()->kind == eRunInTerminalMessageKindPID) 144 return message.get()->GetAsPidMessage()->pid; 145 return ToError(*message.get()); 146 } else { 147 return message.takeError(); 148 } 149 } 150 151 std::string RunInTerminalDebugAdapterCommChannel::GetLauncherError() { 152 // We know there's been an error, so a small timeout is enough. 153 if (Expected<RunInTerminalMessageUP> message = 154 GetNextMessage(m_io, std::chrono::seconds(1))) 155 return toString(ToError(*message.get())); 156 else 157 return toString(message.takeError()); 158 } 159 160 Expected<std::shared_ptr<FifoFile>> CreateRunInTerminalCommFile() { 161 SmallString<256> comm_file; 162 if (std::error_code EC = sys::fs::getPotentiallyUniqueTempFileName( 163 "lldb-dap-run-in-terminal-comm", "", comm_file)) 164 return createStringError(EC, "Error making unique file name for " 165 "runInTerminal communication files"); 166 167 return CreateFifoFile(comm_file.str()); 168 } 169 170 } // namespace lldb_dap 171