Files
llvm-project/lldb/source/Target/SyntheticFrameProvider.cpp
Med Ismail Bennani d6a73d08d5 [lldb] Add priority support to synthetic frame providers (#172848)
This patch adds `get_priority()` support to synthetic frame providers to
enable priority-based selection when multiple providers match a thread.
This is the first step toward supporting frame provider chaining for
visualizing coroutines, Swift async tasks, and et al.

Priority ordering follows Unix nice convention where lower numbers
indicate higher priority (0 = highest). Providers without explicit
priority return `std::nullopt`, which maps to UINT32_MAX (lowest
priority), ensuring backward compatibility with existing providers.

The implementation adds `GetPriority()` as a virtual method to
`SyntheticFrameProvider` base class, implements it through the scripting
interface hierarchy (`ScriptedFrameProviderInterface` and
`ScriptedFrameProviderPythonInterface`), and updates
`Thread::GetStackFrameList()` to sort applicable providers by priority
before attempting to load them.

Python frame providers can now specify priority:

```python
@staticmethod
def get_priority():
   return 10  # Or return None for default priority.
```

Signed-off-by: Med Ismail Bennani <ismail@bennani.ma>
2025-12-18 23:20:38 +01:00

136 lines
4.7 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 "lldb/Target/SyntheticFrameProvider.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h"
#include "lldb/Target/Thread.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/Status.h"
#include "lldb/Utility/Stream.h"
using namespace lldb;
using namespace lldb_private;
SyntheticFrameProvider::SyntheticFrameProvider(StackFrameListSP input_frames)
: m_input_frames(std::move(input_frames)) {}
SyntheticFrameProvider::~SyntheticFrameProvider() = default;
void ScriptedFrameProviderDescriptor::Dump(Stream *s) const {
if (!s)
return;
s->Format(" ID: {0:x}\n", GetID());
s->Printf(" Name: %s\n", GetName().str().c_str());
std::string description = GetDescription();
if (!description.empty())
s->Printf(" Description: %s\n", description.c_str());
// Show priority information.
std::optional<uint32_t> priority = GetPriority();
if (priority.has_value())
s->Printf(" Priority: %u\n", *priority);
else
s->PutCString(" Priority: Default (no priority specified)\n");
// Show thread filter information.
if (thread_specs.empty()) {
s->PutCString(" Thread Filter: (applies to all threads)\n");
} else {
s->Printf(" Thread Filter: %zu specification(s)\n", thread_specs.size());
for (size_t i = 0; i < thread_specs.size(); ++i) {
const ThreadSpec &spec = thread_specs[i];
s->Printf(" [%zu] ", i);
spec.GetDescription(s, lldb::eDescriptionLevelVerbose);
s->PutChar('\n');
}
}
}
uint32_t ScriptedFrameProviderDescriptor::GetID() const {
if (!scripted_metadata_sp)
return 0;
return scripted_metadata_sp->GetID();
}
std::string ScriptedFrameProviderDescriptor::GetDescription() const {
// If we have an interface, call get_description() to fetch it.
if (interface_sp && scripted_metadata_sp)
return interface_sp->GetDescription(scripted_metadata_sp->GetClassName());
return {};
}
std::optional<uint32_t> ScriptedFrameProviderDescriptor::GetPriority() const {
// If we have an interface, call get_priority() to fetch it.
if (interface_sp && scripted_metadata_sp)
return interface_sp->GetPriority(scripted_metadata_sp->GetClassName());
return std::nullopt;
}
llvm::Expected<SyntheticFrameProviderSP> SyntheticFrameProvider::CreateInstance(
StackFrameListSP input_frames,
const ScriptedFrameProviderDescriptor &descriptor) {
if (!input_frames)
return llvm::createStringError(
"cannot create synthetic frame provider: invalid input frames");
// Iterate through all registered ScriptedFrameProvider plugins.
ScriptedFrameProviderCreateInstance create_callback = nullptr;
for (uint32_t idx = 0;
(create_callback =
PluginManager::GetScriptedFrameProviderCreateCallbackAtIndex(
idx)) != nullptr;
++idx) {
auto provider_or_err = create_callback(input_frames, descriptor);
if (!provider_or_err) {
LLDB_LOG_ERROR(GetLog(LLDBLog::Target), provider_or_err.takeError(),
"Failed to create synthetic frame provider: {0}");
continue;
}
if (auto frame_provider_up = std::move(*provider_or_err))
return std::move(frame_provider_up);
}
return llvm::createStringError(
"cannot create synthetic frame provider: no suitable plugin found");
}
llvm::Expected<SyntheticFrameProviderSP> SyntheticFrameProvider::CreateInstance(
StackFrameListSP input_frames, llvm::StringRef plugin_name,
const std::vector<ThreadSpec> &thread_specs) {
if (!input_frames)
return llvm::createStringError(
"cannot create synthetic frame provider: invalid input frames");
// Look up the specific C++ plugin by name.
SyntheticFrameProviderCreateInstance create_callback =
PluginManager::GetSyntheticFrameProviderCreateCallbackForPluginName(
plugin_name);
if (!create_callback)
return llvm::createStringError(
"cannot create synthetic frame provider: C++ plugin '%s' not found",
plugin_name.str().c_str());
auto provider_or_err = create_callback(input_frames, thread_specs);
if (!provider_or_err)
return provider_or_err.takeError();
if (auto frame_provider_sp = std::move(*provider_or_err))
return std::move(frame_provider_sp);
return llvm::createStringError(
"cannot create synthetic frame provider: C++ plugin '%s' returned null",
plugin_name.str().c_str());
}