Skip to content

Commit 5ab9a0b

Browse files
committed
[lldb/Target] Add SyntheticFrameProvider class
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]>
1 parent d584d00 commit 5ab9a0b

File tree

7 files changed

+374
-0
lines changed

7 files changed

+374
-0
lines changed

lldb/include/lldb/Core/PluginManager.h

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

359+
// SyntheticFrameProvider (scripted)
360+
static bool
361+
RegisterPlugin(llvm::StringRef name, llvm::StringRef description,
362+
ScriptedFrameProviderCreateInstance create_callback);
363+
364+
static bool
365+
UnregisterPlugin(ScriptedFrameProviderCreateInstance create_callback);
366+
367+
static ScriptedFrameProviderCreateInstance
368+
GetScriptedFrameProviderCreateCallbackAtIndex(uint32_t idx);
369+
370+
static ScriptedFrameProviderCreateInstance
371+
GetScriptedFrameProviderCreateCallbackForPluginName(llvm::StringRef name);
372+
373+
// SyntheticFrameProvider (C++)
374+
static bool
375+
RegisterPlugin(llvm::StringRef name, llvm::StringRef description,
376+
SyntheticFrameProviderCreateInstance create_callback);
377+
378+
static bool
379+
UnregisterPlugin(SyntheticFrameProviderCreateInstance create_callback);
380+
381+
static SyntheticFrameProviderCreateInstance
382+
GetSyntheticFrameProviderCreateCallbackAtIndex(uint32_t idx);
383+
384+
static SyntheticFrameProviderCreateInstance
385+
GetSyntheticFrameProviderCreateCallbackForPluginName(llvm::StringRef name);
386+
359387
// StructuredDataPlugin
360388

361389
/// Register a StructuredDataPlugin class along with optional
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
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+
virtual ~SyntheticFrameProviderDescriptor() = default;
46+
47+
/// Get the name of this descriptor (the scripted class name).
48+
llvm::StringRef GetName() const {
49+
return scripted_metadata_sp ? scripted_metadata_sp->GetClassName() : "";
50+
}
51+
52+
/// Check if this descriptor applies to the given thread.
53+
bool AppliesToThread(Thread &thread) const {
54+
// If no thread specs specified, applies to all threads
55+
if (thread_specs.empty())
56+
return true;
57+
58+
// Check if the thread matches any of the specs (OR logic)
59+
for (const auto &spec : thread_specs) {
60+
if (spec.ThreadPassesBasicTests(thread))
61+
return true;
62+
}
63+
return false;
64+
}
65+
66+
/// Check if this descriptor has valid metadata for script-based providers.
67+
/// Note: For C++ plugins, the descriptor may not have scripted metadata.
68+
bool HasScriptedMetadata() const { return scripted_metadata_sp != nullptr; }
69+
70+
/// Check if this descriptor is valid (deprecated - use HasScriptedMetadata).
71+
/// This method is kept for backward compatibility.
72+
virtual bool IsValid() const { return HasScriptedMetadata(); }
73+
74+
void Dump(Stream *s) const;
75+
};
76+
77+
/// Base class for all synthetic frame providers.
78+
///
79+
/// Synthetic frame providers allow modifying or replacing the stack frames
80+
/// shown for a thread. This is useful for:
81+
/// - Providing frames for custom calling conventions or languages
82+
/// - Reconstructing missing frames from crash dumps or core files
83+
/// - Adding diagnostic or synthetic frames for debugging
84+
/// - Visualizing state machines or async execution contexts
85+
class SyntheticFrameProvider : public PluginInterface {
86+
public:
87+
/// Try to create a SyntheticFrameProvider instance for the given input
88+
/// frames and descriptor.
89+
///
90+
/// This method iterates through all registered SyntheticFrameProvider
91+
/// plugins and returns the first one that can handle the given descriptor.
92+
///
93+
/// \param[in] input_frames
94+
/// The input stack frame list that this provider will transform.
95+
/// This could be real unwound frames or output from another provider.
96+
///
97+
/// \param[in] descriptor
98+
/// The descriptor containing metadata for the provider.
99+
///
100+
/// \return
101+
/// A shared pointer to a SyntheticFrameProvider if one could be created,
102+
/// otherwise an \a llvm::Error.
103+
static llvm::Expected<lldb::SyntheticFrameProviderSP>
104+
CreateInstance(lldb::StackFrameListSP input_frames,
105+
const SyntheticFrameProviderDescriptor &descriptor);
106+
107+
/// Try to create a SyntheticFrameProvider instance for the given input
108+
/// frames using a specific C++ plugin.
109+
///
110+
/// This method directly invokes a specific SyntheticFrameProvider plugin
111+
/// by name, bypassing the descriptor-based plugin iteration. This is useful
112+
/// for C++ plugins that don't require scripted metadata.
113+
///
114+
/// \param[in] input_frames
115+
/// The input stack frame list that this provider will transform.
116+
/// This could be real unwound frames or output from another provider.
117+
///
118+
/// \param[in] plugin_name
119+
/// The name of the plugin to use for creating the provider.
120+
///
121+
/// \param[in] thread_specs
122+
/// Optional list of thread specifications to which this provider applies.
123+
/// If empty, the provider applies to all threads.
124+
///
125+
/// \return
126+
/// A shared pointer to a SyntheticFrameProvider if one could be created,
127+
/// otherwise an \a llvm::Error.
128+
static llvm::Expected<lldb::SyntheticFrameProviderSP>
129+
CreateInstance(lldb::StackFrameListSP input_frames,
130+
llvm::StringRef plugin_name,
131+
const std::vector<ThreadSpec> &thread_specs = {});
132+
133+
~SyntheticFrameProvider() override;
134+
135+
/// Get a single stack frame at the specified index.
136+
///
137+
/// This method is called lazily - frames are only created when requested.
138+
/// The provider can access its input frames via GetInputFrames() if needed.
139+
///
140+
/// \param[in] idx
141+
/// The index of the frame to create.
142+
///
143+
/// \return
144+
/// An Expected containing the StackFrameSP if successful. Returns an
145+
/// error when the index is beyond the last frame to signal the end of
146+
/// the frame list.
147+
virtual llvm::Expected<lldb::StackFrameSP> GetFrameAtIndex(uint32_t idx) = 0;
148+
149+
/// Get the thread associated with this provider.
150+
Thread &GetThread() { return m_input_frames->GetThread(); }
151+
152+
/// Get the input frames that this provider transforms.
153+
lldb::StackFrameListSP GetInputFrames() const { return m_input_frames; }
154+
155+
protected:
156+
SyntheticFrameProvider(lldb::StackFrameListSP input_frames);
157+
158+
lldb::StackFrameListSP m_input_frames;
159+
};
160+
161+
} // namespace lldb_private
162+
163+
#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: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1300,6 +1300,76 @@ PluginManager::GetScriptInterpreterForLanguage(lldb::ScriptLanguage script_lang,
13001300
return none_instance(debugger);
13011301
}
13021302

