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