1 //===-- ScriptInterpreter.cpp ---------------------------------------------===//
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 "lldb/Interpreter/ScriptInterpreter.h"
10 #include "lldb/Core/Debugger.h"
11 #include "lldb/Host/ConnectionFileDescriptor.h"
12 #include "lldb/Host/Pipe.h"
13 #include "lldb/Host/PseudoTerminal.h"
14 #include "lldb/Interpreter/CommandReturnObject.h"
15 #include "lldb/Utility/Status.h"
16 #include "lldb/Utility/Stream.h"
17 #include "lldb/Utility/StringList.h"
18 #if defined(_WIN32)
19 #include "lldb/Host/windows/ConnectionGenericFileWindows.h"
20 #endif
21 #include <cstdio>
22 #include <cstdlib>
23 #include <memory>
24 #include <optional>
25 #include <string>
26
27 using namespace lldb;
28 using namespace lldb_private;
29
ScriptInterpreter(Debugger & debugger,lldb::ScriptLanguage script_lang)30 ScriptInterpreter::ScriptInterpreter(Debugger &debugger,
31 lldb::ScriptLanguage script_lang)
32 : m_debugger(debugger), m_script_lang(script_lang) {}
33
CollectDataForBreakpointCommandCallback(std::vector<std::reference_wrapper<BreakpointOptions>> & bp_options_vec,CommandReturnObject & result)34 void ScriptInterpreter::CollectDataForBreakpointCommandCallback(
35 std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec,
36 CommandReturnObject &result) {
37 result.AppendError(
38 "This script interpreter does not support breakpoint callbacks.");
39 }
40
CollectDataForWatchpointCommandCallback(WatchpointOptions * bp_options,CommandReturnObject & result)41 void ScriptInterpreter::CollectDataForWatchpointCommandCallback(
42 WatchpointOptions *bp_options, CommandReturnObject &result) {
43 result.AppendError(
44 "This script interpreter does not support watchpoint callbacks.");
45 }
46
GetInterpreterInfo()47 StructuredData::DictionarySP ScriptInterpreter::GetInterpreterInfo() {
48 return nullptr;
49 }
50
LoadScriptingModule(const char * filename,const LoadScriptOptions & options,lldb_private::Status & error,StructuredData::ObjectSP * module_sp,FileSpec extra_search_dir)51 bool ScriptInterpreter::LoadScriptingModule(const char *filename,
52 const LoadScriptOptions &options,
53 lldb_private::Status &error,
54 StructuredData::ObjectSP *module_sp,
55 FileSpec extra_search_dir) {
56 error.SetErrorString(
57 "This script interpreter does not support importing modules.");
58 return false;
59 }
60
LanguageToString(lldb::ScriptLanguage language)61 std::string ScriptInterpreter::LanguageToString(lldb::ScriptLanguage language) {
62 switch (language) {
63 case eScriptLanguageNone:
64 return "None";
65 case eScriptLanguagePython:
66 return "Python";
67 case eScriptLanguageLua:
68 return "Lua";
69 case eScriptLanguageUnknown:
70 return "Unknown";
71 }
72 llvm_unreachable("Unhandled ScriptInterpreter!");
73 }
74
75 lldb::DataExtractorSP
GetDataExtractorFromSBData(const lldb::SBData & data) const76 ScriptInterpreter::GetDataExtractorFromSBData(const lldb::SBData &data) const {
77 return data.m_opaque_sp;
78 }
79
GetOpaqueTypeFromSBBreakpoint(const lldb::SBBreakpoint & breakpoint) const80 lldb::BreakpointSP ScriptInterpreter::GetOpaqueTypeFromSBBreakpoint(
81 const lldb::SBBreakpoint &breakpoint) const {
82 return breakpoint.m_opaque_wp.lock();
83 }
84
GetOpaqueTypeFromSBAttachInfo(const lldb::SBAttachInfo & attach_info) const85 lldb::ProcessAttachInfoSP ScriptInterpreter::GetOpaqueTypeFromSBAttachInfo(
86 const lldb::SBAttachInfo &attach_info) const {
87 return attach_info.m_opaque_sp;
88 }
89
GetOpaqueTypeFromSBLaunchInfo(const lldb::SBLaunchInfo & launch_info) const90 lldb::ProcessLaunchInfoSP ScriptInterpreter::GetOpaqueTypeFromSBLaunchInfo(
91 const lldb::SBLaunchInfo &launch_info) const {
92 return std::make_shared<ProcessLaunchInfo>(
93 *reinterpret_cast<ProcessLaunchInfo *>(launch_info.m_opaque_sp.get()));
94 }
95
96 Status
GetStatusFromSBError(const lldb::SBError & error) const97 ScriptInterpreter::GetStatusFromSBError(const lldb::SBError &error) const {
98 if (error.m_opaque_up)
99 return *error.m_opaque_up;
100
101 return Status();
102 }
103
104 Event *
GetOpaqueTypeFromSBEvent(const lldb::SBEvent & event) const105 ScriptInterpreter::GetOpaqueTypeFromSBEvent(const lldb::SBEvent &event) const {
106 return event.m_opaque_ptr;
107 }
108
GetOpaqueTypeFromSBStream(const lldb::SBStream & stream) const109 lldb::StreamSP ScriptInterpreter::GetOpaqueTypeFromSBStream(
110 const lldb::SBStream &stream) const {
111 if (stream.m_opaque_up) {
112 lldb::StreamSP s = std::make_shared<lldb_private::StreamString>();
113 *s << reinterpret_cast<StreamString *>(stream.m_opaque_up.get())->m_packet;
114 return s;
115 }
116
117 return nullptr;
118 }
119
120 std::optional<MemoryRegionInfo>
GetOpaqueTypeFromSBMemoryRegionInfo(const lldb::SBMemoryRegionInfo & mem_region) const121 ScriptInterpreter::GetOpaqueTypeFromSBMemoryRegionInfo(
122 const lldb::SBMemoryRegionInfo &mem_region) const {
123 if (!mem_region.m_opaque_up)
124 return std::nullopt;
125 return *mem_region.m_opaque_up.get();
126 }
127
128 lldb::ScriptLanguage
StringToLanguage(const llvm::StringRef & language)129 ScriptInterpreter::StringToLanguage(const llvm::StringRef &language) {
130 if (language.equals_insensitive(LanguageToString(eScriptLanguageNone)))
131 return eScriptLanguageNone;
132 if (language.equals_insensitive(LanguageToString(eScriptLanguagePython)))
133 return eScriptLanguagePython;
134 if (language.equals_insensitive(LanguageToString(eScriptLanguageLua)))
135 return eScriptLanguageLua;
136 return eScriptLanguageUnknown;
137 }
138
SetBreakpointCommandCallback(std::vector<std::reference_wrapper<BreakpointOptions>> & bp_options_vec,const char * callback_text)139 Status ScriptInterpreter::SetBreakpointCommandCallback(
140 std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec,
141 const char *callback_text) {
142 Status error;
143 for (BreakpointOptions &bp_options : bp_options_vec) {
144 error = SetBreakpointCommandCallback(bp_options, callback_text,
145 /*is_callback=*/false);
146 if (!error.Success())
147 break;
148 }
149 return error;
150 }
151
SetBreakpointCommandCallbackFunction(std::vector<std::reference_wrapper<BreakpointOptions>> & bp_options_vec,const char * function_name,StructuredData::ObjectSP extra_args_sp)152 Status ScriptInterpreter::SetBreakpointCommandCallbackFunction(
153 std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec,
154 const char *function_name, StructuredData::ObjectSP extra_args_sp) {
155 Status error;
156 for (BreakpointOptions &bp_options : bp_options_vec) {
157 error = SetBreakpointCommandCallbackFunction(bp_options, function_name,
158 extra_args_sp);
159 if (!error.Success())
160 return error;
161 }
162 return error;
163 }
164
165 std::unique_ptr<ScriptInterpreterLocker>
AcquireInterpreterLock()166 ScriptInterpreter::AcquireInterpreterLock() {
167 return std::make_unique<ScriptInterpreterLocker>();
168 }
169
ReadThreadBytesReceived(void * baton,const void * src,size_t src_len)170 static void ReadThreadBytesReceived(void *baton, const void *src,
171 size_t src_len) {
172 if (src && src_len) {
173 Stream *strm = (Stream *)baton;
174 strm->Write(src, src_len);
175 strm->Flush();
176 }
177 }
178
179 llvm::Expected<std::unique_ptr<ScriptInterpreterIORedirect>>
Create(bool enable_io,Debugger & debugger,CommandReturnObject * result)180 ScriptInterpreterIORedirect::Create(bool enable_io, Debugger &debugger,
181 CommandReturnObject *result) {
182 if (enable_io)
183 return std::unique_ptr<ScriptInterpreterIORedirect>(
184 new ScriptInterpreterIORedirect(debugger, result));
185
186 auto nullin = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL),
187 File::eOpenOptionReadOnly);
188 if (!nullin)
189 return nullin.takeError();
190
191 auto nullout = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL),
192 File::eOpenOptionWriteOnly);
193 if (!nullout)
194 return nullin.takeError();
195
196 return std::unique_ptr<ScriptInterpreterIORedirect>(
197 new ScriptInterpreterIORedirect(std::move(*nullin), std::move(*nullout)));
198 }
199
ScriptInterpreterIORedirect(std::unique_ptr<File> input,std::unique_ptr<File> output)200 ScriptInterpreterIORedirect::ScriptInterpreterIORedirect(
201 std::unique_ptr<File> input, std::unique_ptr<File> output)
202 : m_input_file_sp(std::move(input)),
203 m_output_file_sp(std::make_shared<StreamFile>(std::move(output))),
204 m_error_file_sp(m_output_file_sp),
205 m_communication("lldb.ScriptInterpreterIORedirect.comm"),
206 m_disconnect(false) {}
207
ScriptInterpreterIORedirect(Debugger & debugger,CommandReturnObject * result)208 ScriptInterpreterIORedirect::ScriptInterpreterIORedirect(
209 Debugger &debugger, CommandReturnObject *result)
210 : m_communication("lldb.ScriptInterpreterIORedirect.comm"),
211 m_disconnect(false) {
212
213 if (result) {
214 m_input_file_sp = debugger.GetInputFileSP();
215
216 Pipe pipe;
217 Status pipe_result = pipe.CreateNew(false);
218 #if defined(_WIN32)
219 lldb::file_t read_file = pipe.GetReadNativeHandle();
220 pipe.ReleaseReadFileDescriptor();
221 std::unique_ptr<ConnectionGenericFile> conn_up =
222 std::make_unique<ConnectionGenericFile>(read_file, true);
223 #else
224 std::unique_ptr<ConnectionFileDescriptor> conn_up =
225 std::make_unique<ConnectionFileDescriptor>(
226 pipe.ReleaseReadFileDescriptor(), true);
227 #endif
228
229 if (conn_up->IsConnected()) {
230 m_communication.SetConnection(std::move(conn_up));
231 m_communication.SetReadThreadBytesReceivedCallback(
232 ReadThreadBytesReceived, &result->GetOutputStream());
233 m_communication.StartReadThread();
234 m_disconnect = true;
235
236 FILE *outfile_handle = fdopen(pipe.ReleaseWriteFileDescriptor(), "w");
237 m_output_file_sp = std::make_shared<StreamFile>(outfile_handle, true);
238 m_error_file_sp = m_output_file_sp;
239 if (outfile_handle)
240 ::setbuf(outfile_handle, nullptr);
241
242 result->SetImmediateOutputFile(debugger.GetOutputStream().GetFileSP());
243 result->SetImmediateErrorFile(debugger.GetErrorStream().GetFileSP());
244 }
245 }
246
247 if (!m_input_file_sp || !m_output_file_sp || !m_error_file_sp)
248 debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_file_sp, m_output_file_sp,
249 m_error_file_sp);
250 }
251
Flush()252 void ScriptInterpreterIORedirect::Flush() {
253 if (m_output_file_sp)
254 m_output_file_sp->Flush();
255 if (m_error_file_sp)
256 m_error_file_sp->Flush();
257 }
258
~ScriptInterpreterIORedirect()259 ScriptInterpreterIORedirect::~ScriptInterpreterIORedirect() {
260 if (!m_disconnect)
261 return;
262
263 assert(m_output_file_sp);
264 assert(m_error_file_sp);
265 assert(m_output_file_sp == m_error_file_sp);
266
267 // Close the write end of the pipe since we are done with our one line
268 // script. This should cause the read thread that output_comm is using to
269 // exit.
270 m_output_file_sp->GetFile().Close();
271 // The close above should cause this thread to exit when it gets to the end
272 // of file, so let it get all its data.
273 m_communication.JoinReadThread();
274 // Now we can close the read end of the pipe.
275 m_communication.Disconnect();
276 }
277