Med Ismail Bennani 59d8dd79e1 [lldb/Plugins] Add support for ScriptedThread in ScriptedProcess
This patch introduces the `ScriptedThread` class with its python
interface.

When used with `ScriptedProcess`, `ScriptedThreaad` can provide various
information such as the thread state, stop reason or even its register
context.

This can be used to reconstruct the program stack frames using lldb's unwinder.

rdar://74503836

Differential Revision: https://reviews.llvm.org/D107585

Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
2021-10-08 14:54:07 +02:00

211 lines
6.6 KiB
C++

//===-- 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 <memory>
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<std::string> 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.GetDictionarySP());
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());
llvm::Optional<std::string> reg_data =
scripted_thread_interface->GetRegisterContext();
if (!reg_data) {
error.SetErrorString("Failed to get scripted thread registers data.");
return;
}
DataBufferSP data_sp(
std::make_shared<DataBufferHeap>(reg_data->c_str(), reg_data->size()));
if (!data_sp->GetByteSize()) {
error.SetErrorString("Failed to copy raw registers data.");
return;
}
std::shared_ptr<RegisterContextMemory> reg_ctx_memory =
std::make_shared<RegisterContextMemory>(
*this, 0, *GetDynamicRegisterInfo(), LLDB_INVALID_ADDRESS);
if (!reg_ctx_memory) {
error.SetErrorString("Failed to create a register context.");
return;
}
reg_ctx_memory->SetAllRegisterData(data_sp);
m_reg_context_sp = reg_ctx_memory;
}
ScriptedThread::~ScriptedThread() { DestroyThread(); }
const char *ScriptedThread::GetName() {
CheckInterpreterAndScriptObject();
llvm::Optional<std::string> thread_name = GetInterface()->GetName();
if (!thread_name)
return nullptr;
return ConstString(thread_name->c_str()).AsCString();
}
const char *ScriptedThread::GetQueueName() {
CheckInterpreterAndScriptObject();
llvm::Optional<std::string> 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 = std::make_shared<RegisterContextThreadMemory>(
*this, LLDB_INVALID_ADDRESS);
GetInterface()->GetRegisterContext();
}
return m_reg_context_sp;
}
RegisterContextSP
ScriptedThread::CreateRegisterContextForFrame(StackFrame *frame) {
uint32_t concrete_frame_idx = frame ? frame->GetConcreteFrameIndex() : 0;
if (concrete_frame_idx == 0)
return GetRegisterContext();
return GetUnwinder().CreateRegisterContextForFrame(frame);
}
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<bool>(
__PRETTY_FUNCTION__,
"Couldn't find value for key 'type' in stop reason dictionary.", error);
StructuredData::Dictionary *data_dict;
if (!dict_sp->GetValueForKeyAsDictionary("data", data_dict))
return GetInterface()->ErrorWithMessage<bool>(
__PRETTY_FUNCTION__,
"Couldn't find value for key 'type' in stop reason dictionary.", error);
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<bool>(
__PRETTY_FUNCTION__,
llvm::Twine("Unsupported stop reason type (" +
llvm::Twine(stop_reason_type) + llvm::Twine(")."))
.str(),
error);
}
SetStopInfo(stop_info_sp);
return true;
}
void ScriptedThread::RefreshStateAfterStop() {
// TODO: Implement
if (m_reg_context_sp)
m_reg_context_sp->InvalidateAllRegisters();
}
lldb::ScriptedThreadInterfaceSP ScriptedThread::GetInterface() const {
return m_scripted_process.GetInterface().GetScriptedThreadInterface();
}
std::shared_ptr<DynamicRegisterInfo> 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<DynamicRegisterInfo>(
*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;
}