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
2111f2ef4dSJonas Devlieghere #include <memory>
2211f2ef4dSJonas Devlieghere #include <stdio.h>
2311f2ef4dSJonas Devlieghere #include <stdlib.h>
2411f2ef4dSJonas Devlieghere #include <string>
2530fdc8d8SChris Lattner 
2630fdc8d8SChris Lattner using namespace lldb;
2730fdc8d8SChris Lattner using namespace lldb_private;
2830fdc8d8SChris Lattner 
298d1fb843SJonas Devlieghere ScriptInterpreter::ScriptInterpreter(Debugger &debugger,
30b9c1b51eSKate Stone                                      lldb::ScriptLanguage script_lang)
318d1fb843SJonas Devlieghere     : m_debugger(debugger), m_script_lang(script_lang) {}
3230fdc8d8SChris Lattner 
33b9c1b51eSKate Stone ScriptInterpreter::~ScriptInterpreter() {}
3430fdc8d8SChris Lattner 
35b9c1b51eSKate Stone void ScriptInterpreter::CollectDataForBreakpointCommandCallback(
36b5796cb4SJim Ingham     std::vector<BreakpointOptions *> &bp_options_vec,
37b9c1b51eSKate Stone     CommandReturnObject &result) {
3830fdc8d8SChris Lattner   result.SetStatus(eReturnStatusFailed);
39b9c1b51eSKate Stone   result.AppendError(
40ba0eb7b6SJonas Devlieghere       "This script interpreter does not support breakpoint callbacks.");
4130fdc8d8SChris Lattner }
4230fdc8d8SChris Lattner 
43b9c1b51eSKate Stone void ScriptInterpreter::CollectDataForWatchpointCommandCallback(
44b9c1b51eSKate Stone     WatchpointOptions *bp_options, CommandReturnObject &result) {
45e9a5627eSJohnny Chen   result.SetStatus(eReturnStatusFailed);
46b9c1b51eSKate Stone   result.AppendError(
47ba0eb7b6SJonas Devlieghere       "This script interpreter does not support watchpoint callbacks.");
48e9a5627eSJohnny Chen }
49e9a5627eSJohnny Chen 
50*00bb397bSJonas Devlieghere bool ScriptInterpreter::LoadScriptingModule(const char *filename,
51*00bb397bSJonas Devlieghere                                             bool init_session,
52*00bb397bSJonas Devlieghere                                             lldb_private::Status &error,
53*00bb397bSJonas Devlieghere                                             StructuredData::ObjectSP *module_sp,
54*00bb397bSJonas Devlieghere                                             FileSpec extra_search_dir) {
55bd5c8d16SJonas Devlieghere   error.SetErrorString(
56bd5c8d16SJonas Devlieghere       "This script interpreter does not support importing modules.");
57bd5c8d16SJonas Devlieghere   return false;
58bd5c8d16SJonas Devlieghere }
59bd5c8d16SJonas Devlieghere 
60b9c1b51eSKate Stone std::string ScriptInterpreter::LanguageToString(lldb::ScriptLanguage language) {
61b9c1b51eSKate Stone   switch (language) {
623df9a8dfSCaroline Tice   case eScriptLanguageNone:
63acdda134SJonas Devlieghere     return "None";
643df9a8dfSCaroline Tice   case eScriptLanguagePython:
65acdda134SJonas Devlieghere     return "Python";
66acdda134SJonas Devlieghere   case eScriptLanguageLua:
67acdda134SJonas Devlieghere     return "Lua";
68f7e07256SJim Ingham   case eScriptLanguageUnknown:
69acdda134SJonas Devlieghere     return "Unknown";
703df9a8dfSCaroline Tice   }
71fbccef6bSPavel Labath   llvm_unreachable("Unhandled ScriptInterpreter!");
723df9a8dfSCaroline Tice }
732f88aadfSCaroline Tice 
74f7e07256SJim Ingham lldb::ScriptLanguage
75f7e07256SJim Ingham ScriptInterpreter::StringToLanguage(const llvm::StringRef &language) {
76f7e07256SJim Ingham   if (language.equals_lower(LanguageToString(eScriptLanguageNone)))
77f7e07256SJim Ingham     return eScriptLanguageNone;
7845188dd9SDavide Italiano   if (language.equals_lower(LanguageToString(eScriptLanguagePython)))
79f7e07256SJim Ingham     return eScriptLanguagePython;
80acdda134SJonas Devlieghere   if (language.equals_lower(LanguageToString(eScriptLanguageLua)))
81acdda134SJonas Devlieghere     return eScriptLanguageLua;
82f7e07256SJim Ingham   return eScriptLanguageUnknown;
83f7e07256SJim Ingham }
84f7e07256SJim Ingham 
8597206d57SZachary Turner Status ScriptInterpreter::SetBreakpointCommandCallback(
86b9c1b51eSKate Stone     std::vector<BreakpointOptions *> &bp_options_vec,
87b9c1b51eSKate Stone     const char *callback_text) {
8897206d57SZachary Turner   Status return_error;
89b9c1b51eSKate Stone   for (BreakpointOptions *bp_options : bp_options_vec) {
90b5796cb4SJim Ingham     return_error = SetBreakpointCommandCallback(bp_options, callback_text);
91b5796cb4SJim Ingham     if (return_error.Success())
92b5796cb4SJim Ingham       break;
93b5796cb4SJim Ingham   }
94b5796cb4SJim Ingham   return return_error;
95b5796cb4SJim Ingham }
96b5796cb4SJim Ingham 
97738af7a6SJim Ingham Status ScriptInterpreter::SetBreakpointCommandCallbackFunction(
98acdda134SJonas Devlieghere     std::vector<BreakpointOptions *> &bp_options_vec, const char *function_name,
99738af7a6SJim Ingham     StructuredData::ObjectSP extra_args_sp) {
100738af7a6SJim Ingham   Status error;
101b9c1b51eSKate Stone   for (BreakpointOptions *bp_options : bp_options_vec) {
102738af7a6SJim Ingham     error = SetBreakpointCommandCallbackFunction(bp_options, function_name,
103738af7a6SJim Ingham                                                  extra_args_sp);
104738af7a6SJim Ingham     if (!error.Success())
105738af7a6SJim Ingham       return error;
106b5796cb4SJim Ingham   }
107738af7a6SJim Ingham   return error;
108b5796cb4SJim Ingham }
109b5796cb4SJim Ingham 
1107b0992d9SGreg Clayton std::unique_ptr<ScriptInterpreterLocker>
111b9c1b51eSKate Stone ScriptInterpreter::AcquireInterpreterLock() {
11206412daeSJonas Devlieghere   return std::make_unique<ScriptInterpreterLocker>();
113360cc318SEnrico Granata }
114d79273c9SJonas Devlieghere 
115d79273c9SJonas Devlieghere static void ReadThreadBytesReceived(void *baton, const void *src,
116d79273c9SJonas Devlieghere                                     size_t src_len) {
117d79273c9SJonas Devlieghere   if (src && src_len) {
118d79273c9SJonas Devlieghere     Stream *strm = (Stream *)baton;
119d79273c9SJonas Devlieghere     strm->Write(src, src_len);
120d79273c9SJonas Devlieghere     strm->Flush();
121d79273c9SJonas Devlieghere   }
122d79273c9SJonas Devlieghere }
123d79273c9SJonas Devlieghere 
124d79273c9SJonas Devlieghere llvm::Expected<std::unique_ptr<ScriptInterpreterIORedirect>>
12584228365SJonas Devlieghere ScriptInterpreterIORedirect::Create(bool enable_io, Debugger &debugger,
12684228365SJonas Devlieghere                                     CommandReturnObject *result) {
12784228365SJonas Devlieghere   if (enable_io)
12884228365SJonas Devlieghere     return std::unique_ptr<ScriptInterpreterIORedirect>(
12984228365SJonas Devlieghere         new ScriptInterpreterIORedirect(debugger, result));
13084228365SJonas Devlieghere 
131d79273c9SJonas Devlieghere   auto nullin = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL),
132d79273c9SJonas Devlieghere                                             File::eOpenOptionRead);
133d79273c9SJonas Devlieghere   if (!nullin)
134d79273c9SJonas Devlieghere     return nullin.takeError();
135d79273c9SJonas Devlieghere 
136d79273c9SJonas Devlieghere   auto nullout = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL),
137d79273c9SJonas Devlieghere                                              File::eOpenOptionWrite);
138d79273c9SJonas Devlieghere   if (!nullout)
139d79273c9SJonas Devlieghere     return nullin.takeError();
140d79273c9SJonas Devlieghere 
141d79273c9SJonas Devlieghere   return std::unique_ptr<ScriptInterpreterIORedirect>(
142d79273c9SJonas Devlieghere       new ScriptInterpreterIORedirect(std::move(*nullin), std::move(*nullout)));
143d79273c9SJonas Devlieghere }
144d79273c9SJonas Devlieghere 
145d79273c9SJonas Devlieghere ScriptInterpreterIORedirect::ScriptInterpreterIORedirect(
146d79273c9SJonas Devlieghere     std::unique_ptr<File> input, std::unique_ptr<File> output)
147d79273c9SJonas Devlieghere     : m_input_file_sp(std::move(input)),
148d79273c9SJonas Devlieghere       m_output_file_sp(std::make_shared<StreamFile>(std::move(output))),
149d79273c9SJonas Devlieghere       m_error_file_sp(m_output_file_sp),
150d79273c9SJonas Devlieghere       m_communication("lldb.ScriptInterpreterIORedirect.comm"),
151d79273c9SJonas Devlieghere       m_disconnect(false) {}
152d79273c9SJonas Devlieghere 
153d79273c9SJonas Devlieghere ScriptInterpreterIORedirect::ScriptInterpreterIORedirect(
154d79273c9SJonas Devlieghere     Debugger &debugger, CommandReturnObject *result)
155d79273c9SJonas Devlieghere     : m_communication("lldb.ScriptInterpreterIORedirect.comm"),
156d79273c9SJonas Devlieghere       m_disconnect(false) {
157d79273c9SJonas Devlieghere 
158d79273c9SJonas Devlieghere   if (result) {
159d79273c9SJonas Devlieghere     m_input_file_sp = debugger.GetInputFileSP();
160d79273c9SJonas Devlieghere 
161d79273c9SJonas Devlieghere     Pipe pipe;
162d79273c9SJonas Devlieghere     Status pipe_result = pipe.CreateNew(false);
163d79273c9SJonas Devlieghere #if defined(_WIN32)
164d79273c9SJonas Devlieghere     lldb::file_t read_file = pipe.GetReadNativeHandle();
165d79273c9SJonas Devlieghere     pipe.ReleaseReadFileDescriptor();
166d79273c9SJonas Devlieghere     std::unique_ptr<ConnectionGenericFile> conn_up =
167d79273c9SJonas Devlieghere         std::make_unique<ConnectionGenericFile>(read_file, true);
168d79273c9SJonas Devlieghere #else
169d79273c9SJonas Devlieghere     std::unique_ptr<ConnectionFileDescriptor> conn_up =
170d79273c9SJonas Devlieghere         std::make_unique<ConnectionFileDescriptor>(
171d79273c9SJonas Devlieghere             pipe.ReleaseReadFileDescriptor(), true);
172d79273c9SJonas Devlieghere #endif
173d79273c9SJonas Devlieghere 
174d79273c9SJonas Devlieghere     if (conn_up->IsConnected()) {
175d79273c9SJonas Devlieghere       m_communication.SetConnection(std::move(conn_up));
176d79273c9SJonas Devlieghere       m_communication.SetReadThreadBytesReceivedCallback(
177d79273c9SJonas Devlieghere           ReadThreadBytesReceived, &result->GetOutputStream());
178d79273c9SJonas Devlieghere       m_communication.StartReadThread();
179d79273c9SJonas Devlieghere       m_disconnect = true;
180d79273c9SJonas Devlieghere 
181d79273c9SJonas Devlieghere       FILE *outfile_handle = fdopen(pipe.ReleaseWriteFileDescriptor(), "w");
182d79273c9SJonas Devlieghere       m_output_file_sp = std::make_shared<StreamFile>(outfile_handle, true);
183d79273c9SJonas Devlieghere       m_error_file_sp = m_output_file_sp;
184d79273c9SJonas Devlieghere       if (outfile_handle)
185d79273c9SJonas Devlieghere         ::setbuf(outfile_handle, nullptr);
186d79273c9SJonas Devlieghere 
187d79273c9SJonas Devlieghere       result->SetImmediateOutputFile(debugger.GetOutputStream().GetFileSP());
188d79273c9SJonas Devlieghere       result->SetImmediateErrorFile(debugger.GetErrorStream().GetFileSP());
189d79273c9SJonas Devlieghere     }
190d79273c9SJonas Devlieghere   }
191d79273c9SJonas Devlieghere 
192d79273c9SJonas Devlieghere   if (!m_input_file_sp || !m_output_file_sp || !m_error_file_sp)
193d79273c9SJonas Devlieghere     debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_file_sp, m_output_file_sp,
194d79273c9SJonas Devlieghere                                              m_error_file_sp);
195d79273c9SJonas Devlieghere }
196d79273c9SJonas Devlieghere 
197d79273c9SJonas Devlieghere void ScriptInterpreterIORedirect::Flush() {
198d79273c9SJonas Devlieghere   if (m_output_file_sp)
199d79273c9SJonas Devlieghere     m_output_file_sp->Flush();
200d79273c9SJonas Devlieghere   if (m_error_file_sp)
201d79273c9SJonas Devlieghere     m_error_file_sp->Flush();
202d79273c9SJonas Devlieghere }
203d79273c9SJonas Devlieghere 
204d79273c9SJonas Devlieghere ScriptInterpreterIORedirect::~ScriptInterpreterIORedirect() {
205d79273c9SJonas Devlieghere   if (!m_disconnect)
206d79273c9SJonas Devlieghere     return;
207d79273c9SJonas Devlieghere 
208d79273c9SJonas Devlieghere   assert(m_output_file_sp);
209d79273c9SJonas Devlieghere   assert(m_error_file_sp);
210d79273c9SJonas Devlieghere   assert(m_output_file_sp == m_error_file_sp);
211d79273c9SJonas Devlieghere 
212d79273c9SJonas Devlieghere   // Close the write end of the pipe since we are done with our one line
213d79273c9SJonas Devlieghere   // script. This should cause the read thread that output_comm is using to
214d79273c9SJonas Devlieghere   // exit.
215d79273c9SJonas Devlieghere   m_output_file_sp->GetFile().Close();
216d79273c9SJonas Devlieghere   // The close above should cause this thread to exit when it gets to the end
217d79273c9SJonas Devlieghere   // of file, so let it get all its data.
218d79273c9SJonas Devlieghere   m_communication.JoinReadThread();
219d79273c9SJonas Devlieghere   // Now we can close the read end of the pipe.
220d79273c9SJonas Devlieghere   m_communication.Disconnect();
221d79273c9SJonas Devlieghere }
222