Skip to content

[RemoteMirror][swift-inspect] Decode locks in priority-escalation concurrency runtime. #41855

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions include/swift/Concurrency/Actor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//===--- Actor.h - Swift concurrency actor declarations ---------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// Actor-related declarations that are also used by Remote Mirror.
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_CONCURRENCY_ACTOR_H
#define SWIFT_CONCURRENCY_ACTOR_H

#include <stdint.h>

namespace swift {
namespace concurrency {
namespace ActorFlagConstants {

enum : uint32_t {
// Bits 0-2: Actor state
//
// Possible state transitions for an actor:
//
// Idle -> Running
// Running -> Idle
// Running -> Scheduled
// Scheduled -> Running
// Idle -> Deallocated
// Running -> Zombie_ReadyForDeallocation
// Zombie_ReadyForDeallocation -> Deallocated
//
// It is possible for an actor to be in Running and yet completely released
// by clients. However, the actor needs to be kept alive until it is done
// executing the task that is running on it and gives it up. It is only
// after that that we can safely deallocate it.
ActorStateMask = 0x7,

/// The actor is not currently scheduled. Completely redundant
/// with the job list being empty.
Idle = 0x0,
/// There actor is scheduled
Scheduled = 0x1,
/// There is currently a thread running the actor.
Running = 0x2,
/// The actor is ready for deallocation once it stops running
Zombie_ReadyForDeallocation = 0x3,

// Bit 3
DistributedRemote = 0x8,
// Bit 4
IsPriorityEscalated = 0x10,

// Bits 8 - 15. We only need 8 bits of the whole size_t to represent Job
// Priority
PriorityMask = 0xFF00,
PriorityAndOverrideMask = PriorityMask | IsPriorityEscalated,
PriorityShift = 0x8,
};

} // namespace ActorFlagConstants
} // namespace concurrency
} // namespace swift

#endif // SWIFT_CONCURRENCY_ACTOR_H
81 changes: 73 additions & 8 deletions include/swift/Reflection/ReflectionContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

#include "swift/ABI/Enum.h"
#include "swift/ABI/ObjectFile.h"
#include "swift/Concurrency/Actor.h"
#include "swift/Remote/MemoryReader.h"
#include "swift/Remote/MetadataReader.h"
#include "swift/Reflection/Records.h"
Expand Down Expand Up @@ -58,8 +59,11 @@
// with escalation | present | full
// with escalation | not present | DEGRADED
//
// Currently, degraded info means that IsRunning is not available (indicated
// with `HasIsRunning = false`) and async backtraces are not provided.
// Currently, degraded info has these effects:
// 1. Task.IsRunning is not available, indicated with Task.HasIsRunning = false.
// 2. Task async backtraces are not provided.
// 3. Task and actor thread ports are not available, indicated with
// HasThreadPort = false.

#if __has_include(<dispatch/swift_concurrency_private.h>)
#include <dispatch/swift_concurrency_private.h>
Expand Down Expand Up @@ -185,6 +189,9 @@ class ReflectionContext
bool IsRunning;
bool IsEnqueued;

bool HasThreadPort;
uint32_t ThreadPort;

uint64_t Id;
StoredPointer RunJob;
StoredPointer AllocatorSlabPtr;
Expand All @@ -193,8 +200,15 @@ class ReflectionContext
};

struct ActorInfo {
StoredSize Flags;
StoredPointer FirstJob;

uint8_t State;
bool IsDistributedRemote;
bool IsPriorityEscalated;
uint8_t MaxPriority;

bool HasThreadPort;
uint32_t ThreadPort;
};

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

std::pair<bool, uint32_t> getThreadPort(
const AsyncTask<Runtime, ActiveTaskStatusWithEscalation<Runtime>> *Task) {
#if HAS_DISPATCH_LOCK_IS_LOCKED
return {true,
dispatch_lock_owner(Task->PrivateStorage.Status.ExecutionLock[0])};
#else
// The target runtime was built with priority escalation but we don't have
// the swift_concurrency_private.h header needed to decode the lock.
return {false, 0};
#endif
}

std::pair<bool, uint32_t> getThreadPort(
const AsyncTask<Runtime, ActiveTaskStatusWithoutEscalation<Runtime>>
*Task) {
// Tasks without escalation have no thread port to query.
return {false, 0};
}

