Skip to content

Commit ae2b514

Browse files
committed
[RemoteMirror][swift-inspect] Decode locks in priority-escalation concurrency runtime.
When possible, decode the DrainLock/ExecutionLock fields of tasks and actors in concurrency runtimes built with priority escalation, and show the corresponding thread info in swift-inspect output. We weren't properly decoding actor flags previously, so fix that up as well and have Remote Mirror split them out into separate fields so clients don't have to. We were missing the Job Storage field from the definition of DefaultActorImpl in RuntimeInternals.h, fix that so we actually read the right data. rdar://88598003
1 parent a64b75e commit ae2b514

File tree

8 files changed

+391
-176
lines changed

8 files changed

+391
-176
lines changed

include/swift/Concurrency/Actor.h

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
//===--- Actor.h - Swift concurrency actor declarations ---------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// Actor-related declarations that are also used by Remote Mirror.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#ifndef SWIFT_CONCURRENCY_ACTOR_H
18+
#define SWIFT_CONCURRENCY_ACTOR_H
19+
20+
#include <stdint.h>
21+
22+
namespace swift {
23+
namespace concurrency {
24+
namespace ActorFlagConstants {
25+
26+
enum : uint32_t {
27+
// Bits 0-2: Actor state
28+
//
29+
// Possible state transitions for an actor:
30+
//
31+
// Idle -> Running
32+
// Running -> Idle
33+
// Running -> Scheduled
34+
// Scheduled -> Running
35+
// Idle -> Deallocated
36+
// Running -> Zombie_ReadyForDeallocation
37+
// Zombie_ReadyForDeallocation -> Deallocated
38+
//
39+
// It is possible for an actor to be in Running and yet completely released
40+
// by clients. However, the actor needs to be kept alive until it is done
41+
// executing the task that is running on it and gives it up. It is only
42+
// after that that we can safely deallocate it.
43+
ActorStateMask = 0x7,
44+
45+
/// The actor is not currently scheduled. Completely redundant
46+
/// with the job list being empty.
47+
Idle = 0x0,
48+
/// There actor is scheduled
49+
Scheduled = 0x1,
50+
/// There is currently a thread running the actor.
51+
Running = 0x2,
52+
/// The actor is ready for deallocation once it stops running
53+
Zombie_ReadyForDeallocation = 0x3,
54+
55+
// Bit 3
56+
DistributedRemote = 0x8,
57+
// Bit 4
58+
IsPriorityEscalated = 0x10,
59+
60+
// Bits 8 - 15. We only need 8 bits of the whole size_t to represent Job
61+
// Priority
62+
PriorityMask = 0xFF00,
63+
PriorityAndOverrideMask = PriorityMask | IsPriorityEscalated,
64+
PriorityShift = 0x8,
65+
};
66+
67+
} // namespace ActorFlagConstants
68+
} // namespace concurrency
69+
} // namespace swift
70+
71+
#endif // SWIFT_CONCURRENCY_ACTOR_H

include/swift/Reflection/ReflectionContext.h

Lines changed: 73 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
#include "swift/ABI/Enum.h"
2929
#include "swift/ABI/ObjectFile.h"
30+
#include "swift/Concurrency/Actor.h"
3031
#include "swift/Remote/MemoryReader.h"
3132
#include "swift/Remote/MetadataReader.h"
3233
#include "swift/Reflection/Records.h"
@@ -58,8 +59,11 @@
5859
// with escalation | present | full
5960
// with escalation | not present | DEGRADED
6061
//
61-
// Currently, degraded info means that IsRunning is not available (indicated
62-
// with `HasIsRunning = false`) and async backtraces are not provided.
62+
// Currently, degraded info has these effects:
63+
// 1. Task.IsRunning is not available, indicated with Task.HasIsRunning = false.
64+
// 2. Task async backtraces are not provided.
65+
// 3. Task and actor thread ports are not available, indicated with
66+
// HasThreadPort = false.
6367

