1 | //===-- OperatingSystemPython.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/Host/Config.h" |
10 | |
11 | #if LLDB_ENABLE_PYTHON |
12 | |
13 | #include "OperatingSystemPython.h" |
14 | |
15 | #include "Plugins/Process/Utility/RegisterContextDummy.h" |
16 | #include "Plugins/Process/Utility/RegisterContextMemory.h" |
17 | #include "Plugins/Process/Utility/ThreadMemory.h" |
18 | #include "lldb/Core/Debugger.h" |
19 | #include "lldb/Core/Module.h" |
20 | #include "lldb/Core/PluginManager.h" |
21 | #include "lldb/Core/ValueObjectVariable.h" |
22 | #include "lldb/Interpreter/CommandInterpreter.h" |
23 | #include "lldb/Interpreter/ScriptInterpreter.h" |
24 | #include "lldb/Symbol/ObjectFile.h" |
25 | #include "lldb/Symbol/VariableList.h" |
26 | #include "lldb/Target/Process.h" |
27 | #include "lldb/Target/StopInfo.h" |
28 | #include "lldb/Target/Target.h" |
29 | #include "lldb/Target/Thread.h" |
30 | #include "lldb/Target/ThreadList.h" |
31 | #include "lldb/Utility/DataBufferHeap.h" |
32 | #include "lldb/Utility/LLDBLog.h" |
33 | #include "lldb/Utility/RegisterValue.h" |
34 | #include "lldb/Utility/StreamString.h" |
35 | #include "lldb/Utility/StructuredData.h" |
36 | |
37 | #include <memory> |
38 | |
39 | using namespace lldb; |
40 | using namespace lldb_private; |
41 | |
42 | LLDB_PLUGIN_DEFINE(OperatingSystemPython) |
43 | |
44 | void OperatingSystemPython::Initialize() { |
45 | PluginManager::RegisterPlugin(name: GetPluginNameStatic(), |
46 | description: GetPluginDescriptionStatic(), create_callback: CreateInstance, |
47 | debugger_init_callback: nullptr); |
48 | } |
49 | |
50 | void OperatingSystemPython::Terminate() { |
51 | PluginManager::UnregisterPlugin(create_callback: CreateInstance); |
52 | } |
53 | |
54 | OperatingSystem *OperatingSystemPython::CreateInstance(Process *process, |
55 | bool force) { |
56 | // Python OperatingSystem plug-ins must be requested by name, so force must |
57 | // be true |
58 | FileSpec python_os_plugin_spec(process->GetPythonOSPluginPath()); |
59 | if (python_os_plugin_spec && |
60 | FileSystem::Instance().Exists(file_spec: python_os_plugin_spec)) { |
61 | std::unique_ptr<OperatingSystemPython> os_up( |
62 | new OperatingSystemPython(process, python_os_plugin_spec)); |
63 | if (os_up.get() && os_up->IsValid()) |
64 | return os_up.release(); |
65 | } |
66 | return nullptr; |
67 | } |
68 | |
69 | llvm::StringRef OperatingSystemPython::GetPluginDescriptionStatic() { |
70 | return "Operating system plug-in that gathers OS information from a python " |
71 | "class that implements the necessary OperatingSystem functionality." ; |
72 | } |
73 | |
74 | OperatingSystemPython::OperatingSystemPython(lldb_private::Process *process, |
75 | const FileSpec &python_module_path) |
76 | : OperatingSystem(process), m_thread_list_valobj_sp(), m_register_info_up(), |
77 | m_interpreter(nullptr), m_script_object_sp() { |
78 | if (!process) |
79 | return; |
80 | TargetSP target_sp = process->CalculateTarget(); |
81 | if (!target_sp) |
82 | return; |
83 | m_interpreter = target_sp->GetDebugger().GetScriptInterpreter(); |
84 | if (!m_interpreter) |
85 | return; |
86 | |
87 | std::string os_plugin_class_name( |
88 | python_module_path.GetFilename().AsCString(value_if_empty: "" )); |
89 | if (os_plugin_class_name.empty()) |
90 | return; |
91 | |
92 | LoadScriptOptions options; |
93 | char python_module_path_cstr[PATH_MAX]; |
94 | python_module_path.GetPath(path: python_module_path_cstr, |
95 | max_path_length: sizeof(python_module_path_cstr)); |
96 | Status error; |
97 | if (!m_interpreter->LoadScriptingModule(filename: python_module_path_cstr, options, |
98 | error)) |
99 | return; |
100 | |
101 | // Strip the ".py" extension if there is one |
102 | size_t py_extension_pos = os_plugin_class_name.rfind(s: ".py" ); |
103 | if (py_extension_pos != std::string::npos) |
104 | os_plugin_class_name.erase(pos: py_extension_pos); |
105 | // Add ".OperatingSystemPlugIn" to the module name to get a string like |
106 | // "modulename.OperatingSystemPlugIn" |
107 | os_plugin_class_name += ".OperatingSystemPlugIn" ; |
108 | |
109 | auto operating_system_interface = |
110 | m_interpreter->CreateOperatingSystemInterface(); |
111 | if (!operating_system_interface) |
112 | // FIXME: We should pass an Status& to raise the error to the user. |
113 | // return llvm::createStringError( |
114 | // llvm::inconvertibleErrorCode(), |
115 | // "Failed to create scripted thread interface."); |
116 | return; |
117 | |
118 | ExecutionContext exe_ctx(process); |
119 | auto obj_or_err = operating_system_interface->CreatePluginObject( |
120 | class_name: os_plugin_class_name, exe_ctx, args_sp: nullptr); |
121 | |
122 | if (!obj_or_err) { |
123 | llvm::consumeError(Err: obj_or_err.takeError()); |
124 | return; |
125 | } |
126 | |
127 | StructuredData::GenericSP owned_script_object_sp = *obj_or_err; |
128 | if (!owned_script_object_sp->IsValid()) |
129 | // return llvm::createStringError(llvm::inconvertibleErrorCode(), |
130 | // "Created script object is invalid."); |
131 | return; |
132 | |
133 | m_script_object_sp = owned_script_object_sp; |
134 | m_operating_system_interface_sp = operating_system_interface; |
135 | } |
136 | |
137 | OperatingSystemPython::~OperatingSystemPython() = default; |
138 | |
139 | DynamicRegisterInfo *OperatingSystemPython::GetDynamicRegisterInfo() { |
140 | if (m_register_info_up == nullptr) { |
141 | if (!m_interpreter || !m_operating_system_interface_sp) |
142 | return nullptr; |
143 | Log *log = GetLog(mask: LLDBLog::OS); |
144 | |
145 | LLDB_LOGF(log, |
146 | "OperatingSystemPython::GetDynamicRegisterInfo() fetching " |
147 | "thread register definitions from python for pid %" PRIu64, |
148 | m_process->GetID()); |
149 | |
150 | StructuredData::DictionarySP dictionary = |
151 | m_operating_system_interface_sp->GetRegisterInfo(); |
152 | if (!dictionary) |
153 | return nullptr; |
154 | |
155 | m_register_info_up = DynamicRegisterInfo::Create( |
156 | dict: *dictionary, arch: m_process->GetTarget().GetArchitecture()); |
157 | assert(m_register_info_up); |
158 | assert(m_register_info_up->GetNumRegisters() > 0); |
159 | assert(m_register_info_up->GetNumRegisterSets() > 0); |
160 | } |
161 | return m_register_info_up.get(); |
162 | } |
163 | |
164 | bool OperatingSystemPython::UpdateThreadList(ThreadList &old_thread_list, |
165 | ThreadList &core_thread_list, |
166 | ThreadList &new_thread_list) { |
167 | if (!m_interpreter || !m_operating_system_interface_sp) |
168 | return false; |
169 | |
170 | Log *log = GetLog(mask: LLDBLog::OS); |
171 | |
172 | LLDB_LOGF(log, |
173 | "OperatingSystemPython::UpdateThreadList() fetching thread " |
174 | "data from python for pid %" PRIu64, |
175 | m_process->GetID()); |
176 | |
177 | // The threads that are in "core_thread_list" upon entry are the threads from |
178 | // the lldb_private::Process subclass, no memory threads will be in this |
179 | // list. |
180 | StructuredData::ArraySP threads_list = |
181 | m_operating_system_interface_sp->GetThreadInfo(); |
182 | |
183 | const uint32_t num_cores = core_thread_list.GetSize(can_update: false); |
184 | |
185 | // Make a map so we can keep track of which cores were used from the |
186 | // core_thread list. Any real threads/cores that weren't used should later be |
187 | // put back into the "new_thread_list". |
188 | std::vector<bool> core_used_map(num_cores, false); |
189 | if (threads_list) { |
190 | if (log) { |
191 | StreamString strm; |
192 | threads_list->Dump(s&: strm); |
193 | LLDB_LOGF(log, "threads_list = %s" , strm.GetData()); |
194 | } |
195 | |
196 | const uint32_t num_threads = threads_list->GetSize(); |
197 | for (uint32_t i = 0; i < num_threads; ++i) { |
198 | StructuredData::ObjectSP thread_dict_obj = |
199 | threads_list->GetItemAtIndex(idx: i); |
200 | if (auto thread_dict = thread_dict_obj->GetAsDictionary()) { |
201 | ThreadSP thread_sp(CreateThreadFromThreadInfo( |
202 | thread_dict&: *thread_dict, core_thread_list, old_thread_list, core_used_map, |
203 | did_create_ptr: nullptr)); |
204 | if (thread_sp) |
205 | new_thread_list.AddThread(thread_sp); |
206 | } |
207 | } |
208 | } |
209 | |
210 | // Any real core threads that didn't end up backing a memory thread should |
211 | // still be in the main thread list, and they should be inserted at the |
212 | // beginning of the list |
213 | uint32_t insert_idx = 0; |
214 | for (uint32_t core_idx = 0; core_idx < num_cores; ++core_idx) { |
215 | if (!core_used_map[core_idx]) { |
216 | new_thread_list.InsertThread( |
217 | thread_sp: core_thread_list.GetThreadAtIndex(idx: core_idx, can_update: false), idx: insert_idx); |
218 | ++insert_idx; |
219 | } |
220 | } |
221 | |
222 | return new_thread_list.GetSize(can_update: false) > 0; |
223 | } |
224 | |
225 | ThreadSP OperatingSystemPython::CreateThreadFromThreadInfo( |
226 | StructuredData::Dictionary &thread_dict, ThreadList &core_thread_list, |
227 | ThreadList &old_thread_list, std::vector<bool> &core_used_map, |
228 | bool *did_create_ptr) { |
229 | ThreadSP thread_sp; |
230 | tid_t tid = LLDB_INVALID_THREAD_ID; |
231 | if (!thread_dict.GetValueForKeyAsInteger(key: "tid" , result&: tid)) |
232 | return ThreadSP(); |
233 | |
234 | uint32_t core_number; |
235 | addr_t reg_data_addr; |
236 | llvm::StringRef name; |
237 | llvm::StringRef queue; |
238 | |
239 | thread_dict.GetValueForKeyAsInteger(key: "core" , result&: core_number, UINT32_MAX); |
240 | thread_dict.GetValueForKeyAsInteger(key: "register_data_addr" , result&: reg_data_addr, |
241 | LLDB_INVALID_ADDRESS); |
242 | thread_dict.GetValueForKeyAsString(key: "name" , result&: name); |
243 | thread_dict.GetValueForKeyAsString(key: "queue" , result&: queue); |
244 | |
245 | // See if a thread already exists for "tid" |
246 | thread_sp = old_thread_list.FindThreadByID(tid, can_update: false); |
247 | if (thread_sp) { |
248 | // A thread already does exist for "tid", make sure it was an operating |
249 | // system |
250 | // plug-in generated thread. |
251 | if (!IsOperatingSystemPluginThread(thread_sp)) { |
252 | // We have thread ID overlap between the protocol threads and the |
253 | // operating system threads, clear the thread so we create an operating |
254 | // system thread for this. |
255 | thread_sp.reset(); |
256 | } |
257 | } |
258 | |
259 | if (!thread_sp) { |
260 | if (did_create_ptr) |
261 | *did_create_ptr = true; |
262 | thread_sp = std::make_shared<ThreadMemory>(args&: *m_process, args&: tid, args&: name, args&: queue, |
263 | args&: reg_data_addr); |
264 | } |
265 | |
266 | if (core_number < core_thread_list.GetSize(can_update: false)) { |
267 | ThreadSP core_thread_sp( |
268 | core_thread_list.GetThreadAtIndex(idx: core_number, can_update: false)); |
269 | if (core_thread_sp) { |
270 | // Keep track of which cores were set as the backing thread for memory |
271 | // threads... |
272 | if (core_number < core_used_map.size()) |
273 | core_used_map[core_number] = true; |
274 | |
275 | ThreadSP backing_core_thread_sp(core_thread_sp->GetBackingThread()); |
276 | if (backing_core_thread_sp) { |
277 | thread_sp->SetBackingThread(backing_core_thread_sp); |
278 | } else { |
279 | thread_sp->SetBackingThread(core_thread_sp); |
280 | } |
281 | } |
282 | } |
283 | return thread_sp; |
284 | } |
285 | |
286 | void OperatingSystemPython::ThreadWasSelected(Thread *thread) {} |
287 | |
288 | RegisterContextSP |
289 | OperatingSystemPython::CreateRegisterContextForThread(Thread *thread, |
290 | addr_t reg_data_addr) { |
291 | RegisterContextSP reg_ctx_sp; |
292 | if (!m_interpreter || !m_script_object_sp || !thread) |
293 | return reg_ctx_sp; |
294 | |
295 | if (!IsOperatingSystemPluginThread(thread_sp: thread->shared_from_this())) |
296 | return reg_ctx_sp; |
297 | |
298 | Log *log = GetLog(mask: LLDBLog::Thread); |
299 | |
300 | if (reg_data_addr != LLDB_INVALID_ADDRESS) { |
301 | // The registers data is in contiguous memory, just create the register |
302 | // context using the address provided |
303 | LLDB_LOGF(log, |
304 | "OperatingSystemPython::CreateRegisterContextForThread (tid " |
305 | "= 0x%" PRIx64 ", 0x%" PRIx64 ", reg_data_addr = 0x%" PRIx64 |
306 | ") creating memory register context" , |
307 | thread->GetID(), thread->GetProtocolID(), reg_data_addr); |
308 | reg_ctx_sp = std::make_shared<RegisterContextMemory>( |
309 | args&: *thread, args: 0, args&: *GetDynamicRegisterInfo(), args&: reg_data_addr); |
310 | } else { |
311 | // No register data address is provided, query the python plug-in to let it |
312 | // make up the data as it sees fit |
313 | LLDB_LOGF(log, |
314 | "OperatingSystemPython::CreateRegisterContextForThread (tid " |
315 | "= 0x%" PRIx64 ", 0x%" PRIx64 |
316 | ") fetching register data from python" , |
317 | thread->GetID(), thread->GetProtocolID()); |
318 | |
319 | std::optional<std::string> reg_context_data = |
320 | m_operating_system_interface_sp->GetRegisterContextForTID( |
321 | tid: thread->GetID()); |
322 | if (reg_context_data) { |
323 | std::string value = *reg_context_data; |
324 | DataBufferSP data_sp(new DataBufferHeap(value.c_str(), value.length())); |
325 | if (data_sp->GetByteSize()) { |
326 | RegisterContextMemory *reg_ctx_memory = new RegisterContextMemory( |
327 | *thread, 0, *GetDynamicRegisterInfo(), LLDB_INVALID_ADDRESS); |
328 | if (reg_ctx_memory) { |
329 | reg_ctx_sp.reset(p: reg_ctx_memory); |
330 | reg_ctx_memory->SetAllRegisterData(data_sp); |
331 | } |
332 | } |
333 | } |
334 | } |
335 | // if we still have no register data, fallback on a dummy context to avoid |
336 | // crashing |
337 | if (!reg_ctx_sp) { |
338 | LLDB_LOGF(log, |
339 | "OperatingSystemPython::CreateRegisterContextForThread (tid " |
340 | "= 0x%" PRIx64 ") forcing a dummy register context" , |
341 | thread->GetID()); |
342 | Target &target = m_process->GetTarget(); |
343 | reg_ctx_sp = std::make_shared<RegisterContextDummy>( |
344 | args&: *thread, args: 0, args: target.GetArchitecture().GetAddressByteSize()); |
345 | } |
346 | return reg_ctx_sp; |
347 | } |
348 | |
349 | StopInfoSP |
350 | OperatingSystemPython::CreateThreadStopReason(lldb_private::Thread *thread) { |
351 | // We should have gotten the thread stop info from the dictionary of data for |
352 | // the thread in the initial call to get_thread_info(), this should have been |
353 | // cached so we can return it here |
354 | StopInfoSP |
355 | stop_info_sp; //(StopInfo::CreateStopReasonWithSignal (*thread, SIGSTOP)); |
356 | return stop_info_sp; |
357 | } |
358 | |
359 | lldb::ThreadSP OperatingSystemPython::CreateThread(lldb::tid_t tid, |
360 | addr_t context) { |
361 | Log *log = GetLog(mask: LLDBLog::Thread); |
362 | |
363 | LLDB_LOGF(log, |
364 | "OperatingSystemPython::CreateThread (tid = 0x%" PRIx64 |
365 | ", context = 0x%" PRIx64 ") fetching register data from python" , |
366 | tid, context); |
367 | |
368 | if (m_interpreter && m_script_object_sp) { |
369 | |
370 | StructuredData::DictionarySP thread_info_dict = |
371 | m_operating_system_interface_sp->CreateThread(tid, context); |
372 | |
373 | std::vector<bool> core_used_map; |
374 | if (thread_info_dict) { |
375 | ThreadList core_threads(m_process); |
376 | ThreadList &thread_list = m_process->GetThreadList(); |
377 | bool did_create = false; |
378 | ThreadSP thread_sp( |
379 | CreateThreadFromThreadInfo(thread_dict&: *thread_info_dict, core_thread_list&: core_threads, |
380 | old_thread_list&: thread_list, core_used_map, did_create_ptr: &did_create)); |
381 | if (did_create) |
382 | thread_list.AddThread(thread_sp); |
383 | return thread_sp; |
384 | } |
385 | } |
386 | return ThreadSP(); |
387 | } |
388 | |
389 | #endif // #if LLDB_ENABLE_PYTHON |
390 | |