xref: /llvm-project/lldb/source/Interpreter/ScriptInterpreter.cpp (revision 9a9ec228cdcf75d01be82be5be13e1542f0fc75d)
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(Debugger &debugger,
31                                      lldb::ScriptLanguage script_lang)
32     : m_debugger(debugger), m_script_lang(script_lang) {}
33 
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 
41 void ScriptInterpreter::CollectDataForWatchpointCommandCallback(
42     WatchpointOptions *bp_options, CommandReturnObject &result) {
43   result.AppendError(
44       "This script interpreter does not support watchpoint callbacks.");
45 }
46 
47 StructuredData::DictionarySP ScriptInterpreter::GetInterpreterInfo() {
48   return nullptr;
49 }
50 
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 
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
76 ScriptInterpreter::GetDataExtractorFromSBData(const lldb::SBData &data) const {
77   return data.m_opaque_sp;
78 }
79 
80 lldb::BreakpointSP ScriptInterpreter::GetOpaqueTypeFromSBBreakpoint(
81     const lldb::SBBreakpoint &breakpoint) const {
82   return breakpoint.m_opaque_wp.lock();
83 }
84 
85 lldb::ProcessAttachInfoSP ScriptInterpreter::GetOpaqueTypeFromSBAttachInfo(
86     const lldb::SBAttachInfo &attach_info) const {
87   return attach_info.m_opaque_sp;
88 }
89 
90 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
97 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 *
105 ScriptInterpreter::GetOpaqueTypeFromSBEvent(const lldb::SBEvent &event) const {
106   return event.m_opaque_ptr;
107 }
108 
109 Stream *ScriptInterpreter::GetOpaqueTypeFromSBStream(
110     const lldb::SBStream &stream) const {
111   if (stream.m_opaque_up)
112     return const_cast<lldb::SBStream &>(stream).m_opaque_up.get();
113 
114   return nullptr;
115 }
116 
117 std::optional<MemoryRegionInfo>
118 ScriptInterpreter::GetOpaqueTypeFromSBMemoryRegionInfo(
119     const lldb::SBMemoryRegionInfo &mem_region) const {
120   if (!mem_region.m_opaque_up)
121     return std::nullopt;
122   return *mem_region.m_opaque_up.get();
123 }
124 
125 lldb::ScriptLanguage
126 ScriptInterpreter::StringToLanguage(const llvm::StringRef &language) {
127   if (language.equals_insensitive(LanguageToString(eScriptLanguageNone)))
128     return eScriptLanguageNone;
129   if (language.equals_insensitive(LanguageToString(eScriptLanguagePython)))
130     return eScriptLanguagePython;
131   if (language.equals_insensitive(LanguageToString(eScriptLanguageLua)))
132     return eScriptLanguageLua;
133   return eScriptLanguageUnknown;
134 }
135 
136 Status ScriptInterpreter::SetBreakpointCommandCallback(
137     std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec,
138     const char *callback_text) {
139   Status error;
140   for (BreakpointOptions &bp_options : bp_options_vec) {
141     error = SetBreakpointCommandCallback(bp_options, callback_text,
142                                          /*is_callback=*/false);
143     if (!error.Success())
144       break;
145   }
146   return error;
147 }
148 
149 Status ScriptInterpreter::SetBreakpointCommandCallbackFunction(
150     std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec,
151     const char *function_name, StructuredData::ObjectSP extra_args_sp) {
152   Status error;
153   for (BreakpointOptions &bp_options : bp_options_vec) {
154     error = SetBreakpointCommandCallbackFunction(bp_options, function_name,
155                                                  extra_args_sp);
156     if (!error.Success())
157       return error;
158   }
159   return error;
160 }
161 
162 std::unique_ptr<ScriptInterpreterLocker>
163 ScriptInterpreter::AcquireInterpreterLock() {
164   return std::make_unique<ScriptInterpreterLocker>();
165 }
166 
167 static void ReadThreadBytesReceived(void *baton, const void *src,
168                                     size_t src_len) {
169   if (src && src_len) {
170     Stream *strm = (Stream *)baton;
171     strm->Write(src, src_len);
172     strm->Flush();
173   }
174 }
175 
176 llvm::Expected<std::unique_ptr<ScriptInterpreterIORedirect>>
177 ScriptInterpreterIORedirect::Create(bool enable_io, Debugger &debugger,
178                                     CommandReturnObject *result) {
179   if (enable_io)
180     return std::unique_ptr<ScriptInterpreterIORedirect>(
181         new ScriptInterpreterIORedirect(debugger, result));
182 
183   auto nullin = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL),
184                                             File::eOpenOptionReadOnly);
185   if (!nullin)
186     return nullin.takeError();
187 
188   auto nullout = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL),
189                                              File::eOpenOptionWriteOnly);
190   if (!nullout)
191     return nullin.takeError();
192 
193   return std::unique_ptr<ScriptInterpreterIORedirect>(
194       new ScriptInterpreterIORedirect(std::move(*nullin), std::move(*nullout)));
195 }
196 
197 ScriptInterpreterIORedirect::ScriptInterpreterIORedirect(
198     std::unique_ptr<File> input, std::unique_ptr<File> output)
199     : m_input_file_sp(std::move(input)),
200       m_output_file_sp(std::make_shared<StreamFile>(std::move(output))),
201       m_error_file_sp(m_output_file_sp),
202       m_communication("lldb.ScriptInterpreterIORedirect.comm"),
203       m_disconnect(false) {}
204 
205 ScriptInterpreterIORedirect::ScriptInterpreterIORedirect(
206     Debugger &debugger, CommandReturnObject *result)
207     : m_communication("lldb.ScriptInterpreterIORedirect.comm"),
208       m_disconnect(false) {
209 
210   if (result) {
211     m_input_file_sp = debugger.GetInputFileSP();
212 
213     Pipe pipe;
214     Status pipe_result = pipe.CreateNew(false);
215 #if defined(_WIN32)
216     lldb::file_t read_file = pipe.GetReadNativeHandle();
217     pipe.ReleaseReadFileDescriptor();
218     std::unique_ptr<ConnectionGenericFile> conn_up =
219         std::make_unique<ConnectionGenericFile>(read_file, true);
220 #else
221     std::unique_ptr<ConnectionFileDescriptor> conn_up =
222         std::make_unique<ConnectionFileDescriptor>(
223             pipe.ReleaseReadFileDescriptor(), true);
224 #endif
225 
226     if (conn_up->IsConnected()) {
227       m_communication.SetConnection(std::move(conn_up));
228       m_communication.SetReadThreadBytesReceivedCallback(
229           ReadThreadBytesReceived, &result->GetOutputStream());
230       m_communication.StartReadThread();
231       m_disconnect = true;
232 
233       FILE *outfile_handle = fdopen(pipe.ReleaseWriteFileDescriptor(), "w");
234       m_output_file_sp = std::make_shared<StreamFile>(outfile_handle, true);
235       m_error_file_sp = m_output_file_sp;
236       if (outfile_handle)
237         ::setbuf(outfile_handle, nullptr);
238 
239       result->SetImmediateOutputFile(debugger.GetOutputStream().GetFileSP());
240       result->SetImmediateErrorFile(debugger.GetErrorStream().GetFileSP());
241     }
242   }
243 
244   if (!m_input_file_sp || !m_output_file_sp || !m_error_file_sp)
245     debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_file_sp, m_output_file_sp,
246                                              m_error_file_sp);
247 }
248 
249 void ScriptInterpreterIORedirect::Flush() {
250   if (m_output_file_sp)
251     m_output_file_sp->Flush();
252   if (m_error_file_sp)
253     m_error_file_sp->Flush();
254 }
255 
256 ScriptInterpreterIORedirect::~ScriptInterpreterIORedirect() {
257   if (!m_disconnect)
258     return;
259 
260   assert(m_output_file_sp);
261   assert(m_error_file_sp);
262   assert(m_output_file_sp == m_error_file_sp);
263 
264   // Close the write end of the pipe since we are done with our one line
265   // script. This should cause the read thread that output_comm is using to
266   // exit.
267   m_output_file_sp->GetFile().Close();
268   // The close above should cause this thread to exit when it gets to the end
269   // of file, so let it get all its data.
270   m_communication.JoinReadThread();
271   // Now we can close the read end of the pipe.
272   m_communication.Disconnect();
273 }
274