xref: /freebsd/contrib/llvm-project/lldb/source/Interpreter/ScriptInterpreter.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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,lldb::TargetSP loaded_into_target_sp)51 bool ScriptInterpreter::LoadScriptingModule(
52     const char *filename, const LoadScriptOptions &options,
53     lldb_private::Status &error, StructuredData::ObjectSP *module_sp,
54     FileSpec extra_search_dir, lldb::TargetSP loaded_into_target_sp) {
55   error = Status::FromErrorString(
56       "This script interpreter does not support importing modules.");
57   return false;
58 }
59 
LanguageToString(lldb::ScriptLanguage language)60 std::string ScriptInterpreter::LanguageToString(lldb::ScriptLanguage language) {
61   switch (language) {
62   case eScriptLanguageNone:
63     return "None";
64   case eScriptLanguagePython:
65     return "Python";
66   case eScriptLanguageLua:
67     return "Lua";
68   case eScriptLanguageUnknown:
69     return "Unknown";
70   }
71   llvm_unreachable("Unhandled ScriptInterpreter!");
72 }
73 
74 lldb::DataExtractorSP
GetDataExtractorFromSBData(const lldb::SBData & data) const75 ScriptInterpreter::GetDataExtractorFromSBData(const lldb::SBData &data) const {
76   return data.m_opaque_sp;
77 }
78 
GetOpaqueTypeFromSBBreakpoint(const lldb::SBBreakpoint & breakpoint) const79 lldb::BreakpointSP ScriptInterpreter::GetOpaqueTypeFromSBBreakpoint(
80     const lldb::SBBreakpoint &breakpoint) const {
81   return breakpoint.m_opaque_wp.lock();
82 }
83 
GetOpaqueTypeFromSBAttachInfo(const lldb::SBAttachInfo & attach_info) const84 lldb::ProcessAttachInfoSP ScriptInterpreter::GetOpaqueTypeFromSBAttachInfo(
85     const lldb::SBAttachInfo &attach_info) const {
86   return attach_info.m_opaque_sp;
87 }
88 
GetOpaqueTypeFromSBLaunchInfo(const lldb::SBLaunchInfo & launch_info) const89 lldb::ProcessLaunchInfoSP ScriptInterpreter::GetOpaqueTypeFromSBLaunchInfo(
90     const lldb::SBLaunchInfo &launch_info) const {
91   return std::make_shared<ProcessLaunchInfo>(
92       *reinterpret_cast<ProcessLaunchInfo *>(launch_info.m_opaque_sp.get()));
93 }
94 
95 Status
GetStatusFromSBError(const lldb::SBError & error) const96 ScriptInterpreter::GetStatusFromSBError(const lldb::SBError &error) const {
97   if (error.m_opaque_up)
98     return error.m_opaque_up->Clone();
99 
100   return Status();
101 }
102 
103 Event *
GetOpaqueTypeFromSBEvent(const lldb::SBEvent & event) const104 ScriptInterpreter::GetOpaqueTypeFromSBEvent(const lldb::SBEvent &event) const {
105   return event.m_opaque_ptr;
106 }
107 
GetOpaqueTypeFromSBStream(const lldb::SBStream & stream) const108 lldb::StreamSP ScriptInterpreter::GetOpaqueTypeFromSBStream(
109     const lldb::SBStream &stream) const {
110   if (stream.m_opaque_up) {
111     lldb::StreamSP s = std::make_shared<lldb_private::StreamString>();
112     *s << reinterpret_cast<StreamString *>(stream.m_opaque_up.get())->m_packet;
113     return s;
114   }
115 
116   return nullptr;
117 }
118 
119 std::optional<MemoryRegionInfo>
GetOpaqueTypeFromSBMemoryRegionInfo(const lldb::SBMemoryRegionInfo & mem_region) const120 ScriptInterpreter::GetOpaqueTypeFromSBMemoryRegionInfo(
121     const lldb::SBMemoryRegionInfo &mem_region) const {
122   if (!mem_region.m_opaque_up)
123     return std::nullopt;
124   return *mem_region.m_opaque_up.get();
125 }
126 
127 lldb::ExecutionContextRefSP
GetOpaqueTypeFromSBExecutionContext(const lldb::SBExecutionContext & exe_ctx) const128 ScriptInterpreter::GetOpaqueTypeFromSBExecutionContext(
129     const lldb::SBExecutionContext &exe_ctx) const {
130   return exe_ctx.m_exe_ctx_sp;
131 }
132 
133 lldb::ScriptLanguage
StringToLanguage(const llvm::StringRef & language)134 ScriptInterpreter::StringToLanguage(const llvm::StringRef &language) {
135   if (language.equals_insensitive(LanguageToString(eScriptLanguageNone)))
136     return eScriptLanguageNone;
137   if (language.equals_insensitive(LanguageToString(eScriptLanguagePython)))
138     return eScriptLanguagePython;
139   if (language.equals_insensitive(LanguageToString(eScriptLanguageLua)))
140     return eScriptLanguageLua;
141   return eScriptLanguageUnknown;
142 }
143 
SetBreakpointCommandCallback(std::vector<std::reference_wrapper<BreakpointOptions>> & bp_options_vec,const char * callback_text)144 Status ScriptInterpreter::SetBreakpointCommandCallback(
145     std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec,
146     const char *callback_text) {
147   Status error;
148   for (BreakpointOptions &bp_options : bp_options_vec) {
149     error = SetBreakpointCommandCallback(bp_options, callback_text,
150                                          /*is_callback=*/false);
151     if (!error.Success())
152       break;
153   }
154   return error;
155 }
156 
SetBreakpointCommandCallbackFunction(std::vector<std::reference_wrapper<BreakpointOptions>> & bp_options_vec,const char * function_name,StructuredData::ObjectSP extra_args_sp)157 Status ScriptInterpreter::SetBreakpointCommandCallbackFunction(
158     std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec,
159     const char *function_name, StructuredData::ObjectSP extra_args_sp) {
160   Status error;
161   for (BreakpointOptions &bp_options : bp_options_vec) {
162     error = SetBreakpointCommandCallbackFunction(bp_options, function_name,
163                                                  extra_args_sp);
164     if (!error.Success())
165       return error;
166   }
167   return error;
168 }
169 
170 std::unique_ptr<ScriptInterpreterLocker>
AcquireInterpreterLock()171 ScriptInterpreter::AcquireInterpreterLock() {
172   return std::make_unique<ScriptInterpreterLocker>();
173 }
174 
ReadThreadBytesReceived(void * baton,const void * src,size_t src_len)175 static void ReadThreadBytesReceived(void *baton, const void *src,
176                                     size_t src_len) {
177   if (src && src_len) {
178     Stream *strm = (Stream *)baton;
179     strm->Write(src, src_len);
180     strm->Flush();
181   }
182 }
183 
184 llvm::Expected<std::unique_ptr<ScriptInterpreterIORedirect>>
Create(bool enable_io,Debugger & debugger,CommandReturnObject * result)185 ScriptInterpreterIORedirect::Create(bool enable_io, Debugger &debugger,
186                                     CommandReturnObject *result) {
187   if (enable_io)
188     return std::unique_ptr<ScriptInterpreterIORedirect>(
189         new ScriptInterpreterIORedirect(debugger, result));
190 
191   auto nullin = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL),
192                                             File::eOpenOptionReadOnly);
193   if (!nullin)
194     return nullin.takeError();
195 
196   auto nullout = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL),
197                                              File::eOpenOptionWriteOnly);
198   if (!nullout)
199     return nullin.takeError();
200 
201   return std::unique_ptr<ScriptInterpreterIORedirect>(
202       new ScriptInterpreterIORedirect(std::move(*nullin), std::move(*nullout)));
203 }
204 
ScriptInterpreterIORedirect(std::unique_ptr<File> input,std::unique_ptr<File> output)205 ScriptInterpreterIORedirect::ScriptInterpreterIORedirect(
206     std::unique_ptr<File> input, std::unique_ptr<File> output)
207     : m_input_file_sp(std::move(input)),
208       m_output_file_sp(std::make_shared<LockableStreamFile>(std::move(output),
209                                                             m_output_mutex)),
210       m_error_file_sp(m_output_file_sp),
211       m_communication("lldb.ScriptInterpreterIORedirect.comm"),
212       m_disconnect(false) {}
213 
ScriptInterpreterIORedirect(Debugger & debugger,CommandReturnObject * result)214 ScriptInterpreterIORedirect::ScriptInterpreterIORedirect(
215     Debugger &debugger, CommandReturnObject *result)
216     : m_communication("lldb.ScriptInterpreterIORedirect.comm"),
217       m_disconnect(false) {
218 
219   if (result) {
220     m_input_file_sp = debugger.GetInputFileSP();
221 
222     Pipe pipe;
223     Status pipe_result = pipe.CreateNew();
224 #if defined(_WIN32)
225     lldb::file_t read_file = pipe.GetReadNativeHandle();
226     pipe.ReleaseReadFileDescriptor();
227     std::unique_ptr<ConnectionGenericFile> conn_up =
228         std::make_unique<ConnectionGenericFile>(read_file, true);
229 #else
230     std::unique_ptr<ConnectionFileDescriptor> conn_up =
231         std::make_unique<ConnectionFileDescriptor>(
232             pipe.ReleaseReadFileDescriptor(), true);
233 #endif
234 
235     if (conn_up->IsConnected()) {
236       m_communication.SetConnection(std::move(conn_up));
237       m_communication.SetReadThreadBytesReceivedCallback(
238           ReadThreadBytesReceived, &result->GetOutputStream());
239       m_communication.StartReadThread();
240       m_disconnect = true;
241 
242       FILE *outfile_handle = fdopen(pipe.ReleaseWriteFileDescriptor(), "w");
243       m_output_file_sp = std::make_shared<LockableStreamFile>(
244           std::make_shared<StreamFile>(outfile_handle, NativeFile::Owned),
245           m_output_mutex);
246       m_error_file_sp = m_output_file_sp;
247       if (outfile_handle)
248         ::setbuf(outfile_handle, nullptr);
249 
250       result->SetImmediateOutputFile(debugger.GetOutputFileSP());
251       result->SetImmediateErrorFile(debugger.GetErrorFileSP());
252     }
253   }
254 
255   if (!m_input_file_sp || !m_output_file_sp || !m_error_file_sp)
256     debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_file_sp, m_output_file_sp,
257                                              m_error_file_sp);
258 }
259 
Flush()260 void ScriptInterpreterIORedirect::Flush() {
261   if (m_output_file_sp)
262     m_output_file_sp->Lock().Flush();
263   if (m_error_file_sp)
264     m_error_file_sp->Lock().Flush();
265 }
266 
~ScriptInterpreterIORedirect()267 ScriptInterpreterIORedirect::~ScriptInterpreterIORedirect() {
268   if (!m_disconnect)
269     return;
270 
271   assert(m_output_file_sp);
272   assert(m_error_file_sp);
273   assert(m_output_file_sp == m_error_file_sp);
274 
275   // Close the write end of the pipe since we are done with our one line
276   // script. This should cause the read thread that output_comm is using to
277   // exit.
278   m_output_file_sp->GetUnlockedFile().Close();
279   // The close above should cause this thread to exit when it gets to the end
280   // of file, so let it get all its data.
281   m_communication.JoinReadThread();
282   // Now we can close the read end of the pipe.
283   m_communication.Disconnect();
284 }
285