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