1303+
#pragma mark SyntheticFrameProvider (Scripted)
1304+
1305+
typedef PluginInstance<ScriptedFrameProviderCreateInstance>
1306+
ScriptedFrameProviderInstance;
1307+
typedef PluginInstances<ScriptedFrameProviderInstance>
1308+
ScriptedFrameProviderInstances;
1309+
1310+
static ScriptedFrameProviderInstances &GetScriptedFrameProviderInstances() {
1311+
static ScriptedFrameProviderInstances g_instances;
1312+
return g_instances;
1313+
}
1314+
1315+
bool PluginManager::RegisterPlugin(
1316+
llvm::StringRef name, llvm::StringRef description,
1317+
ScriptedFrameProviderCreateInstance create_callback) {
1318+
return GetScriptedFrameProviderInstances().RegisterPlugin(name, description,
1319+
create_callback);
1320+
}
1321+
1322+
bool PluginManager::UnregisterPlugin(
1323+
ScriptedFrameProviderCreateInstance create_callback) {
1324+
return GetScriptedFrameProviderInstances().UnregisterPlugin(create_callback);
1325+
}
1326+
1327+
ScriptedFrameProviderCreateInstance
1328+
PluginManager::GetScriptedFrameProviderCreateCallbackAtIndex(uint32_t idx) {
1329+
return GetScriptedFrameProviderInstances().GetCallbackAtIndex(idx);
1330+
}
1331+
1332+
ScriptedFrameProviderCreateInstance
1333+
PluginManager::GetScriptedFrameProviderCreateCallbackForPluginName(
1334+
llvm::StringRef name) {
1335+
return GetScriptedFrameProviderInstances().GetCallbackForName(name);
1336+
}
1337+
1338+
#pragma mark SyntheticFrameProvider (C++)
1339+
1340+
typedef PluginInstance<SyntheticFrameProviderCreateInstance>
1341+
SyntheticFrameProviderInstance;
1342+
typedef PluginInstances<SyntheticFrameProviderInstance>
1343+
SyntheticFrameProviderInstances;
1344+
1345+
static SyntheticFrameProviderInstances &GetSyntheticFrameProviderInstances() {
1346+
static SyntheticFrameProviderInstances g_instances;
1347+
return g_instances;
1348+
}
1349+
1350+
bool PluginManager::RegisterPlugin(
1351+
llvm::StringRef name, llvm::StringRef description,
1352+
SyntheticFrameProviderCreateInstance create_callback) {
1353+
return GetSyntheticFrameProviderInstances().RegisterPlugin(name, description,
1354+
create_callback);
1355+
}
1356+
1357+
bool PluginManager::UnregisterPlugin(
1358+
SyntheticFrameProviderCreateInstance create_callback) {
1359+
return GetSyntheticFrameProviderInstances().UnregisterPlugin(create_callback);
1360+
}
1361+
1362+
SyntheticFrameProviderCreateInstance
1363+
PluginManager::GetSyntheticFrameProviderCreateCallbackAtIndex(uint32_t idx) {
1364+
return GetSyntheticFrameProviderInstances().GetCallbackAtIndex(idx);
1365+
}
1366+
1367+
SyntheticFrameProviderCreateInstance
1368+
PluginManager::GetSyntheticFrameProviderCreateCallbackForPluginName(
1369+
llvm::StringRef name) {
1370+
return GetSyntheticFrameProviderInstances().GetCallbackForName(name);
1371+
}
1372+
13031373
#pragma mark StructuredDataPlugin
13041374

13051375
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

0 commit comments

Comments
 (0)