Files
llvm/lldb/source/Plugins/SyntheticFrameProvider/ScriptedFrameProvider/ScriptedFrameProvider.cpp
Med Ismail Bennani c50802cbee Reland "[lldb] Introduce ScriptedFrameProvider for real threads (#161870)" (#170236)
This patch re-lands #161870 with fixes to the previous test failures.

rdar://161834688

Signed-off-by: Med Ismail Bennani <ismail@bennani.ma>
2025-12-02 18:59:40 +00:00

222 lines
8.1 KiB
C++

//===----------------------------------------------------------------------===//
//
// 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 "ScriptedFrameProvider.h"
#include "Plugins/Process/scripted/ScriptedFrame.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h"
#include "lldb/Interpreter/ScriptInterpreter.h"
#include "lldb/Target/BorrowedStackFrame.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/Thread.h"
#include "lldb/Utility/ScriptedMetadata.h"
#include "lldb/Utility/Status.h"
#include "llvm/Support/Error.h"
#include <cstdint>
using namespace lldb;
using namespace lldb_private;
void ScriptedFrameProvider::Initialize() {
PluginManager::RegisterPlugin(GetPluginNameStatic(),
"Provides synthetic frames via scripting",
nullptr, ScriptedFrameProvider::CreateInstance);
}
void ScriptedFrameProvider::Terminate() {
PluginManager::UnregisterPlugin(ScriptedFrameProvider::CreateInstance);
}
llvm::Expected<lldb::SyntheticFrameProviderSP>
ScriptedFrameProvider::CreateInstance(
lldb::StackFrameListSP input_frames,
const ScriptedFrameProviderDescriptor &descriptor) {
if (!input_frames)
return llvm::createStringError(
"failed to create scripted frame provider: invalid input frames");
Thread &thread = input_frames->GetThread();
ProcessSP process_sp = thread.GetProcess();
if (!process_sp)
return nullptr;
if (!descriptor.IsValid())
return llvm::createStringError(
"failed to create scripted frame provider: invalid scripted metadata");
if (!descriptor.AppliesToThread(thread))
return nullptr;
ScriptInterpreter *script_interp =
process_sp->GetTarget().GetDebugger().GetScriptInterpreter();
if (!script_interp)
return llvm::createStringError("cannot create scripted frame provider: No "
"script interpreter installed");
ScriptedFrameProviderInterfaceSP interface_sp =
script_interp->CreateScriptedFrameProviderInterface();
if (!interface_sp)
return llvm::createStringError(
"cannot create scripted frame provider: script interpreter couldn't "
"create Scripted Frame Provider Interface");
const ScriptedMetadataSP scripted_metadata = descriptor.scripted_metadata_sp;
// If we shouldn't attach a frame provider to this thread, just exit early.
if (!interface_sp->AppliesToThread(scripted_metadata->GetClassName(),
thread.shared_from_this()))
return nullptr;
auto obj_or_err = interface_sp->CreatePluginObject(
scripted_metadata->GetClassName(), input_frames,
scripted_metadata->GetArgsSP());
if (!obj_or_err)
return obj_or_err.takeError();
StructuredData::ObjectSP object_sp = *obj_or_err;
if (!object_sp || !object_sp->IsValid())
return llvm::createStringError(
"cannot create scripted frame provider: failed to create valid scripted"
"frame provider object");
return std::make_shared<ScriptedFrameProvider>(input_frames, interface_sp,
descriptor);
}
ScriptedFrameProvider::ScriptedFrameProvider(
StackFrameListSP input_frames,
lldb::ScriptedFrameProviderInterfaceSP interface_sp,
const ScriptedFrameProviderDescriptor &descriptor)
: SyntheticFrameProvider(input_frames), m_interface_sp(interface_sp),
m_descriptor(descriptor) {}
ScriptedFrameProvider::~ScriptedFrameProvider() = default;
std::string ScriptedFrameProvider::GetDescription() const {
if (!m_interface_sp)
return {};
return m_interface_sp->GetDescription(m_descriptor.GetName());
}
llvm::Expected<StackFrameSP>
ScriptedFrameProvider::GetFrameAtIndex(uint32_t idx) {
if (!m_interface_sp)
return llvm::createStringError(
"cannot get stack frame: scripted frame provider not initialized");
auto create_frame_from_dict =
[this](StructuredData::Dictionary *dict,
uint32_t index) -> llvm::Expected<StackFrameSP> {
lldb::addr_t pc;
if (!dict->GetValueForKeyAsInteger("pc", pc))
return llvm::createStringError(
"missing 'pc' key from scripted frame dictionary");
Address symbol_addr;
symbol_addr.SetLoadAddress(pc, &GetThread().GetProcess()->GetTarget());
const lldb::addr_t cfa = LLDB_INVALID_ADDRESS;
const bool cfa_is_valid = false;
const bool artificial = false;
const bool behaves_like_zeroth_frame = false;
SymbolContext sc;
symbol_addr.CalculateSymbolContext(&sc);
ThreadSP thread_sp = GetThread().shared_from_this();
return std::make_shared<StackFrame>(thread_sp, index, index, cfa,
cfa_is_valid, pc,
StackFrame::Kind::Synthetic, artificial,
behaves_like_zeroth_frame, &sc);
};
auto create_frame_from_script_object =
[this](
StructuredData::ObjectSP object_sp) -> llvm::Expected<StackFrameSP> {
Status error;
if (!object_sp || !object_sp->GetAsGeneric())
return llvm::createStringError("invalid script object");
ThreadSP thread_sp = GetThread().shared_from_this();
auto frame_or_error = ScriptedFrame::Create(thread_sp, nullptr, nullptr,
object_sp->GetAsGeneric());
if (!frame_or_error) {
ScriptedInterface::ErrorWithMessage<bool>(
LLVM_PRETTY_FUNCTION, toString(frame_or_error.takeError()), error);
return error.ToError();
}
return *frame_or_error;
};
StructuredData::ObjectSP obj_sp = m_interface_sp->GetFrameAtIndex(idx);
// None/null means no more frames or error.
if (!obj_sp || !obj_sp->IsValid())
return llvm::createStringError("invalid script object returned for frame " +
llvm::Twine(idx));
StackFrameSP synth_frame_sp = nullptr;
if (StructuredData::UnsignedInteger *int_obj =
obj_sp->GetAsUnsignedInteger()) {
uint32_t real_frame_index = int_obj->GetValue();
if (real_frame_index < m_input_frames->GetNumFrames()) {
StackFrameSP real_frame_sp =
m_input_frames->GetFrameAtIndex(real_frame_index);
synth_frame_sp =
(real_frame_index == idx)
? real_frame_sp
: std::make_shared<BorrowedStackFrame>(real_frame_sp, idx);
}
} else if (StructuredData::Dictionary *dict = obj_sp->GetAsDictionary()) {
// Check if it's a dictionary describing a frame.
auto frame_from_dict_or_err = create_frame_from_dict(dict, idx);
if (!frame_from_dict_or_err) {
return llvm::createStringError(llvm::Twine(
"couldn't create frame from dictionary at index " + llvm::Twine(idx) +
": " + toString(frame_from_dict_or_err.takeError())));
}
synth_frame_sp = *frame_from_dict_or_err;
} else if (obj_sp->GetAsGeneric()) {
// It's a ScriptedFrame object.
auto frame_from_script_obj_or_err = create_frame_from_script_object(obj_sp);
if (!frame_from_script_obj_or_err) {
return llvm::createStringError(
llvm::Twine("couldn't create frame from script object at index " +
llvm::Twine(idx) + ": " +
toString(frame_from_script_obj_or_err.takeError())));
}
synth_frame_sp = *frame_from_script_obj_or_err;
} else {
return llvm::createStringError(
llvm::Twine("invalid return type from get_frame_at_index at index " +
llvm::Twine(idx)));
}
if (!synth_frame_sp)
return llvm::createStringError(
llvm::Twine("failed to create frame at index " + llvm::Twine(idx)));
synth_frame_sp->SetFrameIndex(idx);
return synth_frame_sp;
}
namespace lldb_private {
void lldb_initialize_ScriptedFrameProvider() {
ScriptedFrameProvider::Initialize();
}
void lldb_terminate_ScriptedFrameProvider() {
ScriptedFrameProvider::Terminate();
}
} // namespace lldb_private