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