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() { 123 auto nullin = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL), 124 File::eOpenOptionRead); 125 if (!nullin) 126 return nullin.takeError(); 127 128 auto nullout = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL), 129 File::eOpenOptionWrite); 130 if (!nullout) 131 return nullin.takeError(); 132 133 return std::unique_ptr<ScriptInterpreterIORedirect>( 134 new ScriptInterpreterIORedirect(std::move(*nullin), std::move(*nullout))); 135 } 136 137 llvm::Expected<std::unique_ptr<ScriptInterpreterIORedirect>> 138 ScriptInterpreterIORedirect::Create(Debugger &debugger, 139 CommandReturnObject *result) { 140 return std::unique_ptr<ScriptInterpreterIORedirect>( 141 new ScriptInterpreterIORedirect(debugger, result)); 142 } 143 144 ScriptInterpreterIORedirect::ScriptInterpreterIORedirect( 145 std::unique_ptr<File> input, std::unique_ptr<File> output) 146 : m_input_file_sp(std::move(input)), 147 m_output_file_sp(std::make_shared<StreamFile>(std::move(output))), 148 m_error_file_sp(m_output_file_sp), 149 m_communication("lldb.ScriptInterpreterIORedirect.comm"), 150 m_disconnect(false) {} 151 152 ScriptInterpreterIORedirect::ScriptInterpreterIORedirect( 153 Debugger &debugger, CommandReturnObject *result) 154 : m_communication("lldb.ScriptInterpreterIORedirect.comm"), 155 m_disconnect(false) { 156 157 if (result) { 158 m_input_file_sp = debugger.GetInputFileSP(); 159 160 Pipe pipe; 161 Status pipe_result = pipe.CreateNew(false); 162 #if defined(_WIN32) 163 lldb::file_t read_file = pipe.GetReadNativeHandle(); 164 pipe.ReleaseReadFileDescriptor(); 165 std::unique_ptr<ConnectionGenericFile> conn_up = 166 std::make_unique<ConnectionGenericFile>(read_file, true); 167 #else 168 std::unique_ptr<ConnectionFileDescriptor> conn_up = 169 std::make_unique<ConnectionFileDescriptor>( 170 pipe.ReleaseReadFileDescriptor(), true); 171 #endif 172 173 if (conn_up->IsConnected()) { 174 m_communication.SetConnection(std::move(conn_up)); 175 m_communication.SetReadThreadBytesReceivedCallback( 176 ReadThreadBytesReceived, &result->GetOutputStream()); 177 m_communication.StartReadThread(); 178 m_disconnect = true; 179 180 FILE *outfile_handle = fdopen(pipe.ReleaseWriteFileDescriptor(), "w"); 181 m_output_file_sp = std::make_shared<StreamFile>(outfile_handle, true); 182 m_error_file_sp = m_output_file_sp; 183 if (outfile_handle) 184 ::setbuf(outfile_handle, nullptr); 185 186 result->SetImmediateOutputFile(debugger.GetOutputStream().GetFileSP()); 187 result->SetImmediateErrorFile(debugger.GetErrorStream().GetFileSP()); 188 } 189 } 190 191 if (!m_input_file_sp || !m_output_file_sp || !m_error_file_sp) 192 debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_file_sp, m_output_file_sp, 193 m_error_file_sp); 194 } 195 196 void ScriptInterpreterIORedirect::Flush() { 197 if (m_output_file_sp) 198 m_output_file_sp->Flush(); 199 if (m_error_file_sp) 200 m_error_file_sp->Flush(); 201 } 202 203 ScriptInterpreterIORedirect::~ScriptInterpreterIORedirect() { 204 if (!m_disconnect) 205 return; 206 207 assert(m_output_file_sp); 208 assert(m_error_file_sp); 209 assert(m_output_file_sp == m_error_file_sp); 210 211 // Close the write end of the pipe since we are done with our one line 212 // script. This should cause the read thread that output_comm is using to 213 // exit. 214 m_output_file_sp->GetFile().Close(); 215 // The close above should cause this thread to exit when it gets to the end 216 // of file, so let it get all its data. 217 m_communication.JoinReadThread(); 218 // Now we can close the read end of the pipe. 219 m_communication.Disconnect(); 220 } 221