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