| 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/Interpreter/CommandInterpreter.h" |
| 22 | #include "lldb/Interpreter/ScriptInterpreter.h" |
| 23 | #include "lldb/Symbol/ObjectFile.h" |
| 24 | #include "lldb/Symbol/VariableList.h" |
| 25 | #include "lldb/Target/Process.h" |
| 26 | #include "lldb/Target/StopInfo.h" |
| 27 | #include "lldb/Target/Target.h" |
| 28 | #include "lldb/Target/Thread.h" |
| 29 | #include "lldb/Target/ThreadList.h" |
| 30 | #include "lldb/Utility/DataBufferHeap.h" |
| 31 | #include "lldb/Utility/LLDBLog.h" |
| 32 | #include "lldb/Utility/RegisterValue.h" |
| 33 | #include "lldb/Utility/StreamString.h" |
| 34 | #include "lldb/Utility/StructuredData.h" |
| 35 | #include "lldb/ValueObject/ValueObjectVariable.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 | lldb::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<ThreadMemoryProvidingNameAndQueue>( |
| 263 | args&: *m_process, args&: tid, args&: name, args&: queue, 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 | bool OperatingSystemPython::DoesPluginReportAllThreads() { |
| 390 | // If the python plugin has a "DoesPluginReportAllThreads" method, use it. |
| 391 | if (std::optional<bool> plugin_answer = |
| 392 | m_operating_system_interface_sp->DoesPluginReportAllThreads()) |
| 393 | return *plugin_answer; |
| 394 | return m_process->GetOSPluginReportsAllThreads(); |
| 395 | } |
| 396 | |
| 397 | #endif // #if LLDB_ENABLE_PYTHON |
| 398 | |