|
13 | 13 | #include "lldb/Target/MemoryRegionInfo.h"
|
14 | 14 | #include "lldb/Target/Process.h"
|
15 | 15 | #include "lldb/Target/Thread.h"
|
| 16 | +#include "lldb/Utility/LLDBLog.h" |
16 | 17 | #include "lldb/Utility/Stream.h"
|
17 | 18 |
|
18 | 19 | using namespace lldb_private;
|
19 | 20 |
|
20 | 21 | bool StackID::IsCFAOnStack(Process &process) const {
|
21 | 22 | if (m_cfa_on_stack == eLazyBoolCalculate) {
|
22 |
| - m_cfa_on_stack = eLazyBoolNo; |
| 23 | + // Conservatively assume stack memory |
| 24 | + m_cfa_on_stack = eLazyBoolYes; |
23 | 25 | if (m_cfa != LLDB_INVALID_ADDRESS) {
|
24 | 26 | MemoryRegionInfo mem_info;
|
25 | 27 | if (process.GetMemoryRegionInfo(m_cfa, mem_info).Success())
|
26 |
| - if (mem_info.IsStackMemory() == MemoryRegionInfo::eYes) |
27 |
| - m_cfa_on_stack = eLazyBoolYes; |
| 28 | + if (mem_info.IsStackMemory() == MemoryRegionInfo::eNo) |
| 29 | + m_cfa_on_stack = eLazyBoolNo; |
28 | 30 | }
|
29 | 31 | }
|
30 | 32 | return m_cfa_on_stack == eLazyBoolYes;
|
@@ -84,14 +86,43 @@ IsYoungerHeapCFAs(const StackID &lhs, const StackID &rhs, Process &process) {
|
84 | 86 | if (lhs_cfa_on_stack && rhs_cfa_on_stack)
|
85 | 87 | return HeapCFAComparisonResult::NoOpinion;
|
86 | 88 |
|
87 |
| - // FIXME: rdar://76119439 |
88 |
| - // At the boundary between an async parent frame calling a regular child |
89 |
| - // frame, the CFA of the parent async function is a heap addresses, and the |
90 |
| - // CFA of concrete child function is a stack address. Therefore, if lhs is |
91 |
| - // on stack, and rhs is not, lhs is considered less than rhs, independent of |
92 |
| - // address values. |
| 89 | + // If one of the frames has a CFA on the stack and the other doesn't, we are |
| 90 | + // at the boundary between an asynchronous and a synchronous function. |
| 91 | + // Synchronous functions cannot call asynchronous functions, therefore the |
| 92 | + // synchronous frame is always younger. |
93 | 93 | if (lhs_cfa_on_stack && !rhs_cfa_on_stack)
|
94 | 94 | return HeapCFAComparisonResult::Younger;
|
| 95 | + if (!lhs_cfa_on_stack && rhs_cfa_on_stack) |
| 96 | + return HeapCFAComparisonResult::Older; |
| 97 | + |
| 98 | + const lldb::addr_t lhs_cfa = lhs.GetCallFrameAddress(); |
| 99 | + const lldb::addr_t rhs_cfa = rhs.GetCallFrameAddress(); |
| 100 | + // If the cfas are the same, fallback to the usual scope comparison. |
| 101 | + if (lhs_cfa == rhs_cfa) |
| 102 | + return HeapCFAComparisonResult::NoOpinion; |
| 103 | + |
| 104 | + // Both CFAs are on the heap and they are distinct. |
| 105 | + // 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 | + |
95 | 126 | return HeapCFAComparisonResult::NoOpinion;
|
96 | 127 | }
|
97 | 128 | // END SWIFT
|
|
0 commit comments