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