359 lines
12 KiB
C++
359 lines
12 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 "Plugins/Process/Utility/StopInfoMachException.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/LLDBLog.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.");
|
|
}
|
|
|
|
llvm::Expected<std::shared_ptr<ScriptedThread>>
|
|
ScriptedThread::Create(ScriptedProcess &process,
|
|
StructuredData::Generic *script_object) {
|
|
if (!process.IsValid())
|
|
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
|
"Invalid scripted process.");
|
|
|
|
process.CheckInterpreterAndScriptObject();
|
|
|
|
auto scripted_thread_interface =
|
|
process.GetInterface().CreateScriptedThreadInterface();
|
|
if (!scripted_thread_interface)
|
|
return llvm::createStringError(
|
|
llvm::inconvertibleErrorCode(),
|
|
"Failed to create scripted thread interface.");
|
|
|
|
llvm::StringRef thread_class_name;
|
|
if (!script_object) {
|
|
llvm::Optional<std::string> class_name =
|
|
process.GetInterface().GetScriptedThreadPluginName();
|
|
if (!class_name || class_name->empty())
|
|
return llvm::createStringError(
|
|
llvm::inconvertibleErrorCode(),
|
|
"Failed to get scripted thread class name.");
|
|
thread_class_name = *class_name;
|
|
}
|
|
|
|
ExecutionContext exe_ctx(process);
|
|
StructuredData::GenericSP owned_script_object_sp =
|
|
scripted_thread_interface->CreatePluginObject(
|
|
thread_class_name, exe_ctx,
|
|
process.m_scripted_process_info.GetArgsSP(), script_object);
|
|
|
|
if (!owned_script_object_sp)
|
|
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
|
"Failed to create script object.");
|
|
if (!owned_script_object_sp->IsValid())
|
|
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
|
"Created script object is invalid.");
|
|
|
|
lldb::tid_t tid = scripted_thread_interface->GetThreadID();
|
|
|
|
return std::make_shared<ScriptedThread>(process, scripted_thread_interface,
|
|
tid, owned_script_object_sp);
|
|
}
|
|
|
|
ScriptedThread::ScriptedThread(ScriptedProcess &process,
|
|
ScriptedThreadInterfaceSP interface_sp,
|
|
lldb::tid_t tid,
|
|
StructuredData::GenericSP script_object_sp)
|
|
: Thread(process, tid), m_scripted_process(process),
|
|
m_scripted_thread_interface_sp(interface_sp),
|
|
m_script_object_sp(script_object_sp) {}
|
|
|
|
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 = 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<std::string> reg_data = GetInterface()->GetRegisterContext();
|
|
if (!reg_data)
|
|
return ScriptedInterface::ErrorWithMessage<lldb::RegisterContextSP>(
|
|
LLVM_PRETTY_FUNCTION, "Failed to get scripted thread registers data.",
|
|
error, LLDBLog::Thread);
|
|
|
|
DataBufferSP data_sp(
|
|
std::make_shared<DataBufferHeap>(reg_data->c_str(), reg_data->size()));
|
|
|
|
if (!data_sp->GetByteSize())
|
|
return ScriptedInterface::ErrorWithMessage<lldb::RegisterContextSP>(
|
|
LLVM_PRETTY_FUNCTION, "Failed to copy raw registers data.", error,
|
|
LLDBLog::Thread);
|
|
|
|
std::shared_ptr<RegisterContextMemory> reg_ctx_memory =
|
|
std::make_shared<RegisterContextMemory>(
|
|
*this, 0, *GetDynamicRegisterInfo(), LLDB_INVALID_ADDRESS);
|
|
if (!reg_ctx_memory)
|
|
return ScriptedInterface::ErrorWithMessage<lldb::RegisterContextSP>(
|
|
LLVM_PRETTY_FUNCTION, "Failed to create a register context.", error,
|
|
LLDBLog::Thread);
|
|
|
|
reg_ctx_memory->SetAllRegisterData(data_sp);
|
|
m_reg_context_sp = reg_ctx_memory;
|
|
|
|
return m_reg_context_sp;
|
|
}
|
|
|
|
bool ScriptedThread::LoadArtificialStackFrames() {
|
|
StructuredData::ArraySP arr_sp = GetInterface()->GetStackFrames();
|
|
|
|
Status error;
|
|
if (!arr_sp)
|
|
return ScriptedInterface::ErrorWithMessage<bool>(
|
|
LLVM_PRETTY_FUNCTION, "Failed to get scripted thread stackframes.",
|
|
error, LLDBLog::Thread);
|
|
|
|
size_t arr_size = arr_sp->GetSize();
|
|
if (arr_size > std::numeric_limits<uint32_t>::max())
|
|
return ScriptedInterface::ErrorWithMessage<bool>(
|
|
LLVM_PRETTY_FUNCTION,
|
|
llvm::Twine(
|
|
"StackFrame array size (" + llvm::Twine(arr_size) +
|
|
llvm::Twine(
|
|
") is greater than maximum authorized for a StackFrameList."))
|
|
.str(),
|
|
error, LLDBLog::Thread);
|
|
|
|
StackFrameListSP frames = GetStackFrameList();
|
|
|
|
for (size_t idx = 0; idx < arr_size; idx++) {
|
|
StructuredData::Dictionary *dict;
|
|
if (!arr_sp->GetItemAtIndexAsDictionary(idx, dict) || !dict)
|
|
return ScriptedInterface::ErrorWithMessage<bool>(
|
|
LLVM_PRETTY_FUNCTION,
|
|
llvm::Twine(
|
|
"Couldn't get artificial stackframe dictionary at index (" +
|
|
llvm::Twine(idx) + llvm::Twine(") from stackframe array."))
|
|
.str(),
|
|
error, LLDBLog::Thread);
|
|
|
|
lldb::addr_t pc;
|
|
if (!dict->GetValueForKeyAsInteger("pc", pc))
|
|
return ScriptedInterface::ErrorWithMessage<bool>(
|
|
LLVM_PRETTY_FUNCTION,
|
|
"Couldn't find value for key 'pc' in stackframe dictionary.", error,
|
|
LLDBLog::Thread);
|
|
|
|
Address symbol_addr;
|
|
symbol_addr.SetLoadAddress(pc, &this->GetProcess()->GetTarget());
|
|
|
|
lldb::addr_t cfa = LLDB_INVALID_ADDRESS;
|
|
bool cfa_is_valid = false;
|
|
const bool behaves_like_zeroth_frame = false;
|
|
SymbolContext sc;
|
|
symbol_addr.CalculateSymbolContext(&sc);
|
|
|
|
StackFrameSP synth_frame_sp = std::make_shared<StackFrame>(
|
|
this->shared_from_this(), idx, idx, cfa, cfa_is_valid, pc,
|
|
StackFrame::Kind::Artificial, behaves_like_zeroth_frame, &sc);
|
|
|
|
if (!frames->SetFrameAtIndex(static_cast<uint32_t>(idx), synth_frame_sp))
|
|
return ScriptedInterface::ErrorWithMessage<bool>(
|
|
LLVM_PRETTY_FUNCTION,
|
|
llvm::Twine("Couldn't add frame (" + llvm::Twine(idx) +
|
|
llvm::Twine(") to ScriptedThread StackFrameList."))
|
|
.str(),
|
|
error, LLDBLog::Thread);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ScriptedThread::CalculateStopInfo() {
|
|
StructuredData::DictionarySP dict_sp = GetInterface()->GetStopReason();
|
|
|
|
Status error;
|
|
if (!dict_sp)
|
|
return ScriptedInterface::ErrorWithMessage<bool>(
|
|
LLVM_PRETTY_FUNCTION, "Failed to get scripted thread stop info.", error,
|
|
LLDBLog::Thread);
|
|
|
|
lldb::StopInfoSP stop_info_sp;
|
|
lldb::StopReason stop_reason_type;
|
|
|
|
if (!dict_sp->GetValueForKeyAsInteger("type", stop_reason_type))
|
|
return ScriptedInterface::ErrorWithMessage<bool>(
|
|
LLVM_PRETTY_FUNCTION,
|
|
"Couldn't find value for key 'type' in stop reason dictionary.", error,
|
|
LLDBLog::Thread);
|
|
|
|
StructuredData::Dictionary *data_dict;
|
|
if (!dict_sp->GetValueForKeyAsDictionary("data", data_dict))
|
|
return ScriptedInterface::ErrorWithMessage<bool>(
|
|
LLVM_PRETTY_FUNCTION,
|
|
"Couldn't find value for key 'data' in stop reason dictionary.", error,
|
|
LLDBLog::Thread);
|
|
|
|
switch (stop_reason_type) {
|
|
case lldb::eStopReasonNone:
|
|
return true;
|
|
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;
|
|
case lldb::eStopReasonException: {
|
|
#if defined(__APPLE__)
|
|
StructuredData::Dictionary *mach_exception;
|
|
if (data_dict->GetValueForKeyAsDictionary("mach_exception",
|
|
mach_exception)) {
|
|
llvm::StringRef value;
|
|
mach_exception->GetValueForKeyAsString("type", value);
|
|
auto exc_type =
|
|
StopInfoMachException::MachException::ExceptionCode(value.data());
|
|
|
|
if (!exc_type)
|
|
return false;
|
|
|
|
uint32_t exc_data_size = 0;
|
|
llvm::SmallVector<uint64_t, 3> raw_codes;
|
|
|
|
StructuredData::Array *exc_rawcodes;
|
|
mach_exception->GetValueForKeyAsArray("rawCodes", exc_rawcodes);
|
|
if (exc_rawcodes) {
|
|
auto fetch_data = [&raw_codes](StructuredData::Object *obj) {
|
|
if (!obj)
|
|
return false;
|
|
raw_codes.push_back(obj->GetIntegerValue());
|
|
return true;
|
|
};
|
|
|
|
exc_rawcodes->ForEach(fetch_data);
|
|
exc_data_size = raw_codes.size();
|
|
}
|
|
|
|
stop_info_sp = StopInfoMachException::CreateStopReasonWithMachException(
|
|
*this, *exc_type, exc_data_size,
|
|
exc_data_size >= 1 ? raw_codes[0] : 0,
|
|
exc_data_size >= 2 ? raw_codes[1] : 0,
|
|
exc_data_size >= 3 ? raw_codes[2] : 0);
|
|
|
|
break;
|
|
}
|
|
#endif
|
|
stop_info_sp =
|
|
StopInfo::CreateStopReasonWithException(*this, "EXC_BAD_ACCESS");
|
|
} break;
|
|
default:
|
|
return ScriptedInterface::ErrorWithMessage<bool>(
|
|
LLVM_PRETTY_FUNCTION,
|
|
llvm::Twine("Unsupported stop reason type (" +
|
|
llvm::Twine(stop_reason_type) + llvm::Twine(")."))
|
|
.str(),
|
|
error, LLDBLog::Thread);
|
|
}
|
|
|
|
if (!stop_info_sp)
|
|
return false;
|
|
|
|
SetStopInfo(stop_info_sp);
|
|
return true;
|
|
}
|
|
|
|
void ScriptedThread::RefreshStateAfterStop() {
|
|
GetRegisterContext()->InvalidateIfNeeded(/*force=*/false);
|
|
LoadArtificialStackFrames();
|
|
}
|
|
|
|
lldb::ScriptedThreadInterfaceSP ScriptedThread::GetInterface() const {
|
|
return m_scripted_thread_interface_sp;
|
|
}
|
|
|
|
std::shared_ptr<DynamicRegisterInfo> ScriptedThread::GetDynamicRegisterInfo() {
|
|
CheckInterpreterAndScriptObject();
|
|
|
|
if (!m_register_info_sp) {
|
|
StructuredData::DictionarySP reg_info = GetInterface()->GetRegisterInfo();
|
|
|
|
Status error;
|
|
if (!reg_info)
|
|
return ScriptedInterface::ErrorWithMessage<
|
|
std::shared_ptr<DynamicRegisterInfo>>(
|
|
LLVM_PRETTY_FUNCTION, "Failed to get scripted thread registers info.",
|
|
error, LLDBLog::Thread);
|
|
|
|
m_register_info_sp = std::make_shared<DynamicRegisterInfo>(
|
|
*reg_info, m_scripted_process.GetTarget().GetArchitecture());
|
|
}
|
|
|
|
return m_register_info_sp;
|
|
}
|
|
|
|
StructuredData::ObjectSP ScriptedThread::FetchThreadExtendedInfo() {
|
|
CheckInterpreterAndScriptObject();
|
|
|
|
Status error;
|
|
StructuredData::ArraySP extended_info_sp = GetInterface()->GetExtendedInfo();
|
|
|
|
if (!extended_info_sp || !extended_info_sp->GetSize())
|
|
return ScriptedInterface::ErrorWithMessage<StructuredData::ObjectSP>(
|
|
LLVM_PRETTY_FUNCTION, "No extended information found", error);
|
|
|
|
return extended_info_sp;
|
|
}
|