15ffd83dbSDimitry Andric //===-- ScriptInterpreter.cpp ---------------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric
90b57cec5SDimitry 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"
130b57cec5SDimitry Andric #include "lldb/Host/PseudoTerminal.h"
140b57cec5SDimitry Andric #include "lldb/Interpreter/CommandReturnObject.h"
150b57cec5SDimitry Andric #include "lldb/Utility/Status.h"
160b57cec5SDimitry Andric #include "lldb/Utility/Stream.h"
170b57cec5SDimitry Andric #include "lldb/Utility/StringList.h"
185ffd83dbSDimitry Andric #if defined(_WIN32)
195ffd83dbSDimitry Andric #include "lldb/Host/windows/ConnectionGenericFileWindows.h"
205ffd83dbSDimitry Andric #endif
21*5f7ddb14SDimitry Andric #include <cstdio>
22*5f7ddb14SDimitry Andric #include <cstdlib>
235ffd83dbSDimitry Andric #include <memory>
245ffd83dbSDimitry Andric #include <string>
250b57cec5SDimitry Andric
260b57cec5SDimitry Andric using namespace lldb;
270b57cec5SDimitry Andric using namespace lldb_private;
280b57cec5SDimitry Andric
ScriptInterpreter(Debugger & debugger,lldb::ScriptLanguage script_lang,lldb::ScriptedProcessInterfaceUP scripted_process_interface_up)29*5f7ddb14SDimitry Andric ScriptInterpreter::ScriptInterpreter(
30*5f7ddb14SDimitry Andric Debugger &debugger, lldb::ScriptLanguage script_lang,
31*5f7ddb14SDimitry Andric lldb::ScriptedProcessInterfaceUP scripted_process_interface_up)
32*5f7ddb14SDimitry Andric : m_debugger(debugger), m_script_lang(script_lang),
33*5f7ddb14SDimitry Andric m_scripted_process_interface_up(
34*5f7ddb14SDimitry Andric std::move(scripted_process_interface_up)) {}
350b57cec5SDimitry Andric
CollectDataForBreakpointCommandCallback(std::vector<std::reference_wrapper<BreakpointOptions>> & bp_options_vec,CommandReturnObject & result)360b57cec5SDimitry Andric void ScriptInterpreter::CollectDataForBreakpointCommandCallback(
37*5f7ddb14SDimitry Andric std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec,
380b57cec5SDimitry Andric CommandReturnObject &result) {
390b57cec5SDimitry Andric result.AppendError(
40480093f4SDimitry Andric "This script interpreter does not support breakpoint callbacks.");
410b57cec5SDimitry Andric }
420b57cec5SDimitry Andric
CollectDataForWatchpointCommandCallback(WatchpointOptions * bp_options,CommandReturnObject & result)430b57cec5SDimitry Andric void ScriptInterpreter::CollectDataForWatchpointCommandCallback(
440b57cec5SDimitry Andric WatchpointOptions *bp_options, CommandReturnObject &result) {
450b57cec5SDimitry Andric result.AppendError(
46480093f4SDimitry Andric "This script interpreter does not support watchpoint callbacks.");
47480093f4SDimitry Andric }
48480093f4SDimitry Andric
LoadScriptingModule(const char * filename,const LoadScriptOptions & options,lldb_private::Status & error,StructuredData::ObjectSP * module_sp,FileSpec extra_search_dir)49af732203SDimitry Andric bool ScriptInterpreter::LoadScriptingModule(const char *filename,
50*5f7ddb14SDimitry Andric const LoadScriptOptions &options,
51af732203SDimitry Andric lldb_private::Status &error,
52af732203SDimitry Andric StructuredData::ObjectSP *module_sp,
53af732203SDimitry Andric FileSpec extra_search_dir) {
54480093f4SDimitry Andric error.SetErrorString(
55480093f4SDimitry Andric "This script interpreter does not support importing modules.");
56480093f4SDimitry Andric return false;
570b57cec5SDimitry Andric }
580b57cec5SDimitry Andric
LanguageToString(lldb::ScriptLanguage language)590b57cec5SDimitry Andric std::string ScriptInterpreter::LanguageToString(lldb::ScriptLanguage language) {
600b57cec5SDimitry Andric switch (language) {
610b57cec5SDimitry Andric case eScriptLanguageNone:
62480093f4SDimitry Andric return "None";
630b57cec5SDimitry Andric case eScriptLanguagePython:
64480093f4SDimitry Andric return "Python";
65480093f4SDimitry Andric case eScriptLanguageLua:
66480093f4SDimitry Andric return "Lua";
670b57cec5SDimitry Andric case eScriptLanguageUnknown:
68480093f4SDimitry Andric return "Unknown";
690b57cec5SDimitry Andric }
70480093f4SDimitry Andric llvm_unreachable("Unhandled ScriptInterpreter!");
710b57cec5SDimitry Andric }
720b57cec5SDimitry Andric
73*5f7ddb14SDimitry Andric lldb::DataExtractorSP
GetDataExtractorFromSBData(const lldb::SBData & data) const74*5f7ddb14SDimitry Andric ScriptInterpreter::GetDataExtractorFromSBData(const lldb::SBData &data) const {
75*5f7ddb14SDimitry Andric return data.m_opaque_sp;
76*5f7ddb14SDimitry Andric }
77*5f7ddb14SDimitry Andric
78*5f7ddb14SDimitry Andric Status
GetStatusFromSBError(const lldb::SBError & error) const79*5f7ddb14SDimitry Andric ScriptInterpreter::GetStatusFromSBError(const lldb::SBError &error) const {
80*5f7ddb14SDimitry Andric if (error.m_opaque_up)
81*5f7ddb14SDimitry Andric return *error.m_opaque_up.get();
82*5f7ddb14SDimitry Andric
83*5f7ddb14SDimitry Andric return Status();
84*5f7ddb14SDimitry Andric }
85*5f7ddb14SDimitry Andric
860b57cec5SDimitry Andric lldb::ScriptLanguage
StringToLanguage(const llvm::StringRef & language)870b57cec5SDimitry Andric ScriptInterpreter::StringToLanguage(const llvm::StringRef &language) {
88*5f7ddb14SDimitry Andric if (language.equals_insensitive(LanguageToString(eScriptLanguageNone)))
890b57cec5SDimitry Andric return eScriptLanguageNone;
90*5f7ddb14SDimitry Andric if (language.equals_insensitive(LanguageToString(eScriptLanguagePython)))
910b57cec5SDimitry Andric return eScriptLanguagePython;
92*5f7ddb14SDimitry Andric if (language.equals_insensitive(LanguageToString(eScriptLanguageLua)))
93480093f4SDimitry Andric return eScriptLanguageLua;
940b57cec5SDimitry Andric return eScriptLanguageUnknown;
950b57cec5SDimitry Andric }
960b57cec5SDimitry Andric
SetBreakpointCommandCallback(std::vector<std::reference_wrapper<BreakpointOptions>> & bp_options_vec,const char * callback_text)970b57cec5SDimitry Andric Status ScriptInterpreter::SetBreakpointCommandCallback(
98*5f7ddb14SDimitry Andric std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec,
990b57cec5SDimitry Andric const char *callback_text) {
1000b57cec5SDimitry Andric Status return_error;
101*5f7ddb14SDimitry Andric for (BreakpointOptions &bp_options : bp_options_vec) {
1020b57cec5SDimitry Andric return_error = SetBreakpointCommandCallback(bp_options, callback_text);
1030b57cec5SDimitry Andric if (return_error.Success())
1040b57cec5SDimitry Andric break;
1050b57cec5SDimitry Andric }
1060b57cec5SDimitry Andric return return_error;
1070b57cec5SDimitry Andric }
1080b57cec5SDimitry Andric
SetBreakpointCommandCallbackFunction(std::vector<std::reference_wrapper<BreakpointOptions>> & bp_options_vec,const char * function_name,StructuredData::ObjectSP extra_args_sp)109480093f4SDimitry Andric Status ScriptInterpreter::SetBreakpointCommandCallbackFunction(
110*5f7ddb14SDimitry Andric std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec,
111*5f7ddb14SDimitry Andric const char *function_name, StructuredData::ObjectSP extra_args_sp) {
112480093f4SDimitry Andric Status error;
113*5f7ddb14SDimitry Andric for (BreakpointOptions &bp_options : bp_options_vec) {
114480093f4SDimitry Andric error = SetBreakpointCommandCallbackFunction(bp_options, function_name,
115480093f4SDimitry Andric extra_args_sp);
116480093f4SDimitry Andric if (!error.Success())
117480093f4SDimitry Andric return error;
1180b57cec5SDimitry Andric }
119480093f4SDimitry Andric return error;
1200b57cec5SDimitry Andric }
1210b57cec5SDimitry Andric
1220b57cec5SDimitry Andric std::unique_ptr<ScriptInterpreterLocker>
AcquireInterpreterLock()1230b57cec5SDimitry Andric ScriptInterpreter::AcquireInterpreterLock() {
1245ffd83dbSDimitry Andric return std::make_unique<ScriptInterpreterLocker>();
1255ffd83dbSDimitry Andric }
1265ffd83dbSDimitry Andric
ReadThreadBytesReceived(void * baton,const void * src,size_t src_len)1275ffd83dbSDimitry Andric static void ReadThreadBytesReceived(void *baton, const void *src,
1285ffd83dbSDimitry Andric size_t src_len) {
1295ffd83dbSDimitry Andric if (src && src_len) {
1305ffd83dbSDimitry Andric Stream *strm = (Stream *)baton;
1315ffd83dbSDimitry Andric strm->Write(src, src_len);
1325ffd83dbSDimitry Andric strm->Flush();
1335ffd83dbSDimitry Andric }
1345ffd83dbSDimitry Andric }
1355ffd83dbSDimitry Andric
1365ffd83dbSDimitry Andric llvm::Expected<std::unique_ptr<ScriptInterpreterIORedirect>>
Create(bool enable_io,Debugger & debugger,CommandReturnObject * result)1375ffd83dbSDimitry Andric ScriptInterpreterIORedirect::Create(bool enable_io, Debugger &debugger,
1385ffd83dbSDimitry Andric CommandReturnObject *result) {
1395ffd83dbSDimitry Andric if (enable_io)
1405ffd83dbSDimitry Andric return std::unique_ptr<ScriptInterpreterIORedirect>(
1415ffd83dbSDimitry Andric new ScriptInterpreterIORedirect(debugger, result));
1425ffd83dbSDimitry Andric
1435ffd83dbSDimitry Andric auto nullin = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL),
1445ffd83dbSDimitry Andric File::eOpenOptionRead);
1455ffd83dbSDimitry Andric if (!nullin)
1465ffd83dbSDimitry Andric return nullin.takeError();
1475ffd83dbSDimitry Andric
1485ffd83dbSDimitry Andric auto nullout = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL),
1495ffd83dbSDimitry Andric File::eOpenOptionWrite);
1505ffd83dbSDimitry Andric if (!nullout)
1515ffd83dbSDimitry Andric return nullin.takeError();
1525ffd83dbSDimitry Andric
1535ffd83dbSDimitry Andric return std::unique_ptr<ScriptInterpreterIORedirect>(
1545ffd83dbSDimitry Andric new ScriptInterpreterIORedirect(std::move(*nullin), std::move(*nullout)));
1555ffd83dbSDimitry Andric }
1565ffd83dbSDimitry Andric
ScriptInterpreterIORedirect(std::unique_ptr<File> input,std::unique_ptr<File> output)1575ffd83dbSDimitry Andric ScriptInterpreterIORedirect::ScriptInterpreterIORedirect(
1585ffd83dbSDimitry Andric std::unique_ptr<File> input, std::unique_ptr<File> output)
1595ffd83dbSDimitry Andric : m_input_file_sp(std::move(input)),
1605ffd83dbSDimitry Andric m_output_file_sp(std::make_shared<StreamFile>(std::move(output))),
1615ffd83dbSDimitry Andric m_error_file_sp(m_output_file_sp),
1625ffd83dbSDimitry Andric m_communication("lldb.ScriptInterpreterIORedirect.comm"),
1635ffd83dbSDimitry Andric m_disconnect(false) {}
1645ffd83dbSDimitry Andric
ScriptInterpreterIORedirect(Debugger & debugger,CommandReturnObject * result)1655ffd83dbSDimitry Andric ScriptInterpreterIORedirect::ScriptInterpreterIORedirect(
1665ffd83dbSDimitry Andric Debugger &debugger, CommandReturnObject *result)
1675ffd83dbSDimitry Andric : m_communication("lldb.ScriptInterpreterIORedirect.comm"),
1685ffd83dbSDimitry Andric m_disconnect(false) {
1695ffd83dbSDimitry Andric
1705ffd83dbSDimitry Andric if (result) {
1715ffd83dbSDimitry Andric m_input_file_sp = debugger.GetInputFileSP();
1725ffd83dbSDimitry Andric
1735ffd83dbSDimitry Andric Pipe pipe;
1745ffd83dbSDimitry Andric Status pipe_result = pipe.CreateNew(false);
1755ffd83dbSDimitry Andric #if defined(_WIN32)
1765ffd83dbSDimitry Andric lldb::file_t read_file = pipe.GetReadNativeHandle();
1775ffd83dbSDimitry Andric pipe.ReleaseReadFileDescriptor();
1785ffd83dbSDimitry Andric std::unique_ptr<ConnectionGenericFile> conn_up =
1795ffd83dbSDimitry Andric std::make_unique<ConnectionGenericFile>(read_file, true);
1805ffd83dbSDimitry Andric #else
1815ffd83dbSDimitry Andric std::unique_ptr<ConnectionFileDescriptor> conn_up =
1825ffd83dbSDimitry Andric std::make_unique<ConnectionFileDescriptor>(
1835ffd83dbSDimitry Andric pipe.ReleaseReadFileDescriptor(), true);
1845ffd83dbSDimitry Andric #endif
1855ffd83dbSDimitry Andric
1865ffd83dbSDimitry Andric if (conn_up->IsConnected()) {
1875ffd83dbSDimitry Andric m_communication.SetConnection(std::move(conn_up));
1885ffd83dbSDimitry Andric m_communication.SetReadThreadBytesReceivedCallback(
1895ffd83dbSDimitry Andric ReadThreadBytesReceived, &result->GetOutputStream());
1905ffd83dbSDimitry Andric m_communication.StartReadThread();
1915ffd83dbSDimitry Andric m_disconnect = true;
1925ffd83dbSDimitry Andric
1935ffd83dbSDimitry Andric FILE *outfile_handle = fdopen(pipe.ReleaseWriteFileDescriptor(), "w");
1945ffd83dbSDimitry Andric m_output_file_sp = std::make_shared<StreamFile>(outfile_handle, true);
1955ffd83dbSDimitry Andric m_error_file_sp = m_output_file_sp;
1965ffd83dbSDimitry Andric if (outfile_handle)
1975ffd83dbSDimitry Andric ::setbuf(outfile_handle, nullptr);
1985ffd83dbSDimitry Andric
1995ffd83dbSDimitry Andric result->SetImmediateOutputFile(debugger.GetOutputStream().GetFileSP());
2005ffd83dbSDimitry Andric result->SetImmediateErrorFile(debugger.GetErrorStream().GetFileSP());
2015ffd83dbSDimitry Andric }
2025ffd83dbSDimitry Andric }
2035ffd83dbSDimitry Andric
2045ffd83dbSDimitry Andric if (!m_input_file_sp || !m_output_file_sp || !m_error_file_sp)
2055ffd83dbSDimitry Andric debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_file_sp, m_output_file_sp,
2065ffd83dbSDimitry Andric m_error_file_sp);
2075ffd83dbSDimitry Andric }
2085ffd83dbSDimitry Andric
Flush()2095ffd83dbSDimitry Andric void ScriptInterpreterIORedirect::Flush() {
2105ffd83dbSDimitry Andric if (m_output_file_sp)
2115ffd83dbSDimitry Andric m_output_file_sp->Flush();
2125ffd83dbSDimitry Andric if (m_error_file_sp)
2135ffd83dbSDimitry Andric m_error_file_sp->Flush();
2145ffd83dbSDimitry Andric }
2155ffd83dbSDimitry Andric
~ScriptInterpreterIORedirect()2165ffd83dbSDimitry Andric ScriptInterpreterIORedirect::~ScriptInterpreterIORedirect() {
2175ffd83dbSDimitry Andric if (!m_disconnect)
2185ffd83dbSDimitry Andric return;
2195ffd83dbSDimitry Andric
2205ffd83dbSDimitry Andric assert(m_output_file_sp);
2215ffd83dbSDimitry Andric assert(m_error_file_sp);
2225ffd83dbSDimitry Andric assert(m_output_file_sp == m_error_file_sp);
2235ffd83dbSDimitry Andric
2245ffd83dbSDimitry Andric // Close the write end of the pipe since we are done with our one line
2255ffd83dbSDimitry Andric // script. This should cause the read thread that output_comm is using to
2265ffd83dbSDimitry Andric // exit.
2275ffd83dbSDimitry Andric m_output_file_sp->GetFile().Close();
2285ffd83dbSDimitry Andric // The close above should cause this thread to exit when it gets to the end
2295ffd83dbSDimitry Andric // of file, so let it get all its data.
2305ffd83dbSDimitry Andric m_communication.JoinReadThread();
2315ffd83dbSDimitry Andric // Now we can close the read end of the pipe.
2325ffd83dbSDimitry Andric m_communication.Disconnect();
2330b57cec5SDimitry Andric }
234