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