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