Skip to content

Commit 3631986

Browse files
medismailbenvinay-deshmukh
authored andcommitted
[lldb/Target] Add SyntheticFrameProvider class (llvm#166664)
This patch introduces a new way to reconstruct the thread stackframe list. New `SyntheticFrameProvider` classes can lazy fetch a StackFrame at index using a provided StackFrameList. In can either be the real unwinder StackFrameList or we could also chain SyntheticFrameProviders to each others. This is the foundation work to implement ScriptedFrameProviders, which will come in a follow-up patch. Signed-off-by: Med Ismail Bennani <[email protected]> Signed-off-by: Med Ismail Bennani <[email protected]>
1 parent 6048be9 commit 3631986

File tree

7 files changed

+342
-0
lines changed

7 files changed

+342
-0
lines changed

lldb/include/lldb/Core/PluginManager.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,24 @@ class PluginManager {
356356
GetScriptInterpreterForLanguage(lldb::ScriptLanguage script_lang,
357357
Debugger &debugger);
358358

359+
// SyntheticFrameProvider
360+
static bool
361+
RegisterPlugin(llvm::StringRef name, llvm::StringRef description,
362+
SyntheticFrameProviderCreateInstance create_native_callback,
363+
ScriptedFrameProviderCreateInstance create_scripted_callback);
364+
365+
static bool
366+
UnregisterPlugin(SyntheticFrameProviderCreateInstance create_callback);
367+
368+
static bool
369+
UnregisterPlugin(ScriptedFrameProviderCreateInstance create_callback);
370+
371+
static SyntheticFrameProviderCreateInstance
372+
GetSyntheticFrameProviderCreateCallbackForPluginName(llvm::StringRef name);
373+
374+
static ScriptedFrameProviderCreateInstance
375+
GetScriptedFrameProviderCreateCallbackAtIndex(uint32_t idx);
376+
359377
// StructuredDataPlugin
360378

361379
/// Register a StructuredDataPlugin class along with optional
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLDB_TARGET_SYNTHETICFRAMEPROVIDER_H
10+
#define LLDB_TARGET_SYNTHETICFRAMEPROVIDER_H
11+
12+
#include "lldb/Core/PluginInterface.h"
13+
#include "lldb/Target/StackFrameList.h"
14+
#include "lldb/Target/ThreadSpec.h"
15+
#include "lldb/Utility/ScriptedMetadata.h"
16+
#include "lldb/Utility/Status.h"
17+
#include "lldb/lldb-forward.h"
18+
#include "llvm/Support/Error.h"
19+
20+
#include <optional>
21+
#include <vector>
22+
23+
namespace lldb_private {
24+
25+
/// This struct contains the metadata needed to instantiate a frame provider
26+
/// and optional filters to control which threads it applies to.
27+
struct SyntheticFrameProviderDescriptor {
28+
/// Metadata for instantiating the provider (e.g. script class name and args).
29+
lldb::ScriptedMetadataSP scripted_metadata_sp;
30+
31+
/// Optional list of thread specifications to which this provider applies.
32+
/// If empty, the provider applies to all threads. A thread matches if it
33+
/// satisfies ANY of the specs in this vector (OR logic).
34+
std::vector<ThreadSpec> thread_specs;
35+
36+
SyntheticFrameProviderDescriptor() = default;
37+
38+
SyntheticFrameProviderDescriptor(lldb::ScriptedMetadataSP metadata_sp)
39+
: scripted_metadata_sp(metadata_sp) {}
40+
41+
SyntheticFrameProviderDescriptor(lldb::ScriptedMetadataSP metadata_sp,
42+
const std::vector<ThreadSpec> &specs)
43+
: scripted_metadata_sp(metadata_sp), thread_specs(specs) {}
44+
45+
/// Get the name of this descriptor (the scripted class name).
46+
llvm::StringRef GetName() const {
47+
return scripted_metadata_sp ? scripted_metadata_sp->GetClassName() : "";
48+
}
49+
50+
/// Check if this descriptor applies to the given thread.
51+
bool AppliesToThread(Thread &thread) const {
52+
// If no thread specs specified, applies to all threads.
53+
if (thread_specs.empty())
54+
return true;
55+
56+
// Check if the thread matches any of the specs (OR logic).
57+
for (const auto &spec : thread_specs) {
58+
if (spec.ThreadPassesBasicTests(thread))
59+
return true;
60+
}
61+
return false;
62+
}
63+
64+
/// Check if this descriptor has valid metadata for script-based providers.
65+
bool IsValid() const { return scripted_metadata_sp != nullptr; }
66+
67+
void Dump(Stream *s) const;
68+
};
69+
70+
/// Base class for all synthetic frame providers.
71+
///
72+
/// Synthetic frame providers allow modifying or replacing the stack frames
73+
/// shown for a thread. This is useful for:
74+
/// - Providing frames for custom calling conventions or languages.
75+
/// - Reconstructing missing frames from crash dumps or core files.
76+
/// - Adding diagnostic or synthetic frames for debugging.
77+
/// - Visualizing state machines or async execution contexts.
78+
class SyntheticFrameProvider : public PluginInterface {
79+
public:
80+
/// Try to create a SyntheticFrameProvider instance for the given input
81+
/// frames and descriptor.
82+
///
83+
/// This method iterates through all registered SyntheticFrameProvider
84+
/// plugins and returns the first one that can handle the given descriptor.
85+
///
86+
/// \param[in] input_frames
87+
/// The input stack frame list that this provider will transform.
88+
/// This could be real unwound frames or output from another provider.
89+
///
90+
/// \param[in] descriptor
91+
/// The descriptor containing metadata for the provider.
92+
///
93+
/// \return
94+
/// A shared pointer to a SyntheticFrameProvider if one could be created,
95+
/// otherwise an \a llvm::Error.
96+
static llvm::Expected<lldb::SyntheticFrameProviderSP>
97+
CreateInstance(lldb::StackFrameListSP input_frames,
98+
const SyntheticFrameProviderDescriptor &descriptor);
99+
100+
/// Try to create a SyntheticFrameProvider instance for the given input
101+
/// frames using a specific C++ plugin.
102+
///
103+
/// This method directly invokes a specific SyntheticFrameProvider plugin
104+
/// by name, bypassing the descriptor-based plugin iteration. This is useful
105+
/// for C++ plugins that don't require scripted metadata.
106+
///
107+
/// \param[in] input_frames
108+
/// The input stack frame list that this provider will transform.
109+
/// This could be real unwound frames or output from another provider.
110+
///
111+
/// \param[in] plugin_name
112+
/// The name of the plugin to use for creating the provider.
113+
///
114+
/// \param[in] thread_specs
115+
/// Optional list of thread specifications to which this provider applies.
116+
/// If empty, the provider applies to all threads.
117+
///
118+
/// \return
119+
/// A shared pointer to a SyntheticFrameProvider if one could be created,
120+
/// otherwise an \a llvm::Error.
121+
static llvm::Expected<lldb::SyntheticFrameProviderSP>
122+
CreateInstance(lldb::StackFrameListSP input_frames,
123+
llvm::StringRef plugin_name,
124+
const std::vector<ThreadSpec> &thread_specs = {});
125+
126+
~SyntheticFrameProvider() override;
127+
128+
/// Get a single stack frame at the specified index.
129+
///
130+
/// This method is called lazily - frames are only created when requested.
131+
/// The provider can access its input frames via GetInputFrames() if needed.
132+
///
133+
/// \param[in] idx
134+
/// The index of the frame to create.
135+
///
136+
/// \return
137+
/// An Expected containing the StackFrameSP if successful. Returns an
138+
/// error when the index is beyond the last frame to signal the end of
139+
/// the frame list.
140+
virtual llvm::Expected<lldb::StackFrameSP> GetFrameAtIndex(uint32_t idx) = 0;
141+
142+
/// Get the thread associated with this provider.
143+
Thread &GetThread() { return m_input_frames->GetThread(); }
144+
145+
/// Get the input frames that this provider transforms.
146+
lldb::StackFrameListSP GetInputFrames() const { return m_input_frames; }
147+
148+
protected:
149+
SyntheticFrameProvider(lldb::StackFrameListSP input_frames);
150+
151+
lldb::StackFrameListSP m_input_frames;
152+
};
153+
154+
} // namespace lldb_private
155+
156+
#endif // LLDB_TARGET_SYNTHETICFRAMEPROVIDER_H

lldb/include/lldb/lldb-forward.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ class SymbolVendor;
235235
class Symtab;
236236
class SyntheticChildren;
237237
class SyntheticChildrenFrontEnd;
238+
class SyntheticFrameProvider;
238239
class SystemRuntime;
239240
class Progress;
240241
class Target;
@@ -411,6 +412,8 @@ typedef std::shared_ptr<lldb_private::ScriptSummaryFormat>
411412
typedef std::shared_ptr<lldb_private::ScriptInterpreter> ScriptInterpreterSP;
412413
typedef std::shared_ptr<lldb_private::ScriptedFrameInterface>
413414
ScriptedFrameInterfaceSP;
415+
typedef std::shared_ptr<lldb_private::SyntheticFrameProvider>
416+
SyntheticFrameProviderSP;
414417
typedef std::shared_ptr<lldb_private::ScriptedMetadata> ScriptedMetadataSP;
415418
typedef std::unique_ptr<lldb_private::ScriptedPlatformInterface>
416419
ScriptedPlatformInterfaceUP;

lldb/include/lldb/lldb-private-interfaces.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class Value;
2525

2626
namespace lldb_private {
2727
class ScriptedInterfaceUsages;
28+
struct SyntheticFrameProviderDescriptor;
2829
typedef lldb::ABISP (*ABICreateInstance)(lldb::ProcessSP process_sp,
2930
const ArchSpec &arch);
3031
typedef std::unique_ptr<Architecture> (*ArchitectureCreateInstance)(
@@ -86,6 +87,14 @@ typedef lldb::RegisterTypeBuilderSP (*RegisterTypeBuilderCreateInstance)(
8687
Target &target);
8788
typedef lldb::ScriptInterpreterSP (*ScriptInterpreterCreateInstance)(
8889
Debugger &debugger);
90+
typedef llvm::Expected<lldb::SyntheticFrameProviderSP> (
91+
*ScriptedFrameProviderCreateInstance)(
92+
lldb::StackFrameListSP input_frames,
93+
const lldb_private::SyntheticFrameProviderDescriptor &descriptor);
94+
typedef llvm::Expected<lldb::SyntheticFrameProviderSP> (
95+
*SyntheticFrameProviderCreateInstance)(
96+
lldb::StackFrameListSP input_frames,
97+
const std::vector<lldb_private::ThreadSpec> &thread_specs);
8998
typedef SymbolFile *(*SymbolFileCreateInstance)(lldb::ObjectFileSP objfile_sp);
9099
typedef SymbolVendor *(*SymbolVendorCreateInstance)(
91100
const lldb::ModuleSP &module_sp,

lldb/source/Core/PluginManager.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1300,6 +1300,61 @@ PluginManager::GetScriptInterpreterForLanguage(lldb::ScriptLanguage script_lang,
13001300
return none_instance(debugger);
13011301
}
13021302

1303+
#pragma mark SyntheticFrameProvider
1304+
1305+
typedef PluginInstance<SyntheticFrameProviderCreateInstance>
1306+
SyntheticFrameProviderInstance;
1307+
typedef PluginInstance<ScriptedFrameProviderCreateInstance>
1308+
ScriptedFrameProviderInstance;
1309+
typedef PluginInstances<SyntheticFrameProviderInstance>
1310+
SyntheticFrameProviderInstances;
1311+
typedef PluginInstances<ScriptedFrameProviderInstance>
1312+
ScriptedFrameProviderInstances;
1313+
1314+
static SyntheticFrameProviderInstances &GetSyntheticFrameProviderInstances() {
1315+
static SyntheticFrameProviderInstances g_instances;
1316+
return g_instances;
1317+
}
1318+
1319+
static ScriptedFrameProviderInstances &GetScriptedFrameProviderInstances() {
1320+
static ScriptedFrameProviderInstances g_instances;
1321+
return g_instances;
1322+
}
1323+
1324+
bool PluginManager::RegisterPlugin(
1325+
llvm::StringRef name, llvm::StringRef description,
1326+
SyntheticFrameProviderCreateInstance create_native_callback,
1327+
ScriptedFrameProviderCreateInstance create_scripted_callback) {
1328+
if (create_native_callback)
1329+
return GetSyntheticFrameProviderInstances().RegisterPlugin(
1330+
name, description, create_native_callback);
1331+
else if (create_scripted_callback)
1332+
return GetScriptedFrameProviderInstances().RegisterPlugin(
1333+
name, description, create_scripted_callback);
1334+
return false;
1335+
}
1336+
1337+
bool PluginManager::UnregisterPlugin(
1338+
SyntheticFrameProviderCreateInstance create_callback) {
1339+
return GetSyntheticFrameProviderInstances().UnregisterPlugin(create_callback);
1340+
}
1341+
1342+
bool PluginManager::UnregisterPlugin(
1343+
ScriptedFrameProviderCreateInstance create_callback) {
1344+
return GetScriptedFrameProviderInstances().UnregisterPlugin(create_callback);
1345+
}
1346+
1347+
SyntheticFrameProviderCreateInstance
1348+
PluginManager::GetSyntheticFrameProviderCreateCallbackForPluginName(
1349+
llvm::StringRef name) {
1350+
return GetSyntheticFrameProviderInstances().GetCallbackForName(name);
1351+
}
1352+
1353+
ScriptedFrameProviderCreateInstance
1354+
PluginManager::GetScriptedFrameProviderCreateCallbackAtIndex(uint32_t idx) {
1355+
return GetScriptedFrameProviderInstances().GetCallbackAtIndex(idx);
1356+
}
1357+
13031358
#pragma mark StructuredDataPlugin
13041359

13051360
struct StructuredDataPluginInstance

lldb/source/Target/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ add_lldb_library(lldbTarget
3838
RegisterNumber.cpp
3939
RemoteAwarePlatform.cpp
4040
ScriptedThreadPlan.cpp
41+
SyntheticFrameProvider.cpp
4142
SectionLoadHistory.cpp
4243
SectionLoadList.cpp
4344
StackFrame.cpp
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "lldb/Target/SyntheticFrameProvider.h"
10+
#include "lldb/Core/PluginManager.h"
11+
#include "lldb/Target/Thread.h"
12+
#include "lldb/Utility/LLDBLog.h"
13+
#include "lldb/Utility/Log.h"
14+
#include "lldb/Utility/Status.h"
15+
16+
using namespace lldb;
17+
using namespace lldb_private;
18+
19+
SyntheticFrameProvider::SyntheticFrameProvider(StackFrameListSP input_frames)
20+
: m_input_frames(std::move(input_frames)) {}
21+
22+
SyntheticFrameProvider::~SyntheticFrameProvider() = default;
23+
24+
void SyntheticFrameProviderDescriptor::Dump(Stream *s) const {
25+
if (!s)
26+
return;
27+
28+
s->Printf(" Name: %s\n", GetName().str().c_str());
29+
30+
// Show thread filter information.
31+
if (thread_specs.empty()) {
32+
s->PutCString(" Thread Filter: (applies to all threads)\n");
33+
} else {
34+
s->Printf(" Thread Filter: %zu specification(s)\n", thread_specs.size());
35+
for (size_t i = 0; i < thread_specs.size(); ++i) {
36+
const ThreadSpec &spec = thread_specs[i];
37+
s->Printf(" [%zu] ", i);
38+
spec.GetDescription(s, lldb::eDescriptionLevelVerbose);
39+
s->PutChar('\n');
40+
}
41+
}
42+
}
43+
44+
llvm::Expected<SyntheticFrameProviderSP> SyntheticFrameProvider::CreateInstance(
45+
StackFrameListSP input_frames,
46+
const SyntheticFrameProviderDescriptor &descriptor) {
47+
if (!input_frames)
48+
return llvm::createStringError(
49+
"cannot create synthetic frame provider: invalid input frames");
50+
51+
// Iterate through all registered ScriptedFrameProvider plugins.
52+
ScriptedFrameProviderCreateInstance create_callback = nullptr;
53+
for (uint32_t idx = 0;
54+
(create_callback =
55+
PluginManager::GetScriptedFrameProviderCreateCallbackAtIndex(
56+
idx)) != nullptr;
57+
++idx) {
58+
auto provider_or_err = create_callback(input_frames, descriptor);
59+
if (!provider_or_err) {
60+
LLDB_LOG_ERROR(GetLog(LLDBLog::Target), provider_or_err.takeError(),
61+
"Failed to create synthetic frame provider: {0}");
62+
continue;
63+
}
64+
65+
if (auto frame_provider_up = std::move(*provider_or_err))
66+
return std::move(frame_provider_up);
67+
}
68+
69+
return llvm::createStringError(
70+
"cannot create synthetic frame provider: no suitable plugin found");
71+
}
72+
73+
llvm::Expected<SyntheticFrameProviderSP> SyntheticFrameProvider::CreateInstance(
74+
StackFrameListSP input_frames, llvm::StringRef plugin_name,
75+
const std::vector<ThreadSpec> &thread_specs) {
76+
if (!input_frames)
77+
return llvm::createStringError(
78+
"cannot create synthetic frame provider: invalid input frames");
79+
80+
// Look up the specific C++ plugin by name.
81+
SyntheticFrameProviderCreateInstance create_callback =
82+
PluginManager::GetSyntheticFrameProviderCreateCallbackForPluginName(
83+
plugin_name);
84+
85+
if (!create_callback)
86+
return llvm::createStringError(
87+
"cannot create synthetic frame provider: C++ plugin '%s' not found",
88+
plugin_name.str().c_str());
89+
90+
auto provider_or_err = create_callback(input_frames, thread_specs);
91+
if (!provider_or_err)
92+
return provider_or_err.takeError();
93+
94+
if (auto frame_provider_sp = std::move(*provider_or_err))
95+
return std::move(frame_provider_sp);
96+
97+
return llvm::createStringError(
98+
"cannot create synthetic frame provider: C++ plugin '%s' returned null",
99+
plugin_name.str().c_str());
100+
}

0 commit comments

Comments
 (0)