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+
4669namespace {
4770
4871template <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 ;
0 commit comments