15ffd83dbSDimitry Andric //===-- ScriptInterpreter.cpp ---------------------------------------------===//
2*0b57cec5SDimitry Andric //
3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*0b57cec5SDimitry Andric //
7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
8*0b57cec5SDimitry Andric 
9*0b57cec5SDimitry Andric #include "lldb/Interpreter/ScriptInterpreter.h"
105ffd83dbSDimitry Andric #include "lldb/Core/Debugger.h"
115ffd83dbSDimitry Andric #include "lldb/Host/ConnectionFileDescriptor.h"
125ffd83dbSDimitry Andric #include "lldb/Host/Pipe.h"
13*0b57cec5SDimitry Andric #include "lldb/Host/PseudoTerminal.h"
14*0b57cec5SDimitry Andric #include "lldb/Interpreter/CommandReturnObject.h"
15*0b57cec5SDimitry Andric #include "lldb/Utility/Status.h"
16*0b57cec5SDimitry Andric #include "lldb/Utility/Stream.h"
17*0b57cec5SDimitry Andric #include "lldb/Utility/StringList.h"
185ffd83dbSDimitry Andric #if defined(_WIN32)
195ffd83dbSDimitry Andric #include "lldb/Host/windows/ConnectionGenericFileWindows.h"
205ffd83dbSDimitry Andric #endif
215ffd83dbSDimitry Andric #include <memory>
225ffd83dbSDimitry Andric #include <stdio.h>
235ffd83dbSDimitry Andric #include <stdlib.h>
245ffd83dbSDimitry Andric #include <string>
25*0b57cec5SDimitry Andric 
26*0b57cec5SDimitry Andric using namespace lldb;
27*0b57cec5SDimitry Andric using namespace lldb_private;
28*0b57cec5SDimitry Andric 
29*0b57cec5SDimitry Andric ScriptInterpreter::ScriptInterpreter(Debugger &debugger,
30*0b57cec5SDimitry Andric                                      lldb::ScriptLanguage script_lang)
31*0b57cec5SDimitry Andric     : m_debugger(debugger), m_script_lang(script_lang) {}
32*0b57cec5SDimitry Andric 
33*0b57cec5SDimitry Andric ScriptInterpreter::~ScriptInterpreter() {}
34*0b57cec5SDimitry Andric 
35*0b57cec5SDimitry Andric void ScriptInterpreter::CollectDataForBreakpointCommandCallback(
36*0b57cec5SDimitry Andric     std::vector<BreakpointOptions *> &bp_options_vec,
37*0b57cec5SDimitry Andric     CommandReturnObject &result) {
38*0b57cec5SDimitry Andric   result.SetStatus(eReturnStatusFailed);
39*0b57cec5SDimitry Andric   result.AppendError(
40480093f4SDimitry Andric       "This script interpreter does not support breakpoint callbacks.");
41*0b57cec5SDimitry Andric }
42*0b57cec5SDimitry Andric 
43*0b57cec5SDimitry Andric void ScriptInterpreter::CollectDataForWatchpointCommandCallback(
44*0b57cec5SDimitry Andric     WatchpointOptions *bp_options, CommandReturnObject &result) {
45*0b57cec5SDimitry Andric   result.SetStatus(eReturnStatusFailed);
46*0b57cec5SDimitry Andric   result.AppendError(
47480093f4SDimitry Andric       "This script interpreter does not support watchpoint callbacks.");
48480093f4SDimitry Andric }
49480093f4SDimitry Andric 
50480093f4SDimitry Andric bool ScriptInterpreter::LoadScriptingModule(
51480093f4SDimitry Andric     const char *filename, bool init_session, lldb_private::Status &error,
52480093f4SDimitry Andric     StructuredData::ObjectSP *module_sp) {
53480093f4SDimitry Andric   error.SetErrorString(
54480093f4SDimitry Andric       "This script interpreter does not support importing modules.");
55480093f4SDimitry Andric   return false;
56*0b57cec5SDimitry Andric }
57*0b57cec5SDimitry Andric 
58*0b57cec5SDimitry Andric std::string ScriptInterpreter::LanguageToString(lldb::ScriptLanguage language) {
59*0b57cec5SDimitry Andric   switch (language) {
60*0b57cec5SDimitry Andric   case eScriptLanguageNone:
61480093f4SDimitry Andric     return "None";
62*0b57cec5SDimitry Andric   case eScriptLanguagePython:
63480093f4SDimitry Andric     return "Python";
64480093f4SDimitry Andric   case eScriptLanguageLua:
65480093f4SDimitry Andric     return "Lua";
66*0b57cec5SDimitry Andric   case eScriptLanguageUnknown:
67480093f4SDimitry Andric     return "Unknown";
68*0b57cec5SDimitry Andric   }
69480093f4SDimitry Andric   llvm_unreachable("Unhandled ScriptInterpreter!");
70*0b57cec5SDimitry Andric }
71*0b57cec5SDimitry Andric 
72*0b57cec5SDimitry Andric lldb::ScriptLanguage
73*0b57cec5SDimitry Andric ScriptInterpreter::StringToLanguage(const llvm::StringRef &language) {
74*0b57cec5SDimitry Andric   if (language.equals_lower(LanguageToString(eScriptLanguageNone)))
75*0b57cec5SDimitry Andric     return eScriptLanguageNone;
76*0b57cec5SDimitry Andric   if (language.equals_lower(LanguageToString(eScriptLanguagePython)))
77*0b57cec5SDimitry Andric     return eScriptLanguagePython;
78480093f4SDimitry Andric   if (language.equals_lower(LanguageToString(eScriptLanguageLua)))
79480093f4SDimitry Andric     return eScriptLanguageLua;
80*0b57cec5SDimitry Andric   return eScriptLanguageUnknown;
81*0b57cec5SDimitry Andric }
82*0b57cec5SDimitry Andric 
83*0b57cec5SDimitry Andric Status ScriptInterpreter::SetBreakpointCommandCallback(
84*0b57cec5SDimitry Andric     std::vector<BreakpointOptions *> &bp_options_vec,
85*0b57cec5SDimitry Andric     const char *callback_text) {
86*0b57cec5SDimitry Andric   Status return_error;
87*0b57cec5SDimitry Andric   for (BreakpointOptions *bp_options : bp_options_vec) {
88*0b57cec5SDimitry Andric     return_error = SetBreakpointCommandCallback(bp_options, callback_text);
89*0b57cec5SDimitry Andric     if (return_error.Success())
90*0b57cec5SDimitry Andric       break;
91*0b57cec5SDimitry Andric   }
92*0b57cec5SDimitry Andric   return return_error;
93*0b57cec5SDimitry Andric }
94*0b57cec5SDimitry Andric 
95480093f4SDimitry Andric Status ScriptInterpreter::SetBreakpointCommandCallbackFunction(
96480093f4SDimitry Andric     std::vector<BreakpointOptions *> &bp_options_vec, const char *function_name,
97480093f4SDimitry Andric     StructuredData::ObjectSP extra_args_sp) {
98480093f4SDimitry Andric   Status error;
99*0b57cec5SDimitry Andric   for (BreakpointOptions *bp_options : bp_options_vec) {
100480093f4SDimitry Andric     error = SetBreakpointCommandCallbackFunction(bp_options, function_name,
101480093f4SDimitry Andric                                                  extra_args_sp);
102480093f4SDimitry Andric     if (!error.Success())
103480093f4SDimitry Andric       return error;
104*0b57cec5SDimitry Andric   }
105480093f4SDimitry Andric   return error;
106*0b57cec5SDimitry Andric }
107*0b57cec5SDimitry Andric 
108*0b57cec5SDimitry Andric std::unique_ptr<ScriptInterpreterLocker>
109*0b57cec5SDimitry Andric ScriptInterpreter::AcquireInterpreterLock() {
1105ffd83dbSDimitry Andric   return std::make_unique<ScriptInterpreterLocker>();
1115ffd83dbSDimitry Andric }
1125ffd83dbSDimitry Andric 
1135ffd83dbSDimitry Andric static void ReadThreadBytesReceived(void *baton, const void *src,
1145ffd83dbSDimitry Andric                                     size_t src_len) {
1155ffd83dbSDimitry Andric   if (src && src_len) {
1165ffd83dbSDimitry Andric     Stream *strm = (Stream *)baton;
1175ffd83dbSDimitry Andric     strm->Write(src, src_len);
1185ffd83dbSDimitry Andric     strm->Flush();
1195ffd83dbSDimitry Andric   }
1205ffd83dbSDimitry Andric }
1215ffd83dbSDimitry Andric 
1225ffd83dbSDimitry Andric llvm::Expected<std::unique_ptr<ScriptInterpreterIORedirect>>
1235ffd83dbSDimitry Andric ScriptInterpreterIORedirect::Create(bool enable_io, Debugger &debugger,
1245ffd83dbSDimitry Andric                                     CommandReturnObject *result) {
1255ffd83dbSDimitry Andric   if (enable_io)
1265ffd83dbSDimitry Andric     return std::unique_ptr<ScriptInterpreterIORedirect>(
1275ffd83dbSDimitry Andric         new ScriptInterpreterIORedirect(debugger, result));
1285ffd83dbSDimitry Andric 
1295ffd83dbSDimitry Andric   auto nullin = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL),
1305ffd83dbSDimitry Andric                                             File::eOpenOptionRead);
1315ffd83dbSDimitry Andric   if (!nullin)
1325ffd83dbSDimitry Andric     return nullin.takeError();
1335ffd83dbSDimitry Andric 
1345ffd83dbSDimitry Andric   auto nullout = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL),
1355ffd83dbSDimitry Andric                                              File::eOpenOptionWrite);
1365ffd83dbSDimitry Andric   if (!nullout)
1375ffd83dbSDimitry Andric     return nullin.takeError();
1385ffd83dbSDimitry Andric 
1395ffd83dbSDimitry Andric   return std::unique_ptr<ScriptInterpreterIORedirect>(
1405ffd83dbSDimitry Andric       new ScriptInterpreterIORedirect(std::move(*nullin), std::move(*nullout)));
1415ffd83dbSDimitry Andric }
1425ffd83dbSDimitry Andric 
1435ffd83dbSDimitry Andric ScriptInterpreterIORedirect::ScriptInterpreterIORedirect(
1445ffd83dbSDimitry Andric     std::unique_ptr<File> input, std::unique_ptr<File> output)
1455ffd83dbSDimitry Andric     : m_input_file_sp(std::move(input)),
1465ffd83dbSDimitry Andric       m_output_file_sp(std::make_shared<StreamFile>(std::move(output))),
1475ffd83dbSDimitry Andric       m_error_file_sp(m_output_file_sp),
1485ffd83dbSDimitry Andric       m_communication("lldb.ScriptInterpreterIORedirect.comm"),
1495ffd83dbSDimitry Andric       m_disconnect(false) {}
1505ffd83dbSDimitry Andric 
1515ffd83dbSDimitry Andric ScriptInterpreterIORedirect::ScriptInterpreterIORedirect(
1525ffd83dbSDimitry Andric     Debugger &debugger, CommandReturnObject *result)
1535ffd83dbSDimitry Andric     : m_communication("lldb.ScriptInterpreterIORedirect.comm"),
1545ffd83dbSDimitry Andric       m_disconnect(false) {
1555ffd83dbSDimitry Andric 
1565ffd83dbSDimitry Andric   if (result) {
1575ffd83dbSDimitry Andric     m_input_file_sp = debugger.GetInputFileSP();
1585ffd83dbSDimitry Andric 
1595ffd83dbSDimitry Andric     Pipe pipe;
1605ffd83dbSDimitry Andric     Status pipe_result = pipe.CreateNew(false);
1615ffd83dbSDimitry Andric #if defined(_WIN32)
1625ffd83dbSDimitry Andric     lldb::file_t read_file = pipe.GetReadNativeHandle();
1635ffd83dbSDimitry Andric     pipe.ReleaseReadFileDescriptor();
1645ffd83dbSDimitry Andric     std::unique_ptr<ConnectionGenericFile> conn_up =
1655ffd83dbSDimitry Andric         std::make_unique<ConnectionGenericFile>(read_file, true);
1665ffd83dbSDimitry Andric #else
1675ffd83dbSDimitry Andric     std::unique_ptr<ConnectionFileDescriptor> conn_up =
1685ffd83dbSDimitry Andric         std::make_unique<ConnectionFileDescriptor>(
1695ffd83dbSDimitry Andric             pipe.ReleaseReadFileDescriptor(), true);
1705ffd83dbSDimitry Andric #endif
1715ffd83dbSDimitry Andric 
1725ffd83dbSDimitry Andric     if (conn_up->IsConnected()) {
1735ffd83dbSDimitry Andric       m_communication.SetConnection(std::move(conn_up));
1745ffd83dbSDimitry Andric       m_communication.SetReadThreadBytesReceivedCallback(
1755ffd83dbSDimitry Andric           ReadThreadBytesReceived, &result->GetOutputStream());
1765ffd83dbSDimitry Andric       m_communication.StartReadThread();
1775ffd83dbSDimitry Andric       m_disconnect = true;
1785ffd83dbSDimitry Andric 
1795ffd83dbSDimitry Andric       FILE *outfile_handle = fdopen(pipe.ReleaseWriteFileDescriptor(), "w");
1805ffd83dbSDimitry Andric       m_output_file_sp = std::make_shared<StreamFile>(outfile_handle, true);
1815ffd83dbSDimitry Andric       m_error_file_sp = m_output_file_sp;
1825ffd83dbSDimitry Andric       if (outfile_handle)
1835ffd83dbSDimitry Andric         ::setbuf(outfile_handle, nullptr);
1845ffd83dbSDimitry Andric 
1855ffd83dbSDimitry Andric       result->SetImmediateOutputFile(debugger.GetOutputStream().GetFileSP());
1865ffd83dbSDimitry Andric       result->SetImmediateErrorFile(debugger.GetErrorStream().GetFileSP());
1875ffd83dbSDimitry Andric     }
1885ffd83dbSDimitry Andric   }
1895ffd83dbSDimitry Andric 
1905ffd83dbSDimitry Andric   if (!m_input_file_sp || !m_output_file_sp || !m_error_file_sp)
1915ffd83dbSDimitry Andric     debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_file_sp, m_output_file_sp,
1925ffd83dbSDimitry Andric                                              m_error_file_sp);
1935ffd83dbSDimitry Andric }
1945ffd83dbSDimitry Andric 
1955ffd83dbSDimitry Andric void ScriptInterpreterIORedirect::Flush() {
1965ffd83dbSDimitry Andric   if (m_output_file_sp)
1975ffd83dbSDimitry Andric     m_output_file_sp->Flush();
1985ffd83dbSDimitry Andric   if (m_error_file_sp)
1995ffd83dbSDimitry Andric     m_error_file_sp->Flush();
2005ffd83dbSDimitry Andric }
2015ffd83dbSDimitry Andric 
2025ffd83dbSDimitry Andric ScriptInterpreterIORedirect::~ScriptInterpreterIORedirect() {
2035ffd83dbSDimitry Andric   if (!m_disconnect)
2045ffd83dbSDimitry Andric     return;
2055ffd83dbSDimitry Andric 
2065ffd83dbSDimitry Andric   assert(m_output_file_sp);
2075ffd83dbSDimitry Andric   assert(m_error_file_sp);
2085ffd83dbSDimitry Andric   assert(m_output_file_sp == m_error_file_sp);
2095ffd83dbSDimitry Andric 
2105ffd83dbSDimitry Andric   // Close the write end of the pipe since we are done with our one line
2115ffd83dbSDimitry Andric   // script. This should cause the read thread that output_comm is using to
2125ffd83dbSDimitry Andric   // exit.
2135ffd83dbSDimitry Andric   m_output_file_sp->GetFile().Close();
2145ffd83dbSDimitry Andric   // The close above should cause this thread to exit when it gets to the end
2155ffd83dbSDimitry Andric   // of file, so let it get all its data.
2165ffd83dbSDimitry Andric   m_communication.JoinReadThread();
2175ffd83dbSDimitry Andric   // Now we can close the read end of the pipe.
2185ffd83dbSDimitry Andric   m_communication.Disconnect();
219*0b57cec5SDimitry Andric }
220