Skip to content

Commit 9cb2811

Browse files
[lldb][swift] Add function to extract funclet numbers
Async functions are broken into many pieces, and each piece is identified by an integer number in the mangling scheme. This establishes an ordering between funclets which we can exploit, in a subsequent commit, to filter out multiple breakpoints locations attributed to the same line. An exception: entry funclets don't have such a number. This commit assigns 0 to them, to better reflect the intent of ordering funclets.
1 parent 638f45c commit 9cb2811

File tree

3 files changed

+78
-0
lines changed

3 files changed

+78
-0
lines changed

lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,14 @@ class SwiftLanguageRuntime : public LanguageRuntime {
181181
/// function, or suspend resume partial function symbol.
182182
static bool IsAnySwiftAsyncFunctionSymbol(swift::Demangle::NodePointer node);
183183

184+
/// If node is a Swift async funclet, return its funclet number.
185+
static std::optional<uint64_t>
186+
GetFuncletNumber(swift::Demangle::NodePointer node);
187+
188+
/// If name is a Swift async funclet, return its funclet number.
189+
static std::optional<uint64_t>
190+
GetFuncletNumber(llvm::StringRef name);
191+
184192
/// Return the async context address using the target's specific register.
185193
static lldb::addr_t GetAsyncContext(RegisterContext *regctx);
186194

lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntimeNames.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,43 @@ AreFuncletsOfSameAsyncClosure(swift::Demangle::NodePointer closure1,
136136
return Node::deepEquals(closure1_function, closure2_function);
137137
}
138138

139+
std::optional<uint64_t>
140+
SwiftLanguageRuntime::GetFuncletNumber(swift::Demangle::NodePointer node) {
141+
if (!node)
142+
return {};
143+
NodePointer node_id =
144+
childAtPath(node, {Node::Kind::AsyncSuspendResumePartialFunction,
145+
Node::Kind::Number});
146+
if (!node_id)
147+
node_id = childAtPath(node, {Node::Kind::AsyncAwaitResumePartialFunction,
148+
Node::Kind::Number});
149+
if (node_id) {
150+
if (node_id->hasIndex())
151+
return node_id->getIndex();
152+
153+
// This should never happen, log it if it does:
154+
std::string node_dump = getNodeTreeAsString(node);
155+
LLDB_LOGF(GetLog(LLDBLog::Demangle),
156+
"%s::Found a number node without a number:\n%s", __FUNCTION__,
157+
node_dump.c_str());
158+
return {};
159+
}
160+
161+
// If this is an async function and there was no number, then this is an entry
162+
// funclet. Assign it number 0.
163+
if (SwiftLanguageRuntime::IsAnySwiftAsyncFunctionSymbol(node))
164+
return 0;
165+
return {};
166+
}
167+
168+
std::optional<uint64_t>
169+
SwiftLanguageRuntime::GetFuncletNumber(llvm::StringRef name) {
170+
using namespace swift::Demangle;
171+
Context ctx;
172+
NodePointer node = SwiftLanguageRuntime::DemangleSymbolAsNode(name, ctx);
173+
return GetFuncletNumber(node);
174+
}
175+
139176
SwiftLanguageRuntime::FuncletComparisonResult
140177
SwiftLanguageRuntime::AreFuncletsOfSameAsyncFunction(llvm::StringRef name1,
141178
llvm::StringRef name2) {

lldb/unittests/Symbol/TestSwiftDemangler.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
#include "gtest/gtest.h"
22

33
#include "Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.h"
4+
#include "llvm/ADT/Sequence.h"
45

56
using namespace lldb;
67
using namespace lldb_private;
78
using namespace llvm;
89
static constexpr auto IsSwiftMangledName =
910
SwiftLanguageRuntime::IsSwiftMangledName;
11+
static constexpr auto GetFuncletNumber = [](StringRef name) {
12+
return SwiftLanguageRuntime::GetFuncletNumber(name);
13+
};
1014
static constexpr auto IsAnySwiftAsyncFunctionSymbol = [](StringRef name) {
1115
return SwiftLanguageRuntime::IsAnySwiftAsyncFunctionSymbol(name);
1216
};
@@ -47,6 +51,13 @@ CheckGroupOfFuncletsFromDifferentFunctions(ArrayRef<StringRef> funclets1,
4751
}
4852
}
4953

54+
/// Check that funclets contain a sequence of funclet names whose "async
55+
/// numbers" go from 0 to size(funclets).
56+
static void CheckFuncletNumbersAreARange(ArrayRef<StringRef> funclets) {
57+
for (auto idx : llvm::seq<int>(0, funclets.size()))
58+
EXPECT_EQ(idx, GetFuncletNumber(funclets[idx]));
59+
}
60+
5061
TEST(TestSwiftDemangleAsyncNames, BasicAsync) {
5162
// "sayBasic" == a basic async function
5263
// "sayGeneric" == a generic async function
@@ -71,6 +82,8 @@ TEST(TestSwiftDemangleAsyncNames, BasicAsync) {
7182
CheckGroupOfFuncletsFromSameFunction(basic_funclets);
7283
CheckGroupOfFuncletsFromSameFunction(generic_funclets);
7384
CheckGroupOfFuncletsFromDifferentFunctions(basic_funclets, generic_funclets);
85+
CheckFuncletNumbersAreARange(basic_funclets);
86+
CheckFuncletNumbersAreARange(generic_funclets);
7487
}
7588

7689
TEST(TestSwiftDemangleAsyncNames, ClosureAsync) {
@@ -127,6 +140,10 @@ TEST(TestSwiftDemangleAsyncNames, ClosureAsync) {
127140
nested2_funclets_top_not_async);
128141
CheckGroupOfFuncletsFromDifferentFunctions(nested2_funclets2,
129142
nested2_funclets_top_not_async);
143+
CheckFuncletNumbersAreARange(nested1_funclets);
144+
CheckFuncletNumbersAreARange(nested2_funclets1);
145+
CheckFuncletNumbersAreARange(nested2_funclets2);
146+
CheckFuncletNumbersAreARange(nested2_funclets_top_not_async);
130147
}
131148

132149
TEST(TestSwiftDemangleAsyncNames, StaticAsync) {
@@ -153,4 +170,20 @@ TEST(TestSwiftDemangleAsyncNames, StaticAsync) {
153170
"$s1a8sayBasicyySSYaF", "$s1a8sayBasicyySSYaFTY0_"};
154171
CheckGroupOfFuncletsFromDifferentFunctions(static_async_funclets,
155172
other_funclets);
173+
CheckFuncletNumbersAreARange(static_async_funclets);
174+
}
175+
176+
TEST(TestSwiftDemangleAsyncNames, NonAsync) {
177+
// func factorial(_ n:Int) -> Int {
178+
{
179+
StringRef func = "$s4test9factorialyS2iF";
180+
EXPECT_EQ(GetFuncletNumber(func), std::nullopt);
181+
}
182+
183+
// func factorial(_ n:Int) async -> Int {
184+
// func inner_factorial(_ n:Int) -> Int {
185+
{
186+
StringRef func = "$s4test9factorialyS2iYaF06inner_B0L_yS2iF";
187+
EXPECT_EQ(GetFuncletNumber(func), std::nullopt);
188+
}
156189
}

0 commit comments

Comments
 (0)