• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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