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 llvm::Optional<MemoryRegionInfo> 87 ScriptInterpreter::GetOpaqueTypeFromSBMemoryRegionInfo( 88 const lldb::SBMemoryRegionInfo &mem_region) const { 89 if (!mem_region.m_opaque_up) 90 return llvm::None; 91 return *mem_region.m_opaque_up.get(); 92 } 93 94 lldb::ScriptLanguage 95 ScriptInterpreter::StringToLanguage(const llvm::StringRef &language) { 96 if (language.equals_insensitive(LanguageToString(eScriptLanguageNone))) 97 return eScriptLanguageNone; 98 if (language.equals_insensitive(LanguageToString(eScriptLanguagePython))) 99 return eScriptLanguagePython; 100 if (language.equals_insensitive(LanguageToString(eScriptLanguageLua))) 101 return eScriptLanguageLua; 102 return eScriptLanguageUnknown; 103 } 104 105 Status ScriptInterpreter::SetBreakpointCommandCallback( 106 std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec, 107 const char *callback_text) { 108 Status return_error; 109 for (BreakpointOptions &bp_options : bp_options_vec) { 110 return_error = SetBreakpointCommandCallback(bp_options, callback_text); 111 if (return_error.Success()) 112 break; 113 } 114 return return_error; 115 } 116 117 Status ScriptInterpreter::SetBreakpointCommandCallbackFunction( 118 std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec, 119 const char *function_name, StructuredData::ObjectSP extra_args_sp) { 120 Status error; 121 for (BreakpointOptions &bp_options : bp_options_vec) { 122 error = SetBreakpointCommandCallbackFunction(bp_options, function_name, 123 extra_args_sp); 124 if (!error.Success()) 125 return error; 126 } 127 return error; 128 } 129 130 std::unique_ptr<ScriptInterpreterLocker> 131 ScriptInterpreter::AcquireInterpreterLock() { 132 return std::make_unique<ScriptInterpreterLocker>(); 133 } 134 135 static void ReadThreadBytesReceived(void *baton, const void *src, 136 size_t src_len) { 137 if (src && src_len) { 138 Stream *strm = (Stream *)baton; 139 strm->Write(src, src_len); 140 strm->Flush(); 141 } 142 } 143 144 llvm::Expected<std::unique_ptr<ScriptInterpreterIORedirect>> 145 ScriptInterpreterIORedirect::Create(bool enable_io, Debugger &debugger, 146 CommandReturnObject *result) { 147 if (enable_io) 148 return std::unique_ptr<ScriptInterpreterIORedirect>( 149 new ScriptInterpreterIORedirect(debugger, result)); 150 151 auto nullin = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL), 152 File::eOpenOptionReadOnly); 153 if (!nullin) 154 return nullin.takeError(); 155 156 auto nullout = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL), 157 File::eOpenOptionWriteOnly); 158 if (!nullout) 159 return nullin.takeError(); 160 161 return std::unique_ptr<ScriptInterpreterIORedirect>( 162 new ScriptInterpreterIORedirect(std::move(*nullin), std::move(*nullout))); 163 } 164 165 ScriptInterpreterIORedirect::ScriptInterpreterIORedirect( 166 std::unique_ptr<File> input, std::unique_ptr<File> output) 167 : m_input_file_sp(std::move(input)), 168 m_output_file_sp(std::make_shared<StreamFile>(std::move(output))), 169 m_error_file_sp(m_output_file_sp), 170 m_communication("lldb.ScriptInterpreterIORedirect.comm"), 171 m_disconnect(false) {} 172 173 ScriptInterpreterIORedirect::ScriptInterpreterIORedirect( 174 Debugger &debugger, CommandReturnObject *result) 175 : m_communication("lldb.ScriptInterpreterIORedirect.comm"), 176 m_disconnect(false) { 177 178 if (result) { 179 m_input_file_sp = debugger.GetInputFileSP(); 180 181 Pipe pipe; 182 Status pipe_result = pipe.CreateNew(false); 183 #if defined(_WIN32) 184 lldb::file_t read_file = pipe.GetReadNativeHandle(); 185 pipe.ReleaseReadFileDescriptor(); 186 std::unique_ptr<ConnectionGenericFile> conn_up = 187 std::make_unique<ConnectionGenericFile>(read_file, true); 188 #else 189 std::unique_ptr<ConnectionFileDescriptor> conn_up = 190 std::make_unique<ConnectionFileDescriptor>( 191 pipe.ReleaseReadFileDescriptor(), true); 192 #endif 193 194 if (conn_up->IsConnected()) { 195 m_communication.SetConnection(std::move(conn_up)); 196 m_communication.SetReadThreadBytesReceivedCallback( 197 ReadThreadBytesReceived, &result->GetOutputStream()); 198 m_communication.StartReadThread(); 199 m_disconnect = true; 200 201 FILE *outfile_handle = fdopen(pipe.ReleaseWriteFileDescriptor(), "w"); 202 m_output_file_sp = std::make_shared<StreamFile>(outfile_handle, true); 203 m_error_file_sp = m_output_file_sp; 204 if (outfile_handle) 205 ::setbuf(outfile_handle, nullptr); 206 207 result->SetImmediateOutputFile(debugger.GetOutputStream().GetFileSP()); 208 result->SetImmediateErrorFile(debugger.GetErrorStream().GetFileSP()); 209 } 210 } 211 212 if (!m_input_file_sp || !m_output_file_sp || !m_error_file_sp) 213 debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_file_sp, m_output_file_sp, 214 m_error_file_sp); 215 } 216 217 void ScriptInterpreterIORedirect::Flush() { 218 if (m_output_file_sp) 219 m_output_file_sp->Flush(); 220 if (m_error_file_sp) 221 m_error_file_sp->Flush(); 222 } 223 224 ScriptInterpreterIORedirect::~ScriptInterpreterIORedirect() { 225 if (!m_disconnect) 226 return; 227 228 assert(m_output_file_sp); 229 assert(m_error_file_sp); 230 assert(m_output_file_sp == m_error_file_sp); 231 232 // Close the write end of the pipe since we are done with our one line 233 // script. This should cause the read thread that output_comm is using to 234 // exit. 235 m_output_file_sp->GetFile().Close(); 236 // The close above should cause this thread to exit when it gets to the end 237 // of file, so let it get all its data. 238 m_communication.JoinReadThread(); 239 // Now we can close the read end of the pipe. 240 m_communication.Disconnect(); 241 } 242