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"
18*11f2ef4dSJonas Devlieghere #if defined(_WIN32)
19*11f2ef4dSJonas Devlieghere #include "lldb/Host/windows/ConnectionGenericFileWindows.h"
20*11f2ef4dSJonas Devlieghere #endif
21*11f2ef4dSJonas Devlieghere #include <memory>
22*11f2ef4dSJonas Devlieghere #include <stdio.h>
23*11f2ef4dSJonas Devlieghere #include <stdlib.h>
24*11f2ef4dSJonas 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 
50bd5c8d16SJonas Devlieghere bool ScriptInterpreter::LoadScriptingModule(
5115625112SJonas Devlieghere     const char *filename, bool init_session, lldb_private::Status &error,
5215625112SJonas Devlieghere     StructuredData::ObjectSP *module_sp) {
53bd5c8d16SJonas Devlieghere   error.SetErrorString(
54bd5c8d16SJonas Devlieghere       "This script interpreter does not support importing modules.");
55bd5c8d16SJonas Devlieghere   return false;
56bd5c8d16SJonas Devlieghere }
57bd5c8d16SJonas Devlieghere 
58b9c1b51eSKate Stone std::string ScriptInterpreter::LanguageToString(lldb::ScriptLanguage language) {
59b9c1b51eSKate Stone   switch (language) {
603df9a8dfSCaroline Tice   case eScriptLanguageNone:
61acdda134SJonas Devlieghere     return "None";
623df9a8dfSCaroline Tice   case eScriptLanguagePython:
63acdda134SJonas Devlieghere     return "Python";
64acdda134SJonas Devlieghere   case eScriptLanguageLua:
65acdda134SJonas Devlieghere     return "Lua";
66f7e07256SJim Ingham   case eScriptLanguageUnknown:
67acdda134SJonas Devlieghere     return "Unknown";
683df9a8dfSCaroline Tice   }
69fbccef6bSPavel Labath   llvm_unreachable("Unhandled ScriptInterpreter!");
703df9a8dfSCaroline Tice }
712f88aadfSCaroline Tice 
72f7e07256SJim Ingham lldb::ScriptLanguage
73f7e07256SJim Ingham ScriptInterpreter::StringToLanguage(const llvm::StringRef &language) {
74f7e07256SJim Ingham   if (language.equals_lower(LanguageToString(eScriptLanguageNone)))
75f7e07256SJim Ingham     return eScriptLanguageNone;
7645188dd9SDavide Italiano   if (language.equals_lower(LanguageToString(eScriptLanguagePython)))
77f7e07256SJim Ingham     return eScriptLanguagePython;
78acdda134SJonas Devlieghere   if (language.equals_lower(LanguageToString(eScriptLanguageLua)))
79acdda134SJonas Devlieghere     return eScriptLanguageLua;
80f7e07256SJim Ingham   return eScriptLanguageUnknown;
81f7e07256SJim Ingham }
82f7e07256SJim Ingham 
8397206d57SZachary Turner Status ScriptInterpreter::SetBreakpointCommandCallback(
84b9c1b51eSKate Stone     std::vector<BreakpointOptions *> &bp_options_vec,
85b9c1b51eSKate Stone     const char *callback_text) {
8697206d57SZachary Turner   Status return_error;
87b9c1b51eSKate Stone   for (BreakpointOptions *bp_options : bp_options_vec) {
88b5796cb4SJim Ingham     return_error = SetBreakpointCommandCallback(bp_options, callback_text);
89b5796cb4SJim Ingham     if (return_error.Success())
90b5796cb4SJim Ingham       break;
91b5796cb4SJim Ingham   }
92b5796cb4SJim Ingham   return return_error;
93b5796cb4SJim Ingham }
94b5796cb4SJim Ingham 
95738af7a6SJim Ingham Status ScriptInterpreter::SetBreakpointCommandCallbackFunction(
96acdda134SJonas Devlieghere     std::vector<BreakpointOptions *> &bp_options_vec, const char *function_name,
97738af7a6SJim Ingham     StructuredData::ObjectSP extra_args_sp) {
98738af7a6SJim Ingham   Status error;
99b9c1b51eSKate Stone   for (BreakpointOptions *bp_options : bp_options_vec) {
100738af7a6SJim Ingham     error = SetBreakpointCommandCallbackFunction(bp_options, function_name,
101738af7a6SJim Ingham                                                  extra_args_sp);
102738af7a6SJim Ingham     if (!error.Success())
103738af7a6SJim Ingham       return error;
104b5796cb4SJim Ingham   }
105738af7a6SJim Ingham   return error;
106b5796cb4SJim Ingham }
107b5796cb4SJim Ingham 
1087b0992d9SGreg Clayton std::unique_ptr<ScriptInterpreterLocker>
109b9c1b51eSKate Stone ScriptInterpreter::AcquireInterpreterLock() {
11006412daeSJonas Devlieghere   return std::make_unique<ScriptInterpreterLocker>();
111360cc318SEnrico Granata }
112d79273c9SJonas Devlieghere 
113d79273c9SJonas Devlieghere static void ReadThreadBytesReceived(void *baton, const void *src,
114d79273c9SJonas Devlieghere                                     size_t src_len) {
115d79273c9SJonas Devlieghere   if (src && src_len) {
116d79273c9SJonas Devlieghere     Stream *strm = (Stream *)baton;
117d79273c9SJonas Devlieghere     strm->Write(src, src_len);
118d79273c9SJonas Devlieghere     strm->Flush();
119d79273c9SJonas Devlieghere   }
120d79273c9SJonas Devlieghere }
121d79273c9SJonas Devlieghere 
122d79273c9SJonas Devlieghere llvm::Expected<std::unique_ptr<ScriptInterpreterIORedirect>>
12384228365SJonas Devlieghere ScriptInterpreterIORedirect::Create(bool enable_io, Debugger &debugger,
12484228365SJonas Devlieghere                                     CommandReturnObject *result) {
12584228365SJonas Devlieghere   if (enable_io)
12684228365SJonas Devlieghere     return std::unique_ptr<ScriptInterpreterIORedirect>(
12784228365SJonas Devlieghere         new ScriptInterpreterIORedirect(debugger, result));
12884228365SJonas Devlieghere 
129d79273c9SJonas Devlieghere   auto nullin = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL),
130d79273c9SJonas Devlieghere                                             File::eOpenOptionRead);
131d79273c9SJonas Devlieghere   if (!nullin)
132d79273c9SJonas Devlieghere     return nullin.takeError();
133d79273c9SJonas Devlieghere 
134d79273c9SJonas Devlieghere   auto nullout = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL),
135d79273c9SJonas Devlieghere                                              File::eOpenOptionWrite);
136d79273c9SJonas Devlieghere   if (!nullout)
137d79273c9SJonas Devlieghere     return nullin.takeError();
138d79273c9SJonas Devlieghere 
139d79273c9SJonas Devlieghere   return std::unique_ptr<ScriptInterpreterIORedirect>(
140d79273c9SJonas Devlieghere       new ScriptInterpreterIORedirect(std::move(*nullin), std::move(*nullout)));
141d79273c9SJonas Devlieghere }
142d79273c9SJonas Devlieghere 
143d79273c9SJonas Devlieghere ScriptInterpreterIORedirect::ScriptInterpreterIORedirect(
144d79273c9SJonas Devlieghere     std::unique_ptr<File> input, std::unique_ptr<File> output)
145d79273c9SJonas Devlieghere     : m_input_file_sp(std::move(input)),
146d79273c9SJonas Devlieghere       m_output_file_sp(std::make_shared<StreamFile>(std::move(output))),
147d79273c9SJonas Devlieghere       m_error_file_sp(m_output_file_sp),
148d79273c9SJonas Devlieghere       m_communication("lldb.ScriptInterpreterIORedirect.comm"),
149d79273c9SJonas Devlieghere       m_disconnect(false) {}
150d79273c9SJonas Devlieghere 
151d79273c9SJonas Devlieghere ScriptInterpreterIORedirect::ScriptInterpreterIORedirect(
152d79273c9SJonas Devlieghere     Debugger &debugger, CommandReturnObject *result)
153d79273c9SJonas Devlieghere     : m_communication("lldb.ScriptInterpreterIORedirect.comm"),
154d79273c9SJonas Devlieghere       m_disconnect(false) {
155d79273c9SJonas Devlieghere 
156d79273c9SJonas Devlieghere   if (result) {
157d79273c9SJonas Devlieghere     m_input_file_sp = debugger.GetInputFileSP();
158d79273c9SJonas Devlieghere 
159d79273c9SJonas Devlieghere     Pipe pipe;
160d79273c9SJonas Devlieghere     Status pipe_result = pipe.CreateNew(false);
161d79273c9SJonas Devlieghere #if defined(_WIN32)
162d79273c9SJonas Devlieghere     lldb::file_t read_file = pipe.GetReadNativeHandle();
163d79273c9SJonas Devlieghere     pipe.ReleaseReadFileDescriptor();
164d79273c9SJonas Devlieghere     std::unique_ptr<ConnectionGenericFile> conn_up =
165d79273c9SJonas Devlieghere         std::make_unique<ConnectionGenericFile>(read_file, true);
166d79273c9SJonas Devlieghere #else
167d79273c9SJonas Devlieghere     std::unique_ptr<ConnectionFileDescriptor> conn_up =
168d79273c9SJonas Devlieghere         std::make_unique<ConnectionFileDescriptor>(
169d79273c9SJonas Devlieghere             pipe.ReleaseReadFileDescriptor(), true);
170d79273c9SJonas Devlieghere #endif
171d79273c9SJonas Devlieghere 
172d79273c9SJonas Devlieghere     if (conn_up->IsConnected()) {
173d79273c9SJonas Devlieghere       m_communication.SetConnection(std::move(conn_up));
174d79273c9SJonas Devlieghere       m_communication.SetReadThreadBytesReceivedCallback(
175d79273c9SJonas Devlieghere           ReadThreadBytesReceived, &result->GetOutputStream());
176d79273c9SJonas Devlieghere       m_communication.StartReadThread();
177d79273c9SJonas Devlieghere       m_disconnect = true;
178d79273c9SJonas Devlieghere 
179d79273c9SJonas Devlieghere       FILE *outfile_handle = fdopen(pipe.ReleaseWriteFileDescriptor(), "w");
180d79273c9SJonas Devlieghere       m_output_file_sp = std::make_shared<StreamFile>(outfile_handle, true);
181d79273c9SJonas Devlieghere       m_error_file_sp = m_output_file_sp;
182d79273c9SJonas Devlieghere       if (outfile_handle)
183d79273c9SJonas Devlieghere         ::setbuf(outfile_handle, nullptr);
184d79273c9SJonas Devlieghere 
185d79273c9SJonas Devlieghere       result->SetImmediateOutputFile(debugger.GetOutputStream().GetFileSP());
186d79273c9SJonas Devlieghere       result->SetImmediateErrorFile(debugger.GetErrorStream().GetFileSP());
187d79273c9SJonas Devlieghere     }
188d79273c9SJonas Devlieghere   }
189d79273c9SJonas Devlieghere 
190d79273c9SJonas Devlieghere   if (!m_input_file_sp || !m_output_file_sp || !m_error_file_sp)
191d79273c9SJonas Devlieghere     debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_file_sp, m_output_file_sp,
192d79273c9SJonas Devlieghere                                              m_error_file_sp);
193d79273c9SJonas Devlieghere }
194d79273c9SJonas Devlieghere 
195d79273c9SJonas Devlieghere void ScriptInterpreterIORedirect::Flush() {
196d79273c9SJonas Devlieghere   if (m_output_file_sp)
197d79273c9SJonas Devlieghere     m_output_file_sp->Flush();
198d79273c9SJonas Devlieghere   if (m_error_file_sp)
199d79273c9SJonas Devlieghere     m_error_file_sp->Flush();
200d79273c9SJonas Devlieghere }
201d79273c9SJonas Devlieghere 
202d79273c9SJonas Devlieghere ScriptInterpreterIORedirect::~ScriptInterpreterIORedirect() {
203d79273c9SJonas Devlieghere   if (!m_disconnect)
204d79273c9SJonas Devlieghere     return;
205d79273c9SJonas Devlieghere 
206d79273c9SJonas Devlieghere   assert(m_output_file_sp);
207d79273c9SJonas Devlieghere   assert(m_error_file_sp);
208d79273c9SJonas Devlieghere   assert(m_output_file_sp == m_error_file_sp);
209d79273c9SJonas Devlieghere 
210d79273c9SJonas Devlieghere   // Close the write end of the pipe since we are done with our one line
211d79273c9SJonas Devlieghere   // script. This should cause the read thread that output_comm is using to
212d79273c9SJonas Devlieghere   // exit.
213d79273c9SJonas Devlieghere   m_output_file_sp->GetFile().Close();
214d79273c9SJonas Devlieghere   // The close above should cause this thread to exit when it gets to the end
215d79273c9SJonas Devlieghere   // of file, so let it get all its data.
216d79273c9SJonas Devlieghere   m_communication.JoinReadThread();
217d79273c9SJonas Devlieghere   // Now we can close the read end of the pipe.
218d79273c9SJonas Devlieghere   m_communication.Disconnect();
219d79273c9SJonas Devlieghere }
220