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