1 //===-- RunInTerminal.cpp ---------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "RunInTerminal.h"
10 #include "JSONUtils.h"
14 #include <sys/types.h>
21 #include "llvm/Support/FileSystem.h"
27 const RunInTerminalMessagePid
*RunInTerminalMessage::GetAsPidMessage() const {
28 return static_cast<const RunInTerminalMessagePid
*>(this);
31 const RunInTerminalMessageError
*
32 RunInTerminalMessage::GetAsErrorMessage() const {
33 return static_cast<const RunInTerminalMessageError
*>(this);
36 RunInTerminalMessage::RunInTerminalMessage(RunInTerminalMessageKind kind
)
39 RunInTerminalMessagePid::RunInTerminalMessagePid(lldb::pid_t pid
)
40 : RunInTerminalMessage(eRunInTerminalMessageKindPID
), pid(pid
) {}
42 json::Value
RunInTerminalMessagePid::ToJSON() const {
43 return json::Object
{{"kind", "pid"}, {"pid", static_cast<int64_t>(pid
)}};
46 RunInTerminalMessageError::RunInTerminalMessageError(StringRef error
)
47 : RunInTerminalMessage(eRunInTerminalMessageKindError
), error(error
) {}
49 json::Value
RunInTerminalMessageError::ToJSON() const {
50 return json::Object
{{"kind", "error"}, {"value", error
}};
53 RunInTerminalMessageDidAttach::RunInTerminalMessageDidAttach()
54 : RunInTerminalMessage(eRunInTerminalMessageKindDidAttach
) {}
56 json::Value
RunInTerminalMessageDidAttach::ToJSON() const {
57 return json::Object
{{"kind", "didAttach"}};
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")) {
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
>();
77 return createStringError(inconvertibleErrorCode(),
78 "Incorrect JSON message: " + JSONToString(json
));
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
);
86 return json
.takeError();
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()));
98 RunInTerminalLauncherCommChannel::RunInTerminalLauncherCommChannel(
100 : m_io(comm_file
, "debug adaptor") {}
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();
109 return ToError(*message
.get());
111 return message
.takeError();
114 Error
RunInTerminalLauncherCommChannel::NotifyPid() {
115 return m_io
.SendJSON(RunInTerminalMessagePid(getpid()).ToJSON());
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";
124 RunInTerminalDebugAdapterCommChannel::RunInTerminalDebugAdapterCommChannel(
126 : m_io(comm_file
, "runInTerminal launcher") {}
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
, [&]() {
133 if (llvm::Error err
=
134 m_io
.SendJSON(RunInTerminalMessageDidAttach().ToJSON()))
135 error
.SetErrorString(llvm::toString(std::move(err
)).c_str());
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());
147 return message
.takeError();
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()));
157 return toString(message
.takeError());
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");
167 return CreateFifoFile(comm_file
.str());
170 } // namespace lldb_dap