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