std::pair<bool, uint32_t> getThreadPort(
const DefaultActorImpl<Runtime, ActiveActorStatusWithEscalation<Runtime>>
*Actor) {
#if HAS_DISPATCH_LOCK_IS_LOCKED
return {true, dispatch_lock_owner(Actor->Status.DrainLock[0])};
#else
// The target runtime was built with priority escalation but we don't have
// the swift_concurrency_private.h header needed to decode the lock.
return {false, 0};
#endif
}

std::pair<bool, uint32_t>
getThreadPort(const DefaultActorImpl<
Runtime, ActiveActorStatusWithoutEscalation<Runtime>> *Actor) {
// Actors without escalation have no thread port to query.
return {false, 0};
}

template <typename AsyncTaskType>
std::pair<llvm::Optional<std::string>, AsyncTaskInfo>
asyncTaskInfo(StoredPointer AsyncTaskPtr) {
Expand All @@ -1557,6 +1609,8 @@ class ReflectionContext
Info.IsEnqueued = TaskStatusFlags & ActiveTaskStatusFlags::IsEnqueued;

setIsRunning(Info, AsyncTaskObj.get());
std::tie(Info.HasThreadPort, Info.ThreadPort) =
getThreadPort(AsyncTaskObj.get());

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

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

// Status is the low 3 bits of Flags. Status of 0 is Idle. Don't read
// FirstJob when idle.
auto Status = Info.Flags & 0x7;
if (Status != 0) {
uint32_t Flags = ActorObj->Status.Flags[0];
Info.State = Flags & concurrency::ActorFlagConstants::ActorStateMask;
Info.IsDistributedRemote =
Flags & concurrency::ActorFlagConstants::DistributedRemote;
Info.IsPriorityEscalated =
Flags & concurrency::ActorFlagConstants::IsPriorityEscalated;
Info.MaxPriority =
(Flags & concurrency::ActorFlagConstants::PriorityMask) >>
concurrency::ActorFlagConstants::PriorityShift;

// Don't read FirstJob when idle.
if (Info.State != concurrency::ActorFlagConstants::Idle) {
// This is a JobRef which stores flags in the low bits.
Info.FirstJob = ActorObj->Status.FirstJob & ~StoredPointer(0x3);
}

std::tie(Info.HasThreadPort, Info.ThreadPort) =
getThreadPort(ActorObj.get());

return {llvm::None, Info};
}

Expand Down
1 change: 1 addition & 0 deletions include/swift/Reflection/RuntimeInternals.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ struct ActiveActorStatusWithoutEscalation {
template <typename Runtime, typename ActiveActorStatus>
struct DefaultActorImpl {
HeapObject<Runtime> HeapObject;
Job<Runtime> JobStorage;
ActiveActorStatus Status;
};

Expand Down
33 changes: 22 additions & 11 deletions include/swift/SwiftRemoteMirror/SwiftRemoteMirrorTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#ifndef SWIFT_REMOTE_MIRROR_TYPES_H
#define SWIFT_REMOTE_MIRROR_TYPES_H

#include <stdbool.h>
#include <stdint.h>

#ifdef __cplusplus
Expand Down Expand Up @@ -235,18 +236,21 @@ typedef struct swift_async_task_info {

unsigned Kind;
unsigned EnqueuePriority;
uint8_t IsChildTask;
uint8_t IsFuture;
uint8_t IsGroupChildTask;
uint8_t IsAsyncLetTask;
bool IsChildTask;
bool IsFuture;
bool IsGroupChildTask;
bool IsAsyncLetTask;

unsigned MaxPriority;
uint8_t IsCancelled;
uint8_t IsStatusRecordLocked;
uint8_t IsEscalated;
uint8_t HasIsRunning; // If false, the IsRunning flag is not valid.
uint8_t IsRunning;
uint8_t IsEnqueued;
bool IsCancelled;
bool IsStatusRecordLocked;
bool IsEscalated;
bool HasIsRunning; // If false, the IsRunning flag is not valid.
bool IsRunning;
bool IsEnqueued;

bool HasThreadPort;
uint32_t ThreadPort;

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

uint64_t Flags;
uint8_t State;
bool IsDistributedRemote;
bool IsPriorityEscalated;
uint8_t MaxPriority;

swift_reflection_ptr_t FirstJob;

bool HasThreadPort;
uint32_t ThreadPort;
} swift_actor_info_t;

/// An opaque pointer to a context which maintains state and
Expand Down
Loading