Skip to content

Commit 5fd87a8

Browse files
committed
[swift-inspect][RemoteMirror] Decode job/task/actor flags.
Have RemoteMirror internally decode these flags fields and return them as separate fields in the task/actor info. Handle the structures both with and without task escalation support. Also show when a task is the current task on a thread in swift-inspect's task listing. rdar://88598003
1 parent 9664560 commit 5fd87a8

File tree

6 files changed

+230
-72
lines changed

6 files changed

+230
-72
lines changed

include/swift/Reflection/ReflectionContext.h

Lines changed: 131 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,29 @@
4343

4444
#include <inttypes.h>
4545

46+
// The Swift runtime can be built in two ways: with or without
47+
// SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION enabled. In order to decode the
48+
// lock used in a runtime with priority escalation enabled, we need inline
49+
// functions from dispatch/swift_concurrency_private.h. If we don't have that
50+
// header at build time, we can still build but we'll be unable to decode the
51+
// lock and thus information about a running task is degraded. There are four
52+
// combinations:
53+
//
54+
// Runtime | swift_concurrency_private.h | task running info
55+
// --------------------+-----------------------------+------------------
56+
// without escalation | present | full
57+
// without escalation | not present | full
58+
// with escalation | present | full
59+
// with escalation | not present | DEGRADED
60+
//
61+
// Currently, degraded info means that IsRunning is not available (indicated
62+
// with `HasIsRunning = false`) and async backtraces are not provided.
63+
64+
#if __has_include(<dispatch/swift_concurrency_private.h>)
65+
#include <dispatch/swift_concurrency_private.h>
66+
#define HAS_DISPATCH_LOCK_IS_LOCKED 1
67+
#endif
68+
4669
namespace {
4770

4871
template <unsigned PointerSize> struct MachOTraits;
@@ -145,8 +168,23 @@ class ReflectionContext
145168
};
146169

