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"
13 #include <sys/types.h>
22 #include "llvm/Support/FileSystem.h"
24 #include "lldb/lldb-defines.h"
30 const RunInTerminalMessagePid
*RunInTerminalMessage::GetAsPidMessage() const {
31 return static_cast<const RunInTerminalMessagePid
*>(this);
34 const RunInTerminalMessageError
*
35 RunInTerminalMessage::GetAsErrorMessage() const {
36 return static_cast<const RunInTerminalMessageError
*>(this);
39 RunInTerminalMessage::RunInTerminalMessage(RunInTerminalMessageKind kind
)
42 RunInTerminalMessagePid::RunInTerminalMessagePid(lldb::pid_t pid
)
43 : RunInTerminalMessage(eRunInTerminalMessageKindPID
), pid(pid
) {}
45 json::Value
RunInTerminalMessagePid::ToJSON() const {
46 return json::Object
{{"kind", "pid"}, {"pid", static_cast<int64_t>(pid
)}};
49 RunInTerminalMessageError::RunInTerminalMessageError(StringRef error
)
50 : RunInTerminalMessage(eRunInTerminalMessageKindError
), error(error
) {}
52 json::Value
RunInTerminalMessageError::ToJSON() const {
53 return json::Object
{{"kind", "error"}, {"value", error
}};
56 RunInTerminalMessageDidAttach::RunInTerminalMessageDidAttach()
57 : RunInTerminalMessage(eRunInTerminalMessageKindDidAttach
) {}
59 json::Value
RunInTerminalMessageDidAttach::ToJSON() const {
60 return json::Object
{{"kind", "didAttach"}};
63 static Expected
<RunInTerminalMessageUP
>
64 ParseJSONMessage(const json::Value
&json
) {
65 if (const json::Object
*obj
= json
.getAsObject()) {
66 if (std::optional
<StringRef
> kind
= obj
->getString("kind")) {
68 if (std::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 (std::optional
<StringRef
> error
= obj
->getString("error"))
73 return std::make_unique
<RunInTerminalMessageError
>(*error
);
74 } else if (*kind
== "didAttach") {
75 return std::make_unique
<RunInTerminalMessageDidAttach
>();
80 return createStringError(inconvertibleErrorCode(),
81 "Incorrect JSON message: " + JSONToString(json
));
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
);
89 return json
.takeError();
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()));
101 RunInTerminalLauncherCommChannel::RunInTerminalLauncherCommChannel(
103 : m_io(comm_file
, "debug adaptor") {}
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();
112 return ToError(*message
.get());
114 return message
.takeError();
117 Error
RunInTerminalLauncherCommChannel::NotifyPid() {
118 return m_io
.SendJSON(RunInTerminalMessagePid(getpid()).ToJSON());
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";
127 RunInTerminalDebugAdapterCommChannel::RunInTerminalDebugAdapterCommChannel(
129 : m_io(comm_file
, "runInTerminal launcher") {}
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
, [&]() {
136 if (llvm::Error err
=
137 m_io
.SendJSON(RunInTerminalMessageDidAttach().ToJSON()))
138 error
.SetErrorString(llvm::toString(std::move(err
)).c_str());
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());
150 return message
.takeError();
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()));
160 return toString(message
.takeError());
163 Expected
<std::shared_ptr
<FifoFile
>> CreateRunInTerminalCommFile() {
164 SmallString
<256> comm_file
;
165 if (std::error_code EC
= sys::fs::getPotentiallyUniqueTempFileName(
166 "lldb-dap-run-in-terminal-comm", "", comm_file
))
167 return createStringError(EC
, "Error making unique file name for "
168 "runInTerminal communication files");
170 return CreateFifoFile(comm_file
.str());
173 } // namespace lldb_dap