//===-- ScriptedThread.cpp ------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "ScriptedThread.h" #include "Plugins/Process/Utility/RegisterContextThreadMemory.h" #include "lldb/Target/OperatingSystem.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/StopInfo.h" #include "lldb/Target/Unwind.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/Logging.h" #include using namespace lldb; using namespace lldb_private; void ScriptedThread::CheckInterpreterAndScriptObject() const { lldbassert(m_script_object_sp && "Invalid Script Object."); lldbassert(GetInterface() && "Invalid Scripted Thread Interface."); } ScriptedThread::ScriptedThread(ScriptedProcess &process, Status &error) : Thread(process, LLDB_INVALID_THREAD_ID), m_scripted_process(process) { if (!process.IsValid()) { error.SetErrorString("Invalid scripted process"); return; } process.CheckInterpreterAndScriptObject(); auto scripted_thread_interface = GetInterface(); if (!scripted_thread_interface) { error.SetErrorString("Failed to get scripted thread interface."); return; } llvm::Optional class_name = process.GetInterface().GetScriptedThreadPluginName(); if (!class_name || class_name->empty()) { error.SetErrorString("Failed to get scripted thread class name."); return; } ExecutionContext exe_ctx(process); StructuredData::GenericSP object_sp = scripted_thread_interface->CreatePluginObject( class_name->c_str(), exe_ctx, process.m_scripted_process_info.GetArgsSP()); if (!object_sp || !object_sp->IsValid()) { error.SetErrorString("Failed to create valid script object"); return; } m_script_object_sp = object_sp; SetID(scripted_thread_interface->GetThreadID()); } ScriptedThread::~ScriptedThread() { DestroyThread(); } const char *ScriptedThread::GetName() { CheckInterpreterAndScriptObject(); llvm::Optional thread_name = GetInterface()->GetName(); if (!thread_name) return nullptr; return ConstString(thread_name->c_str()).AsCString(); } const char *ScriptedThread::GetQueueName() { CheckInterpreterAndScriptObject(); llvm::Optional queue_name = GetInterface()->GetQueue(); if (!queue_name) return nullptr; return ConstString(queue_name->c_str()).AsCString(); } void ScriptedThread::WillResume(StateType resume_state) {} void ScriptedThread::ClearStackFrames() { Thread::ClearStackFrames(); } RegisterContextSP ScriptedThread::GetRegisterContext() { if (!m_reg_context_sp) m_reg_context_sp = CreateRegisterContextForFrame(nullptr); return m_reg_context_sp; } RegisterContextSP ScriptedThread::CreateRegisterContextForFrame(StackFrame *frame) { const uint32_t concrete_frame_idx = frame ? frame->GetConcreteFrameIndex() : 0; if (concrete_frame_idx) return GetUnwinder().CreateRegisterContextForFrame(frame); lldb::RegisterContextSP reg_ctx_sp; Status error; llvm::Optional reg_data = GetInterface()->GetRegisterContext(); if (!reg_data) return GetInterface()->ErrorWithMessage( LLVM_PRETTY_FUNCTION, "Failed to get scripted thread registers data.", error, LIBLLDB_LOG_THREAD); DataBufferSP data_sp( std::make_shared(reg_data->c_str(), reg_data->size())); if (!data_sp->GetByteSize()) return GetInterface()->ErrorWithMessage( LLVM_PRETTY_FUNCTION, "Failed to copy raw registers data.", error, LIBLLDB_LOG_THREAD); std::shared_ptr reg_ctx_memory = std::make_shared( *this, 0, *GetDynamicRegisterInfo(), LLDB_INVALID_ADDRESS); if (!reg_ctx_memory) return GetInterface()->ErrorWithMessage( LLVM_PRETTY_FUNCTION, "Failed to create a register context.", error, LIBLLDB_LOG_THREAD); reg_ctx_memory->SetAllRegisterData(data_sp); m_reg_context_sp = reg_ctx_memory; return m_reg_context_sp; } bool ScriptedThread::CalculateStopInfo() { StructuredData::DictionarySP dict_sp = GetInterface()->GetStopReason(); Status error; lldb::StopInfoSP stop_info_sp; lldb::StopReason stop_reason_type; if (!dict_sp->GetValueForKeyAsInteger("type", stop_reason_type)) return GetInterface()->ErrorWithMessage( LLVM_PRETTY_FUNCTION, "Couldn't find value for key 'type' in stop reason dictionary.", error, LIBLLDB_LOG_THREAD); StructuredData::Dictionary *data_dict; if (!dict_sp->GetValueForKeyAsDictionary("data", data_dict)) return GetInterface()->ErrorWithMessage( LLVM_PRETTY_FUNCTION, "Couldn't find value for key 'type' in stop reason dictionary.", error, LIBLLDB_LOG_THREAD); switch (stop_reason_type) { case lldb::eStopReasonNone: break; case lldb::eStopReasonBreakpoint: { lldb::break_id_t break_id; data_dict->GetValueForKeyAsInteger("break_id", break_id, LLDB_INVALID_BREAK_ID); stop_info_sp = StopInfo::CreateStopReasonWithBreakpointSiteID(*this, break_id); } break; case lldb::eStopReasonSignal: { int signal; llvm::StringRef description; data_dict->GetValueForKeyAsInteger("signal", signal, LLDB_INVALID_SIGNAL_NUMBER); data_dict->GetValueForKeyAsString("desc", description); stop_info_sp = StopInfo::CreateStopReasonWithSignal(*this, signal, description.data()); } break; default: return GetInterface()->ErrorWithMessage( LLVM_PRETTY_FUNCTION, llvm::Twine("Unsupported stop reason type (" + llvm::Twine(stop_reason_type) + llvm::Twine(").")) .str(), error, LIBLLDB_LOG_THREAD); } SetStopInfo(stop_info_sp); return true; } void ScriptedThread::RefreshStateAfterStop() { GetRegisterContext()->InvalidateIfNeeded(/*force=*/false); } lldb::ScriptedThreadInterfaceSP ScriptedThread::GetInterface() const { return m_scripted_process.GetInterface().GetScriptedThreadInterface(); } std::shared_ptr ScriptedThread::GetDynamicRegisterInfo() { CheckInterpreterAndScriptObject(); if (!m_register_info_sp) { StructuredData::DictionarySP reg_info = GetInterface()->GetRegisterInfo(); if (!reg_info) return nullptr; m_register_info_sp = std::make_shared( *reg_info, m_scripted_process.GetTarget().GetArchitecture()); assert(m_register_info_sp->GetNumRegisters() > 0); assert(m_register_info_sp->GetNumRegisterSets() > 0); } return m_register_info_sp; }