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