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