xref: /openbsd-src/gnu/llvm/lldb/source/Interpreter/ScriptInterpreter.cpp (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
1dda28197Spatrick //===-- ScriptInterpreter.cpp ---------------------------------------------===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick 
9061da546Spatrick #include "lldb/Interpreter/ScriptInterpreter.h"
10dda28197Spatrick #include "lldb/Core/Debugger.h"
11dda28197Spatrick #include "lldb/Host/ConnectionFileDescriptor.h"
12dda28197Spatrick #include "lldb/Host/Pipe.h"
13061da546Spatrick #include "lldb/Host/PseudoTerminal.h"
14061da546Spatrick #include "lldb/Interpreter/CommandReturnObject.h"
15061da546Spatrick #include "lldb/Utility/Status.h"
16061da546Spatrick #include "lldb/Utility/Stream.h"
17061da546Spatrick #include "lldb/Utility/StringList.h"
18dda28197Spatrick #if defined(_WIN32)
19dda28197Spatrick #include "lldb/Host/windows/ConnectionGenericFileWindows.h"
20dda28197Spatrick #endif
21be691f3bSpatrick #include <cstdio>
22be691f3bSpatrick #include <cstdlib>
23dda28197Spatrick #include <memory>
24*f6aab3d8Srobert #include <optional>
25dda28197Spatrick #include <string>
26061da546Spatrick 
27061da546Spatrick using namespace lldb;
28061da546Spatrick using namespace lldb_private;
29061da546Spatrick 
ScriptInterpreter(Debugger & debugger,lldb::ScriptLanguage script_lang,lldb::ScriptedProcessInterfaceUP scripted_process_interface_up,lldb::ScriptedPlatformInterfaceUP scripted_platform_interface_up)30be691f3bSpatrick ScriptInterpreter::ScriptInterpreter(
31be691f3bSpatrick     Debugger &debugger, lldb::ScriptLanguage script_lang,
32*f6aab3d8Srobert     lldb::ScriptedProcessInterfaceUP scripted_process_interface_up,
33*f6aab3d8Srobert     lldb::ScriptedPlatformInterfaceUP scripted_platform_interface_up)
34be691f3bSpatrick     : m_debugger(debugger), m_script_lang(script_lang),
35*f6aab3d8Srobert       m_scripted_process_interface_up(std::move(scripted_process_interface_up)),
36*f6aab3d8Srobert       m_scripted_platform_interface_up(
37*f6aab3d8Srobert           std::move(scripted_platform_interface_up)) {}
38061da546Spatrick 
CollectDataForBreakpointCommandCallback(std::vector<std::reference_wrapper<BreakpointOptions>> & bp_options_vec,CommandReturnObject & result)39061da546Spatrick void ScriptInterpreter::CollectDataForBreakpointCommandCallback(
40be691f3bSpatrick     std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec,
41061da546Spatrick     CommandReturnObject &result) {
42061da546Spatrick   result.AppendError(
43061da546Spatrick       "This script interpreter does not support breakpoint callbacks.");
44061da546Spatrick }
45061da546Spatrick 
CollectDataForWatchpointCommandCallback(WatchpointOptions * bp_options,CommandReturnObject & result)46061da546Spatrick void ScriptInterpreter::CollectDataForWatchpointCommandCallback(
47061da546Spatrick     WatchpointOptions *bp_options, CommandReturnObject &result) {
48061da546Spatrick   result.AppendError(
49061da546Spatrick       "This script interpreter does not support watchpoint callbacks.");
50061da546Spatrick }
51061da546Spatrick 
GetInterpreterInfo()52*f6aab3d8Srobert StructuredData::DictionarySP ScriptInterpreter::GetInterpreterInfo() {
53*f6aab3d8Srobert   return nullptr;
54*f6aab3d8Srobert }
55*f6aab3d8Srobert 
LoadScriptingModule(const char * filename,const LoadScriptOptions & options,lldb_private::Status & error,StructuredData::ObjectSP * module_sp,FileSpec extra_search_dir)56be691f3bSpatrick bool ScriptInterpreter::LoadScriptingModule(const char *filename,
57be691f3bSpatrick                                             const LoadScriptOptions &options,
58be691f3bSpatrick                                             lldb_private::Status &error,
59be691f3bSpatrick                                             StructuredData::ObjectSP *module_sp,
60be691f3bSpatrick                                             FileSpec extra_search_dir) {
61061da546Spatrick   error.SetErrorString(
62061da546Spatrick       "This script interpreter does not support importing modules.");
63061da546Spatrick   return false;
64061da546Spatrick }
65061da546Spatrick 
LanguageToString(lldb::ScriptLanguage language)66061da546Spatrick std::string ScriptInterpreter::LanguageToString(lldb::ScriptLanguage language) {
67061da546Spatrick   switch (language) {
68061da546Spatrick   case eScriptLanguageNone:
69061da546Spatrick     return "None";
70061da546Spatrick   case eScriptLanguagePython:
71061da546Spatrick     return "Python";
72061da546Spatrick   case eScriptLanguageLua:
73061da546Spatrick     return "Lua";
74061da546Spatrick   case eScriptLanguageUnknown:
75061da546Spatrick     return "Unknown";
76061da546Spatrick   }
77061da546Spatrick   llvm_unreachable("Unhandled ScriptInterpreter!");
78061da546Spatrick }
79061da546Spatrick 
80be691f3bSpatrick lldb::DataExtractorSP
GetDataExtractorFromSBData(const lldb::SBData & data) const81be691f3bSpatrick ScriptInterpreter::GetDataExtractorFromSBData(const lldb::SBData &data) const {
82be691f3bSpatrick   return data.m_opaque_sp;
83be691f3bSpatrick }
84be691f3bSpatrick 
85be691f3bSpatrick Status
GetStatusFromSBError(const lldb::SBError & error) const86be691f3bSpatrick ScriptInterpreter::GetStatusFromSBError(const lldb::SBError &error) const {
87be691f3bSpatrick   if (error.m_opaque_up)
88*f6aab3d8Srobert     return *error.m_opaque_up;
89be691f3bSpatrick 
90be691f3bSpatrick   return Status();
91be691f3bSpatrick }
92be691f3bSpatrick 
93*f6aab3d8Srobert std::optional<MemoryRegionInfo>
GetOpaqueTypeFromSBMemoryRegionInfo(const lldb::SBMemoryRegionInfo & mem_region) const94*f6aab3d8Srobert ScriptInterpreter::GetOpaqueTypeFromSBMemoryRegionInfo(
95*f6aab3d8Srobert     const lldb::SBMemoryRegionInfo &mem_region) const {
96*f6aab3d8Srobert   if (!mem_region.m_opaque_up)
97*f6aab3d8Srobert     return std::nullopt;
98*f6aab3d8Srobert   return *mem_region.m_opaque_up.get();
99*f6aab3d8Srobert }
100*f6aab3d8Srobert 
101061da546Spatrick lldb::ScriptLanguage
StringToLanguage(const llvm::StringRef & language)102061da546Spatrick ScriptInterpreter::StringToLanguage(const llvm::StringRef &language) {
103be691f3bSpatrick   if (language.equals_insensitive(LanguageToString(eScriptLanguageNone)))
104061da546Spatrick     return eScriptLanguageNone;
105be691f3bSpatrick   if (language.equals_insensitive(LanguageToString(eScriptLanguagePython)))
106061da546Spatrick     return eScriptLanguagePython;
107be691f3bSpatrick   if (language.equals_insensitive(LanguageToString(eScriptLanguageLua)))
108061da546Spatrick     return eScriptLanguageLua;
109061da546Spatrick   return eScriptLanguageUnknown;
110061da546Spatrick }
111061da546Spatrick 
SetBreakpointCommandCallback(std::vector<std::reference_wrapper<BreakpointOptions>> & bp_options_vec,const char * callback_text)112061da546Spatrick Status ScriptInterpreter::SetBreakpointCommandCallback(
113be691f3bSpatrick     std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec,
114061da546Spatrick     const char *callback_text) {
115*f6aab3d8Srobert   Status error;
116be691f3bSpatrick   for (BreakpointOptions &bp_options : bp_options_vec) {
117*f6aab3d8Srobert     error = SetBreakpointCommandCallback(bp_options, callback_text);
118*f6aab3d8Srobert     if (!error.Success())
119061da546Spatrick       break;
120061da546Spatrick   }
121*f6aab3d8Srobert   return error;
122061da546Spatrick }
123061da546Spatrick 
SetBreakpointCommandCallbackFunction(std::vector<std::reference_wrapper<BreakpointOptions>> & bp_options_vec,const char * function_name,StructuredData::ObjectSP extra_args_sp)124061da546Spatrick Status ScriptInterpreter::SetBreakpointCommandCallbackFunction(
125be691f3bSpatrick     std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec,
126be691f3bSpatrick     const char *function_name, StructuredData::ObjectSP extra_args_sp) {
127061da546Spatrick   Status error;
128be691f3bSpatrick   for (BreakpointOptions &bp_options : bp_options_vec) {
129061da546Spatrick     error = SetBreakpointCommandCallbackFunction(bp_options, function_name,
130061da546Spatrick                                                  extra_args_sp);
131061da546Spatrick     if (!error.Success())
132061da546Spatrick       return error;
133061da546Spatrick   }
134061da546Spatrick   return error;
135061da546Spatrick }
136061da546Spatrick 
137061da546Spatrick std::unique_ptr<ScriptInterpreterLocker>
AcquireInterpreterLock()138061da546Spatrick ScriptInterpreter::AcquireInterpreterLock() {
139dda28197Spatrick   return std::make_unique<ScriptInterpreterLocker>();
140dda28197Spatrick }
141dda28197Spatrick 
ReadThreadBytesReceived(void * baton,const void * src,size_t src_len)142dda28197Spatrick static void ReadThreadBytesReceived(void *baton, const void *src,
143dda28197Spatrick                                     size_t src_len) {
144dda28197Spatrick   if (src && src_len) {
145dda28197Spatrick     Stream *strm = (Stream *)baton;
146dda28197Spatrick     strm->Write(src, src_len);
147dda28197Spatrick     strm->Flush();
148dda28197Spatrick   }
149dda28197Spatrick }
150dda28197Spatrick 
151dda28197Spatrick llvm::Expected<std::unique_ptr<ScriptInterpreterIORedirect>>
Create(bool enable_io,Debugger & debugger,CommandReturnObject * result)152dda28197Spatrick ScriptInterpreterIORedirect::Create(bool enable_io, Debugger &debugger,
153dda28197Spatrick                                     CommandReturnObject *result) {
154dda28197Spatrick   if (enable_io)
155dda28197Spatrick     return std::unique_ptr<ScriptInterpreterIORedirect>(
156dda28197Spatrick         new ScriptInterpreterIORedirect(debugger, result));
157dda28197Spatrick 
158dda28197Spatrick   auto nullin = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL),
159*f6aab3d8Srobert                                             File::eOpenOptionReadOnly);
160dda28197Spatrick   if (!nullin)
161dda28197Spatrick     return nullin.takeError();
162dda28197Spatrick 
163dda28197Spatrick   auto nullout = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL),
164*f6aab3d8Srobert                                              File::eOpenOptionWriteOnly);
165dda28197Spatrick   if (!nullout)
166dda28197Spatrick     return nullin.takeError();
167dda28197Spatrick 
168dda28197Spatrick   return std::unique_ptr<ScriptInterpreterIORedirect>(
169dda28197Spatrick       new ScriptInterpreterIORedirect(std::move(*nullin), std::move(*nullout)));
170dda28197Spatrick }
171dda28197Spatrick 
ScriptInterpreterIORedirect(std::unique_ptr<File> input,std::unique_ptr<File> output)172dda28197Spatrick ScriptInterpreterIORedirect::ScriptInterpreterIORedirect(
173dda28197Spatrick     std::unique_ptr<File> input, std::unique_ptr<File> output)
174dda28197Spatrick     : m_input_file_sp(std::move(input)),
175dda28197Spatrick       m_output_file_sp(std::make_shared<StreamFile>(std::move(output))),
176dda28197Spatrick       m_error_file_sp(m_output_file_sp),
177dda28197Spatrick       m_communication("lldb.ScriptInterpreterIORedirect.comm"),
178dda28197Spatrick       m_disconnect(false) {}
179dda28197Spatrick 
ScriptInterpreterIORedirect(Debugger & debugger,CommandReturnObject * result)180dda28197Spatrick ScriptInterpreterIORedirect::ScriptInterpreterIORedirect(
181dda28197Spatrick     Debugger &debugger, CommandReturnObject *result)
182dda28197Spatrick     : m_communication("lldb.ScriptInterpreterIORedirect.comm"),
183dda28197Spatrick       m_disconnect(false) {
184dda28197Spatrick 
185dda28197Spatrick   if (result) {
186dda28197Spatrick     m_input_file_sp = debugger.GetInputFileSP();
187dda28197Spatrick 
188dda28197Spatrick     Pipe pipe;
189dda28197Spatrick     Status pipe_result = pipe.CreateNew(false);
190dda28197Spatrick #if defined(_WIN32)
191dda28197Spatrick     lldb::file_t read_file = pipe.GetReadNativeHandle();
192dda28197Spatrick     pipe.ReleaseReadFileDescriptor();
193dda28197Spatrick     std::unique_ptr<ConnectionGenericFile> conn_up =
194dda28197Spatrick         std::make_unique<ConnectionGenericFile>(read_file, true);
195dda28197Spatrick #else
196dda28197Spatrick     std::unique_ptr<ConnectionFileDescriptor> conn_up =
197dda28197Spatrick         std::make_unique<ConnectionFileDescriptor>(
198dda28197Spatrick             pipe.ReleaseReadFileDescriptor(), true);
199dda28197Spatrick #endif
200dda28197Spatrick 
201dda28197Spatrick     if (conn_up->IsConnected()) {
202dda28197Spatrick       m_communication.SetConnection(std::move(conn_up));
203dda28197Spatrick       m_communication.SetReadThreadBytesReceivedCallback(
204dda28197Spatrick           ReadThreadBytesReceived, &result->GetOutputStream());
205dda28197Spatrick       m_communication.StartReadThread();
206dda28197Spatrick       m_disconnect = true;
207dda28197Spatrick 
208dda28197Spatrick       FILE *outfile_handle = fdopen(pipe.ReleaseWriteFileDescriptor(), "w");
209dda28197Spatrick       m_output_file_sp = std::make_shared<StreamFile>(outfile_handle, true);
210dda28197Spatrick       m_error_file_sp = m_output_file_sp;
211dda28197Spatrick       if (outfile_handle)
212dda28197Spatrick         ::setbuf(outfile_handle, nullptr);
213dda28197Spatrick 
214dda28197Spatrick       result->SetImmediateOutputFile(debugger.GetOutputStream().GetFileSP());
215dda28197Spatrick       result->SetImmediateErrorFile(debugger.GetErrorStream().GetFileSP());
216dda28197Spatrick     }
217dda28197Spatrick   }
218dda28197Spatrick 
219dda28197Spatrick   if (!m_input_file_sp || !m_output_file_sp || !m_error_file_sp)
220dda28197Spatrick     debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_file_sp, m_output_file_sp,
221dda28197Spatrick                                              m_error_file_sp);
222dda28197Spatrick }
223dda28197Spatrick 
Flush()224dda28197Spatrick void ScriptInterpreterIORedirect::Flush() {
225dda28197Spatrick   if (m_output_file_sp)
226dda28197Spatrick     m_output_file_sp->Flush();
227dda28197Spatrick   if (m_error_file_sp)
228dda28197Spatrick     m_error_file_sp->Flush();
229dda28197Spatrick }
230dda28197Spatrick 
~ScriptInterpreterIORedirect()231dda28197Spatrick ScriptInterpreterIORedirect::~ScriptInterpreterIORedirect() {
232dda28197Spatrick   if (!m_disconnect)
233dda28197Spatrick     return;
234dda28197Spatrick 
235dda28197Spatrick   assert(m_output_file_sp);
236dda28197Spatrick   assert(m_error_file_sp);
237dda28197Spatrick   assert(m_output_file_sp == m_error_file_sp);
238dda28197Spatrick 
239dda28197Spatrick   // Close the write end of the pipe since we are done with our one line
240dda28197Spatrick   // script. This should cause the read thread that output_comm is using to
241dda28197Spatrick   // exit.
242dda28197Spatrick   m_output_file_sp->GetFile().Close();
243dda28197Spatrick   // The close above should cause this thread to exit when it gets to the end
244dda28197Spatrick   // of file, so let it get all its data.
245dda28197Spatrick   m_communication.JoinReadThread();
246dda28197Spatrick   // Now we can close the read end of the pipe.
247dda28197Spatrick   m_communication.Disconnect();
248061da546Spatrick }
249