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