//===-- ScriptInterpreter.cpp ---------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "lldb/Interpreter/ScriptInterpreter.h" #include "lldb/Core/Debugger.h" #include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/Pipe.h" #include "lldb/Host/PseudoTerminal.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/Stream.h" #include "lldb/Utility/StringList.h" #if defined(_WIN32) #include "lldb/Host/windows/ConnectionGenericFileWindows.h" #endif #include #include #include #include #include using namespace lldb; using namespace lldb_private; ScriptInterpreter::ScriptInterpreter(Debugger &debugger, lldb::ScriptLanguage script_lang) : m_debugger(debugger), m_script_lang(script_lang) {} void ScriptInterpreter::CollectDataForBreakpointCommandCallback( std::vector> &bp_options_vec, CommandReturnObject &result) { result.AppendError( "This script interpreter does not support breakpoint callbacks."); } void ScriptInterpreter::CollectDataForWatchpointCommandCallback( WatchpointOptions *bp_options, CommandReturnObject &result) { result.AppendError( "This script interpreter does not support watchpoint callbacks."); } StructuredData::DictionarySP ScriptInterpreter::GetInterpreterInfo() { return nullptr; } bool ScriptInterpreter::LoadScriptingModule(const char *filename, const LoadScriptOptions &options, lldb_private::Status &error, StructuredData::ObjectSP *module_sp, FileSpec extra_search_dir) { error.SetErrorString( "This script interpreter does not support importing modules."); return false; } std::string ScriptInterpreter::LanguageToString(lldb::ScriptLanguage language) { switch (language) { case eScriptLanguageNone: return "None"; case eScriptLanguagePython: return "Python"; case eScriptLanguageLua: return "Lua"; case eScriptLanguageUnknown: return "Unknown"; } llvm_unreachable("Unhandled ScriptInterpreter!"); } lldb::DataExtractorSP ScriptInterpreter::GetDataExtractorFromSBData(const lldb::SBData &data) const { return data.m_opaque_sp; } lldb::BreakpointSP ScriptInterpreter::GetOpaqueTypeFromSBBreakpoint( const lldb::SBBreakpoint &breakpoint) const { return breakpoint.m_opaque_wp.lock(); } lldb::ProcessAttachInfoSP ScriptInterpreter::GetOpaqueTypeFromSBAttachInfo( const lldb::SBAttachInfo &attach_info) const { return attach_info.m_opaque_sp; } lldb::ProcessLaunchInfoSP ScriptInterpreter::GetOpaqueTypeFromSBLaunchInfo( const lldb::SBLaunchInfo &launch_info) const { return std::make_shared( *reinterpret_cast(launch_info.m_opaque_sp.get())); } Status ScriptInterpreter::GetStatusFromSBError(const lldb::SBError &error) const { if (error.m_opaque_up) return *error.m_opaque_up; return Status(); } Event * ScriptInterpreter::GetOpaqueTypeFromSBEvent(const lldb::SBEvent &event) const { return event.m_opaque_ptr; } lldb::StreamSP ScriptInterpreter::GetOpaqueTypeFromSBStream( const lldb::SBStream &stream) const { if (stream.m_opaque_up) { lldb::StreamSP s = std::make_shared(); *s << reinterpret_cast(stream.m_opaque_up.get())->m_packet; return s; } return nullptr; } std::optional ScriptInterpreter::GetOpaqueTypeFromSBMemoryRegionInfo( const lldb::SBMemoryRegionInfo &mem_region) const { if (!mem_region.m_opaque_up) return std::nullopt; return *mem_region.m_opaque_up.get(); } lldb::ScriptLanguage ScriptInterpreter::StringToLanguage(const llvm::StringRef &language) { if (language.equals_insensitive(LanguageToString(eScriptLanguageNone))) return eScriptLanguageNone; if (language.equals_insensitive(LanguageToString(eScriptLanguagePython))) return eScriptLanguagePython; if (language.equals_insensitive(LanguageToString(eScriptLanguageLua))) return eScriptLanguageLua; return eScriptLanguageUnknown; } Status ScriptInterpreter::SetBreakpointCommandCallback( std::vector> &bp_options_vec, const char *callback_text) { Status error; for (BreakpointOptions &bp_options : bp_options_vec) { error = SetBreakpointCommandCallback(bp_options, callback_text, /*is_callback=*/false); if (!error.Success()) break; } return error; } Status ScriptInterpreter::SetBreakpointCommandCallbackFunction( std::vector> &bp_options_vec, const char *function_name, StructuredData::ObjectSP extra_args_sp) { Status error; for (BreakpointOptions &bp_options : bp_options_vec) { error = SetBreakpointCommandCallbackFunction(bp_options, function_name, extra_args_sp); if (!error.Success()) return error; } return error; } std::unique_ptr ScriptInterpreter::AcquireInterpreterLock() { return std::make_unique(); } static void ReadThreadBytesReceived(void *baton, const void *src, size_t src_len) { if (src && src_len) { Stream *strm = (Stream *)baton; strm->Write(src, src_len); strm->Flush(); } } llvm::Expected> ScriptInterpreterIORedirect::Create(bool enable_io, Debugger &debugger, CommandReturnObject *result) { if (enable_io) return std::unique_ptr( new ScriptInterpreterIORedirect(debugger, result)); auto nullin = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL), File::eOpenOptionReadOnly); if (!nullin) return nullin.takeError(); auto nullout = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL), File::eOpenOptionWriteOnly); if (!nullout) return nullin.takeError(); return std::unique_ptr( new ScriptInterpreterIORedirect(std::move(*nullin), std::move(*nullout))); } ScriptInterpreterIORedirect::ScriptInterpreterIORedirect( std::unique_ptr input, std::unique_ptr output) : m_input_file_sp(std::move(input)), m_output_file_sp(std::make_shared(std::move(output))), m_error_file_sp(m_output_file_sp), m_communication("lldb.ScriptInterpreterIORedirect.comm"), m_disconnect(false) {} ScriptInterpreterIORedirect::ScriptInterpreterIORedirect( Debugger &debugger, CommandReturnObject *result) : m_communication("lldb.ScriptInterpreterIORedirect.comm"), m_disconnect(false) { if (result) { m_input_file_sp = debugger.GetInputFileSP(); Pipe pipe; Status pipe_result = pipe.CreateNew(false); #if defined(_WIN32) lldb::file_t read_file = pipe.GetReadNativeHandle(); pipe.ReleaseReadFileDescriptor(); std::unique_ptr conn_up = std::make_unique(read_file, true); #else std::unique_ptr conn_up = std::make_unique( pipe.ReleaseReadFileDescriptor(), true); #endif if (conn_up->IsConnected()) { m_communication.SetConnection(std::move(conn_up)); m_communication.SetReadThreadBytesReceivedCallback( ReadThreadBytesReceived, &result->GetOutputStream()); m_communication.StartReadThread(); m_disconnect = true; FILE *outfile_handle = fdopen(pipe.ReleaseWriteFileDescriptor(), "w"); m_output_file_sp = std::make_shared(outfile_handle, true); m_error_file_sp = m_output_file_sp; if (outfile_handle) ::setbuf(outfile_handle, nullptr); result->SetImmediateOutputFile(debugger.GetOutputStream().GetFileSP()); result->SetImmediateErrorFile(debugger.GetErrorStream().GetFileSP()); } } if (!m_input_file_sp || !m_output_file_sp || !m_error_file_sp) debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_file_sp, m_output_file_sp, m_error_file_sp); } void ScriptInterpreterIORedirect::Flush() { if (m_output_file_sp) m_output_file_sp->Flush(); if (m_error_file_sp) m_error_file_sp->Flush(); } ScriptInterpreterIORedirect::~ScriptInterpreterIORedirect() { if (!m_disconnect) return; assert(m_output_file_sp); assert(m_error_file_sp); assert(m_output_file_sp == m_error_file_sp); // Close the write end of the pipe since we are done with our one line // script. This should cause the read thread that output_comm is using to // exit. m_output_file_sp->GetFile().Close(); // The close above should cause this thread to exit when it gets to the end // of file, so let it get all its data. m_communication.JoinReadThread(); // Now we can close the read end of the pipe. m_communication.Disconnect(); }