180814287SRaphael Isemann //===-- ScriptInterpreter.cpp ---------------------------------------------===//
230fdc8d8SChris Lattner //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
630fdc8d8SChris Lattner //
730fdc8d8SChris Lattner //===----------------------------------------------------------------------===//
830fdc8d8SChris Lattner 
930fdc8d8SChris Lattner #include "lldb/Interpreter/ScriptInterpreter.h"
10d79273c9SJonas Devlieghere #include "lldb/Core/Debugger.h"
11d79273c9SJonas Devlieghere #include "lldb/Host/ConnectionFileDescriptor.h"
12d79273c9SJonas Devlieghere #include "lldb/Host/Pipe.h"
1324ae6294SZachary Turner #include "lldb/Host/PseudoTerminal.h"
1430fdc8d8SChris Lattner #include "lldb/Interpreter/CommandReturnObject.h"
1597206d57SZachary Turner #include "lldb/Utility/Status.h"
16bf9a7730SZachary Turner #include "lldb/Utility/Stream.h"
17573ab909SZachary Turner #include "lldb/Utility/StringList.h"
1811f2ef4dSJonas Devlieghere #if defined(_WIN32)
1911f2ef4dSJonas Devlieghere #include "lldb/Host/windows/ConnectionGenericFileWindows.h"
2011f2ef4dSJonas Devlieghere #endif
2176e47d48SRaphael Isemann #include <cstdio>
2276e47d48SRaphael Isemann #include <cstdlib>
2311f2ef4dSJonas Devlieghere #include <memory>
2411f2ef4dSJonas Devlieghere #include <string>
2530fdc8d8SChris Lattner 
2630fdc8d8SChris Lattner using namespace lldb;
2730fdc8d8SChris Lattner using namespace lldb_private;
2830fdc8d8SChris Lattner 
ScriptInterpreter(Debugger & debugger,lldb::ScriptLanguage script_lang,lldb::ScriptedProcessInterfaceUP scripted_process_interface_up)291f6a57c1SMed Ismail Bennani ScriptInterpreter::ScriptInterpreter(
301f6a57c1SMed Ismail Bennani     Debugger &debugger, lldb::ScriptLanguage script_lang,
311f6a57c1SMed Ismail Bennani     lldb::ScriptedProcessInterfaceUP scripted_process_interface_up)
321f6a57c1SMed Ismail Bennani     : m_debugger(debugger), m_script_lang(script_lang),
331f6a57c1SMed Ismail Bennani       m_scripted_process_interface_up(
341f6a57c1SMed Ismail Bennani           std::move(scripted_process_interface_up)) {}
3530fdc8d8SChris Lattner 
CollectDataForBreakpointCommandCallback(std::vector<std::reference_wrapper<BreakpointOptions>> & bp_options_vec,CommandReturnObject & result)36b9c1b51eSKate Stone void ScriptInterpreter::CollectDataForBreakpointCommandCallback(
37cfb96d84SJim Ingham     std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec,
38b9c1b51eSKate Stone     CommandReturnObject &result) {
39b9c1b51eSKate Stone   result.AppendError(
40ba0eb7b6SJonas Devlieghere       "This script interpreter does not support breakpoint callbacks.");
4130fdc8d8SChris Lattner }
4230fdc8d8SChris Lattner 
CollectDataForWatchpointCommandCallback(WatchpointOptions * bp_options,CommandReturnObject & result)43b9c1b51eSKate Stone void ScriptInterpreter::CollectDataForWatchpointCommandCallback(
44b9c1b51eSKate Stone     WatchpointOptions *bp_options, CommandReturnObject &result) {
45b9c1b51eSKate Stone   result.AppendError(
46ba0eb7b6SJonas Devlieghere       "This script interpreter does not support watchpoint callbacks.");
47e9a5627eSJohnny Chen }
48e9a5627eSJohnny Chen 
GetInterpreterInfo()49bbef51ebSLawrence D'Anna StructuredData::DictionarySP ScriptInterpreter::GetInterpreterInfo() {
50bbef51ebSLawrence D'Anna   return nullptr;
51bbef51ebSLawrence D'Anna }
52bbef51ebSLawrence D'Anna 
LoadScriptingModule(const char * filename,const LoadScriptOptions & options,lldb_private::Status & error,StructuredData::ObjectSP * module_sp,FileSpec extra_search_dir)5300bb397bSJonas Devlieghere bool ScriptInterpreter::LoadScriptingModule(const char *filename,
54f9517353SJonas Devlieghere                                             const LoadScriptOptions &options,
5500bb397bSJonas Devlieghere                                             lldb_private::Status &error,
5600bb397bSJonas Devlieghere                                             StructuredData::ObjectSP *module_sp,
5700bb397bSJonas Devlieghere                                             FileSpec extra_search_dir) {
58bd5c8d16SJonas Devlieghere   error.SetErrorString(
59bd5c8d16SJonas Devlieghere       "This script interpreter does not support importing modules.");
60bd5c8d16SJonas Devlieghere   return false;
61bd5c8d16SJonas Devlieghere }
62bd5c8d16SJonas Devlieghere 
LanguageToString(lldb::ScriptLanguage language)63b9c1b51eSKate Stone std::string ScriptInterpreter::LanguageToString(lldb::ScriptLanguage language) {
64b9c1b51eSKate Stone   switch (language) {
653df9a8dfSCaroline Tice   case eScriptLanguageNone:
66acdda134SJonas Devlieghere     return "None";
673df9a8dfSCaroline Tice   case eScriptLanguagePython:
68acdda134SJonas Devlieghere     return "Python";
69acdda134SJonas Devlieghere   case eScriptLanguageLua:
70acdda134SJonas Devlieghere     return "Lua";
71f7e07256SJim Ingham   case eScriptLanguageUnknown:
72acdda134SJonas Devlieghere     return "Unknown";
733df9a8dfSCaroline Tice   }
74fbccef6bSPavel Labath   llvm_unreachable("Unhandled ScriptInterpreter!");
753df9a8dfSCaroline Tice }
762f88aadfSCaroline Tice 
771f6a57c1SMed Ismail Bennani lldb::DataExtractorSP
GetDataExtractorFromSBData(const lldb::SBData & data) const781f6a57c1SMed Ismail Bennani ScriptInterpreter::GetDataExtractorFromSBData(const lldb::SBData &data) const {
791f6a57c1SMed Ismail Bennani   return data.m_opaque_sp;
801f6a57c1SMed Ismail Bennani }
811f6a57c1SMed Ismail Bennani 
821f6a57c1SMed Ismail Bennani Status
GetStatusFromSBError(const lldb::SBError & error) const831f6a57c1SMed Ismail Bennani ScriptInterpreter::GetStatusFromSBError(const lldb::SBError &error) const {
841f6a57c1SMed Ismail Bennani   if (error.m_opaque_up)
851f6a57c1SMed Ismail Bennani     return *error.m_opaque_up.get();
861f6a57c1SMed Ismail Bennani 
871f6a57c1SMed Ismail Bennani   return Status();
881f6a57c1SMed Ismail Bennani }
891f6a57c1SMed Ismail Bennani 
90a758c9f7SMed Ismail Bennani llvm::Optional<MemoryRegionInfo>
GetOpaqueTypeFromSBMemoryRegionInfo(const lldb::SBMemoryRegionInfo & mem_region) const91a758c9f7SMed Ismail Bennani ScriptInterpreter::GetOpaqueTypeFromSBMemoryRegionInfo(
92a758c9f7SMed Ismail Bennani     const lldb::SBMemoryRegionInfo &mem_region) const {
93a758c9f7SMed Ismail Bennani   if (!mem_region.m_opaque_up)
94a758c9f7SMed Ismail Bennani     return llvm::None;
95a758c9f7SMed Ismail Bennani   return *mem_region.m_opaque_up.get();
96a758c9f7SMed Ismail Bennani }
97a758c9f7SMed Ismail Bennani 
98f7e07256SJim Ingham lldb::ScriptLanguage
StringToLanguage(const llvm::StringRef & language)99f7e07256SJim Ingham ScriptInterpreter::StringToLanguage(const llvm::StringRef &language) {
100e50f9c41SMartin Storsjö   if (language.equals_insensitive(LanguageToString(eScriptLanguageNone)))
101f7e07256SJim Ingham     return eScriptLanguageNone;
102e50f9c41SMartin Storsjö   if (language.equals_insensitive(LanguageToString(eScriptLanguagePython)))
103f7e07256SJim Ingham     return eScriptLanguagePython;
104e50f9c41SMartin Storsjö   if (language.equals_insensitive(LanguageToString(eScriptLanguageLua)))
105acdda134SJonas Devlieghere     return eScriptLanguageLua;
106f7e07256SJim Ingham   return eScriptLanguageUnknown;
107f7e07256SJim Ingham }
108f7e07256SJim Ingham 
SetBreakpointCommandCallback(std::vector<std::reference_wrapper<BreakpointOptions>> & bp_options_vec,const char * callback_text)10997206d57SZachary Turner Status ScriptInterpreter::SetBreakpointCommandCallback(
110cfb96d84SJim Ingham     std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec,
111b9c1b51eSKate Stone     const char *callback_text) {
112*d92f7f79SJim Ingham   Status error;
113cfb96d84SJim Ingham   for (BreakpointOptions &bp_options : bp_options_vec) {
114*d92f7f79SJim Ingham     error = SetBreakpointCommandCallback(bp_options, callback_text);
115*d92f7f79SJim Ingham     if (!error.Success())
116b5796cb4SJim Ingham       break;
117b5796cb4SJim Ingham   }
118*d92f7f79SJim Ingham   return error;
119b5796cb4SJim Ingham }
120b5796cb4SJim Ingham 
SetBreakpointCommandCallbackFunction(std::vector<std::reference_wrapper<BreakpointOptions>> & bp_options_vec,const char * function_name,StructuredData::ObjectSP extra_args_sp)121738af7a6SJim Ingham Status ScriptInterpreter::SetBreakpointCommandCallbackFunction(
122cfb96d84SJim Ingham     std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec,
123cfb96d84SJim Ingham     const char *function_name, StructuredData::ObjectSP extra_args_sp) {
124738af7a6SJim Ingham   Status error;
125cfb96d84SJim Ingham   for (BreakpointOptions &bp_options : bp_options_vec) {
126738af7a6SJim Ingham     error = SetBreakpointCommandCallbackFunction(bp_options, function_name,
127738af7a6SJim Ingham                                                  extra_args_sp);
128738af7a6SJim Ingham     if (!error.Success())
129738af7a6SJim Ingham       return error;
130b5796cb4SJim Ingham   }
131738af7a6SJim Ingham   return error;
132b5796cb4SJim Ingham }
133b5796cb4SJim Ingham 
1347b0992d9SGreg Clayton std::unique_ptr<ScriptInterpreterLocker>
AcquireInterpreterLock()135b9c1b51eSKate Stone ScriptInterpreter::AcquireInterpreterLock() {
13606412daeSJonas Devlieghere   return std::make_unique<ScriptInterpreterLocker>();
137360cc318SEnrico Granata }
138d79273c9SJonas Devlieghere 
ReadThreadBytesReceived(void * baton,const void * src,size_t src_len)139d79273c9SJonas Devlieghere static void ReadThreadBytesReceived(void *baton, const void *src,
140d79273c9SJonas Devlieghere                                     size_t src_len) {
141d79273c9SJonas Devlieghere   if (src && src_len) {
142d79273c9SJonas Devlieghere     Stream *strm = (Stream *)baton;
143d79273c9SJonas Devlieghere     strm->Write(src, src_len);
144d79273c9SJonas Devlieghere     strm->Flush();
145d79273c9SJonas Devlieghere   }
146d79273c9SJonas Devlieghere }
147d79273c9SJonas Devlieghere 
148d79273c9SJonas Devlieghere llvm::Expected<std::unique_ptr<ScriptInterpreterIORedirect>>
Create(bool enable_io,Debugger & debugger,CommandReturnObject * result)14984228365SJonas Devlieghere ScriptInterpreterIORedirect::Create(bool enable_io, Debugger &debugger,
15084228365SJonas Devlieghere                                     CommandReturnObject *result) {
15184228365SJonas Devlieghere   if (enable_io)
15284228365SJonas Devlieghere     return std::unique_ptr<ScriptInterpreterIORedirect>(
15384228365SJonas Devlieghere         new ScriptInterpreterIORedirect(debugger, result));
15484228365SJonas Devlieghere 
155d79273c9SJonas Devlieghere   auto nullin = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL),
15614735cabSMichał Górny                                             File::eOpenOptionReadOnly);
157d79273c9SJonas Devlieghere   if (!nullin)
158d79273c9SJonas Devlieghere     return nullin.takeError();
159d79273c9SJonas Devlieghere 
160d79273c9SJonas Devlieghere   auto nullout = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL),
16114735cabSMichał Górny                                              File::eOpenOptionWriteOnly);
162d79273c9SJonas Devlieghere   if (!nullout)
163d79273c9SJonas Devlieghere     return nullin.takeError();
164d79273c9SJonas Devlieghere 
165d79273c9SJonas Devlieghere   return std::unique_ptr<ScriptInterpreterIORedirect>(
166d79273c9SJonas Devlieghere       new ScriptInterpreterIORedirect(std::move(*nullin), std::move(*nullout)));
167d79273c9SJonas Devlieghere }
168d79273c9SJonas Devlieghere 
ScriptInterpreterIORedirect(std::unique_ptr<File> input,std::unique_ptr<File> output)169d79273c9SJonas Devlieghere ScriptInterpreterIORedirect::ScriptInterpreterIORedirect(
170d79273c9SJonas Devlieghere     std::unique_ptr<File> input, std::unique_ptr<File> output)
171d79273c9SJonas Devlieghere     : m_input_file_sp(std::move(input)),
172d79273c9SJonas Devlieghere       m_output_file_sp(std::make_shared<StreamFile>(std::move(output))),
173d79273c9SJonas Devlieghere       m_error_file_sp(m_output_file_sp),
174d79273c9SJonas Devlieghere       m_communication("lldb.ScriptInterpreterIORedirect.comm"),
175d79273c9SJonas Devlieghere       m_disconnect(false) {}
176d79273c9SJonas Devlieghere 
ScriptInterpreterIORedirect(Debugger & debugger,CommandReturnObject * result)177d79273c9SJonas Devlieghere ScriptInterpreterIORedirect::ScriptInterpreterIORedirect(
178d79273c9SJonas Devlieghere     Debugger &debugger, CommandReturnObject *result)
179d79273c9SJonas Devlieghere     : m_communication("lldb.ScriptInterpreterIORedirect.comm"),
180d79273c9SJonas Devlieghere       m_disconnect(false) {
181d79273c9SJonas Devlieghere 
182d79273c9SJonas Devlieghere   if (result) {
183d79273c9SJonas Devlieghere     m_input_file_sp = debugger.GetInputFileSP();
184d79273c9SJonas Devlieghere 
185d79273c9SJonas Devlieghere     Pipe pipe;
186d79273c9SJonas Devlieghere     Status pipe_result = pipe.CreateNew(false);
187d79273c9SJonas Devlieghere #if defined(_WIN32)
188d79273c9SJonas Devlieghere     lldb::file_t read_file = pipe.GetReadNativeHandle();
189d79273c9SJonas Devlieghere     pipe.ReleaseReadFileDescriptor();
190d79273c9SJonas Devlieghere     std::unique_ptr<ConnectionGenericFile> conn_up =
191d79273c9SJonas Devlieghere         std::make_unique<ConnectionGenericFile>(read_file, true);
192d79273c9SJonas Devlieghere #else
193d79273c9SJonas Devlieghere     std::unique_ptr<ConnectionFileDescriptor> conn_up =
194d79273c9SJonas Devlieghere         std::make_unique<ConnectionFileDescriptor>(
195d79273c9SJonas Devlieghere             pipe.ReleaseReadFileDescriptor(), true);
196d79273c9SJonas Devlieghere #endif
197d79273c9SJonas Devlieghere 
198d79273c9SJonas Devlieghere     if (conn_up->IsConnected()) {
199d79273c9SJonas Devlieghere       m_communication.SetConnection(std::move(conn_up));
200d79273c9SJonas Devlieghere       m_communication.SetReadThreadBytesReceivedCallback(
201d79273c9SJonas Devlieghere           ReadThreadBytesReceived, &result->GetOutputStream());
202d79273c9SJonas Devlieghere       m_communication.StartReadThread();
203d79273c9SJonas Devlieghere       m_disconnect = true;
204d79273c9SJonas Devlieghere 
205d79273c9SJonas Devlieghere       FILE *outfile_handle = fdopen(pipe.ReleaseWriteFileDescriptor(), "w");
206d79273c9SJonas Devlieghere       m_output_file_sp = std::make_shared<StreamFile>(outfile_handle, true);
207d79273c9SJonas Devlieghere       m_error_file_sp = m_output_file_sp;
208d79273c9SJonas Devlieghere       if (outfile_handle)
209d79273c9SJonas Devlieghere         ::setbuf(outfile_handle, nullptr);
210d79273c9SJonas Devlieghere 
211d79273c9SJonas Devlieghere       result->SetImmediateOutputFile(debugger.GetOutputStream().GetFileSP());
212d79273c9SJonas Devlieghere       result->SetImmediateErrorFile(debugger.GetErrorStream().GetFileSP());
213d79273c9SJonas Devlieghere     }
214d79273c9SJonas Devlieghere   }
215d79273c9SJonas Devlieghere 
216d79273c9SJonas Devlieghere   if (!m_input_file_sp || !m_output_file_sp || !m_error_file_sp)
217d79273c9SJonas Devlieghere     debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_file_sp, m_output_file_sp,
218d79273c9SJonas Devlieghere                                              m_error_file_sp);
219d79273c9SJonas Devlieghere }
220d79273c9SJonas Devlieghere 
Flush()221d79273c9SJonas Devlieghere void ScriptInterpreterIORedirect::Flush() {
222d79273c9SJonas Devlieghere   if (m_output_file_sp)
223d79273c9SJonas Devlieghere     m_output_file_sp->Flush();
224d79273c9SJonas Devlieghere   if (m_error_file_sp)
225d79273c9SJonas Devlieghere     m_error_file_sp->Flush();
226d79273c9SJonas Devlieghere }
227d79273c9SJonas Devlieghere 
~ScriptInterpreterIORedirect()228d79273c9SJonas Devlieghere ScriptInterpreterIORedirect::~ScriptInterpreterIORedirect() {
229d79273c9SJonas Devlieghere   if (!m_disconnect)
230d79273c9SJonas Devlieghere     return;
231d79273c9SJonas Devlieghere 
232d79273c9SJonas Devlieghere   assert(m_output_file_sp);
233d79273c9SJonas Devlieghere   assert(m_error_file_sp);
234d79273c9SJonas Devlieghere   assert(m_output_file_sp == m_error_file_sp);
235d79273c9SJonas Devlieghere 
236d79273c9SJonas Devlieghere   // Close the write end of the pipe since we are done with our one line
237d79273c9SJonas Devlieghere   // script. This should cause the read thread that output_comm is using to
238d79273c9SJonas Devlieghere   // exit.
239d79273c9SJonas Devlieghere   m_output_file_sp->GetFile().Close();
240d79273c9SJonas Devlieghere   // The close above should cause this thread to exit when it gets to the end
241d79273c9SJonas Devlieghere   // of file, so let it get all its data.
242d79273c9SJonas Devlieghere   m_communication.JoinReadThread();
243d79273c9SJonas Devlieghere   // Now we can close the read end of the pipe.
244d79273c9SJonas Devlieghere   m_communication.Disconnect();
245d79273c9SJonas Devlieghere }
246