310 lines · cpp
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-exception6//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#endif21#include <cstdio>22#include <cstdlib>23#include <memory>24#include <optional>25#include <string>26 27using namespace lldb;28using namespace lldb_private;29 30ScriptInterpreter::ScriptInterpreter(Debugger &debugger,31 lldb::ScriptLanguage script_lang)32 : m_debugger(debugger), m_script_lang(script_lang) {}33 34void 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 41void ScriptInterpreter::CollectDataForWatchpointCommandCallback(42 WatchpointOptions *bp_options, CommandReturnObject &result) {43 result.AppendError(44 "This script interpreter does not support watchpoint callbacks.");45}46 47StructuredData::DictionarySP ScriptInterpreter::GetInterpreterInfo() {48 return nullptr;49}50 51bool ScriptInterpreter::LoadScriptingModule(52 const char *filename, const LoadScriptOptions &options,53 lldb_private::Status &error, StructuredData::ObjectSP *module_sp,54 FileSpec extra_search_dir, lldb::TargetSP loaded_into_target_sp) {55 error = Status::FromErrorString(56 "This script interpreter does not support importing modules.");57 return false;58}59 60std::string ScriptInterpreter::LanguageToString(lldb::ScriptLanguage language) {61 switch (language) {62 case eScriptLanguageNone:63 return "None";64 case eScriptLanguagePython:65 return "Python";66 case eScriptLanguageLua:67 return "Lua";68 case eScriptLanguageUnknown:69 return "Unknown";70 }71 llvm_unreachable("Unhandled ScriptInterpreter!");72}73 74lldb::DataExtractorSP75ScriptInterpreter::GetDataExtractorFromSBData(const lldb::SBData &data) const {76 return data.m_opaque_sp;77}78 79lldb::BreakpointSP ScriptInterpreter::GetOpaqueTypeFromSBBreakpoint(80 const lldb::SBBreakpoint &breakpoint) const {81 return breakpoint.m_opaque_wp.lock();82}83 84lldb::BreakpointLocationSP85ScriptInterpreter::GetOpaqueTypeFromSBBreakpointLocation(86 const lldb::SBBreakpointLocation &break_loc) const {87 return break_loc.m_opaque_wp.lock();88}89 90lldb::ProcessAttachInfoSP ScriptInterpreter::GetOpaqueTypeFromSBAttachInfo(91 const lldb::SBAttachInfo &attach_info) const {92 return attach_info.m_opaque_sp;93}94 95lldb::ProcessLaunchInfoSP ScriptInterpreter::GetOpaqueTypeFromSBLaunchInfo(96 const lldb::SBLaunchInfo &launch_info) const {97 return std::make_shared<ProcessLaunchInfo>(98 *reinterpret_cast<ProcessLaunchInfo *>(launch_info.m_opaque_sp.get()));99}100 101Status102ScriptInterpreter::GetStatusFromSBError(const lldb::SBError &error) const {103 if (error.m_opaque_up)104 return error.m_opaque_up->Clone();105 106 return Status();107}108 109lldb::StackFrameSP110ScriptInterpreter::GetOpaqueTypeFromSBFrame(const lldb::SBFrame &frame) const {111 if (frame.m_opaque_sp)112 return frame.m_opaque_sp->GetFrameSP();113 return nullptr;114}115 116Event *117ScriptInterpreter::GetOpaqueTypeFromSBEvent(const lldb::SBEvent &event) const {118 return event.m_opaque_ptr;119}120 121lldb::StreamSP ScriptInterpreter::GetOpaqueTypeFromSBStream(122 const lldb::SBStream &stream) const {123 if (stream.m_opaque_up) {124 lldb::StreamSP s = std::make_shared<lldb_private::StreamString>();125 *s << reinterpret_cast<StreamString *>(stream.m_opaque_up.get())->m_packet;126 return s;127 }128 129 return nullptr;130}131 132SymbolContext ScriptInterpreter::GetOpaqueTypeFromSBSymbolContext(133 const lldb::SBSymbolContext &sb_sym_ctx) const {134 if (sb_sym_ctx.m_opaque_up)135 return *sb_sym_ctx.m_opaque_up;136 return {};137}138 139std::optional<lldb_private::MemoryRegionInfo>140ScriptInterpreter::GetOpaqueTypeFromSBMemoryRegionInfo(141 const lldb::SBMemoryRegionInfo &mem_region) const {142 if (!mem_region.m_opaque_up)143 return std::nullopt;144 return *mem_region.m_opaque_up.get();145}146 147lldb::ExecutionContextRefSP148ScriptInterpreter::GetOpaqueTypeFromSBExecutionContext(149 const lldb::SBExecutionContext &exe_ctx) const {150 return exe_ctx.m_exe_ctx_sp;151}152 153lldb::StackFrameListSP ScriptInterpreter::GetOpaqueTypeFromSBFrameList(154 const lldb::SBFrameList &frame_list) const {155 return frame_list.m_opaque_sp;156}157 158lldb::ScriptLanguage159ScriptInterpreter::StringToLanguage(const llvm::StringRef &language) {160 if (language.equals_insensitive(LanguageToString(eScriptLanguageNone)))161 return eScriptLanguageNone;162 if (language.equals_insensitive(LanguageToString(eScriptLanguagePython)))163 return eScriptLanguagePython;164 if (language.equals_insensitive(LanguageToString(eScriptLanguageLua)))165 return eScriptLanguageLua;166 return eScriptLanguageUnknown;167}168 169Status ScriptInterpreter::SetBreakpointCommandCallback(170 std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec,171 const char *callback_text) {172 Status error;173 for (BreakpointOptions &bp_options : bp_options_vec) {174 error = SetBreakpointCommandCallback(bp_options, callback_text,175 /*is_callback=*/false);176 if (!error.Success())177 break;178 }179 return error;180}181 182Status ScriptInterpreter::SetBreakpointCommandCallbackFunction(183 std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec,184 const char *function_name, StructuredData::ObjectSP extra_args_sp) {185 Status error;186 for (BreakpointOptions &bp_options : bp_options_vec) {187 error = SetBreakpointCommandCallbackFunction(bp_options, function_name,188 extra_args_sp);189 if (!error.Success())190 return error;191 }192 return error;193}194 195std::unique_ptr<ScriptInterpreterLocker>196ScriptInterpreter::AcquireInterpreterLock() {197 return std::make_unique<ScriptInterpreterLocker>();198}199 200static void ReadThreadBytesReceived(void *baton, const void *src,201 size_t src_len) {202 if (src && src_len) {203 Stream *strm = (Stream *)baton;204 strm->Write(src, src_len);205 strm->Flush();206 }207}208 209llvm::Expected<std::unique_ptr<ScriptInterpreterIORedirect>>210ScriptInterpreterIORedirect::Create(bool enable_io, Debugger &debugger,211 CommandReturnObject *result) {212 if (enable_io)213 return std::unique_ptr<ScriptInterpreterIORedirect>(214 new ScriptInterpreterIORedirect(debugger, result));215 216 auto nullin = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL),217 File::eOpenOptionReadOnly);218 if (!nullin)219 return nullin.takeError();220 221 auto nullout = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL),222 File::eOpenOptionWriteOnly);223 if (!nullout)224 return nullin.takeError();225 226 return std::unique_ptr<ScriptInterpreterIORedirect>(227 new ScriptInterpreterIORedirect(std::move(*nullin), std::move(*nullout)));228}229 230ScriptInterpreterIORedirect::ScriptInterpreterIORedirect(231 std::unique_ptr<File> input, std::unique_ptr<File> output)232 : m_input_file_sp(std::move(input)),233 m_output_file_sp(std::make_shared<LockableStreamFile>(std::move(output),234 m_output_mutex)),235 m_error_file_sp(m_output_file_sp),236 m_communication("lldb.ScriptInterpreterIORedirect.comm"),237 m_disconnect(false) {}238 239ScriptInterpreterIORedirect::ScriptInterpreterIORedirect(240 Debugger &debugger, CommandReturnObject *result)241 : m_communication("lldb.ScriptInterpreterIORedirect.comm"),242 m_disconnect(false) {243 244 if (result) {245 m_input_file_sp = debugger.GetInputFileSP();246 247 Pipe pipe;248 Status pipe_result = pipe.CreateNew();249#if defined(_WIN32)250 lldb::file_t read_file = pipe.GetReadNativeHandle();251 pipe.ReleaseReadFileDescriptor();252 std::unique_ptr<ConnectionGenericFile> conn_up =253 std::make_unique<ConnectionGenericFile>(read_file, true);254#else255 std::unique_ptr<ConnectionFileDescriptor> conn_up =256 std::make_unique<ConnectionFileDescriptor>(257 pipe.ReleaseReadFileDescriptor(), true);258#endif259 260 if (conn_up->IsConnected()) {261 m_communication.SetConnection(std::move(conn_up));262 m_communication.SetReadThreadBytesReceivedCallback(263 ReadThreadBytesReceived, &result->GetOutputStream());264 m_communication.StartReadThread();265 m_disconnect = true;266 267 FILE *outfile_handle = fdopen(pipe.ReleaseWriteFileDescriptor(), "w");268 m_output_file_sp = std::make_shared<LockableStreamFile>(269 std::make_shared<StreamFile>(outfile_handle, NativeFile::Owned),270 m_output_mutex);271 m_error_file_sp = m_output_file_sp;272 if (outfile_handle)273 ::setbuf(outfile_handle, nullptr);274 275 result->SetImmediateOutputFile(debugger.GetOutputFileSP());276 result->SetImmediateErrorFile(debugger.GetErrorFileSP());277 }278 }279 280 if (!m_input_file_sp || !m_output_file_sp || !m_error_file_sp)281 debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_file_sp, m_output_file_sp,282 m_error_file_sp);283}284 285void ScriptInterpreterIORedirect::Flush() {286 if (m_output_file_sp)287 m_output_file_sp->Lock().Flush();288 if (m_error_file_sp)289 m_error_file_sp->Lock().Flush();290}291 292ScriptInterpreterIORedirect::~ScriptInterpreterIORedirect() {293 if (!m_disconnect)294 return;295 296 assert(m_output_file_sp);297 assert(m_error_file_sp);298 assert(m_output_file_sp == m_error_file_sp);299 300 // Close the write end of the pipe since we are done with our one line301 // script. This should cause the read thread that output_comm is using to302 // exit.303 m_output_file_sp->GetUnlockedFile().Close();304 // The close above should cause this thread to exit when it gets to the end305 // of file, so let it get all its data.306 m_communication.JoinReadThread();307 // Now we can close the read end of the pipe.308 m_communication.Disconnect();309}310