147170
struct AsyncTaskInfo {
148-
uint32_t JobFlags;
149-
uint64_t TaskStatusFlags;
171+
// Job flags.
172+
unsigned Kind;
173+
unsigned EnqueuePriority;
174+
bool IsChildTask;
175+
bool IsFuture;
176+
bool IsGroupChildTask;
177+
bool IsAsyncLetTask;
178+
179+
// Task flags.
180+
unsigned MaxPriority;
181+
bool IsCancelled;
182+
bool IsStatusRecordLocked;
183+
bool IsEscalated;
184+
bool HasIsRunning; // If false, the IsRunning flag is not valid.
185+
bool IsRunning;
186+
bool IsEnqueued;
187+
150188
uint64_t Id;
151189
StoredPointer RunJob;
152190
StoredPointer AllocatorSlabPtr;
@@ -1435,18 +1473,91 @@ class ReflectionContext
14351473
asyncTaskInfo(StoredPointer AsyncTaskPtr) {
14361474
loadTargetPointers();
14371475

1438-
if (supportsPriorityEscalation) {
1439-
return {std::string("Failure reading async task with escalation support"), {}};
1440-
}
1476+
if (supportsPriorityEscalation)
1477+
return asyncTaskInfo<
1478+
AsyncTask<Runtime, ActiveTaskStatusWithEscalation<Runtime>>>(
1479+
AsyncTaskPtr);
1480+
else
1481+
return asyncTaskInfo<
1482+
AsyncTask<Runtime, ActiveTaskStatusWithoutEscalation<Runtime>>>(
1483+
AsyncTaskPtr);
1484+
}
1485+
1486+
std::pair<llvm::Optional<std::string>, ActorInfo>
1487+
actorInfo(StoredPointer ActorPtr) {
1488+
if (supportsPriorityEscalation)
1489+
return actorInfo<
1490+
DefaultActorImpl<Runtime, ActiveActorStatusWithEscalation<Runtime>>>(
1491+
ActorPtr);
1492+
else
1493+
return actorInfo<DefaultActorImpl<
1494+
Runtime, ActiveActorStatusWithoutEscalation<Runtime>>>(ActorPtr);
1495+
}
1496+
1497+
StoredPointer nextJob(StoredPointer JobPtr) {
1498+
using Job = Job<Runtime>;
1499+
1500+
auto JobBytes = getReader().readBytes(RemoteAddress(JobPtr), sizeof(Job));
1501+
auto *JobObj = reinterpret_cast<const Job *>(JobBytes.get());
1502+
if (!JobObj)
1503+
return 0;
1504+
1505+
// This is a JobRef which stores flags in the low bits.
1506+
return JobObj->SchedulerPrivate[0] & ~StoredPointer(0x3);
1507+
}
1508+
1509+
private:
1510+
void setIsRunning(
1511+
AsyncTaskInfo &Info,
1512+
const AsyncTask<Runtime, ActiveTaskStatusWithEscalation<Runtime>> *Task) {
1513+
#if HAS_DISPATCH_LOCK_IS_LOCKED
1514+
Info.HasIsRunning = true;
1515+
Info.IsRunning =
1516+
dispatch_lock_is_locked(Task->PrivateStorage.Status.ExecutionLock[0]);
1517+
#else
1518+
// The target runtime was built with priority escalation but we don't have
1519+
// the swift_concurrency_private.h header needed to decode the running
1520+
// status in the task. Set HasIsRunning to false to indicate that we can't
1521+
// tell whether or not the task is running.
1522+
Info.HasIsRunning = false;
1523+
#endif
1524+
}
1525+
1526+
void setIsRunning(
1527+
AsyncTaskInfo &Info,
1528+
const AsyncTask<Runtime, ActiveTaskStatusWithoutEscalation<Runtime>>
1529+
*Task) {
1530+
Info.HasIsRunning = true;
1531+
Info.IsRunning =
1532+
Task->PrivateStorage.Status.Flags[0] & ActiveTaskStatusFlags::IsRunning;
1533+
}
14411534

1442-
using AsyncTask = AsyncTask<Runtime, ActiveTaskStatusWithoutEscalation<Runtime>>;
1443-
auto AsyncTaskObj = readObj<AsyncTask>(AsyncTaskPtr);
1535+
template <typename AsyncTaskType>
1536+
std::pair<llvm::Optional<std::string>, AsyncTaskInfo>
1537+
asyncTaskInfo(StoredPointer AsyncTaskPtr) {
1538+
auto AsyncTaskObj = readObj<AsyncTaskType>(AsyncTaskPtr);
14441539
if (!AsyncTaskObj)
14451540
return {std::string("failure reading async task"), {}};
14461541

14471542
AsyncTaskInfo Info{};
1448-
Info.JobFlags = AsyncTaskObj->Flags;
1449-
Info.TaskStatusFlags = AsyncTaskObj->PrivateStorage.Status.Flags[0];
1543+
1544+
swift::JobFlags JobFlags(AsyncTaskObj->Flags);
1545+
Info.Kind = static_cast<unsigned>(JobFlags.getKind());
1546+
Info.EnqueuePriority = static_cast<unsigned>(JobFlags.getPriority());
1547+
Info.IsChildTask = JobFlags.task_isChildTask();
1548+
Info.IsFuture = JobFlags.task_isFuture();
1549+
Info.IsGroupChildTask = JobFlags.task_isGroupChildTask();
1550+
Info.IsAsyncLetTask = JobFlags.task_isAsyncLetTask();
1551+
1552+
uint32_t TaskStatusFlags = AsyncTaskObj->PrivateStorage.Status.Flags[0];
1553+
Info.IsCancelled = TaskStatusFlags & ActiveTaskStatusFlags::IsCancelled;
1554+
Info.IsStatusRecordLocked =
1555+
TaskStatusFlags & ActiveTaskStatusFlags::IsStatusRecordLocked;
1556+
Info.IsEscalated = TaskStatusFlags & ActiveTaskStatusFlags::IsEscalated;
1557+
Info.IsEnqueued = TaskStatusFlags & ActiveTaskStatusFlags::IsEnqueued;
1558+
1559+
setIsRunning(Info, AsyncTaskObj.get());
1560+
14501561
Info.Id =
14511562
AsyncTaskObj->Id | ((uint64_t)AsyncTaskObj->PrivateStorage.Id << 32);
14521563
Info.AllocatorSlabPtr = AsyncTaskObj->PrivateStorage.Allocator.FirstSlab;
@@ -1479,8 +1590,7 @@ class ReflectionContext
14791590
while (ChildTask) {
14801591
Info.ChildTasks.push_back(ChildTask);
14811592

1482-
StoredPointer ChildFragmentAddr =
1483-
ChildTask + sizeof(AsyncTask);
1593+
StoredPointer ChildFragmentAddr = ChildTask + sizeof(*AsyncTaskObj);
14841594
auto ChildFragmentObj =
14851595
readObj<ChildFragment<Runtime>>(ChildFragmentAddr);
14861596
if (ChildFragmentObj)
@@ -1492,13 +1602,8 @@ class ReflectionContext
14921602
RecordPtr = RecordObj->Parent;
14931603
}
14941604

1495-
// Walk the async backtrace if the task isn't running or cancelled.
1496-
// TODO: Use isEnqueued from https://github.com/apple/swift/pull/41088/ once
1497-
// that's available.
1498-
int IsCancelledFlag = 0x100;
1499-
int IsRunningFlag = 0x800;
1500-
if (!(AsyncTaskObj->PrivateStorage.Status.Flags[0] & IsCancelledFlag) &&
1501-
!(AsyncTaskObj->PrivateStorage.Status.Flags[0] & IsRunningFlag)) {
1605+
// Walk the async backtrace.
1606+
if (Info.HasIsRunning && !Info.IsRunning) {
15021607
auto ResumeContext = AsyncTaskObj->ResumeContextAndReserved[0];
15031608
while (ResumeContext) {
15041609
auto ResumeContextObj = readObj<AsyncContext<Runtime>>(ResumeContext);
@@ -1513,15 +1618,10 @@ class ReflectionContext
15131618
return {llvm::None, Info};
15141619
}
15151620

1621+
template <typename ActorType>
15161622
std::pair<llvm::Optional<std::string>, ActorInfo>
15171623
actorInfo(StoredPointer ActorPtr) {
1518-
if (supportsPriorityEscalation) {
1519-
return {std::string("Failure reading actor with escalation support"), {}};
1520-
}
1521-
1522-
using DefaultActorImpl = DefaultActorImpl<Runtime, ActiveActorStatusWithoutEscalation<Runtime>>;
1523-
1524-
auto ActorObj = readObj<DefaultActorImpl>(ActorPtr);
1624+
auto ActorObj = readObj<ActorType>(ActorPtr);
15251625
if (!ActorObj)
15261626
return {std::string("failure reading actor"), {}};
15271627

@@ -1538,22 +1638,10 @@ class ReflectionContext
15381638
return {llvm::None, Info};
15391639
}
15401640

1541-
StoredPointer nextJob(StoredPointer JobPtr) {
1542-
using Job = Job<Runtime>;
1543-
1544-
auto JobBytes = getReader().readBytes(RemoteAddress(JobPtr), sizeof(Job));
1545-
auto *JobObj = reinterpret_cast<const Job *>(JobBytes.get());
1546-
if (!JobObj)
1547-
return 0;
1548-
1549-
// This is a JobRef which stores flags in the low bits.
1550-
return JobObj->SchedulerPrivate[0] & ~StoredPointer(0x3);
1551-
}
1552-
1553-
private:
15541641
// Get the most human meaningful "run job" function pointer from the task,
15551642
// like AsyncTask::getResumeFunctionForLogging does.
1556-
StoredPointer getRunJob(const AsyncTask<Runtime, ActiveTaskStatusWithoutEscalation<Runtime>> *AsyncTaskObj) {
1643+
template <typename AsyncTaskType>
1644+
StoredPointer getRunJob(const AsyncTaskType *AsyncTaskObj) {
15571645
auto Fptr = stripSignedPointer(AsyncTaskObj->RunJob);
15581646

15591647
loadTargetPointers();
@@ -1612,9 +1700,11 @@ class ReflectionContext
16121700
getFunc("_swift_concurrency_debug_task_wait_throwing_resume_adapter");
16131701
target_task_future_wait_resume_adapter =
16141702
getFunc("_swift_concurrency_debug_task_future_wait_resume_adapter");
1615-
auto supportsPriorityEscalationAddr = getReader().getSymbolAddress("_swift_concurrency_debug_supportsPriorityEscalation");
1703+
auto supportsPriorityEscalationAddr = getReader().getSymbolAddress(
1704+
"_swift_concurrency_debug_supportsPriorityEscalation");
16161705
if (supportsPriorityEscalationAddr) {
1617-
getReader().readInteger(supportsPriorityEscalationAddr, &supportsPriorityEscalation);
1706+
getReader().readInteger(supportsPriorityEscalationAddr,
1707+
&supportsPriorityEscalation);
16181708
}
16191709

16201710
setupTargetPointers = true;

include/swift/Reflection/RuntimeInternals.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ struct StackAllocator {
9595

9696
template <typename Runtime>
9797
struct ActiveTaskStatusWithEscalation {
98-
uint32_t Flags;
98+
uint32_t Flags[1];
9999
uint32_t ExecutionLock[(sizeof(typename Runtime::StoredPointer) == 8) ? 1 : 2];
100100
typename Runtime::StoredPointer Record;
101101
};
@@ -106,6 +106,16 @@ struct ActiveTaskStatusWithoutEscalation {
106106
typename Runtime::StoredPointer Record;
107107
};
108108

109+
struct ActiveTaskStatusFlags {
110+
static const uint32_t PriorityMask = 0xFF;
111+
static const uint32_t IsCancelled = 0x100;
112+
static const uint32_t IsStatusRecordLocked = 0x200;
113+
static const uint32_t IsEscalated = 0x400;
114+
static const uint32_t IsRunning = 0x800;
115+
static const uint32_t IsEnqueued = 0x1000;
116+
static const uint32_t IsComplete = 0x2000;
117+
};
118+
109119
template <typename Runtime, typename ActiveTaskStatus>
110120
struct AsyncTaskPrivateStorage {
111121
typename Runtime::StoredPointer ExclusivityAccessSet[2];
@@ -150,7 +160,7 @@ struct FutureAsyncContextPrefix {
150160

151161
template <typename Runtime>
152162
struct ActiveActorStatusWithEscalation {
153-
uint32_t Flags;
163+
uint32_t Flags[1];
154164
uint32_t DrainLock[(sizeof(typename Runtime::StoredPointer) == 8) ? 1 : 2];
155165
typename Runtime::StoredPointer FirstJob;
156166
};

include/swift/SwiftRemoteMirror/SwiftRemoteMirrorTypes.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,8 +233,21 @@ typedef struct swift_async_task_info {
233233
/// swift_reflection call on the given context.
234234
const char *Error;
235235

236-
uint32_t JobFlags;
237-
uint64_t TaskStatusFlags;
236+
unsigned Kind;
237+
unsigned EnqueuePriority;
238+
uint8_t IsChildTask;
239+
uint8_t IsFuture;
240+
uint8_t IsGroupChildTask;
241+
uint8_t IsAsyncLetTask;
242+
243+
unsigned MaxPriority;
244+
uint8_t IsCancelled;
245+
uint8_t IsStatusRecordLocked;
246+
uint8_t IsEscalated;
247+
uint8_t HasIsRunning; // If false, the IsRunning flag is not valid.
248+
uint8_t IsRunning;
249+
uint8_t IsEnqueued;
250+
238251
uint64_t Id;
239252
swift_reflection_ptr_t RunJob;
240253
swift_reflection_ptr_t AllocatorSlabPtr;

stdlib/public/Concurrency/TaskPrivate.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,10 @@ class alignas(2 * sizeof(void*)) ActiveTaskStatus {
295295
#endif
296296
};
297297

298+
// Note: this structure is mirrored by ActiveTaskStatusWithEscalation and
299+
// ActiveTaskStatusWithoutEscalation in
300+
// include/swift/Reflection/RuntimeInternals.h. Any changes to the layout here
301+
// must also be made there.
298302
#if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION && SWIFT_POINTER_IS_4_BYTES
299303
uint32_t Flags;
300304
dispatch_lock_t ExecutionLock;

stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -875,9 +875,23 @@ swift_reflection_asyncTaskInfo(SwiftReflectionContextRef ContextRef,
875875
Result.Error = returnableCString(ContextRef, Error);
876876
return Result;
877877
}
878-
Result.JobFlags = TaskInfo.JobFlags;
879-
Result.TaskStatusFlags = TaskInfo.TaskStatusFlags;
878+
879+
Result.Kind = TaskInfo.Kind;
880+
Result.EnqueuePriority = TaskInfo.EnqueuePriority;
881+
Result.IsChildTask = TaskInfo.IsChildTask;
882+
Result.IsFuture = TaskInfo.IsFuture;
883+
Result.IsGroupChildTask = TaskInfo.IsGroupChildTask;
884+
Result.IsAsyncLetTask = TaskInfo.IsAsyncLetTask;
885+
886+
Result.MaxPriority = TaskInfo.MaxPriority;
887+
Result.IsCancelled = TaskInfo.IsCancelled;
888+
Result.IsStatusRecordLocked = TaskInfo.IsStatusRecordLocked;
889+
Result.IsEscalated = TaskInfo.IsEscalated;
890+
Result.HasIsRunning = TaskInfo.HasIsRunning;
891+
Result.IsRunning = TaskInfo.IsRunning;
892+
Result.IsEnqueued = TaskInfo.IsEnqueued;
880893
Result.Id = TaskInfo.Id;
894+
881895
Result.RunJob = TaskInfo.RunJob;
882896
Result.AllocatorSlabPtr = TaskInfo.AllocatorSlabPtr;
883897

0 commit comments

Comments
 (0)