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