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 <cstdio>
22#include <cstdlib>
23#include <memory>
24#include <optional>
25#include <string>
26
27using namespace lldb;
28using namespace lldb_private;
29
30ScriptInterpreter::ScriptInterpreter(Debugger &debugger,
31 lldb::ScriptLanguage script_lang)
32 : m_debugger(debugger), m_script_lang(script_lang) {}
33
34void ScriptInterpreter::CollectDataForBreakpointCommandCallback(
35 std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec,
36 CommandReturnObject &result) {
37 result.AppendError(
38 in_string: "This script interpreter does not support breakpoint callbacks.");
39}
40
41void ScriptInterpreter::CollectDataForWatchpointCommandCallback(
42 WatchpointOptions *bp_options, CommandReturnObject &result) {
43 result.AppendError(
44 in_string: "This script interpreter does not support watchpoint callbacks.");
45}
46
47StructuredData::DictionarySP ScriptInterpreter::GetInterpreterInfo() {
48 return nullptr;
49}
50
51bool ScriptInterpreter::LoadScriptingModule(const char *filename,
52 const LoadScriptOptions &options,
53 lldb_private::Status &error,
54 StructuredData::ObjectSP *module_sp,
55 FileSpec extra_search_dir) {
56 error.SetErrorString(
57 "This script interpreter does not support importing modules.");
58 return false;
59}
60
61std::string ScriptInterpreter::LanguageToString(lldb::ScriptLanguage language) {
62 switch (language) {
63 case eScriptLanguageNone:
64 return "None";
65 case eScriptLanguagePython:
66 return "Python";
67 case eScriptLanguageLua:
68 return "Lua";
69 case eScriptLanguageUnknown:
70 return "Unknown";
71 }
72 llvm_unreachable("Unhandled ScriptInterpreter!");
73}
74
75lldb::DataExtractorSP
76ScriptInterpreter::GetDataExtractorFromSBData(const lldb::SBData &data) const {
77 return data.m_opaque_sp;
78}
79
80lldb::BreakpointSP ScriptInterpreter::GetOpaqueTypeFromSBBreakpoint(
81 const lldb::SBBreakpoint &breakpoint) const {
82 return breakpoint.m_opaque_wp.lock();
83}
84
85lldb::ProcessAttachInfoSP ScriptInterpreter::GetOpaqueTypeFromSBAttachInfo(
86 const lldb::SBAttachInfo &attach_info) const {
87 return attach_info.m_opaque_sp;
88}
89
90lldb::ProcessLaunchInfoSP ScriptInterpreter::GetOpaqueTypeFromSBLaunchInfo(
91 const lldb::SBLaunchInfo &launch_info) const {
92 return std::make_shared<ProcessLaunchInfo>(
93 args&: *reinterpret_cast<ProcessLaunchInfo *>(launch_info.m_opaque_sp.get()));
94}
95
96Status
97ScriptInterpreter::GetStatusFromSBError(const lldb::SBError &error) const {
98 if (error.m_opaque_up)
99 return *error.m_opaque_up;
100
101 return Status();
102}
103
104std::optional<MemoryRegionInfo>
105ScriptInterpreter::GetOpaqueTypeFromSBMemoryRegionInfo(
106 const lldb::SBMemoryRegionInfo &mem_region) const {
107 if (!mem_region.m_opaque_up)
108 return std::nullopt;
109 return *mem_region.m_opaque_up.get();
110}
111
112lldb::ScriptLanguage
113ScriptInterpreter::StringToLanguage(const llvm::StringRef &language) {
114 if (language.equals_insensitive(RHS: LanguageToString(language: eScriptLanguageNone)))
115 return eScriptLanguageNone;
116 if (language.equals_insensitive(RHS: LanguageToString(language: eScriptLanguagePython)))
117 return eScriptLanguagePython;
118 if (language.equals_insensitive(RHS: LanguageToString(language: eScriptLanguageLua)))
119 return eScriptLanguageLua;
120 return eScriptLanguageUnknown;
121}
122
123Status ScriptInterpreter::SetBreakpointCommandCallback(
124 std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec,
125 const char *callback_text) {
126 Status error;
127 for (BreakpointOptions &bp_options : bp_options_vec) {
128 error = SetBreakpointCommandCallback(bp_options, callback_text,
129 /*is_callback=*/false);
130 if (!error.Success())
131 break;
132 }
133 return error;
134}
135
136Status ScriptInterpreter::SetBreakpointCommandCallbackFunction(
137 std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec,
138 const char *function_name, StructuredData::ObjectSP extra_args_sp) {
139 Status error;
140 for (BreakpointOptions &bp_options : bp_options_vec) {
141 error = SetBreakpointCommandCallbackFunction(bp_options, function_name,
142 extra_args_sp);
143 if (!error.Success())
144 return error;
145 }
146 return error;
147}
148
149std::unique_ptr<ScriptInterpreterLocker>
150ScriptInterpreter::AcquireInterpreterLock() {
151 return std::make_unique<ScriptInterpreterLocker>();
152}
153
154static void ReadThreadBytesReceived(void *baton, const void *src,
155 size_t src_len) {
156 if (src && src_len) {
157 Stream *strm = (Stream *)baton;
158 strm->Write(src, src_len);
159 strm->Flush();
160 }
161}
162
163llvm::Expected<std::unique_ptr<ScriptInterpreterIORedirect>>
164ScriptInterpreterIORedirect::Create(bool enable_io, Debugger &debugger,
165 CommandReturnObject *result) {
166 if (enable_io)
167 return std::unique_ptr<ScriptInterpreterIORedirect>(
168 new ScriptInterpreterIORedirect(debugger, result));
169
170 auto nullin = FileSystem::Instance().Open(file_spec: FileSpec(FileSystem::DEV_NULL),
171 options: File::eOpenOptionReadOnly);
172 if (!nullin)
173 return nullin.takeError();
174
175 auto nullout = FileSystem::Instance().Open(file_spec: FileSpec(FileSystem::DEV_NULL),
176 options: File::eOpenOptionWriteOnly);
177 if (!nullout)
178 return nullin.takeError();
179
180 return std::unique_ptr<ScriptInterpreterIORedirect>(
181 new ScriptInterpreterIORedirect(std::move(*nullin), std::move(*nullout)));
182}
183
184ScriptInterpreterIORedirect::ScriptInterpreterIORedirect(
185 std::unique_ptr<File> input, std::unique_ptr<File> output)
186 : m_input_file_sp(std::move(input)),
187 m_output_file_sp(std::make_shared<StreamFile>(args: std::move(output))),
188 m_error_file_sp(m_output_file_sp),
189 m_communication("lldb.ScriptInterpreterIORedirect.comm"),
190 m_disconnect(false) {}
191
192ScriptInterpreterIORedirect::ScriptInterpreterIORedirect(
193 Debugger &debugger, CommandReturnObject *result)
194 : m_communication("lldb.ScriptInterpreterIORedirect.comm"),
195 m_disconnect(false) {
196
197 if (result) {
198 m_input_file_sp = debugger.GetInputFileSP();
199
200 Pipe pipe;
201 Status pipe_result = pipe.CreateNew(child_process_inherit: false);
202#if defined(_WIN32)
203 lldb::file_t read_file = pipe.GetReadNativeHandle();
204 pipe.ReleaseReadFileDescriptor();
205 std::unique_ptr<ConnectionGenericFile> conn_up =
206 std::make_unique<ConnectionGenericFile>(read_file, true);
207#else
208 std::unique_ptr<ConnectionFileDescriptor> conn_up =
209 std::make_unique<ConnectionFileDescriptor>(
210 args: pipe.ReleaseReadFileDescriptor(), args: true);
211#endif
212
213 if (conn_up->IsConnected()) {
214 m_communication.SetConnection(std::move(conn_up));
215 m_communication.SetReadThreadBytesReceivedCallback(
216 callback: ReadThreadBytesReceived, callback_baton: &result->GetOutputStream());
217 m_communication.StartReadThread();
218 m_disconnect = true;
219
220 FILE *outfile_handle = fdopen(fd: pipe.ReleaseWriteFileDescriptor(), modes: "w");
221 m_output_file_sp = std::make_shared<StreamFile>(args&: outfile_handle, args: true);
222 m_error_file_sp = m_output_file_sp;
223 if (outfile_handle)
224 ::setbuf(stream: outfile_handle, buf: nullptr);
225
226 result->SetImmediateOutputFile(debugger.GetOutputStream().GetFileSP());
227 result->SetImmediateErrorFile(debugger.GetErrorStream().GetFileSP());
228 }
229 }
230
231 if (!m_input_file_sp || !m_output_file_sp || !m_error_file_sp)
232 debugger.AdoptTopIOHandlerFilesIfInvalid(in&: m_input_file_sp, out&: m_output_file_sp,
233 err&: m_error_file_sp);
234}
235
236void ScriptInterpreterIORedirect::Flush() {
237 if (m_output_file_sp)
238 m_output_file_sp->Flush();
239 if (m_error_file_sp)
240 m_error_file_sp->Flush();
241}
242
243ScriptInterpreterIORedirect::~ScriptInterpreterIORedirect() {
244 if (!m_disconnect)
245 return;
246
247 assert(m_output_file_sp);
248 assert(m_error_file_sp);
249 assert(m_output_file_sp == m_error_file_sp);
250
251 // Close the write end of the pipe since we are done with our one line
252 // script. This should cause the read thread that output_comm is using to
253 // exit.
254 m_output_file_sp->GetFile().Close();
255 // The close above should cause this thread to exit when it gets to the end
256 // of file, so let it get all its data.
257 m_communication.JoinReadThread();
258 // Now we can close the read end of the pipe.
259 m_communication.Disconnect();
260}
261

source code of lldb/source/Interpreter/ScriptInterpreter.cpp