Skip to content

Commit 6e0f052

Browse files
Merge pull request swiftlang#10127 from felipepiovezan/felipe/fix_heap_cfa
[lldb] Fix HeapCFA comparison to be symmetric
2 parents e7d5e2c + 1b3b686 commit 6e0f052

File tree

2 files changed

+70
-22
lines changed

2 files changed

+70
-22
lines changed

lldb/source/Target/StackID.cpp

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,40 @@ bool lldb_private::operator!=(const StackID &lhs, const StackID &rhs) {
7676
}
7777

7878
// BEGIN SWIFT
79+
/// Given two async contexts, source and maybe_parent, chase continuation
80+
/// pointers to check if maybe_parent can be reached from source. The search
81+
/// stops when it hits the end of the chain (parent_ctx == 0) or a safety limit
82+
/// in case of an invalid continuation chain.
83+
static llvm::Expected<bool> IsReachableParent(lldb::addr_t source,
84+
lldb::addr_t maybe_parent,
85+
Process &process) {
86+
auto max_num_frames = 512;
87+
for (lldb::addr_t parent_ctx = source; parent_ctx && max_num_frames;
88+
max_num_frames--) {
89+
Status error;
90+
lldb::addr_t old_parent_ctx = parent_ctx;
91+
// The continuation's context is the first field of an async context.
92+
parent_ctx = process.ReadPointerFromMemory(old_parent_ctx, error);
93+
if (error.Fail())
94+
return llvm::createStringError(llvm::formatv(
95+
"Failed to read parent async context of: {0:x}. Error: {1}",
96+
old_parent_ctx, error.AsCString()));
97+
if (parent_ctx == maybe_parent)
98+
return true;
99+
}
100+
if (max_num_frames == 0)
101+
return llvm::createStringError(
102+
llvm::formatv("Failed to read continuation chain from {0:x} to "
103+
"possible parent {1:x}. Reached limit of frames.",
104+
source, maybe_parent));
105+
return false;
106+
}
107+
79108
enum class HeapCFAComparisonResult { Younger, Older, NoOpinion };
80109
/// If at least one of the stack IDs (lhs, rhs) is a heap CFA, perform the
81110
/// swift-specific async frame comparison. Otherwise, returns NoOpinion.
82111
static HeapCFAComparisonResult
83-
IsYoungerHeapCFAs(const StackID &lhs, const StackID &rhs, Process &process) {
112+
CompareHeapCFAs(const StackID &lhs, const StackID &rhs, Process &process) {
84113
const bool lhs_cfa_on_stack = lhs.IsCFAOnStack(process);
85114
const bool rhs_cfa_on_stack = rhs.IsCFAOnStack(process);
86115
if (lhs_cfa_on_stack && rhs_cfa_on_stack)
@@ -103,34 +132,26 @@ IsYoungerHeapCFAs(const StackID &lhs, const StackID &rhs, Process &process) {
103132

104133
// Both CFAs are on the heap and they are distinct.
105134
// LHS is younger if and only if its continuation async context is (directly
106-
// or indirectly) RHS. Chase continuation pointers to check this case, until
107-
// we hit the end of the chain (parent_ctx == 0) or a safety limit in case of
108-
// an invalid continuation chain.
109-
auto max_num_frames = 512;
110-
for (lldb::addr_t parent_ctx = lhs_cfa; parent_ctx && max_num_frames;
111-
max_num_frames--) {
112-
Status error;
113-
lldb::addr_t old_parent_ctx = parent_ctx;
114-
// The continuation's context is the first field of an async context.
115-
parent_ctx = process.ReadPointerFromMemory(old_parent_ctx, error);
116-
if (error.Fail()) {
117-
Log *log = GetLog(LLDBLog::Unwind);
118-
LLDB_LOGF(log, "Failed to read parent async context of: 0x%8.8" PRIx64,
119-
old_parent_ctx);
120-
break;
121-
}
122-
if (parent_ctx == rhs_cfa)
123-
return HeapCFAComparisonResult::Younger;
124-
}
125-
135+
// or indirectly) RHS.
136+
llvm::Expected<bool> lhs_younger =
137+
IsReachableParent(lhs_cfa, rhs_cfa, process);
138+
if (auto E = lhs_younger.takeError())
139+
LLDB_LOG_ERROR(GetLog(LLDBLog::Unwind), std::move(E), "{0}");
140+
else if (*lhs_younger)
141+
return HeapCFAComparisonResult::Younger;
142+
llvm::Expected<bool> lhs_older = IsReachableParent(rhs_cfa, lhs_cfa, process);
143+
if (auto E = lhs_older.takeError())
144+
LLDB_LOG_ERROR(GetLog(LLDBLog::Unwind), std::move(E), "{0}");
145+
else if (*lhs_older)
146+
return HeapCFAComparisonResult::Older;
126147
return HeapCFAComparisonResult::NoOpinion;
127148
}
128149
// END SWIFT
129150

130151
bool StackID::IsYounger(const StackID &lhs, const StackID &rhs,
131152
Process &process) {
132153
// BEGIN SWIFT
133-
switch (IsYoungerHeapCFAs(lhs, rhs, process)) {
154+
switch (CompareHeapCFAs(lhs, rhs, process)) {
134155
case HeapCFAComparisonResult::Younger:
135156
return true;
136157
case HeapCFAComparisonResult::Older:

lldb/unittests/StackID/StackIDTest.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,30 @@ TEST_F(StackIDTest, HeapHeapCFAComparison) {
123123
EXPECT_TRUE(StackID::IsYounger(middle_cfa, oldest_cfa, process));
124124
EXPECT_FALSE(StackID::IsYounger(oldest_cfa, middle_cfa, process));
125125
}
126+
127+
TEST_F(StackIDTest, HeapHeapCFAComparisonDecreasing) {
128+
// Create a mock async continuation chain:
129+
// 100 -> 90 -> 80 -> 0
130+
// This should be read as:
131+
// "Async context whose address is 100 has a continuation context whose
132+
// address is 90", etc.
133+
llvm::DenseMap<addr_t, addr_t> memory_map;
134+
memory_map[100] = 90;
135+
memory_map[90] = 80;
136+
memory_map[80] = 0;
137+
auto process = MockProcess(m_target_sp, Listener::MakeListener("dummy"),
138+
std::move(memory_map));
139+
140+
MockStackID oldest_cfa(/*cfa*/ 80, OnStack::No);
141+
MockStackID middle_cfa(/*cfa*/ 90, OnStack::No);
142+
MockStackID youngest_cfa(/*cfa*/ 100, OnStack::No);
143+
144+
EXPECT_TRUE(StackID::IsYounger(youngest_cfa, oldest_cfa, process));
145+
EXPECT_FALSE(StackID::IsYounger(oldest_cfa, youngest_cfa, process));
146+
147+
EXPECT_TRUE(StackID::IsYounger(youngest_cfa, middle_cfa, process));
148+
EXPECT_FALSE(StackID::IsYounger(middle_cfa, youngest_cfa, process));
149+
150+
EXPECT_TRUE(StackID::IsYounger(middle_cfa, oldest_cfa, process));
151+
EXPECT_FALSE(StackID::IsYounger(oldest_cfa, middle_cfa, process));
152+
}

0 commit comments

Comments
 (0)