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