Skip to content

Commit 8b51941

Browse files
authored
Merge pull request #41855 from mikeash/swift-inspect-locks
[RemoteMirror][swift-inspect] Decode locks in priority-escalation concurrency runtime.
2 parents a6a330c + ae2b514 commit 8b51941

File tree

8 files changed

+389
-174
lines changed

8 files changed

+389
-174
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)