6468
#if __has_include(<dispatch/swift_concurrency_private.h>)
6569
#include <dispatch/swift_concurrency_private.h>
@@ -185,6 +189,9 @@ class ReflectionContext
185189
bool IsRunning;
186190
bool IsEnqueued;
187191

192+
bool HasThreadPort;
193+
uint32_t ThreadPort;
194+
188195
uint64_t Id;
189196
StoredPointer RunJob;
190197
StoredPointer AllocatorSlabPtr;
@@ -193,8 +200,15 @@ class ReflectionContext
193200
};
194201

195202
struct ActorInfo {
196-
StoredSize Flags;
197203
StoredPointer FirstJob;
204+
205+
uint8_t State;
206+
bool IsDistributedRemote;
207+
bool IsPriorityEscalated;
208+
uint8_t MaxPriority;
209+
210+
bool HasThreadPort;
211+
uint32_t ThreadPort;
198212
};
199213

200214
explicit ReflectionContext(std::shared_ptr<MemoryReader> reader)
@@ -1532,6 +1546,44 @@ class ReflectionContext
15321546
Task->PrivateStorage.Status.Flags[0] & ActiveTaskStatusFlags::IsRunning;
15331547
}
15341548

1549+
std::pair<bool, uint32_t> getThreadPort(
1550+
const AsyncTask<Runtime, ActiveTaskStatusWithEscalation<Runtime>> *Task) {
1551+
#if HAS_DISPATCH_LOCK_IS_LOCKED
1552+
return {true,
1553+
dispatch_lock_owner(Task->PrivateStorage.Status.ExecutionLock[0])};
1554+
#else
1555+
// The target runtime was built with priority escalation but we don't have
1556+
// the swift_concurrency_private.h header needed to decode the lock.
1557+
return {false, 0};
1558+
#endif
1559+
}
1560+
1561+
std::pair<bool, uint32_t> getThreadPort(
1562+
const AsyncTask<Runtime, ActiveTaskStatusWithoutEscalation<Runtime>>
1563+
*Task) {
1564+
// Tasks without escalation have no thread port to query.
1565+
return {false, 0};
1566+
}
1567+
1568+
std::pair<bool, uint32_t> getThreadPort(
1569+
const DefaultActorImpl<Runtime, ActiveActorStatusWithEscalation<Runtime>>
1570+
*Actor) {
1571+
#if HAS_DISPATCH_LOCK_IS_LOCKED
1572+
return {true, dispatch_lock_owner(Actor->Status.DrainLock[0])};
1573+
#else
1574+
// The target runtime was built with priority escalation but we don't have
1575+
// the swift_concurrency_private.h header needed to decode the lock.
1576+
return {false, 0};
1577+
#endif
1578+
}
1579+
1580+
std::pair<bool, uint32_t>
1581+
getThreadPort(const DefaultActorImpl<
1582+
Runtime, ActiveActorStatusWithoutEscalation<Runtime>> *Actor) {
1583+
// Actors without escalation have no thread port to query.
1584+
return {false, 0};
1585+
}
1586+
15351587
template <typename AsyncTaskType>
15361588
std::pair<llvm::Optional<std::string>, AsyncTaskInfo>
15371589
asyncTaskInfo(StoredPointer AsyncTaskPtr) {
@@ -1557,6 +1609,8 @@ class ReflectionContext
15571609
Info.IsEnqueued = TaskStatusFlags & ActiveTaskStatusFlags::IsEnqueued;
15581610

15591611
setIsRunning(Info, AsyncTaskObj.get());
1612+
std::tie(Info.HasThreadPort, Info.ThreadPort) =
1613+
getThreadPort(AsyncTaskObj.get());
15601614

15611615
Info.Id =
15621616
AsyncTaskObj->Id | ((uint64_t)AsyncTaskObj->PrivateStorage.Id << 32);
@@ -1626,15 +1680,26 @@ class ReflectionContext
16261680
return {std::string("failure reading actor"), {}};
16271681

16281682
ActorInfo Info{};
1629-
Info.Flags = ActorObj->Status.Flags[0];
16301683

1631-
// Status is the low 3 bits of Flags. Status of 0 is Idle. Don't read
1632-
// FirstJob when idle.
1633-
auto Status = Info.Flags & 0x7;
1634-
if (Status != 0) {
1684+
uint32_t Flags = ActorObj->Status.Flags[0];
1685+
Info.State = Flags & concurrency::ActorFlagConstants::ActorStateMask;
1686+
Info.IsDistributedRemote =
1687+
Flags & concurrency::ActorFlagConstants::DistributedRemote;
1688+
Info.IsPriorityEscalated =
1689+
Flags & concurrency::ActorFlagConstants::IsPriorityEscalated;
1690+
Info.MaxPriority =
1691+
(Flags & concurrency::ActorFlagConstants::PriorityMask) >>
1692+
concurrency::ActorFlagConstants::PriorityShift;
1693+
1694+
// Don't read FirstJob when idle.
1695+
if (Info.State != concurrency::ActorFlagConstants::Idle) {
16351696
// This is a JobRef which stores flags in the low bits.
16361697
Info.FirstJob = ActorObj->Status.FirstJob & ~StoredPointer(0x3);
16371698
}
1699+
1700+
std::tie(Info.HasThreadPort, Info.ThreadPort) =
1701+
getThreadPort(ActorObj.get());
1702+
16381703
return {llvm::None, Info};
16391704
}
16401705

include/swift/Reflection/RuntimeInternals.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ struct ActiveActorStatusWithoutEscalation {
174174
template <typename Runtime, typename ActiveActorStatus>
175175
struct DefaultActorImpl {
176176
HeapObject<Runtime> HeapObject;
177+
Job<Runtime> JobStorage;
177178
ActiveActorStatus Status;
178179
};
179180

include/swift/SwiftRemoteMirror/SwiftRemoteMirrorTypes.h

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#ifndef SWIFT_REMOTE_MIRROR_TYPES_H
1919
#define SWIFT_REMOTE_MIRROR_TYPES_H
2020

21+
#include <stdbool.h>
2122
#include <stdint.h>
2223

2324
#ifdef __cplusplus
@@ -235,18 +236,21 @@ typedef struct swift_async_task_info {
235236

236237
unsigned Kind;
237238
unsigned EnqueuePriority;
238-
uint8_t IsChildTask;
239-
uint8_t IsFuture;
240-
uint8_t IsGroupChildTask;
241-
uint8_t IsAsyncLetTask;
239+
bool IsChildTask;
240+
bool IsFuture;
241+
bool IsGroupChildTask;
242+
bool IsAsyncLetTask;
242243

243244
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;
245+
bool IsCancelled;
246+
bool IsStatusRecordLocked;
247+
bool IsEscalated;
248+
bool HasIsRunning; // If false, the IsRunning flag is not valid.
249+
bool IsRunning;
250+
bool IsEnqueued;
251+
252+
bool HasThreadPort;
253+
uint32_t ThreadPort;
250254

251255
uint64_t Id;
252256
swift_reflection_ptr_t RunJob;
@@ -265,8 +269,15 @@ typedef struct swift_actor_info {
265269
/// swift_reflection call on the given context.
266270
const char *Error;
267271

268-
uint64_t Flags;
272+
uint8_t State;
273+
bool IsDistributedRemote;
274+
bool IsPriorityEscalated;
275+
uint8_t MaxPriority;
276+
269277
swift_reflection_ptr_t FirstJob;
278+
279+
bool HasThreadPort;
280+
uint32_t ThreadPort;
270281
} swift_actor_info_t;
271282

272283
/// An opaque pointer to a context which maintains state and

0 commit comments

Comments
 (0)