1 //===-- ScriptInterpreter.cpp ---------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "lldb/Interpreter/ScriptInterpreter.h" 10 #include "lldb/Core/Debugger.h" 11 #include "lldb/Host/ConnectionFileDescriptor.h" 12 #include "lldb/Host/Pipe.h" 13 #include "lldb/Host/PseudoTerminal.h" 14 #include "lldb/Interpreter/CommandReturnObject.h" 15 #include "lldb/Utility/Status.h" 16 #include "lldb/Utility/Stream.h" 17 #include "lldb/Utility/StringList.h" 18 #if defined(_WIN32) 19 #include "lldb/Host/windows/ConnectionGenericFileWindows.h" 20 #endif 21 #include <cstdio> 22 #include <cstdlib> 23 #include <memory> 24 #include <string> 25 26 using namespace lldb; 27 using namespace lldb_private; 28 29 ScriptInterpreter::ScriptInterpreter( 30 Debugger &debugger, lldb::ScriptLanguage script_lang, 31 lldb::ScriptedProcessInterfaceUP scripted_process_interface_up) 32 : m_debugger(debugger), m_script_lang(script_lang), 33 m_scripted_process_interface_up( 34 std::move(scripted_process_interface_up)) {} 35 36 void ScriptInterpreter::CollectDataForBreakpointCommandCallback( 37 std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec, 38 CommandReturnObject &result) { 39 result.AppendError( 40 "This script interpreter does not support breakpoint callbacks."); 41 } 42 43 void ScriptInterpreter::CollectDataForWatchpointCommandCallback( 44 WatchpointOptions *bp_options, CommandReturnObject &result) { 45 result.AppendError( 46 "This script interpreter does not support watchpoint callbacks."); 47 } 48 49 bool ScriptInterpreter::LoadScriptingModule(const char *filename, 50 const LoadScriptOptions &options, 51 lldb_private::Status &error, 52 StructuredData::ObjectSP *module_sp, 53 FileSpec extra_search_dir) { 54 error.SetErrorString( 55 "This script interpreter does not support importing modules."); 56 return false; 57 } 58 59 std::string ScriptInterpreter::LanguageToString(lldb::ScriptLanguage language) { 60 switch (language) { 61 case eScriptLanguageNone: 62 return "None"; 63 case eScriptLanguagePython: 64 return "Python"; 65 case eScriptLanguageLua: 66 return "Lua"; 67 case eScriptLanguageUnknown: 68 return "Unknown"; 69 } 70 llvm_unreachable("Unhandled ScriptInterpreter!"); 71 } 72 73 lldb::DataExtractorSP 74 ScriptInterpreter::GetDataExtractorFromSBData(const lldb::SBData &data) const { 75 return data.m_opaque_sp; 76 } 77 78 Status 79 ScriptInterpreter::GetStatusFromSBError(const lldb::SBError &error) const { 80 if (error.m_opaque_up) 81 return *error.m_opaque_up.get(); 82 83 return Status(); 84 } 85 86 lldb::ScriptLanguage 87 ScriptInterpreter::StringToLanguage(const llvm::StringRef &language) { 88 if (language.equals_insensitive(LanguageToString(eScriptLanguageNone))) 89 return eScriptLanguageNone; 90 if (language.equals_insensitive(LanguageToString(eScriptLanguagePython))) 91 return eScriptLanguagePython; 92 if (language.equals_insensitive(LanguageToString(eScriptLanguageLua))) 93 return eScriptLanguageLua; 94 return eScriptLanguageUnknown; 95 } 96 97 Status ScriptInterpreter::SetBreakpointCommandCallback( 98 std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec, 99 const char *callback_text) { 100 Status return_error; 101 for (BreakpointOptions &bp_options : bp_options_vec) { 102 return_error = SetBreakpointCommandCallback(bp_options, callback_text); 103 if (return_error.Success()) 104 break; 105 } 106 return return_error; 107 } 108 109 Status ScriptInterpreter::SetBreakpointCommandCallbackFunction( 110 std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec, 111 const char *function_name, StructuredData::ObjectSP extra_args_sp) { 112 Status error; 113 for (BreakpointOptions &bp_options : bp_options_vec) { 114 error = SetBreakpointCommandCallbackFunction(bp_options, function_name, 115 extra_args_sp); 116 if (!error.Success()) 117 return error; 118 } 119 return error; 120 } 121 122 std::unique_ptr<ScriptInterpreterLocker> 123 ScriptInterpreter::AcquireInterpreterLock() { 124 return std::make_unique<ScriptInterpreterLocker>(); 125 } 126 127 static void ReadThreadBytesReceived(void *baton, const void *src, 128 size_t src_len) { 129 if (src && src_len) { 130 Stream *strm = (Stream *)baton; 131 strm->Write(src, src_len); 132 strm->Flush(); 133 } 134 } 135 136 llvm::Expected<std::unique_ptr<ScriptInterpreterIORedirect>> 137 ScriptInterpreterIORedirect::Create(bool enable_io, Debugger &debugger, 138 CommandReturnObject *result) { 139 if (enable_io) 140 return std::unique_ptr<ScriptInterpreterIORedirect>( 141 new ScriptInterpreterIORedirect(debugger, result)); 142 143 auto nullin = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL), 144 File::eOpenOptionRead); 145 if (!nullin) 146 return nullin.takeError(); 147 148 auto nullout = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL), 149 File::eOpenOptionWrite); 150 if (!nullout) 151 return nullin.takeError(); 152 153 return std::unique_ptr<ScriptInterpreterIORedirect>( 154 new ScriptInterpreterIORedirect(std::move(*nullin), std::move(*nullout))); 155 } 156 157 ScriptInterpreterIORedirect::ScriptInterpreterIORedirect( 158 std::unique_ptr<File> input, std::unique_ptr<File> output) 159 : m_input_file_sp(std::move(input)), 160 m_output_file_sp(std::make_shared<StreamFile>(std::move(output))), 161 m_error_file_sp(m_output_file_sp), 162 m_communication("lldb.ScriptInterpreterIORedirect.comm"), 163 m_disconnect(false) {} 164 165 ScriptInterpreterIORedirect::ScriptInterpreterIORedirect( 166 Debugger &debugger, CommandReturnObject *result) 167 : m_communication("lldb.ScriptInterpreterIORedirect.comm"), 168 m_disconnect(false) { 169 170 if (result) { 171 m_input_file_sp = debugger.GetInputFileSP(); 172 173 Pipe pipe; 174 Status pipe_result = pipe.CreateNew(false); 175 #if defined(_WIN32) 176 lldb::file_t read_file = pipe.GetReadNativeHandle(); 177 pipe.ReleaseReadFileDescriptor(); 178 std::unique_ptr<ConnectionGenericFile> conn_up = 179 std::make_unique<ConnectionGenericFile>(read_file, true); 180 #else 181 std::unique_ptr<ConnectionFileDescriptor> conn_up = 182 std::make_unique<ConnectionFileDescriptor>( 183 pipe.ReleaseReadFileDescriptor(), true); 184 #endif 185 186 if (conn_up->IsConnected()) { 187 m_communication.SetConnection(std::move(conn_up)); 188 m_communication.SetReadThreadBytesReceivedCallback( 189 ReadThreadBytesReceived, &result->GetOutputStream()); 190 m_communication.StartReadThread(); 191 m_disconnect = true; 192 193 FILE *outfile_handle = fdopen(pipe.ReleaseWriteFileDescriptor(), "w"); 194 m_output_file_sp = std::make_shared<StreamFile>(outfile_handle, true); 195 m_error_file_sp = m_output_file_sp; 196 if (outfile_handle) 197 ::setbuf(outfile_handle, nullptr); 198 199 result->SetImmediateOutputFile(debugger.GetOutputStream().GetFileSP()); 200 result->SetImmediateErrorFile(debugger.GetErrorStream().GetFileSP()); 201 } 202 } 203 204 if (!m_input_file_sp || !m_output_file_sp || !m_error_file_sp) 205 debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_file_sp, m_output_file_sp, 206 m_error_file_sp); 207 } 208 209 void ScriptInterpreterIORedirect::Flush() { 210 if (m_output_file_sp) 211 m_output_file_sp->Flush(); 212 if (m_error_file_sp) 213 m_error_file_sp->Flush(); 214 } 215 216 ScriptInterpreterIORedirect::~ScriptInterpreterIORedirect() { 217 if (!m_disconnect) 218 return; 219 220 assert(m_output_file_sp); 221 assert(m_error_file_sp); 222 assert(m_output_file_sp == m_error_file_sp); 223 224 // Close the write end of the pipe since we are done with our one line 225 // script. This should cause the read thread that output_comm is using to 226 // exit. 227 m_output_file_sp->GetFile().Close(); 228 // The close above should cause this thread to exit when it gets to the end 229 // of file, so let it get all its data. 230 m_communication.JoinReadThread(); 231 // Now we can close the read end of the pipe. 232 m_communication.Disconnect(); 233 } 234