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() {
123   auto nullin = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL),
124                                             File::eOpenOptionRead);
125   if (!nullin)
126     return nullin.takeError();
127 
128   auto nullout = FileSystem::Instance().Open(FileSpec(FileSystem::DEV_NULL),
129                                              File::eOpenOptionWrite);
130   if (!nullout)
131     return nullin.takeError();
132 
133   return std::unique_ptr<ScriptInterpreterIORedirect>(
134       new ScriptInterpreterIORedirect(std::move(*nullin), std::move(*nullout)));
135 }
136 
137 llvm::Expected<std::unique_ptr<ScriptInterpreterIORedirect>>
138 ScriptInterpreterIORedirect::Create(Debugger &debugger,
139                                     CommandReturnObject *result) {
140   return std::unique_ptr<ScriptInterpreterIORedirect>(
141       new ScriptInterpreterIORedirect(debugger, result));
142 }
143 
144 ScriptInterpreterIORedirect::ScriptInterpreterIORedirect(
145     std::unique_ptr<File> input, std::unique_ptr<File> output)
146     : m_input_file_sp(std::move(input)),
147       m_output_file_sp(std::make_shared<StreamFile>(std::move(output))),
148       m_error_file_sp(m_output_file_sp),
149       m_communication("lldb.ScriptInterpreterIORedirect.comm"),
150       m_disconnect(false) {}
151 
152 ScriptInterpreterIORedirect::ScriptInterpreterIORedirect(
153     Debugger &debugger, CommandReturnObject *result)
154     : m_communication("lldb.ScriptInterpreterIORedirect.comm"),
155       m_disconnect(false) {
156 
157   if (result) {
158     m_input_file_sp = debugger.GetInputFileSP();
159 
160     Pipe pipe;
161     Status pipe_result = pipe.CreateNew(false);
162 #if defined(_WIN32)
163     lldb::file_t read_file = pipe.GetReadNativeHandle();
164     pipe.ReleaseReadFileDescriptor();
165     std::unique_ptr<ConnectionGenericFile> conn_up =
166         std::make_unique<ConnectionGenericFile>(read_file, true);
167 #else
168     std::unique_ptr<ConnectionFileDescriptor> conn_up =
169         std::make_unique<ConnectionFileDescriptor>(
170             pipe.ReleaseReadFileDescriptor(), true);
171 #endif
172 
173     if (conn_up->IsConnected()) {
174       m_communication.SetConnection(std::move(conn_up));
175       m_communication.SetReadThreadBytesReceivedCallback(
176           ReadThreadBytesReceived, &result->GetOutputStream());
177       m_communication.StartReadThread();
178       m_disconnect = true;
179 
180       FILE *outfile_handle = fdopen(pipe.ReleaseWriteFileDescriptor(), "w");
181       m_output_file_sp = std::make_shared<StreamFile>(outfile_handle, true);
182       m_error_file_sp = m_output_file_sp;
183       if (outfile_handle)
184         ::setbuf(outfile_handle, nullptr);
185 
186       result->SetImmediateOutputFile(debugger.GetOutputStream().GetFileSP());
187       result->SetImmediateErrorFile(debugger.GetErrorStream().GetFileSP());
188     }
189   }
190 
191   if (!m_input_file_sp || !m_output_file_sp || !m_error_file_sp)
192     debugger.AdoptTopIOHandlerFilesIfInvalid(m_input_file_sp, m_output_file_sp,
193                                              m_error_file_sp);
194 }
195 
196 void ScriptInterpreterIORedirect::Flush() {
197   if (m_output_file_sp)
198     m_output_file_sp->Flush();
199   if (m_error_file_sp)
200     m_error_file_sp->Flush();
201 }
202 
203 ScriptInterpreterIORedirect::~ScriptInterpreterIORedirect() {
204   if (!m_disconnect)
205     return;
206 
207   assert(m_output_file_sp);
208   assert(m_error_file_sp);
209   assert(m_output_file_sp == m_error_file_sp);
210 
211   // Close the write end of the pipe since we are done with our one line
212   // script. This should cause the read thread that output_comm is using to
213   // exit.
214   m_output_file_sp->GetFile().Close();
215   // The close above should cause this thread to exit when it gets to the end
216   // of file, so let it get all its data.
217   m_communication.JoinReadThread();
218   // Now we can close the read end of the pipe.
219   m_communication.Disconnect();
220 }
221