1*312b43daSMed Ismail Bennani //===-- ScriptedProcess.cpp -----------------------------------------------===//
2*312b43daSMed Ismail Bennani //
3*312b43daSMed Ismail Bennani // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*312b43daSMed Ismail Bennani // See https://llvm.org/LICENSE.txt for license information.
5*312b43daSMed Ismail Bennani // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*312b43daSMed Ismail Bennani //
7*312b43daSMed Ismail Bennani //===----------------------------------------------------------------------===//
8*312b43daSMed Ismail Bennani 
9*312b43daSMed Ismail Bennani #include "ScriptedProcess.h"
10*312b43daSMed Ismail Bennani 
11*312b43daSMed Ismail Bennani #include "lldb/Core/Debugger.h"
12*312b43daSMed Ismail Bennani #include "lldb/Core/Module.h"
13*312b43daSMed Ismail Bennani #include "lldb/Core/PluginManager.h"
14*312b43daSMed Ismail Bennani 
15*312b43daSMed Ismail Bennani #include "lldb/Host/OptionParser.h"
16*312b43daSMed Ismail Bennani #include "lldb/Host/ThreadLauncher.h"
17*312b43daSMed Ismail Bennani #include "lldb/Interpreter/CommandInterpreter.h"
18*312b43daSMed Ismail Bennani #include "lldb/Interpreter/OptionArgParser.h"
19*312b43daSMed Ismail Bennani #include "lldb/Interpreter/OptionGroupBoolean.h"
20*312b43daSMed Ismail Bennani #include "lldb/Interpreter/ScriptInterpreter.h"
21*312b43daSMed Ismail Bennani #include "lldb/Target/MemoryRegionInfo.h"
22*312b43daSMed Ismail Bennani #include "lldb/Target/RegisterContext.h"
23*312b43daSMed Ismail Bennani 
24*312b43daSMed Ismail Bennani #include "lldb/Utility/Log.h"
25*312b43daSMed Ismail Bennani #include "lldb/Utility/Logging.h"
26*312b43daSMed Ismail Bennani #include "lldb/Utility/State.h"
27*312b43daSMed Ismail Bennani 
28*312b43daSMed Ismail Bennani #include <mutex>
29*312b43daSMed Ismail Bennani 
30*312b43daSMed Ismail Bennani LLDB_PLUGIN_DEFINE(ScriptedProcess)
31*312b43daSMed Ismail Bennani 
32*312b43daSMed Ismail Bennani using namespace lldb;
33*312b43daSMed Ismail Bennani using namespace lldb_private;
34*312b43daSMed Ismail Bennani 
35*312b43daSMed Ismail Bennani ConstString ScriptedProcess::GetPluginNameStatic() {
36*312b43daSMed Ismail Bennani   static ConstString g_name("ScriptedProcess");
37*312b43daSMed Ismail Bennani   return g_name;
38*312b43daSMed Ismail Bennani }
39*312b43daSMed Ismail Bennani 
40*312b43daSMed Ismail Bennani const char *ScriptedProcess::GetPluginDescriptionStatic() {
41*312b43daSMed Ismail Bennani   return "Scripted Process plug-in.";
42*312b43daSMed Ismail Bennani }
43*312b43daSMed Ismail Bennani 
44*312b43daSMed Ismail Bennani static constexpr lldb::ScriptLanguage g_supported_script_languages[] = {
45*312b43daSMed Ismail Bennani     ScriptLanguage::eScriptLanguagePython,
46*312b43daSMed Ismail Bennani };
47*312b43daSMed Ismail Bennani 
48*312b43daSMed Ismail Bennani bool ScriptedProcess::IsScriptLanguageSupported(lldb::ScriptLanguage language) {
49*312b43daSMed Ismail Bennani   llvm::ArrayRef<lldb::ScriptLanguage> supported_languages =
50*312b43daSMed Ismail Bennani       llvm::makeArrayRef(g_supported_script_languages);
51*312b43daSMed Ismail Bennani 
52*312b43daSMed Ismail Bennani   return llvm::is_contained(supported_languages, language);
53*312b43daSMed Ismail Bennani }
54*312b43daSMed Ismail Bennani 
55*312b43daSMed Ismail Bennani void ScriptedProcess::CheckInterpreterAndScriptObject() const {
56*312b43daSMed Ismail Bennani   lldbassert(m_interpreter && "Invalid Script Interpreter.");
57*312b43daSMed Ismail Bennani   lldbassert(m_script_object_sp && "Invalid Script Object.");
58*312b43daSMed Ismail Bennani }
59*312b43daSMed Ismail Bennani 
60*312b43daSMed Ismail Bennani lldb::ProcessSP ScriptedProcess::CreateInstance(lldb::TargetSP target_sp,
61*312b43daSMed Ismail Bennani                                                 lldb::ListenerSP listener_sp,
62*312b43daSMed Ismail Bennani                                                 const FileSpec *file,
63*312b43daSMed Ismail Bennani                                                 bool can_connect) {
64*312b43daSMed Ismail Bennani   if (!target_sp ||
65*312b43daSMed Ismail Bennani       !IsScriptLanguageSupported(target_sp->GetDebugger().GetScriptLanguage()))
66*312b43daSMed Ismail Bennani     return nullptr;
67*312b43daSMed Ismail Bennani 
68*312b43daSMed Ismail Bennani   Status error;
69*312b43daSMed Ismail Bennani   ScriptedProcess::ScriptedProcessInfo scripted_process_info(
70*312b43daSMed Ismail Bennani       target_sp->GetProcessLaunchInfo());
71*312b43daSMed Ismail Bennani 
72*312b43daSMed Ismail Bennani   auto process_sp = std::make_shared<ScriptedProcess>(
73*312b43daSMed Ismail Bennani       target_sp, listener_sp, scripted_process_info, error);
74*312b43daSMed Ismail Bennani 
75*312b43daSMed Ismail Bennani   if (error.Fail() || !process_sp || !process_sp->m_script_object_sp ||
76*312b43daSMed Ismail Bennani       !process_sp->m_script_object_sp->IsValid()) {
77*312b43daSMed Ismail Bennani     LLDB_LOGF(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS), "%s",
78*312b43daSMed Ismail Bennani               error.AsCString());
79*312b43daSMed Ismail Bennani     return nullptr;
80*312b43daSMed Ismail Bennani   }
81*312b43daSMed Ismail Bennani 
82*312b43daSMed Ismail Bennani   return process_sp;
83*312b43daSMed Ismail Bennani }
84*312b43daSMed Ismail Bennani 
85*312b43daSMed Ismail Bennani bool ScriptedProcess::CanDebug(lldb::TargetSP target_sp,
86*312b43daSMed Ismail Bennani                                bool plugin_specified_by_name) {
87*312b43daSMed Ismail Bennani   return true;
88*312b43daSMed Ismail Bennani }
89*312b43daSMed Ismail Bennani 
90*312b43daSMed Ismail Bennani ScriptedProcess::ScriptedProcess(
91*312b43daSMed Ismail Bennani     lldb::TargetSP target_sp, lldb::ListenerSP listener_sp,
92*312b43daSMed Ismail Bennani     const ScriptedProcess::ScriptedProcessInfo &scripted_process_info,
93*312b43daSMed Ismail Bennani     Status &error)
94*312b43daSMed Ismail Bennani     : Process(target_sp, listener_sp),
95*312b43daSMed Ismail Bennani       m_scripted_process_info(scripted_process_info) {
96*312b43daSMed Ismail Bennani 
97*312b43daSMed Ismail Bennani   if (!target_sp) {
98*312b43daSMed Ismail Bennani     error.SetErrorStringWithFormat("ScriptedProcess::%s () - ERROR: %s",
99*312b43daSMed Ismail Bennani                                    __FUNCTION__, "Invalid target");
100*312b43daSMed Ismail Bennani     return;
101*312b43daSMed Ismail Bennani   }
102*312b43daSMed Ismail Bennani 
103*312b43daSMed Ismail Bennani   m_interpreter = target_sp->GetDebugger().GetScriptInterpreter();
104*312b43daSMed Ismail Bennani 
105*312b43daSMed Ismail Bennani   if (!m_interpreter) {
106*312b43daSMed Ismail Bennani     error.SetErrorStringWithFormat("ScriptedProcess::%s () - ERROR: %s",
107*312b43daSMed Ismail Bennani                                    __FUNCTION__,
108*312b43daSMed Ismail Bennani                                    "Debugger has no Script Interpreter");
109*312b43daSMed Ismail Bennani     return;
110*312b43daSMed Ismail Bennani   }
111*312b43daSMed Ismail Bennani 
112*312b43daSMed Ismail Bennani   StructuredData::ObjectSP object_sp = GetInterface().CreatePluginObject(
113*312b43daSMed Ismail Bennani       m_scripted_process_info.GetClassName().c_str(), target_sp,
114*312b43daSMed Ismail Bennani       m_scripted_process_info.GetDictionarySP());
115*312b43daSMed Ismail Bennani 
116*312b43daSMed Ismail Bennani   if (!object_sp || !object_sp->IsValid()) {
117*312b43daSMed Ismail Bennani     error.SetErrorStringWithFormat("ScriptedProcess::%s () - ERROR: %s",
118*312b43daSMed Ismail Bennani                                    __FUNCTION__,
119*312b43daSMed Ismail Bennani                                    "Failed to create valid script object");
120*312b43daSMed Ismail Bennani     return;
121*312b43daSMed Ismail Bennani   }
122*312b43daSMed Ismail Bennani 
123*312b43daSMed Ismail Bennani   m_script_object_sp = object_sp;
124*312b43daSMed Ismail Bennani }
125*312b43daSMed Ismail Bennani 
126*312b43daSMed Ismail Bennani ScriptedProcess::~ScriptedProcess() {
127*312b43daSMed Ismail Bennani   Clear();
128*312b43daSMed Ismail Bennani   // We need to call finalize on the process before destroying ourselves to
129*312b43daSMed Ismail Bennani   // make sure all of the broadcaster cleanup goes as planned. If we destruct
130*312b43daSMed Ismail Bennani   // this class, then Process::~Process() might have problems trying to fully
131*312b43daSMed Ismail Bennani   // destroy the broadcaster.
132*312b43daSMed Ismail Bennani   Finalize();
133*312b43daSMed Ismail Bennani }
134*312b43daSMed Ismail Bennani 
135*312b43daSMed Ismail Bennani void ScriptedProcess::Initialize() {
136*312b43daSMed Ismail Bennani   static llvm::once_flag g_once_flag;
137*312b43daSMed Ismail Bennani 
138*312b43daSMed Ismail Bennani   llvm::call_once(g_once_flag, []() {
139*312b43daSMed Ismail Bennani     PluginManager::RegisterPlugin(GetPluginNameStatic(),
140*312b43daSMed Ismail Bennani                                   GetPluginDescriptionStatic(), CreateInstance);
141*312b43daSMed Ismail Bennani   });
142*312b43daSMed Ismail Bennani }
143*312b43daSMed Ismail Bennani 
144*312b43daSMed Ismail Bennani void ScriptedProcess::Terminate() {
145*312b43daSMed Ismail Bennani   PluginManager::UnregisterPlugin(ScriptedProcess::CreateInstance);
146*312b43daSMed Ismail Bennani }
147*312b43daSMed Ismail Bennani 
148*312b43daSMed Ismail Bennani ConstString ScriptedProcess::GetPluginName() { return GetPluginNameStatic(); }
149*312b43daSMed Ismail Bennani 
150*312b43daSMed Ismail Bennani uint32_t ScriptedProcess::GetPluginVersion() { return 1; }
151*312b43daSMed Ismail Bennani 
152*312b43daSMed Ismail Bennani Status ScriptedProcess::DoLoadCore() {
153*312b43daSMed Ismail Bennani   ProcessLaunchInfo launch_info = GetTarget().GetProcessLaunchInfo();
154*312b43daSMed Ismail Bennani 
155*312b43daSMed Ismail Bennani   return DoLaunch(nullptr, launch_info);
156*312b43daSMed Ismail Bennani }
157*312b43daSMed Ismail Bennani 
158*312b43daSMed Ismail Bennani Status ScriptedProcess::DoLaunch(Module *exe_module,
159*312b43daSMed Ismail Bennani                                  ProcessLaunchInfo &launch_info) {
160*312b43daSMed Ismail Bennani   CheckInterpreterAndScriptObject();
161*312b43daSMed Ismail Bennani 
162*312b43daSMed Ismail Bennani   /* FIXME: This doesn't reflect how lldb actually launches a process.
163*312b43daSMed Ismail Bennani            In reality, it attaches to debugserver, then resume the process. */
164*312b43daSMed Ismail Bennani   Status error = GetInterface().Launch();
165*312b43daSMed Ismail Bennani   SetPrivateState(eStateRunning);
166*312b43daSMed Ismail Bennani 
167*312b43daSMed Ismail Bennani   if (error.Fail())
168*312b43daSMed Ismail Bennani     return error;
169*312b43daSMed Ismail Bennani 
170*312b43daSMed Ismail Bennani   // TODO: Fetch next state from stopped event queue then send stop event
171*312b43daSMed Ismail Bennani   //  const StateType state = SetThreadStopInfo(response);
172*312b43daSMed Ismail Bennani   //  if (state != eStateInvalid) {
173*312b43daSMed Ismail Bennani   //    SetPrivateState(state);
174*312b43daSMed Ismail Bennani 
175*312b43daSMed Ismail Bennani   SetPrivateState(eStateStopped);
176*312b43daSMed Ismail Bennani 
177*312b43daSMed Ismail Bennani   UpdateThreadListIfNeeded();
178*312b43daSMed Ismail Bennani   GetThreadList();
179*312b43daSMed Ismail Bennani 
180*312b43daSMed Ismail Bennani   return {};
181*312b43daSMed Ismail Bennani }
182*312b43daSMed Ismail Bennani 
183*312b43daSMed Ismail Bennani void ScriptedProcess::DidLaunch() {
184*312b43daSMed Ismail Bennani   CheckInterpreterAndScriptObject();
185*312b43daSMed Ismail Bennani   m_pid = GetInterface().GetProcessID();
186*312b43daSMed Ismail Bennani }
187*312b43daSMed Ismail Bennani 
188*312b43daSMed Ismail Bennani Status ScriptedProcess::DoResume() {
189*312b43daSMed Ismail Bennani   CheckInterpreterAndScriptObject();
190*312b43daSMed Ismail Bennani 
191*312b43daSMed Ismail Bennani   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
192*312b43daSMed Ismail Bennani   // FIXME: Fetch data from thread.
193*312b43daSMed Ismail Bennani   const StateType thread_resume_state = eStateRunning;
194*312b43daSMed Ismail Bennani   LLDB_LOGF(log, "ScriptedProcess::%s thread_resume_state = %s", __FUNCTION__,
195*312b43daSMed Ismail Bennani             StateAsCString(thread_resume_state));
196*312b43daSMed Ismail Bennani 
197*312b43daSMed Ismail Bennani   bool resume = (thread_resume_state == eStateRunning);
198*312b43daSMed Ismail Bennani   assert(thread_resume_state == eStateRunning && "invalid thread resume state");
199*312b43daSMed Ismail Bennani 
200*312b43daSMed Ismail Bennani   Status error;
201*312b43daSMed Ismail Bennani   if (resume) {
202*312b43daSMed Ismail Bennani     LLDB_LOGF(log, "ScriptedProcess::%s sending resume", __FUNCTION__);
203*312b43daSMed Ismail Bennani 
204*312b43daSMed Ismail Bennani     SetPrivateState(eStateRunning);
205*312b43daSMed Ismail Bennani     SetPrivateState(eStateStopped);
206*312b43daSMed Ismail Bennani     error = GetInterface().Resume();
207*312b43daSMed Ismail Bennani   }
208*312b43daSMed Ismail Bennani 
209*312b43daSMed Ismail Bennani   return error;
210*312b43daSMed Ismail Bennani }
211*312b43daSMed Ismail Bennani 
212*312b43daSMed Ismail Bennani Status ScriptedProcess::DoStop() {
213*312b43daSMed Ismail Bennani   CheckInterpreterAndScriptObject();
214*312b43daSMed Ismail Bennani 
215*312b43daSMed Ismail Bennani   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
216*312b43daSMed Ismail Bennani 
217*312b43daSMed Ismail Bennani   if (GetInterface().ShouldStop()) {
218*312b43daSMed Ismail Bennani     SetPrivateState(eStateStopped);
219*312b43daSMed Ismail Bennani     LLDB_LOGF(log, "ScriptedProcess::%s Immediate stop", __FUNCTION__);
220*312b43daSMed Ismail Bennani     return {};
221*312b43daSMed Ismail Bennani   }
222*312b43daSMed Ismail Bennani 
223*312b43daSMed Ismail Bennani   LLDB_LOGF(log, "ScriptedProcess::%s Delayed stop", __FUNCTION__);
224*312b43daSMed Ismail Bennani   return GetInterface().Stop();
225*312b43daSMed Ismail Bennani }
226*312b43daSMed Ismail Bennani 
227*312b43daSMed Ismail Bennani Status ScriptedProcess::DoDestroy() { return Status(); }
228*312b43daSMed Ismail Bennani 
229*312b43daSMed Ismail Bennani bool ScriptedProcess::IsAlive() {
230*312b43daSMed Ismail Bennani   if (m_interpreter && m_script_object_sp)
231*312b43daSMed Ismail Bennani     return GetInterface().IsAlive();
232*312b43daSMed Ismail Bennani   return false;
233*312b43daSMed Ismail Bennani }
234*312b43daSMed Ismail Bennani 
235*312b43daSMed Ismail Bennani size_t ScriptedProcess::DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
236*312b43daSMed Ismail Bennani                                      Status &error) {
237*312b43daSMed Ismail Bennani 
238*312b43daSMed Ismail Bennani   auto error_with_message = [&error](llvm::StringRef message) {
239*312b43daSMed Ismail Bennani     error.SetErrorString(message);
240*312b43daSMed Ismail Bennani     return 0;
241*312b43daSMed Ismail Bennani   };
242*312b43daSMed Ismail Bennani 
243*312b43daSMed Ismail Bennani   if (!m_interpreter)
244*312b43daSMed Ismail Bennani     return error_with_message("No interpreter.");
245*312b43daSMed Ismail Bennani 
246*312b43daSMed Ismail Bennani   lldb::DataExtractorSP data_extractor_sp =
247*312b43daSMed Ismail Bennani       GetInterface().ReadMemoryAtAddress(addr, size, error);
248*312b43daSMed Ismail Bennani 
249*312b43daSMed Ismail Bennani   if (!data_extractor_sp || error.Fail())
250*312b43daSMed Ismail Bennani     return 0;
251*312b43daSMed Ismail Bennani 
252*312b43daSMed Ismail Bennani   offset_t bytes_copied = data_extractor_sp->CopyByteOrderedData(
253*312b43daSMed Ismail Bennani       0, data_extractor_sp->GetByteSize(), buf, size, GetByteOrder());
254*312b43daSMed Ismail Bennani 
255*312b43daSMed Ismail Bennani   if (!bytes_copied || bytes_copied == LLDB_INVALID_OFFSET)
256*312b43daSMed Ismail Bennani     return error_with_message("Failed to copy read memory to buffer.");
257*312b43daSMed Ismail Bennani 
258*312b43daSMed Ismail Bennani   return size;
259*312b43daSMed Ismail Bennani }
260*312b43daSMed Ismail Bennani 
261*312b43daSMed Ismail Bennani ArchSpec ScriptedProcess::GetArchitecture() {
262*312b43daSMed Ismail Bennani   return GetTarget().GetArchitecture();
263*312b43daSMed Ismail Bennani }
264*312b43daSMed Ismail Bennani 
265*312b43daSMed Ismail Bennani Status ScriptedProcess::GetMemoryRegionInfo(lldb::addr_t load_addr,
266*312b43daSMed Ismail Bennani                                             MemoryRegionInfo &region) {
267*312b43daSMed Ismail Bennani   // TODO: Implement
268*312b43daSMed Ismail Bennani   return Status();
269*312b43daSMed Ismail Bennani }
270*312b43daSMed Ismail Bennani 
271*312b43daSMed Ismail Bennani Status ScriptedProcess::GetMemoryRegions(MemoryRegionInfos &region_list) {
272*312b43daSMed Ismail Bennani   CheckInterpreterAndScriptObject();
273*312b43daSMed Ismail Bennani 
274*312b43daSMed Ismail Bennani   lldb::addr_t address = 0;
275*312b43daSMed Ismail Bennani   lldb::MemoryRegionInfoSP mem_region_sp = nullptr;
276*312b43daSMed Ismail Bennani 
277*312b43daSMed Ismail Bennani   while ((mem_region_sp =
278*312b43daSMed Ismail Bennani               GetInterface().GetMemoryRegionContainingAddress(address))) {
279*312b43daSMed Ismail Bennani     auto range = mem_region_sp->GetRange();
280*312b43daSMed Ismail Bennani     address += range.GetRangeBase() + range.GetByteSize();
281*312b43daSMed Ismail Bennani     region_list.push_back(*mem_region_sp.get());
282*312b43daSMed Ismail Bennani   }
283*312b43daSMed Ismail Bennani 
284*312b43daSMed Ismail Bennani   return {};
285*312b43daSMed Ismail Bennani }
286*312b43daSMed Ismail Bennani 
287*312b43daSMed Ismail Bennani void ScriptedProcess::Clear() { Process::m_thread_list.Clear(); }
288*312b43daSMed Ismail Bennani 
289*312b43daSMed Ismail Bennani bool ScriptedProcess::DoUpdateThreadList(ThreadList &old_thread_list,
290*312b43daSMed Ismail Bennani                                          ThreadList &new_thread_list) {
291*312b43daSMed Ismail Bennani   // TODO: Implement
292*312b43daSMed Ismail Bennani   // This is supposed to get the current set of threads, if any of them are in
293*312b43daSMed Ismail Bennani   // old_thread_list then they get copied to new_thread_list, and then any
294*312b43daSMed Ismail Bennani   // actually new threads will get added to new_thread_list.
295*312b43daSMed Ismail Bennani   return new_thread_list.GetSize(false) > 0;
296*312b43daSMed Ismail Bennani }
297*312b43daSMed Ismail Bennani 
298*312b43daSMed Ismail Bennani bool ScriptedProcess::GetProcessInfo(ProcessInstanceInfo &info) {
299*312b43daSMed Ismail Bennani   info.Clear();
300*312b43daSMed Ismail Bennani   info.SetProcessID(GetID());
301*312b43daSMed Ismail Bennani   info.SetArchitecture(GetArchitecture());
302*312b43daSMed Ismail Bennani   lldb::ModuleSP module_sp = GetTarget().GetExecutableModule();
303*312b43daSMed Ismail Bennani   if (module_sp) {
304*312b43daSMed Ismail Bennani     const bool add_exe_file_as_first_arg = false;
305*312b43daSMed Ismail Bennani     info.SetExecutableFile(GetTarget().GetExecutableModule()->GetFileSpec(),
306*312b43daSMed Ismail Bennani                            add_exe_file_as_first_arg);
307*312b43daSMed Ismail Bennani   }
308*312b43daSMed Ismail Bennani   return true;
309*312b43daSMed Ismail Bennani }
310*312b43daSMed Ismail Bennani 
311*312b43daSMed Ismail Bennani ScriptedProcessInterface &ScriptedProcess::GetInterface() const {
312*312b43daSMed Ismail Bennani   return m_interpreter->GetScriptedProcessInterface();
313*312b43daSMed Ismail Bennani }
314