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