Skip to content

Commit 8971304

Browse files
Merge pull request #6839 from apple/dl/lldb-use-unique-line-count-in-swift_task_switch-logic
[lldb] Use unique line count in swift_task_switch logic
2 parents 116b307 + 5c3acb0 commit 8971304

File tree

7 files changed

+107
-20
lines changed

7 files changed

+107
-20
lines changed

lldb/include/lldb/Target/StackID.h

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,7 @@ class StackID {
1919
// Constructors and Destructors
2020
StackID() = default;
2121

22-
explicit StackID(lldb::addr_t pc, lldb::addr_t cfa,
23-
SymbolContextScope *symbol_scope)
24-
: m_pc(pc), m_cfa(cfa), m_symbol_scope(symbol_scope) {}
25-
26-
StackID(const StackID &rhs)
27-
: m_pc(rhs.m_pc), m_cfa(rhs.m_cfa), m_symbol_scope(rhs.m_symbol_scope) {}
22+
StackID(const StackID &rhs) = default;
2823

2924
~StackID() = default;
3025

@@ -41,6 +36,7 @@ class StackID {
4136
void Clear() {
4237
m_pc = LLDB_INVALID_ADDRESS;
4338
m_cfa = LLDB_INVALID_ADDRESS;
39+
m_cfa_on_stack = true;
4440
m_symbol_scope = nullptr;
4541
}
4642

@@ -55,14 +51,22 @@ class StackID {
5551
if (this != &rhs) {
5652
m_pc = rhs.m_pc;
5753
m_cfa = rhs.m_cfa;
54+
m_cfa_on_stack = rhs.m_cfa_on_stack;
5855
m_symbol_scope = rhs.m_symbol_scope;
5956
}
6057
return *this;
6158
}
6259

60+
bool IsCFAOnStack() const { return m_cfa_on_stack; }
61+
6362
protected:
6463
friend class StackFrame;
6564

65+
explicit StackID(lldb::addr_t pc, lldb::addr_t cfa, lldb::ThreadSP thread_sp)
66+
: m_pc(pc), m_cfa(cfa), m_cfa_on_stack(IsStackAddress(cfa, thread_sp)) {}
67+
68+
bool IsStackAddress(lldb::addr_t addr, lldb::ThreadSP thread_sp) const;
69+
6670
void SetPC(lldb::addr_t pc) { m_pc = pc; }
6771

6872
void SetCFA(lldb::addr_t cfa) { m_cfa = cfa; }
@@ -78,6 +82,9 @@ class StackID {
7882
// at the beginning of the function that uniquely
7983
// identifies this frame (along with m_symbol_scope
8084
// below)
85+
// True if the CFA is an address on the stack, false if it's an address
86+
// elsewhere (ie heap).
87+
bool m_cfa_on_stack = true;
8188
SymbolContextScope *m_symbol_scope =
8289
nullptr; // If nullptr, there is no block or symbol for this frame.
8390
// If not nullptr, this will either be the scope for the

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

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
#include "Plugins/Process/Utility/RegisterContext_x86.h"
3030
#include "Utility/ARM64_DWARF_Registers.h"
31+
#include "llvm/ADT/SmallSet.h"
3132

3233
using namespace lldb;
3334
using namespace lldb_private;
@@ -234,7 +235,7 @@ class ThreadPlanStepInAsync : public ThreadPlan {
234235
return false;
235236
auto fn_start = sc.symbol->GetFileAddress();
236237
auto fn_end = sc.symbol->GetFileAddress() + sc.symbol->GetByteSize();
237-
int line_entry_count = 0;
238+
llvm::SmallSet<uint32_t, 2> unique_debug_lines;
238239
if (auto *line_table = sc.comp_unit->GetLineTable()) {
239240
for (uint32_t i = 0; i < line_table->GetSize(); ++i) {
240241
LineEntry line_entry;
@@ -243,11 +244,23 @@ class ThreadPlanStepInAsync : public ThreadPlan {
243244
continue;
244245

245246
auto line_start = line_entry.range.GetBaseAddress().GetFileAddress();
246-
if (line_start >= fn_start && line_start < fn_end)
247-
if (++line_entry_count > 1)
248-
// This is an async function with a proper body of code, no step
249-
// into `swift_task_switch` required.
247+
if (fn_start <= line_start && line_start < fn_end) {
248+
unique_debug_lines.insert(line_entry.line);
249+
// This logic is to distinguish between async functions that only
250+
// call `swift_task_switch` (which, from the perspective of the
251+
// user, has no meaningful function body), vs async functions that
252+
// do have a function body. In the first case, lldb should step
253+
// further to find the function body, in the second case lldb has
254+
// found a body and should stop.
255+
//
256+
// Currently, async functions that go through `swift_task_switch`
257+
// are generated with a reference to a single line. If this function
258+
// has more than one unique debug line, then it is a function that
259+
// has a body, and execution can stop here.
260+
if (unique_debug_lines.size() >= 2)
261+
// No step into `swift_task_switch` required.
250262
return false;
263+
}
251264
}
252265
}
253266
}

lldb/source/Target/StackFrame.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx,
5757
const SymbolContext *sc_ptr)
5858
: m_thread_wp(thread_sp), m_frame_index(frame_idx),
5959
m_concrete_frame_index(unwind_frame_index), m_reg_context_sp(),
60-
m_id(pc, cfa, nullptr), m_frame_code_addr(pc), m_sc(), m_flags(),
60+
m_id(pc, cfa, thread_sp), m_frame_code_addr(pc), m_sc(), m_flags(),
6161
m_frame_base(), m_frame_base_error(), m_cfa_is_valid(cfa_is_valid),
6262
m_stack_frame_kind(kind),
6363
m_behaves_like_zeroth_frame(behaves_like_zeroth_frame),
@@ -83,7 +83,7 @@ StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx,
8383
const SymbolContext *sc_ptr)
8484
: m_thread_wp(thread_sp), m_frame_index(frame_idx),
8585
m_concrete_frame_index(unwind_frame_index),
86-
m_reg_context_sp(reg_context_sp), m_id(pc, cfa, nullptr),
86+
m_reg_context_sp(reg_context_sp), m_id(pc, cfa, thread_sp),
8787
m_frame_code_addr(pc), m_sc(), m_flags(), m_frame_base(),
8888
m_frame_base_error(), m_cfa_is_valid(true),
8989
m_stack_frame_kind(StackFrame::Kind::Regular),
@@ -111,7 +111,7 @@ StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx,
111111
m_concrete_frame_index(unwind_frame_index),
112112
m_reg_context_sp(reg_context_sp),
113113
m_id(pc_addr.GetLoadAddress(thread_sp->CalculateTarget().get()), cfa,
114-
nullptr),
114+
thread_sp),
115115
m_frame_code_addr(pc_addr), m_sc(), m_flags(), m_frame_base(),
116116
m_frame_base_error(), m_cfa_is_valid(true),
117117
m_stack_frame_kind(StackFrame::Kind::Regular),

lldb/source/Target/StackID.cpp

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,31 @@
1010
#include "lldb/Symbol/Block.h"
1111
#include "lldb/Symbol/Symbol.h"
1212
#include "lldb/Symbol/SymbolContext.h"
13+
#include "lldb/Target/MemoryRegionInfo.h"
14+
#include "lldb/Target/Process.h"
15+
#include "lldb/Target/Thread.h"
1316
#include "lldb/Utility/Stream.h"
1417

1518
using namespace lldb_private;
1619

20+
bool StackID::IsStackAddress(lldb::addr_t addr,
21+
lldb::ThreadSP thread_sp) const {
22+
if (addr == LLDB_INVALID_ADDRESS)
23+
return false;
24+
25+
if (thread_sp)
26+
if (auto process_sp = thread_sp->GetProcess()) {
27+
MemoryRegionInfo mem_info;
28+
if (process_sp->GetMemoryRegionInfo(addr, mem_info).Success())
29+
return mem_info.IsStackMemory() == MemoryRegionInfo::eYes;
30+
}
31+
return true; // assumed
32+
}
33+
1734
void StackID::Dump(Stream *s) {
1835
s->Printf("StackID (pc = 0x%16.16" PRIx64 ", cfa = 0x%16.16" PRIx64
19-
", symbol_scope = %p",
20-
m_pc, m_cfa, static_cast<void *>(m_symbol_scope));
36+
", cfa_on_stack = %d, symbol_scope = %p",
37+
m_pc, m_cfa, m_cfa_on_stack, static_cast<void *>(m_symbol_scope));
2138
if (m_symbol_scope) {
2239
SymbolContext sc;
2340

@@ -62,10 +79,12 @@ bool lldb_private::operator<(const StackID &lhs, const StackID &rhs) {
6279
const lldb::addr_t rhs_cfa = rhs.GetCallFrameAddress();
6380

6481
// FIXME: rdar://76119439
65-
// This heuristic is a *temporary* fallback while proper fixes are
66-
// determined. The heuristic assumes the CFA of async functions is a low
67-
// (heap) address, and for normal functions it's a high (stack) address.
68-
if (lhs_cfa - rhs_cfa >= 0x00007ff000000000ULL)
82+
// Heuristic: At the boundary between an async parent frame calling a regular
83+
// child frame, the CFA of the parent async function is a heap addresses, and
84+
// the CFA of concrete child function is a stack addresses. Therefore, if lhs
85+
// is on stack, and rhs is not, lhs is considered less than rhs (regardless of
86+
// address values).
87+
if (lhs.IsCFAOnStack() && !rhs.IsCFAOnStack())
6988
return true;
7089

7190
// FIXME: We are assuming that the stacks grow downward in memory. That's not
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
SWIFT_SOURCES := main.swift
2+
SWIFTFLAGS_EXTRAS := -parse-as-library
3+
include Makefile.rules
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import lldb
2+
from lldbsuite.test.decorators import *
3+
import lldbsuite.test.lldbtest as lldbtest
4+
import lldbsuite.test.lldbutil as lldbutil
5+
6+
7+
class TestCase(lldbtest.TestBase):
8+
@swiftTest
9+
@skipIf(oslist=["windows", "linux"])
10+
def test(self):
11+
"""Test conditions for async step-in."""
12+
self.build()
13+
14+
src = lldb.SBFileSpec("main.swift")
15+
target, _, thread, _ = lldbutil.run_to_source_breakpoint(self, "await f()", src)
16+
self.assertEqual(thread.frame[0].function.mangled, "$s1a5entryO4mainyyYaFZ")
17+
18+
function = target.FindFunctions("$s1a5entryO4mainyyYaFZTQ0_")[0].function
19+
instructions = list(function.GetInstructions(target))
20+
21+
# Expected to be a trampoline that tail calls `swift_task_switch`.
22+
self.assertIn("swift_task_switch", instructions[-1].GetComment(target))
23+
24+
# Using the line table, build a set of the non-zero line numbers for
25+
# this this function - and verify that there is exactly one line.
26+
lines = {inst.addr.line_entry.line for inst in instructions}
27+
lines.remove(0)
28+
self.assertEqual(lines, {3})
29+
30+
# Required for builds that have debug info.
31+
self.runCmd("settings set target.process.thread.step-avoid-libraries libswift_Concurrency.dylib")
32+
thread.StepInto()
33+
frame = thread.frame[0]
34+
# Step in from `main` should progress through to `f`.
35+
self.assertEqual(frame.name, "a.f() async -> Swift.Int")
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
@main enum entry {
2+
static func main() async {
3+
let x = await f()
4+
print(x)
5+
}
6+
}
7+
8+
func f() async -> Int {
9+
return 30
10+
}

0 commit comments

Comments
 (0)