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