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