Skip to content

[lldb][swift] Add function to extract funclet numbers #10247

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,14 @@ class SwiftLanguageRuntime : public LanguageRuntime {
/// function, or suspend resume partial function symbol.
static bool IsAnySwiftAsyncFunctionSymbol(swift::Demangle::NodePointer node);

/// If node is a Swift async funclet, return its funclet number.
static std::optional<uint64_t>
GetFuncletNumber(swift::Demangle::NodePointer node);

/// If name is a Swift async funclet, return its funclet number.
static std::optional<uint64_t>
GetFuncletNumber(llvm::StringRef name);

/// Return the async context address using the target's specific register.
static lldb::addr_t GetAsyncContext(RegisterContext *regctx);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,43 @@ AreFuncletsOfSameAsyncClosure(swift::Demangle::NodePointer closure1,
return Node::deepEquals(closure1_function, closure2_function);
}

std::optional<uint64_t>
SwiftLanguageRuntime::GetFuncletNumber(swift::Demangle::NodePointer node) {
if (!node)
return {};
NodePointer node_id =
childAtPath(node, {Node::Kind::AsyncSuspendResumePartialFunction,
Node::Kind::Number});
if (!node_id)
node_id = childAtPath(node, {Node::Kind::AsyncAwaitResumePartialFunction,
Node::Kind::Number});
if (node_id) {
if (node_id->hasIndex())
return node_id->getIndex();

// This should never happen, log it if it does:
std::string node_dump = getNodeTreeAsString(node);
LLDB_LOGF(GetLog(LLDBLog::Demangle),
"%s::Found a number node without a number:\n%s", __FUNCTION__,
node_dump.c_str());
return {};
}

// If this is an async function and there was no number, then this is an entry
// funclet. Assign it number 0.
if (SwiftLanguageRuntime::IsAnySwiftAsyncFunctionSymbol(node))
return 0;
return {};
}

std::optional<uint64_t>
SwiftLanguageRuntime::GetFuncletNumber(llvm::StringRef name) {
using namespace swift::Demangle;
Context ctx;
NodePointer node = SwiftLanguageRuntime::DemangleSymbolAsNode(name, ctx);
return GetFuncletNumber(node);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should there be a log (perhaps verbose only) to capture symbols that fail to get a funclet number?

Suggested change
return GetFuncletNumber(node);
auto number = GetFuncletNumber(node);
if (node && !number)
LLDB_LOG(..., "could not get funclet number for mangled name {0}", name);
return number;

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The challenge with this is that the StringRef overload of this function is only used for testing.

The real users of this function will use the NodePointer overload (which is called by the StringRef overload), as they use the NodePointer for other purposes. As such, we wouldn't be logging on the most important path (there is no way to grab the name from the NodePointer).

With that in mind, I'll make sure to add logging on the next PR, the one that uses these functions to filter breakpoints.

}

SwiftLanguageRuntime::FuncletComparisonResult
SwiftLanguageRuntime::AreFuncletsOfSameAsyncFunction(llvm::StringRef name1,
llvm::StringRef name2) {
Expand Down
33 changes: 33 additions & 0 deletions lldb/unittests/Symbol/TestSwiftDemangler.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
#include "gtest/gtest.h"

#include "Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.h"
#include "llvm/ADT/Sequence.h"

using namespace lldb;
using namespace lldb_private;
using namespace llvm;
static constexpr auto IsSwiftMangledName =
SwiftLanguageRuntime::IsSwiftMangledName;
static constexpr auto GetFuncletNumber = [](StringRef name) {
return SwiftLanguageRuntime::GetFuncletNumber(name);
};
static constexpr auto IsAnySwiftAsyncFunctionSymbol = [](StringRef name) {
return SwiftLanguageRuntime::IsAnySwiftAsyncFunctionSymbol(name);
};
Expand Down Expand Up @@ -47,6 +51,13 @@ CheckGroupOfFuncletsFromDifferentFunctions(ArrayRef<StringRef> funclets1,
}
}

/// Check that funclets contain a sequence of funclet names whose "async
/// numbers" go from 0 to size(funclets).
static void CheckFuncletNumbersAreARange(ArrayRef<StringRef> funclets) {
for (auto idx : llvm::seq<int>(0, funclets.size()))
EXPECT_EQ(idx, GetFuncletNumber(funclets[idx]));
}

TEST(TestSwiftDemangleAsyncNames, BasicAsync) {
// "sayBasic" == a basic async function
// "sayGeneric" == a generic async function
Expand All @@ -71,6 +82,8 @@ TEST(TestSwiftDemangleAsyncNames, BasicAsync) {
CheckGroupOfFuncletsFromSameFunction(basic_funclets);
CheckGroupOfFuncletsFromSameFunction(generic_funclets);
CheckGroupOfFuncletsFromDifferentFunctions(basic_funclets, generic_funclets);
CheckFuncletNumbersAreARange(basic_funclets);
CheckFuncletNumbersAreARange(generic_funclets);
}

TEST(TestSwiftDemangleAsyncNames, ClosureAsync) {
Expand Down Expand Up @@ -127,6 +140,10 @@ TEST(TestSwiftDemangleAsyncNames, ClosureAsync) {
nested2_funclets_top_not_async);
CheckGroupOfFuncletsFromDifferentFunctions(nested2_funclets2,
nested2_funclets_top_not_async);
CheckFuncletNumbersAreARange(nested1_funclets);
CheckFuncletNumbersAreARange(nested2_funclets1);
CheckFuncletNumbersAreARange(nested2_funclets2);
CheckFuncletNumbersAreARange(nested2_funclets_top_not_async);
}

TEST(TestSwiftDemangleAsyncNames, StaticAsync) {
Expand All @@ -153,4 +170,20 @@ TEST(TestSwiftDemangleAsyncNames, StaticAsync) {
"$s1a8sayBasicyySSYaF", "$s1a8sayBasicyySSYaFTY0_"};
CheckGroupOfFuncletsFromDifferentFunctions(static_async_funclets,
other_funclets);
CheckFuncletNumbersAreARange(static_async_funclets);
}

TEST(TestSwiftDemangleAsyncNames, NonAsync) {
// func factorial(_ n:Int) -> Int {
{
StringRef func = "$s4test9factorialyS2iF";
EXPECT_EQ(GetFuncletNumber(func), std::nullopt);
}

// func factorial(_ n:Int) async -> Int {
// func inner_factorial(_ n:Int) -> Int {
{
StringRef func = "$s4test9factorialyS2iYaF06inner_B0L_yS2iF";
EXPECT_EQ(GetFuncletNumber(func), std::nullopt);
}
}