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 <memory> 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <string> 25 26 using namespace lldb; 27 using namespace lldb_private; 28 29 ScriptInterpreter::ScriptInterpreter(Debugger &debugger, 30 lldb::ScriptLanguage script_lang) 31 : m_debugger(debugger), m_script_lang(script_lang) {} 32 33 ScriptInterpreter::~ScriptInterpreter() {} 34 35 void ScriptInterpreter::CollectDataForBreakpointCommandCallback( 36 std::vector<BreakpointOptions *> &bp_options_vec, 37 CommandReturnObject &result) { 38 result.SetStatus(eReturnStatusFailed); 39 result.AppendError( 40 "This script interpreter does not support breakpoint callbacks."); 41 } 42 43 void ScriptInterpreter::CollectDataForWatchpointCommandCallback( 44 WatchpointOptions *bp_options, CommandReturnObject &result) { 45 result.SetStatus(eReturnStatusFailed); 46 result.AppendError( 47 "This script interpreter does not support watchpoint callbacks."); 48 } 49 50 bool ScriptInterpreter::LoadScriptingModule(const char *filename, 51 bool init_session, 52 lldb_private::Status &error, 53 StructuredData::ObjectSP *module_sp, 54 FileSpec extra_search_dir) { 55 error.SetErrorString( 56 "This script interpreter does not support importing modules."); 57 return false; 58 } 59 60 std::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 74 lldb::ScriptLanguage 75 ScriptInterpreter::StringToLanguage(const llvm::StringRef &language) { 76 if (language.equals_lower(LanguageToString(eScriptLanguageNone))) 77 return eScriptLanguageNone; 78 if (language.equals_lower(LanguageToString(eScriptLanguagePython))) 79 return eScriptLanguagePython; 80 if (language.equals_lower(LanguageToString(eScriptLanguageLua))) 81 return eScriptLanguageLua; 82 return eScriptLanguageUnknown; 83 } 84 85 Status ScriptInterpreter::SetBreakpointCommandCallback( 86 std::vector<BreakpointOptions *> &bp_options_vec, 87 const char *callback_text) { 88 Status return_error; 89 for (BreakpointOptions *bp_options : bp_options_vec) { 90 return_error = SetBreakpointCommandCallback(bp_options, callback_text); 91 if (return_error.Success()) 92 break; 93 } 94 return return_error; 95 } 96 97 Status ScriptInterpreter::SetBreakpointCommandCallbackFunction( 98 std::vector<BreakpointOptions *> &bp_options_vec, const char *function_name, 99 StructuredData::ObjectSP extra_args_sp) { 100 Status error; 101 for (BreakpointOptions *bp_options : bp_options_vec) { 102 error = SetBreakpointCommandCallbackFunction(bp_options, function_name, 103 extra_args_sp); 104 if (!error.Success()) 105 return error; 106 } 107 return error; 108 } 109 110 std::unique_ptr<ScriptInterpreterLocker> 111 ScriptInterpreter::AcquireInterpreterLock() { 112 return std::make_unique<ScriptInterpreterLocker>(); 113 } 114 115 static void ReadThreadBytesReceived(void *baton, const void *src, 116 size_t src_len) { 117 if (src && src_len) { 118 Stream *strm = (Stream *)baton; 119 strm->Write(src, src_len); 120 strm->Flush(); 121 } 122 } 123 124 llvm::Expected<std::unique_ptr<ScriptInterpreterIORedirect>> 125 ScriptInterpreterIORedirect::Create(bool enable_io, Debugger &debugger, 126 CommandReturnObject *result) { 127 if (enable_io) 128 return std::unique_ptr<ScriptInterpreterIORedirect>( 129 new ScriptInterpreterIORedirect(debugger, result)); 130 131 auto nullin = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL), 132 File::eOpenOptionRead); 133 if (!nullin) 134 return nullin.takeError(); 135 136 auto nullout = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL), 137 File::eOpenOptionWrite); 138 if (!nullout) 139 return nullin.takeError(); 140 141 return std::unique_ptr<ScriptInterpreterIORedirect>( 142 new ScriptInterpreterIORedirect(std::move(*nullin), std::move(*nullout))); 143 } 144 145 ScriptInterpreterIORedirect::ScriptInterpreterIORedirect( 146 std::unique_ptr<File> input, std::unique_ptr<File> output) 147 : m_input_file_sp(std::move(input)), 148 m_output_file_sp(std::make_shared<StreamFile>(std::move(output))), 149 m_error_file_sp(m_output_file_sp), 150 m_communication("lldb.ScriptInterpreterIORedirect.comm"), 151 m_disconnect(false) {} 152 153 ScriptInterpreterIORedirect::ScriptInterpreterIORedirect( 154 Debugger &debugger, CommandReturnObject *result) 155 : m_communication("lldb.ScriptInterpreterIORedirect.comm"), 156 m_disconnect(false) { 157 158 if (result) { 159 m_input_file_sp = debugger.GetInputFileSP(); 160 161 Pipe pipe; 162 Status pipe_result = pipe.CreateNew(false); 163 #if defined(_WIN32) 164 lldb::file_t read_file = pipe.GetReadNativeHandle(); 165 pipe.ReleaseReadFileDescriptor(); 166 std::unique_ptr<ConnectionGenericFile> conn_up = 167 std::make_unique<ConnectionGenericFile>(read_file, true); 168 #else 169 std::unique_ptr<ConnectionFileDescriptor> conn_up = 170 std::make_unique<ConnectionFileDescriptor>( 171 pipe.ReleaseReadFileDescriptor(), true); 172 #endif 173 174 if (conn_up->IsConnected()) { 175 m_communication.SetConnection(std::move(conn_up)); 176 m_communication.SetReadThreadBytesReceivedCallback( 177 ReadThreadBytesReceived, &result->GetOutputStream()); 178 m_communication.StartReadThread(); 179 m_disconnect = true; 180 181 FILE *outfile_handle = fdopen(pipe.ReleaseWriteFileDescriptor(), "w"); 182 m_output_file_sp = std::make_shared<StreamFile>(outfile_handle, true); 183 m_error_file_sp = m_output_file_sp; 184 if (outfile_handle) 185 ::setbuf(outfile_handle, nullptr); 186 187 result->SetImmediateOutputFile(debugger.GetOutputStream().GetFileSP()); 188 result->SetImmediateErrorFile(debugger.GetErrorStream().GetFileSP()); 189 } 190 } 191 192 if (!m_input_file_sp || !m_output_file_sp || !m_error_file_sp) 193 debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_file_sp, m_output_file_sp, 194 m_error_file_sp); 195 } 196 197 void ScriptInterpreterIORedirect::Flush() { 198 if (m_output_file_sp) 199 m_output_file_sp->Flush(); 200 if (m_error_file_sp) 201 m_error_file_sp->Flush(); 202 } 203 204 ScriptInterpreterIORedirect::~ScriptInterpreterIORedirect() { 205 if (!m_disconnect) 206 return; 207 208 assert(m_output_file_sp); 209 assert(m_error_file_sp); 210 assert(m_output_file_sp == m_error_file_sp); 211 212 // Close the write end of the pipe since we are done with our one line 213 // script. This should cause the read thread that output_comm is using to 214 // exit. 215 m_output_file_sp->GetFile().Close(); 216 // The close above should cause this thread to exit when it gets to the end 217 // of file, so let it get all its data. 218 m_communication.JoinReadThread(); 219 // Now we can close the read end of the pipe. 220 m_communication.Disconnect(); 221 } 222