Skip to content

Commit 8060b84

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 cb2519d commit 8060b84

File tree

7 files changed

+252
-0
lines changed

7 files changed

+252
-0
lines changed

lldb/include/lldb/Core/PluginManager.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,17 @@ 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_callback);
363+
364+
static bool
365+
UnregisterPlugin(SyntheticFrameProviderCreateInstance create_callback);
366+
367+
static SyntheticFrameProviderCreateInstance
368+
GetSyntheticFrameProviderCreateCallbackAtIndex(uint32_t idx);
369+
359370
// StructuredDataPlugin
360371

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

lldb/include/lldb/lldb-forward.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,8 @@ typedef std::shared_ptr<lldb_private::ScriptSummaryFormat>
411411
typedef std::shared_ptr<lldb_private::ScriptInterpreter> ScriptInterpreterSP;
412412
typedef std::shared_ptr<lldb_private::ScriptedFrameInterface>
413413
ScriptedFrameInterfaceSP;
414+
typedef std::shared_ptr<lldb_private::SyntheticFrameProvider>
415+
SyntheticFrameProviderSP;
414416
typedef std::shared_ptr<lldb_private::ScriptedMetadata> ScriptedMetadataSP;
415417
typedef std::unique_ptr<lldb_private::ScriptedPlatformInterface>
416418
ScriptedPlatformInterfaceUP;

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

Lines changed: 5 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,10 @@ typedef lldb::RegisterTypeBuilderSP (*RegisterTypeBuilderCreateInstance)(
8687
Target &target);
8788
typedef lldb::ScriptInterpreterSP (*ScriptInterpreterCreateInstance)(
8889
Debugger &debugger);
90+
typedef llvm::Expected<lldb::SyntheticFrameProviderSP> (
91+
*SyntheticFrameProviderCreateInstance)(
92+
lldb::StackFrameListSP input_frames,
93+
const lldb_private::SyntheticFrameProviderDescriptor &descriptor);
8994
typedef SymbolFile *(*SymbolFileCreateInstance)(lldb::ObjectFileSP objfile_sp);
9095
typedef SymbolVendor *(*SymbolVendorCreateInstance)(
9196
const lldb::ModuleSP &module_sp,

lldb/source/Core/PluginManager.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1300,6 +1300,35 @@ 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 PluginInstances<SyntheticFrameProviderInstance>
1308+
SyntheticFrameProviderInstances;
1309+
1310+
static SyntheticFrameProviderInstances &GetSyntheticFrameProviderInstances() {
1311+
static SyntheticFrameProviderInstances g_instances;
1312+
return g_instances;
1313+
}
1314+
1315+
bool PluginManager::RegisterPlugin(
1316+
llvm::StringRef name, llvm::StringRef description,
1317+
SyntheticFrameProviderCreateInstance create_callback) {
1318+
return GetSyntheticFrameProviderInstances().RegisterPlugin(name, description,
1319+
create_callback);
1320+
}
1321+
1322+
bool PluginManager::UnregisterPlugin(
1323+
SyntheticFrameProviderCreateInstance create_callback) {
1324+
return GetSyntheticFrameProviderInstances().UnregisterPlugin(create_callback);
1325+
}
1326+
1327+
SyntheticFrameProviderCreateInstance
1328+
PluginManager::GetSyntheticFrameProviderCreateCallbackAtIndex(uint32_t idx) {
1329+
return GetSyntheticFrameProviderInstances().GetCallbackAtIndex(idx);
1330+
}
1331+
13031332
#pragma mark StructuredDataPlugin
13041333

13051334
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: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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 SyntheticFrameProvider plugins
52+
SyntheticFrameProviderCreateInstance create_callback = nullptr;
53+
for (uint32_t idx = 0;
54+
(create_callback =
55+
PluginManager::GetSyntheticFrameProviderCreateCallbackAtIndex(
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+
}

0 commit comments

Comments
 (0)