mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[RemoteMirror][swift-inspect] Add a command to inspect the state of the concurrency runtime.
Most of the new inspection logic is in Remote Mirror. New code in swift-inspect calls the new Remote Mirror functions and formats the resulting information for display. Specific Remote Mirror changes: * Add a call to check if a given metadata is an actor. * Add calls to get information about actors and tasks. * Add a `readObj` call to MemoryReader that combines the read and the cast, greatly simplifying code chasing pointers in the remote process. * Add a generalized facility to the C shims that can allocate a temporary object that remains valid until at least the next call, which is used to return various temporary arrays from the new calls. Remove the existing `lastString` and `lastChunks` member variables in favor of this new facility. Swift-inspect changes: * Add a new dump-concurrency command. * Add a new `ConcurrencyDumper.swift` file with the implementation. The dumper needs to do some additional work with the results from Remote Mirror to build up the task tree and this keeps it all organized. * Extend `Inspector` to query the target's threads and fetch each thread's current task. Concurrency runtime changes: * Add `_swift_concurrency_debug` variables pointing to the various future adapters. Remote Mirror uses these to provide a better view of a tasks's resume pointer. rdar://85231338
This commit is contained in:
@@ -101,6 +101,12 @@ class ReflectionContext
|
|||||||
std::vector<MemoryReader::ReadBytesResult> savedBuffers;
|
std::vector<MemoryReader::ReadBytesResult> savedBuffers;
|
||||||
std::vector<std::tuple<RemoteAddress, RemoteAddress>> imageRanges;
|
std::vector<std::tuple<RemoteAddress, RemoteAddress>> imageRanges;
|
||||||
|
|
||||||
|
bool setupTargetPointers = false;
|
||||||
|
typename super::StoredPointer target_non_future_adapter = 0;
|
||||||
|
typename super::StoredPointer target_future_adapter = 0;
|
||||||
|
typename super::StoredPointer target_task_wait_throwing_resume_adapter = 0;
|
||||||
|
typename super::StoredPointer target_task_future_wait_resume_adapter = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using super::getBuilder;
|
using super::getBuilder;
|
||||||
using super::readDemanglingForContextDescriptor;
|
using super::readDemanglingForContextDescriptor;
|
||||||
@@ -137,6 +143,21 @@ public:
|
|||||||
std::vector<AsyncTaskAllocationChunk> Chunks;
|
std::vector<AsyncTaskAllocationChunk> Chunks;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AsyncTaskInfo {
|
||||||
|
uint32_t JobFlags;
|
||||||
|
uint64_t TaskStatusFlags;
|
||||||
|
uint64_t Id;
|
||||||
|
StoredPointer RunJob;
|
||||||
|
StoredPointer AllocatorSlabPtr;
|
||||||
|
std::vector<StoredPointer> ChildTasks;
|
||||||
|
std::vector<StoredPointer> AsyncBacktraceFrames;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ActorInfo {
|
||||||
|
StoredSize Flags;
|
||||||
|
StoredPointer FirstJob;
|
||||||
|
};
|
||||||
|
|
||||||
explicit ReflectionContext(std::shared_ptr<MemoryReader> reader)
|
explicit ReflectionContext(std::shared_ptr<MemoryReader> reader)
|
||||||
: super(std::move(reader), *this)
|
: super(std::move(reader), *this)
|
||||||
{}
|
{}
|
||||||
@@ -1067,6 +1088,31 @@ public:
|
|||||||
return dyn_cast_or_null<const RecordTypeInfo>(TypeInfo);
|
return dyn_cast_or_null<const RecordTypeInfo>(TypeInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool metadataIsActor(StoredPointer MetadataAddress) {
|
||||||
|
auto Metadata = readMetadata(MetadataAddress);
|
||||||
|
if (!Metadata)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Only classes can be actors.
|
||||||
|
if (Metadata->getKind() != MetadataKind::Class)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto DescriptorAddress =
|
||||||
|
super::readAddressOfNominalTypeDescriptor(Metadata);
|
||||||
|
if (!DescriptorAddress)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto DescriptorBytes =
|
||||||
|
getReader().readBytes(RemoteAddress(DescriptorAddress),
|
||||||
|
sizeof(TargetTypeContextDescriptor<Runtime>));
|
||||||
|
if (!DescriptorBytes)
|
||||||
|
return false;
|
||||||
|
auto Descriptor =
|
||||||
|
reinterpret_cast<const TargetTypeContextDescriptor<Runtime> *>(
|
||||||
|
DescriptorBytes.get());
|
||||||
|
return Descriptor->getTypeContextDescriptorFlags().class_isActor();
|
||||||
|
}
|
||||||
|
|
||||||
/// Iterate the protocol conformance cache tree rooted at NodePtr, calling
|
/// Iterate the protocol conformance cache tree rooted at NodePtr, calling
|
||||||
/// Call with the type and protocol in each node.
|
/// Call with the type and protocol in each node.
|
||||||
void iterateConformanceTree(StoredPointer NodePtr,
|
void iterateConformanceTree(StoredPointer NodePtr,
|
||||||
@@ -1378,22 +1424,179 @@ public:
|
|||||||
return {llvm::None, {Slab->Next, SlabSize, {Chunk}}};
|
return {llvm::None, {Slab->Next, SlabSize, {Chunk}}};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<llvm::Optional<std::string>, StoredPointer>
|
std::pair<llvm::Optional<std::string>, AsyncTaskInfo>
|
||||||
asyncTaskSlabPtr(StoredPointer AsyncTaskPtr) {
|
asyncTaskInfo(StoredPointer AsyncTaskPtr) {
|
||||||
using AsyncTask = AsyncTask<Runtime>;
|
auto AsyncTaskObj = readObj<AsyncTask<Runtime>>(AsyncTaskPtr);
|
||||||
|
|
||||||
auto AsyncTaskBytes =
|
|
||||||
getReader().readBytes(RemoteAddress(AsyncTaskPtr), sizeof(AsyncTask));
|
|
||||||
auto *AsyncTaskObj =
|
|
||||||
reinterpret_cast<const AsyncTask *>(AsyncTaskBytes.get());
|
|
||||||
if (!AsyncTaskObj)
|
if (!AsyncTaskObj)
|
||||||
return {std::string("failure reading async task"), 0};
|
return {std::string("failure reading async task"), {}};
|
||||||
|
|
||||||
StoredPointer SlabPtr = AsyncTaskObj->PrivateStorage.Allocator.FirstSlab;
|
AsyncTaskInfo Info{};
|
||||||
return {llvm::None, SlabPtr};
|
Info.JobFlags = AsyncTaskObj->Flags;
|
||||||
|
Info.TaskStatusFlags = AsyncTaskObj->PrivateStorage.Status.Flags;
|
||||||
|
Info.Id =
|
||||||
|
AsyncTaskObj->Id | ((uint64_t)AsyncTaskObj->PrivateStorage.Id << 32);
|
||||||
|
Info.AllocatorSlabPtr = AsyncTaskObj->PrivateStorage.Allocator.FirstSlab;
|
||||||
|
Info.RunJob = getRunJob(AsyncTaskObj.get());
|
||||||
|
|
||||||
|
// Find all child tasks.
|
||||||
|
auto RecordPtr = AsyncTaskObj->PrivateStorage.Status.Record;
|
||||||
|
while (RecordPtr) {
|
||||||
|
auto RecordObj = readObj<TaskStatusRecord<Runtime>>(RecordPtr);
|
||||||
|
if (!RecordObj)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// This cuts off high bits if our size_t doesn't match the target's. We
|
||||||
|
// only read the Kind bits which are at the bottom, so that's OK here.
|
||||||
|
// Beware of this when reading anything else.
|
||||||
|
TaskStatusRecordFlags Flags{RecordObj->Flags};
|
||||||
|
auto Kind = Flags.getKind();
|
||||||
|
|
||||||
|
StoredPointer ChildTask = 0;
|
||||||
|
if (Kind == TaskStatusRecordKind::ChildTask) {
|
||||||
|
auto RecordObj = readObj<ChildTaskStatusRecord<Runtime>>(RecordPtr);
|
||||||
|
if (RecordObj)
|
||||||
|
ChildTask = RecordObj->FirstChild;
|
||||||
|
} else if (Kind == TaskStatusRecordKind::TaskGroup) {
|
||||||
|
auto RecordObj = readObj<TaskGroupTaskStatusRecord<Runtime>>(RecordPtr);
|
||||||
|
if (RecordObj)
|
||||||
|
ChildTask = RecordObj->FirstChild;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (ChildTask) {
|
||||||
|
Info.ChildTasks.push_back(ChildTask);
|
||||||
|
|
||||||
|
StoredPointer ChildFragmentAddr =
|
||||||
|
ChildTask + sizeof(AsyncTask<Runtime>);
|
||||||
|
auto ChildFragmentObj =
|
||||||
|
readObj<ChildFragment<Runtime>>(ChildFragmentAddr);
|
||||||
|
if (ChildFragmentObj)
|
||||||
|
ChildTask = ChildFragmentObj->NextChild;
|
||||||
|
else
|
||||||
|
ChildTask = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
RecordPtr = RecordObj->Parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Walk the async backtrace if the task isn't running or cancelled.
|
||||||
|
// TODO: Use isEnqueued from https://github.com/apple/swift/pull/41088/ once
|
||||||
|
// that's available.
|
||||||
|
int IsCancelledFlag = 0x100;
|
||||||
|
int IsRunningFlag = 0x800;
|
||||||
|
if (!(AsyncTaskObj->PrivateStorage.Status.Flags & IsCancelledFlag) &&
|
||||||
|
!(AsyncTaskObj->PrivateStorage.Status.Flags & IsRunningFlag)) {
|
||||||
|
auto ResumeContext = AsyncTaskObj->ResumeContextAndReserved[0];
|
||||||
|
while (ResumeContext) {
|
||||||
|
auto ResumeContextObj = readObj<AsyncContext<Runtime>>(ResumeContext);
|
||||||
|
if (!ResumeContextObj)
|
||||||
|
break;
|
||||||
|
Info.AsyncBacktraceFrames.push_back(
|
||||||
|
stripSignedPointer(ResumeContextObj->ResumeParent));
|
||||||
|
ResumeContext = stripSignedPointer(ResumeContextObj->Parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {llvm::None, Info};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<llvm::Optional<std::string>, ActorInfo>
|
||||||
|
actorInfo(StoredPointer ActorPtr) {
|
||||||
|
using DefaultActorImpl = DefaultActorImpl<Runtime>;
|
||||||
|
|
||||||
|
auto ActorObj = readObj<DefaultActorImpl>(ActorPtr);
|
||||||
|
if (!ActorObj)
|
||||||
|
return {std::string("failure reading actor"), {}};
|
||||||
|
|
||||||
|
ActorInfo Info{};
|
||||||
|
Info.Flags = ActorObj->Flags;
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
// This is a JobRef which stores flags in the low bits.
|
||||||
|
Info.FirstJob = ActorObj->FirstJob & ~StoredPointer(0x3);
|
||||||
|
}
|
||||||
|
return {llvm::None, Info};
|
||||||
|
}
|
||||||
|
|
||||||
|
StoredPointer nextJob(StoredPointer JobPtr) {
|
||||||
|
using Job = Job<Runtime>;
|
||||||
|
|
||||||
|
auto JobBytes = getReader().readBytes(RemoteAddress(JobPtr), sizeof(Job));
|
||||||
|
auto *JobObj = reinterpret_cast<const Job *>(JobBytes.get());
|
||||||
|
if (!JobObj)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// This is a JobRef which stores flags in the low bits.
|
||||||
|
return JobObj->SchedulerPrivate[0] & ~StoredPointer(0x3);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Get the most human meaningful "run job" function pointer from the task,
|
||||||
|
// like AsyncTask::getResumeFunctionForLogging does.
|
||||||
|
StoredPointer getRunJob(const AsyncTask<Runtime> *AsyncTaskObj) {
|
||||||
|
auto Fptr = stripSignedPointer(AsyncTaskObj->RunJob);
|
||||||
|
|
||||||
|
loadTargetPointers();
|
||||||
|
auto ResumeContextPtr = AsyncTaskObj->ResumeContextAndReserved[0];
|
||||||
|
if (target_non_future_adapter && Fptr == target_non_future_adapter) {
|
||||||
|
using Prefix = AsyncContextPrefix<Runtime>;
|
||||||
|
auto PrefixAddr = ResumeContextPtr - sizeof(Prefix);
|
||||||
|
auto PrefixBytes =
|
||||||
|
getReader().readBytes(RemoteAddress(PrefixAddr), sizeof(Prefix));
|
||||||
|
if (PrefixBytes) {
|
||||||
|
auto PrefixPtr = reinterpret_cast<const Prefix *>(PrefixBytes.get());
|
||||||
|
return stripSignedPointer(PrefixPtr->AsyncEntryPoint);
|
||||||
|
}
|
||||||
|
} else if (target_future_adapter && Fptr == target_future_adapter) {
|
||||||
|
using Prefix = FutureAsyncContextPrefix<Runtime>;
|
||||||
|
auto PrefixAddr = ResumeContextPtr - sizeof(Prefix);
|
||||||
|
auto PrefixBytes =
|
||||||
|
getReader().readBytes(RemoteAddress(PrefixAddr), sizeof(Prefix));
|
||||||
|
if (PrefixBytes) {
|
||||||
|
auto PrefixPtr = reinterpret_cast<const Prefix *>(PrefixBytes.get());
|
||||||
|
return stripSignedPointer(PrefixPtr->AsyncEntryPoint);
|
||||||
|
}
|
||||||
|
} else if ((target_task_wait_throwing_resume_adapter &&
|
||||||
|
Fptr == target_task_wait_throwing_resume_adapter) ||
|
||||||
|
(target_task_future_wait_resume_adapter &&
|
||||||
|
Fptr == target_task_future_wait_resume_adapter)) {
|
||||||
|
auto ContextBytes = getReader().readBytes(RemoteAddress(ResumeContextPtr),
|
||||||
|
sizeof(AsyncContext<Runtime>));
|
||||||
|
if (ContextBytes) {
|
||||||
|
auto ContextPtr =
|
||||||
|
reinterpret_cast<const AsyncContext<Runtime> *>(ContextBytes.get());
|
||||||
|
return stripSignedPointer(ContextPtr->ResumeParent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Fptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadTargetPointers() {
|
||||||
|
if (setupTargetPointers)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto getFunc = [&](const std::string &name) -> StoredPointer {
|
||||||
|
auto Symbol = getReader().getSymbolAddress(name);
|
||||||
|
if (!Symbol)
|
||||||
|
return 0;
|
||||||
|
auto Pointer = getReader().readPointer(Symbol, sizeof(StoredPointer));
|
||||||
|
if (!Pointer)
|
||||||
|
return 0;
|
||||||
|
return Pointer->getResolvedAddress().getAddressData();
|
||||||
|
};
|
||||||
|
target_non_future_adapter =
|
||||||
|
getFunc("_swift_concurrency_debug_non_future_adapter");
|
||||||
|
target_future_adapter = getFunc("_swift_concurrency_debug_future_adapter");
|
||||||
|
target_task_wait_throwing_resume_adapter =
|
||||||
|
getFunc("_swift_concurrency_debug_task_wait_throwing_resume_adapter");
|
||||||
|
target_task_future_wait_resume_adapter =
|
||||||
|
getFunc("_swift_concurrency_debug_task_future_wait_resume_adapter");
|
||||||
|
setupTargetPointers = true;
|
||||||
|
}
|
||||||
|
|
||||||
const TypeInfo *
|
const TypeInfo *
|
||||||
getClosureContextInfo(StoredPointer Context, const ClosureContextInfo &Info,
|
getClosureContextInfo(StoredPointer Context, const ClosureContextInfo &Info,
|
||||||
remote::TypeInfoProvider *ExternalTypeInfo) {
|
remote::TypeInfoProvider *ExternalTypeInfo) {
|
||||||
@@ -1615,6 +1818,11 @@ private:
|
|||||||
|
|
||||||
return llvm::None;
|
return llvm::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
MemoryReader::ReadObjResult<T> readObj(StoredPointer Ptr) {
|
||||||
|
return getReader().template readObj<T>(RemoteAddress(Ptr));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end namespace reflection
|
} // end namespace reflection
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ struct Job {
|
|||||||
uint32_t Flags;
|
uint32_t Flags;
|
||||||
uint32_t Id;
|
uint32_t Id;
|
||||||
typename Runtime::StoredPointer Reserved[2];
|
typename Runtime::StoredPointer Reserved[2];
|
||||||
typename Runtime::StoredPointer RunJob;
|
typename Runtime::StoredSignedPointer RunJob;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Runtime>
|
template <typename Runtime>
|
||||||
@@ -104,6 +104,8 @@ struct AsyncTaskPrivateStorage {
|
|||||||
ActiveTaskStatus<Runtime> Status;
|
ActiveTaskStatus<Runtime> Status;
|
||||||
StackAllocator<Runtime> Allocator;
|
StackAllocator<Runtime> Allocator;
|
||||||
typename Runtime::StoredPointer Local;
|
typename Runtime::StoredPointer Local;
|
||||||
|
typename Runtime::StoredPointer ExclusivityAccessSet[2];
|
||||||
|
uint32_t Id;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Runtime>
|
template <typename Runtime>
|
||||||
@@ -112,7 +114,61 @@ struct AsyncTask: Job<Runtime> {
|
|||||||
typename Runtime::StoredPointer ResumeContextAndReserved[
|
typename Runtime::StoredPointer ResumeContextAndReserved[
|
||||||
sizeof(typename Runtime::StoredPointer) == 8 ? 2 : 1];
|
sizeof(typename Runtime::StoredPointer) == 8 ? 2 : 1];
|
||||||
|
|
||||||
AsyncTaskPrivateStorage<Runtime> PrivateStorage;
|
union {
|
||||||
|
AsyncTaskPrivateStorage<Runtime> PrivateStorage;
|
||||||
|
typename Runtime::StoredPointer PrivateStorageRaw[14];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Runtime>
|
||||||
|
struct AsyncContext {
|
||||||
|
typename Runtime::StoredSignedPointer Parent;
|
||||||
|
typename Runtime::StoredSignedPointer ResumeParent;
|
||||||
|
uint32_t Flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Runtime>
|
||||||
|
struct AsyncContextPrefix {
|
||||||
|
typename Runtime::StoredSignedPointer AsyncEntryPoint;
|
||||||
|
typename Runtime::StoredPointer ClosureContext;
|
||||||
|
typename Runtime::StoredPointer ErrorResult;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Runtime>
|
||||||
|
struct FutureAsyncContextPrefix {
|
||||||
|
typename Runtime::StoredPointer IndirectResult;
|
||||||
|
typename Runtime::StoredSignedPointer AsyncEntryPoint;
|
||||||
|
typename Runtime::StoredPointer ClosureContext;
|
||||||
|
typename Runtime::StoredPointer ErrorResult;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Runtime>
|
||||||
|
struct DefaultActorImpl {
|
||||||
|
HeapObject<Runtime> HeapObject;
|
||||||
|
typename Runtime::StoredPointer FirstJob;
|
||||||
|
typename Runtime::StoredSize Flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Runtime>
|
||||||
|
struct TaskStatusRecord {
|
||||||
|
typename Runtime::StoredSize Flags;
|
||||||
|
typename Runtime::StoredPointer Parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Runtime>
|
||||||
|
struct ChildTaskStatusRecord : TaskStatusRecord<Runtime> {
|
||||||
|
typename Runtime::StoredPointer FirstChild;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Runtime>
|
||||||
|
struct TaskGroupTaskStatusRecord : TaskStatusRecord<Runtime> {
|
||||||
|
typename Runtime::StoredPointer FirstChild;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Runtime>
|
||||||
|
struct ChildFragment {
|
||||||
|
typename Runtime::StoredPointer Parent;
|
||||||
|
typename Runtime::StoredPointer NextChild;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end namespace reflection
|
} // end namespace reflection
|
||||||
|
|||||||
@@ -40,6 +40,10 @@ public:
|
|||||||
using ReadBytesResult =
|
using ReadBytesResult =
|
||||||
std::unique_ptr<const void, std::function<void(const void *)>>;
|
std::unique_ptr<const void, std::function<void(const void *)>>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using ReadObjResult =
|
||||||
|
std::unique_ptr<const T, std::function<void(const void *)>>;
|
||||||
|
|
||||||
virtual bool queryDataLayout(DataLayoutQueryType type, void *inBuffer,
|
virtual bool queryDataLayout(DataLayoutQueryType type, void *inBuffer,
|
||||||
void *outBuffer) = 0;
|
void *outBuffer) = 0;
|
||||||
|
|
||||||
@@ -90,6 +94,15 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
ReadObjResult<T> readObj(RemoteAddress address) {
|
||||||
|
auto bytes = readBytes(address, sizeof(T));
|
||||||
|
auto deleter = bytes.get_deleter();
|
||||||
|
auto ptr = bytes.get();
|
||||||
|
bytes.release();
|
||||||
|
return ReadObjResult<T>(reinterpret_cast<const T *>(ptr), deleter);
|
||||||
|
}
|
||||||
|
|
||||||
/// Attempts to read 'size' bytes from the given address in the remote process.
|
/// Attempts to read 'size' bytes from the given address in the remote process.
|
||||||
///
|
///
|
||||||
/// Returns a pointer to the requested data and a function that must be called to
|
/// Returns a pointer to the requested data and a function that must be called to
|
||||||
|
|||||||
@@ -151,6 +151,11 @@ swift_reflection_metadataNominalTypeDescriptor(SwiftReflectionContextRef Context
|
|||||||
swift_reflection_ptr_t Metadata);
|
swift_reflection_ptr_t Metadata);
|
||||||
|
|
||||||
|
|
||||||
|
SWIFT_REMOTE_MIRROR_LINKAGE
|
||||||
|
int
|
||||||
|
swift_reflection_metadataIsActor(SwiftReflectionContextRef ContextRef,
|
||||||
|
swift_reflection_ptr_t Metadata);
|
||||||
|
|
||||||
/// Returns an opaque type reference for a class or closure context
|
/// Returns an opaque type reference for a class or closure context
|
||||||
/// instance pointer, or NULL if one can't be constructed.
|
/// instance pointer, or NULL if one can't be constructed.
|
||||||
///
|
///
|
||||||
@@ -435,6 +440,21 @@ swift_async_task_slab_allocations_return_t
|
|||||||
swift_reflection_asyncTaskSlabAllocations(SwiftReflectionContextRef ContextRef,
|
swift_reflection_asyncTaskSlabAllocations(SwiftReflectionContextRef ContextRef,
|
||||||
swift_reflection_ptr_t SlabPtr);
|
swift_reflection_ptr_t SlabPtr);
|
||||||
|
|
||||||
|
SWIFT_REMOTE_MIRROR_LINKAGE
|
||||||
|
swift_async_task_info_t
|
||||||
|
swift_reflection_asyncTaskInfo(SwiftReflectionContextRef ContextRef,
|
||||||
|
swift_reflection_ptr_t AsyncTaskPtr);
|
||||||
|
|
||||||
|
SWIFT_REMOTE_MIRROR_LINKAGE
|
||||||
|
swift_actor_info_t
|
||||||
|
swift_reflection_actorInfo(SwiftReflectionContextRef ContextRef,
|
||||||
|
swift_reflection_ptr_t ActorPtr);
|
||||||
|
|
||||||
|
SWIFT_REMOTE_MIRROR_LINKAGE
|
||||||
|
swift_reflection_ptr_t
|
||||||
|
swift_reflection_nextJob(SwiftReflectionContextRef ContextRef,
|
||||||
|
swift_reflection_ptr_t JobPtr);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -225,6 +225,35 @@ typedef struct swift_async_task_slab_allocations_return {
|
|||||||
swift_async_task_allocation_chunk_t *Chunks;
|
swift_async_task_allocation_chunk_t *Chunks;
|
||||||
} swift_async_task_slab_allocations_return_t;
|
} swift_async_task_slab_allocations_return_t;
|
||||||
|
|
||||||
|
typedef struct swift_async_task_info {
|
||||||
|
/// On failure, a pointer to a string describing the error. On success, NULL.
|
||||||
|
/// This pointer remains valid until the next
|
||||||
|
/// swift_reflection call on the given context.
|
||||||
|
const char *Error;
|
||||||
|
|
||||||
|
uint32_t JobFlags;
|
||||||
|
uint64_t TaskStatusFlags;
|
||||||
|
uint64_t Id;
|
||||||
|
swift_reflection_ptr_t RunJob;
|
||||||
|
swift_reflection_ptr_t AllocatorSlabPtr;
|
||||||
|
|
||||||
|
unsigned ChildTaskCount;
|
||||||
|
swift_reflection_ptr_t *ChildTasks;
|
||||||
|
|
||||||
|
unsigned AsyncBacktraceFramesCount;
|
||||||
|
swift_reflection_ptr_t *AsyncBacktraceFrames;
|
||||||
|
} swift_async_task_info_t;
|
||||||
|
|
||||||
|
typedef struct swift_actor_info {
|
||||||
|
/// On failure, a pointer to a string describing the error. On success, NULL.
|
||||||
|
/// This pointer remains valid until the next
|
||||||
|
/// swift_reflection call on the given context.
|
||||||
|
const char *Error;
|
||||||
|
|
||||||
|
uint64_t Flags;
|
||||||
|
swift_reflection_ptr_t FirstJob;
|
||||||
|
} swift_actor_info_t;
|
||||||
|
|
||||||
/// An opaque pointer to a context which maintains state and
|
/// An opaque pointer to a context which maintains state and
|
||||||
/// caching of reflection structure for heap instances.
|
/// caching of reflection structure for heap instances.
|
||||||
typedef struct SwiftReflectionContext *SwiftReflectionContextRef;
|
typedef struct SwiftReflectionContext *SwiftReflectionContextRef;
|
||||||
|
|||||||
@@ -36,6 +36,15 @@ const void *const _swift_concurrency_debug_asyncTaskMetadata;
|
|||||||
SWIFT_EXPORT_FROM(swift_Concurrency)
|
SWIFT_EXPORT_FROM(swift_Concurrency)
|
||||||
const void *const _swift_concurrency_debug_asyncTaskSlabMetadata;
|
const void *const _swift_concurrency_debug_asyncTaskSlabMetadata;
|
||||||
|
|
||||||
|
SWIFT_EXPORT_FROM(swift_Concurrency)
|
||||||
|
const void *const _swift_concurrency_debug_non_future_adapter;
|
||||||
|
SWIFT_EXPORT_FROM(swift_Concurrency)
|
||||||
|
const void *const _swift_concurrency_debug_future_adapter;
|
||||||
|
SWIFT_EXPORT_FROM(swift_Concurrency)
|
||||||
|
const void *const _swift_concurrency_debug_task_wait_throwing_resume_adapter;
|
||||||
|
SWIFT_EXPORT_FROM(swift_Concurrency)
|
||||||
|
const void *const _swift_concurrency_debug_task_future_wait_resume_adapter;
|
||||||
|
|
||||||
} // namespace swift
|
} // namespace swift
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -450,6 +450,17 @@ task_future_wait_resume_adapter(SWIFT_ASYNC_CONTEXT AsyncContext *_context) {
|
|||||||
return _context->ResumeParent(_context->Parent);
|
return _context->ResumeParent(_context->Parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const void *const swift::_swift_concurrency_debug_non_future_adapter =
|
||||||
|
reinterpret_cast<void *>(non_future_adapter);
|
||||||
|
const void *const swift::_swift_concurrency_debug_future_adapter =
|
||||||
|
reinterpret_cast<void *>(future_adapter);
|
||||||
|
const void
|
||||||
|
*const swift::_swift_concurrency_debug_task_wait_throwing_resume_adapter =
|
||||||
|
reinterpret_cast<void *>(task_wait_throwing_resume_adapter);
|
||||||
|
const void
|
||||||
|
*const swift::_swift_concurrency_debug_task_future_wait_resume_adapter =
|
||||||
|
reinterpret_cast<void *>(task_future_wait_resume_adapter);
|
||||||
|
|
||||||
const void *AsyncTask::getResumeFunctionForLogging() {
|
const void *AsyncTask::getResumeFunctionForLogging() {
|
||||||
if (ResumeTask == non_future_adapter) {
|
if (ResumeTask == non_future_adapter) {
|
||||||
auto asyncContextPrefix = reinterpret_cast<AsyncContextPrefix *>(
|
auto asyncContextPrefix = reinterpret_cast<AsyncContextPrefix *>(
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ if(SWIFT_BUILD_DYNAMIC_STDLIB)
|
|||||||
${SWIFT_RUNTIME_LINK_FLAGS}
|
${SWIFT_RUNTIME_LINK_FLAGS}
|
||||||
INCORPORATE_OBJECT_LIBRARIES swiftLLVMSupport
|
INCORPORATE_OBJECT_LIBRARIES swiftLLVMSupport
|
||||||
SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS}
|
SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS}
|
||||||
|
DARWIN_INSTALL_NAME_DIR "${SWIFTLIB_DARWIN_INSTALL_NAME_DIR}"
|
||||||
INSTALL_IN_COMPONENT
|
INSTALL_IN_COMPONENT
|
||||||
swift-remote-mirror)
|
swift-remote-mirror)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -47,8 +47,8 @@ struct SwiftReflectionContext {
|
|||||||
NativeReflectionContext *nativeContext;
|
NativeReflectionContext *nativeContext;
|
||||||
std::vector<std::function<void()>> freeFuncs;
|
std::vector<std::function<void()>> freeFuncs;
|
||||||
std::vector<std::tuple<swift_addr_t, swift_addr_t>> dataSegments;
|
std::vector<std::tuple<swift_addr_t, swift_addr_t>> dataSegments;
|
||||||
std::string lastString;
|
|
||||||
std::vector<swift_async_task_allocation_chunk_t> lastChunks;
|
std::function<void(void)> freeTemporaryAllocation = [] {};
|
||||||
|
|
||||||
SwiftReflectionContext(MemoryReaderImpl impl) {
|
SwiftReflectionContext(MemoryReaderImpl impl) {
|
||||||
auto Reader = std::make_shared<CMemoryReader>(impl);
|
auto Reader = std::make_shared<CMemoryReader>(impl);
|
||||||
@@ -56,10 +56,38 @@ struct SwiftReflectionContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
~SwiftReflectionContext() {
|
~SwiftReflectionContext() {
|
||||||
|
freeTemporaryAllocation();
|
||||||
delete nativeContext;
|
delete nativeContext;
|
||||||
for (auto f : freeFuncs)
|
for (auto f : freeFuncs)
|
||||||
f();
|
f();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Allocate a single temporary object that will stay allocated until the next
|
||||||
|
// call to this method, or until the context is destroyed.
|
||||||
|
template <typename T>
|
||||||
|
T *allocateTemporaryObject() {
|
||||||
|
freeTemporaryAllocation();
|
||||||
|
T *obj = new T;
|
||||||
|
freeTemporaryAllocation = [obj] { delete obj; };
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate a single temporary object that will stay allocated until the next
|
||||||
|
// call to allocateTemporaryObject, or until the context is destroyed. Does
|
||||||
|
// NOT free any existing objects created with allocateTemporaryObject or
|
||||||
|
// allocateSubsequentTemporaryObject. Use to allocate additional objects after
|
||||||
|
// a call to allocateTemporaryObject when muliple objects are needed
|
||||||
|
// simultaneously.
|
||||||
|
template <typename T>
|
||||||
|
T *allocateSubsequentTemporaryObject() {
|
||||||
|
T *obj = new T;
|
||||||
|
auto oldFree = freeTemporaryAllocation;
|
||||||
|
freeTemporaryAllocation = [obj, oldFree] {
|
||||||
|
delete obj;
|
||||||
|
oldFree();
|
||||||
|
};
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -297,6 +325,12 @@ swift_reflection_metadataNominalTypeDescriptor(SwiftReflectionContextRef Context
|
|||||||
return Context->nominalTypeDescriptorFromMetadata(MetadataAddress);
|
return Context->nominalTypeDescriptorFromMetadata(MetadataAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int swift_reflection_metadataIsActor(SwiftReflectionContextRef ContextRef,
|
||||||
|
swift_reflection_ptr_t Metadata) {
|
||||||
|
auto Context = ContextRef->nativeContext;
|
||||||
|
return Context->metadataIsActor(Metadata);
|
||||||
|
}
|
||||||
|
|
||||||
swift_typeref_t
|
swift_typeref_t
|
||||||
swift_reflection_typeRefForInstance(SwiftReflectionContextRef ContextRef,
|
swift_reflection_typeRefForInstance(SwiftReflectionContextRef ContextRef,
|
||||||
uintptr_t Object) {
|
uintptr_t Object) {
|
||||||
@@ -505,8 +539,9 @@ static swift_layout_kind_t convertAllocationChunkKind(
|
|||||||
static const char *returnableCString(SwiftReflectionContextRef ContextRef,
|
static const char *returnableCString(SwiftReflectionContextRef ContextRef,
|
||||||
llvm::Optional<std::string> String) {
|
llvm::Optional<std::string> String) {
|
||||||
if (String) {
|
if (String) {
|
||||||
ContextRef->lastString = *String;
|
auto *TmpStr = ContextRef->allocateTemporaryObject<std::string>();
|
||||||
return ContextRef->lastString.c_str();
|
*TmpStr = *String;
|
||||||
|
return TmpStr->c_str();
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -782,14 +817,10 @@ const char *swift_reflection_iterateMetadataAllocationBacktraces(
|
|||||||
swift_async_task_slab_return_t
|
swift_async_task_slab_return_t
|
||||||
swift_reflection_asyncTaskSlabPointer(SwiftReflectionContextRef ContextRef,
|
swift_reflection_asyncTaskSlabPointer(SwiftReflectionContextRef ContextRef,
|
||||||
swift_reflection_ptr_t AsyncTaskPtr) {
|
swift_reflection_ptr_t AsyncTaskPtr) {
|
||||||
auto Context = ContextRef->nativeContext;
|
auto Info = swift_reflection_asyncTaskInfo(ContextRef, AsyncTaskPtr);
|
||||||
llvm::Optional<std::string> Error;
|
|
||||||
NativeReflectionContext::StoredPointer SlabPtr;
|
|
||||||
std::tie(Error, SlabPtr) = Context->asyncTaskSlabPtr(AsyncTaskPtr);
|
|
||||||
|
|
||||||
swift_async_task_slab_return_t Result = {};
|
swift_async_task_slab_return_t Result = {};
|
||||||
Result.Error = returnableCString(ContextRef, Error);
|
Result.Error = Info.Error;
|
||||||
Result.SlabPtr = SlabPtr;
|
Result.SlabPtr = Info.AllocatorSlabPtr;
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -802,23 +833,88 @@ swift_reflection_asyncTaskSlabAllocations(SwiftReflectionContextRef ContextRef,
|
|||||||
std::tie(Error, Info) = Context->asyncTaskSlabAllocations(SlabPtr);
|
std::tie(Error, Info) = Context->asyncTaskSlabAllocations(SlabPtr);
|
||||||
|
|
||||||
swift_async_task_slab_allocations_return_t Result = {};
|
swift_async_task_slab_allocations_return_t Result = {};
|
||||||
Result.Error = returnableCString(ContextRef, Error);
|
if (Result.Error) {
|
||||||
|
Result.Error = returnableCString(ContextRef, Error);
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
Result.NextSlab = Info.NextSlab;
|
Result.NextSlab = Info.NextSlab;
|
||||||
Result.SlabSize = Info.SlabSize;
|
Result.SlabSize = Info.SlabSize;
|
||||||
|
|
||||||
ContextRef->lastChunks.clear();
|
auto *Chunks = ContextRef->allocateTemporaryObject<
|
||||||
ContextRef->lastChunks.reserve(Info.Chunks.size());
|
std::vector<swift_async_task_allocation_chunk_t>>();
|
||||||
|
Chunks->reserve(Info.Chunks.size());
|
||||||
for (auto &Chunk : Info.Chunks) {
|
for (auto &Chunk : Info.Chunks) {
|
||||||
swift_async_task_allocation_chunk_t ConvertedChunk;
|
swift_async_task_allocation_chunk_t ConvertedChunk;
|
||||||
ConvertedChunk.Start = Chunk.Start;
|
ConvertedChunk.Start = Chunk.Start;
|
||||||
ConvertedChunk.Length = Chunk.Length;
|
ConvertedChunk.Length = Chunk.Length;
|
||||||
ConvertedChunk.Kind = convertAllocationChunkKind(Chunk.Kind);
|
ConvertedChunk.Kind = convertAllocationChunkKind(Chunk.Kind);
|
||||||
ContextRef->lastChunks.push_back(ConvertedChunk);
|
Chunks->push_back(ConvertedChunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result.ChunkCount = ContextRef->lastChunks.size();
|
Result.ChunkCount = Chunks->size();
|
||||||
Result.Chunks = ContextRef->lastChunks.data();
|
Result.Chunks = Chunks->data();
|
||||||
|
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
swift_async_task_info_t
|
||||||
|
swift_reflection_asyncTaskInfo(SwiftReflectionContextRef ContextRef,
|
||||||
|
swift_reflection_ptr_t AsyncTaskPtr) {
|
||||||
|
auto Context = ContextRef->nativeContext;
|
||||||
|
llvm::Optional<std::string> Error;
|
||||||
|
NativeReflectionContext::AsyncTaskInfo TaskInfo;
|
||||||
|
std::tie(Error, TaskInfo) = Context->asyncTaskInfo(AsyncTaskPtr);
|
||||||
|
|
||||||
|
swift_async_task_info_t Result = {};
|
||||||
|
if (Error) {
|
||||||
|
Result.Error = returnableCString(ContextRef, Error);
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
Result.JobFlags = TaskInfo.JobFlags;
|
||||||
|
Result.TaskStatusFlags = TaskInfo.TaskStatusFlags;
|
||||||
|
Result.Id = TaskInfo.Id;
|
||||||
|
Result.RunJob = TaskInfo.RunJob;
|
||||||
|
Result.AllocatorSlabPtr = TaskInfo.AllocatorSlabPtr;
|
||||||
|
|
||||||
|
auto *ChildTasks =
|
||||||
|
ContextRef
|
||||||
|
->allocateTemporaryObject<std::vector<swift_reflection_ptr_t>>();
|
||||||
|
std::copy(TaskInfo.ChildTasks.begin(), TaskInfo.ChildTasks.end(),
|
||||||
|
std::back_inserter(*ChildTasks));
|
||||||
|
Result.ChildTaskCount = ChildTasks->size();
|
||||||
|
Result.ChildTasks = ChildTasks->data();
|
||||||
|
|
||||||
|
auto *AsyncBacktraceFrames = ContextRef->allocateSubsequentTemporaryObject<
|
||||||
|
std::vector<swift_reflection_ptr_t>>();
|
||||||
|
std::copy(TaskInfo.AsyncBacktraceFrames.begin(),
|
||||||
|
TaskInfo.AsyncBacktraceFrames.end(),
|
||||||
|
std::back_inserter(*AsyncBacktraceFrames));
|
||||||
|
Result.AsyncBacktraceFramesCount = AsyncBacktraceFrames->size();
|
||||||
|
Result.AsyncBacktraceFrames = AsyncBacktraceFrames->data();
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
swift_actor_info_t
|
||||||
|
swift_reflection_actorInfo(SwiftReflectionContextRef ContextRef,
|
||||||
|
swift_reflection_ptr_t ActorPtr) {
|
||||||
|
auto Context = ContextRef->nativeContext;
|
||||||
|
llvm::Optional<std::string> Error;
|
||||||
|
NativeReflectionContext::ActorInfo TaskInfo;
|
||||||
|
std::tie(Error, TaskInfo) = Context->actorInfo(ActorPtr);
|
||||||
|
|
||||||
|
swift_actor_info_t Result = {};
|
||||||
|
Result.Error = returnableCString(ContextRef, Error);
|
||||||
|
Result.Flags = TaskInfo.Flags;
|
||||||
|
Result.FirstJob = TaskInfo.FirstJob;
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
swift_reflection_ptr_t
|
||||||
|
swift_reflection_nextJob(SwiftReflectionContextRef ContextRef,
|
||||||
|
swift_reflection_ptr_t JobPtr) {
|
||||||
|
auto Context = ContextRef->nativeContext;
|
||||||
|
return Context->nextJob(JobPtr);
|
||||||
|
}
|
||||||
|
|||||||
@@ -44,6 +44,7 @@
|
|||||||
// RUN: -e _ZNSt6vectorIjSaIjEE13_M_insert_auxIJRKjEEEvN9__gnu_cxx17__normal_iteratorIPjS1_EEDpOT_ \
|
// RUN: -e _ZNSt6vectorIjSaIjEE13_M_insert_auxIJRKjEEEvN9__gnu_cxx17__normal_iteratorIPjS1_EEDpOT_ \
|
||||||
// RUN: -e _ZNSt6vectorIjSaIjEE13_M_insert_auxIJjEEEvN9__gnu_cxx17__normal_iteratorIPjS1_EEDpOT_ \
|
// RUN: -e _ZNSt6vectorIjSaIjEE13_M_insert_auxIJjEEEvN9__gnu_cxx17__normal_iteratorIPjS1_EEDpOT_ \
|
||||||
// RUN: -e _ZNSt6vectorIjSaIjEE17_M_realloc_insertIJjEEEvN9__gnu_cxx17__normal_iteratorIPjS1_EEDpOT_ \
|
// RUN: -e _ZNSt6vectorIjSaIjEE17_M_realloc_insertIJjEEEvN9__gnu_cxx17__normal_iteratorIPjS1_EEDpOT_ \
|
||||||
|
// RUN: -e _ZNSt6vectorImSaImEE17_M_realloc_insertIJRKmEEEvN9__gnu_cxx17__normal_iteratorIPmS1_EEDpOT_ \
|
||||||
// RUN: -e _ZNSt6vectorISt10unique_ptrIKvSt8functionIFvPS1_EEESaIS6_EE19_M_emplace_back_auxIJS6_EEEvDpOT_ \
|
// RUN: -e _ZNSt6vectorISt10unique_ptrIKvSt8functionIFvPS1_EEESaIS6_EE19_M_emplace_back_auxIJS6_EEEvDpOT_ \
|
||||||
// RUN: -e _ZNSt6vectorISt10unique_ptrIKvSt8functionIFvPS1_EEESaIS6_EE17_M_realloc_insertIJS6_EEEvN9__gnu_cxx17__normal_iteratorIPS6_S8_EEDpOT_ \
|
// RUN: -e _ZNSt6vectorISt10unique_ptrIKvSt8functionIFvPS1_EEESaIS6_EE17_M_realloc_insertIJS6_EEEvN9__gnu_cxx17__normal_iteratorIPS6_S8_EEDpOT_ \
|
||||||
// RUN: -e _ZNSt10_HashtableImSt4pairIKmSt10unique_ptrIKvSt8functionIFvPS3_EEEESaIS9_ENSt8__detail10_Select1stESt8equal_toImESt4hashImENSB_18_Mod_range_hashingENSB_20_Default_ranged_hashENSB_20_Prime_rehash_policyENSB_17_Hashtable_traitsILb0ELb0ELb1EEEE10_M_emplaceIJS0_ImS8_EEEES0_INSB_14_Node_iteratorIS9_Lb0ELb0EEEbESt17integral_constantIbLb1EEDpOT_ \
|
// RUN: -e _ZNSt10_HashtableImSt4pairIKmSt10unique_ptrIKvSt8functionIFvPS3_EEEESaIS9_ENSt8__detail10_Select1stESt8equal_toImESt4hashImENSB_18_Mod_range_hashingENSB_20_Default_ranged_hashENSB_20_Prime_rehash_policyENSB_17_Hashtable_traitsILb0ELb0ELb1EEEE10_M_emplaceIJS0_ImS8_EEEES0_INSB_14_Node_iteratorIS9_Lb0ELb0EEEbESt17integral_constantIbLb1EEDpOT_ \
|
||||||
|
|||||||
401
tools/swift-inspect/Sources/swift-inspect/DumpConcurrency.swift
Normal file
401
tools/swift-inspect/Sources/swift-inspect/DumpConcurrency.swift
Normal file
@@ -0,0 +1,401 @@
|
|||||||
|
import SwiftRemoteMirror
|
||||||
|
|
||||||
|
func dumpConcurrency(
|
||||||
|
context: SwiftReflectionContextRef,
|
||||||
|
inspector: Inspector
|
||||||
|
) throws {
|
||||||
|
let dumper = ConcurrencyDumper(context: context, inspector: inspector)
|
||||||
|
dumper.dumpTasks()
|
||||||
|
dumper.dumpActors()
|
||||||
|
dumper.dumpThreads()
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate class ConcurrencyDumper {
|
||||||
|
let context: SwiftReflectionContextRef
|
||||||
|
let inspector: Inspector
|
||||||
|
let jobMetadata: swift_reflection_ptr_t?
|
||||||
|
let taskMetadata: swift_reflection_ptr_t?
|
||||||
|
|
||||||
|
struct TaskInfo {
|
||||||
|
var address: swift_reflection_ptr_t
|
||||||
|
var jobFlags: UInt32
|
||||||
|
var taskStatusFlags: UInt64
|
||||||
|
var id: UInt64
|
||||||
|
var runJob: swift_reflection_ptr_t
|
||||||
|
var allocatorSlabPtr: swift_reflection_ptr_t
|
||||||
|
var allocatorTotalSize: Int
|
||||||
|
var allocatorTotalChunks: Int
|
||||||
|
var childTasks: [swift_reflection_ptr_t]
|
||||||
|
var asyncBacktrace: [swift_reflection_ptr_t]
|
||||||
|
var parent: swift_reflection_ptr_t?
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HeapInfo {
|
||||||
|
var tasks: [swift_reflection_ptr_t] = []
|
||||||
|
var jobs: [swift_reflection_ptr_t] = []
|
||||||
|
var actors: [swift_reflection_ptr_t] = []
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy var heapInfo: HeapInfo = gatherHeapInfo()
|
||||||
|
|
||||||
|
lazy var threadCurrentTasks = inspector.threadCurrentTasks().filter{ $0.currentTask != 0 }
|
||||||
|
|
||||||
|
lazy var tasks: [swift_reflection_ptr_t: TaskInfo] = gatherTasks()
|
||||||
|
|
||||||
|
var actors: [swift_reflection_ptr_t] {
|
||||||
|
heapInfo.actors
|
||||||
|
}
|
||||||
|
|
||||||
|
var metadataIsActorCache: [swift_reflection_ptr_t: Bool] = [:]
|
||||||
|
var metadataNameCache: [swift_reflection_ptr_t: String?] = [:]
|
||||||
|
|
||||||
|
init(context: SwiftReflectionContextRef, inspector: Inspector) {
|
||||||
|
self.context = context
|
||||||
|
self.inspector = inspector
|
||||||
|
|
||||||
|
func getMetadata(symbolName: String) -> swift_reflection_ptr_t? {
|
||||||
|
let addr = inspector.getAddr(symbolName: symbolName)
|
||||||
|
if let ptr = inspector.read(address: addr, size: MemoryLayout<UInt>.size) {
|
||||||
|
return swift_reflection_ptr_t(ptr.load(as: UInt.self))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
jobMetadata = getMetadata(symbolName: "_swift_concurrency_debug_jobMetadata")
|
||||||
|
taskMetadata = getMetadata(symbolName: "_swift_concurrency_debug_asyncTaskMetadata")
|
||||||
|
}
|
||||||
|
|
||||||
|
func gatherHeapInfo() -> HeapInfo {
|
||||||
|
var result = HeapInfo()
|
||||||
|
|
||||||
|
inspector.enumerateMallocs { (pointer, size) in
|
||||||
|
let metadata = swift_reflection_ptr_t(swift_reflection_metadataForObject(context, UInt(pointer)))
|
||||||
|
if metadata == jobMetadata {
|
||||||
|
result.jobs.append(pointer)
|
||||||
|
} else if metadata == taskMetadata {
|
||||||
|
result.tasks.append(pointer)
|
||||||
|
} else if isActorMetadata(metadata) {
|
||||||
|
result.actors.append(pointer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func gatherTasks() -> [swift_reflection_ptr_t: TaskInfo] {
|
||||||
|
var map: [swift_reflection_ptr_t: TaskInfo] = [:]
|
||||||
|
var tasksToScan: Set<swift_reflection_ptr_t> = []
|
||||||
|
tasksToScan.formUnion(heapInfo.tasks)
|
||||||
|
tasksToScan.formUnion(threadCurrentTasks.map{ $0.currentTask }.filter{ $0 != 0 })
|
||||||
|
|
||||||
|
while !tasksToScan.isEmpty {
|
||||||
|
let taskToScan = tasksToScan.removeFirst()
|
||||||
|
if let info = info(forTask: taskToScan) {
|
||||||
|
map[taskToScan] = info
|
||||||
|
for child in info.childTasks {
|
||||||
|
let childMetadata = swift_reflection_metadataForObject(context, UInt(child))
|
||||||
|
if let taskMetadata = taskMetadata, childMetadata != taskMetadata {
|
||||||
|
print("Inconsistent data detected! Child task \(hex: child) has unknown metadata \(hex: taskMetadata)")
|
||||||
|
}
|
||||||
|
if map[child] == nil {
|
||||||
|
tasksToScan.insert(child)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (task, info) in map {
|
||||||
|
for child in info.childTasks {
|
||||||
|
map[child]?.parent = task
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return map
|
||||||
|
}
|
||||||
|
|
||||||
|
func isActorMetadata(_ metadata: swift_reflection_ptr_t) -> Bool {
|
||||||
|
if let cached = metadataIsActorCache[metadata] {
|
||||||
|
return cached
|
||||||
|
}
|
||||||
|
let result = swift_reflection_metadataIsActor(context, metadata) != 0
|
||||||
|
metadataIsActorCache[metadata] = result
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func name(metadata: swift_reflection_ptr_t) -> String? {
|
||||||
|
if let cached = metadataNameCache[metadata] {
|
||||||
|
return cached
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = context.name(metadata: metadata)
|
||||||
|
metadataNameCache[metadata] = name
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
func info(forTask task: swift_reflection_ptr_t) -> TaskInfo? {
|
||||||
|
let reflectionInfo = swift_reflection_asyncTaskInfo(context, task)
|
||||||
|
if let error = reflectionInfo.Error {
|
||||||
|
print("Error getting info for async task \(hex: task): \(String(cString: error))")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// These arrays are temporary pointers which we must copy out before we call
|
||||||
|
// into Remote Mirror again.
|
||||||
|
let children = Array(UnsafeBufferPointer(
|
||||||
|
start: reflectionInfo.ChildTasks,
|
||||||
|
count: Int(reflectionInfo.ChildTaskCount)))
|
||||||
|
let asyncBacktraceFrames = Array(UnsafeBufferPointer(
|
||||||
|
start: reflectionInfo.AsyncBacktraceFrames,
|
||||||
|
count: Int(reflectionInfo.AsyncBacktraceFramesCount)))
|
||||||
|
|
||||||
|
var allocatorSlab = reflectionInfo.AllocatorSlabPtr
|
||||||
|
var allocatorTotalSize = 0
|
||||||
|
var allocatorTotalChunks = 0
|
||||||
|
while allocatorSlab != 0 {
|
||||||
|
let allocations = swift_reflection_asyncTaskSlabAllocations(context,
|
||||||
|
allocatorSlab)
|
||||||
|
guard allocations.Error == nil else { break }
|
||||||
|
allocatorTotalSize += Int(allocations.SlabSize)
|
||||||
|
allocatorTotalChunks += Int(allocations.ChunkCount)
|
||||||
|
|
||||||
|
allocatorSlab = allocations.NextSlab
|
||||||
|
}
|
||||||
|
|
||||||
|
return TaskInfo(
|
||||||
|
address: task,
|
||||||
|
jobFlags: reflectionInfo.JobFlags,
|
||||||
|
taskStatusFlags: reflectionInfo.TaskStatusFlags,
|
||||||
|
id: reflectionInfo.Id,
|
||||||
|
runJob: reflectionInfo.RunJob,
|
||||||
|
allocatorSlabPtr: reflectionInfo.AllocatorSlabPtr,
|
||||||
|
allocatorTotalSize: allocatorTotalSize,
|
||||||
|
allocatorTotalChunks: allocatorTotalChunks,
|
||||||
|
childTasks: children,
|
||||||
|
asyncBacktrace: asyncBacktraceFrames
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func taskHierarchy() -> [(level: Int, lastChild: Bool, task: TaskInfo)] {
|
||||||
|
var hierarchy: [(level: Int, lastChild: Bool, task: TaskInfo)] = []
|
||||||
|
|
||||||
|
let topLevelTasks = tasks.values.filter{ $0.parent == nil }
|
||||||
|
for top in topLevelTasks.sorted(by: { $0.id < $1.id }) {
|
||||||
|
var stack: [(index: Int, task: TaskInfo)] = [(0, top)]
|
||||||
|
hierarchy.append((0, true, top))
|
||||||
|
|
||||||
|
while let (index, task) = stack.popLast() {
|
||||||
|
if index < task.childTasks.count {
|
||||||
|
stack.append((index + 1, task))
|
||||||
|
let childPtr = task.childTasks[index]
|
||||||
|
let childTask = tasks[childPtr]!
|
||||||
|
hierarchy.append((stack.count, index == task.childTasks.count - 1, childTask))
|
||||||
|
stack.append((0, childTask))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hierarchy
|
||||||
|
}
|
||||||
|
|
||||||
|
func remove(from: String, upTo: String) -> String {
|
||||||
|
from.withCString {
|
||||||
|
if let found = strstr($0, upTo) {
|
||||||
|
return String(cString: found + strlen(upTo))
|
||||||
|
}
|
||||||
|
return from
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func symbolicateBacktracePointer(ptr: swift_reflection_ptr_t) -> String {
|
||||||
|
guard let name = inspector.getSymbol(address: ptr).name else {
|
||||||
|
return "<\(hex: ptr)>"
|
||||||
|
}
|
||||||
|
|
||||||
|
return remove(from: name, upTo: " resume partial function for ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func flagsStrings<T: BinaryInteger>(flags: T, strings: [T: String]) -> [String] {
|
||||||
|
return strings.sorted{ $0.key < $1.key }
|
||||||
|
.filter({ ($0.key & flags) != 0})
|
||||||
|
.map{ $0.value }
|
||||||
|
}
|
||||||
|
|
||||||
|
func flagsString<T: BinaryInteger>(flags: T, strings: [T: String]) -> String {
|
||||||
|
let flagStrs = flagsStrings(flags: flags, strings: strings)
|
||||||
|
if flagStrs.isEmpty {
|
||||||
|
return "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
let flagsStr = flagStrs.joined(separator: "|")
|
||||||
|
return flagsStr
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeTaskFlags(_ info: TaskInfo) -> (
|
||||||
|
priority: UInt32,
|
||||||
|
flags: String
|
||||||
|
) {
|
||||||
|
let priority = (info.jobFlags >> 8) & 0xff
|
||||||
|
let jobFlags = flagsStrings(flags: info.jobFlags, strings: [
|
||||||
|
1 << 24: "childTask",
|
||||||
|
1 << 25: "future",
|
||||||
|
1 << 26: "groupChildTask",
|
||||||
|
1 << 28: "asyncLetTask"
|
||||||
|
])
|
||||||
|
let taskFlags = flagsStrings(flags: info.taskStatusFlags, strings: [
|
||||||
|
0x100: "cancelled",
|
||||||
|
0x200: "locked",
|
||||||
|
0x400: "escalated",
|
||||||
|
0x800: "running"
|
||||||
|
])
|
||||||
|
let allFlags = jobFlags + taskFlags
|
||||||
|
let flagsStr = allFlags.isEmpty ? "0" : allFlags.joined(separator: "|")
|
||||||
|
return (priority, flagsStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeActorFlags(_ flags: UInt64) -> (
|
||||||
|
status: String,
|
||||||
|
flags: String,
|
||||||
|
maxPriority: UInt64
|
||||||
|
) {
|
||||||
|
let statuses: [UInt64: String] = [
|
||||||
|
0: "idle",
|
||||||
|
1: "scheduled",
|
||||||
|
2: "running",
|
||||||
|
3: "zombie-latching",
|
||||||
|
4: "zombie-ready-for-deallocation"
|
||||||
|
]
|
||||||
|
let flagsString = flagsString(flags: flags, strings: [
|
||||||
|
1 << 3: "hasActiveInlineJob",
|
||||||
|
1 << 4: "isDistributedRemote"
|
||||||
|
])
|
||||||
|
|
||||||
|
let status = flags & 0x7
|
||||||
|
let maxPriority = (flags >> 8) & 0xff
|
||||||
|
return (
|
||||||
|
status: statuses[status] ?? "unknown(\(status))",
|
||||||
|
flags: flagsString,
|
||||||
|
maxPriority: maxPriority
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dumpTasks() {
|
||||||
|
print("TASKS")
|
||||||
|
|
||||||
|
var lastChilds: [Bool] = []
|
||||||
|
|
||||||
|
let hierarchy = taskHierarchy()
|
||||||
|
for (i, (level, lastChild, task)) in hierarchy.enumerated() {
|
||||||
|
lastChilds.removeSubrange(level...)
|
||||||
|
lastChilds.append(lastChild)
|
||||||
|
|
||||||
|
let nextEntry = i < hierarchy.count - 1 ? hierarchy[i + 1] : nil
|
||||||
|
let prevEntry = i > 0 ? hierarchy[i - 1] : nil
|
||||||
|
|
||||||
|
let levelWillDecrease = level > (nextEntry?.level ?? -1)
|
||||||
|
let levelDidIncrease = level > (prevEntry?.level ?? -1)
|
||||||
|
|
||||||
|
var prefix = ""
|
||||||
|
for lastChild in lastChilds {
|
||||||
|
prefix += lastChild ? " " : " | "
|
||||||
|
}
|
||||||
|
prefix += " "
|
||||||
|
let firstPrefix = String(prefix.dropLast(5) + (
|
||||||
|
level == 0 ? " " :
|
||||||
|
lastChild ? "`--" :
|
||||||
|
"+--"))
|
||||||
|
if levelDidIncrease {
|
||||||
|
print(prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
var firstLine = true
|
||||||
|
func output(_ str: String) {
|
||||||
|
print((firstLine ? firstPrefix : prefix) + str)
|
||||||
|
firstLine = false
|
||||||
|
}
|
||||||
|
|
||||||
|
let runJobSymbol = inspector.getSymbol(address: task.runJob)
|
||||||
|
let runJobLibrary = runJobSymbol.library ?? "<unknown>"
|
||||||
|
|
||||||
|
let symbolicatedBacktrace = task.asyncBacktrace.map(symbolicateBacktracePointer)
|
||||||
|
|
||||||
|
let flags = decodeTaskFlags(task)
|
||||||
|
|
||||||
|
output("Task \(hex: task.id) - flags=\(flags.flags) priority=\(hex: flags.priority) address=\(hex: task.address)")
|
||||||
|
if let parent = task.parent {
|
||||||
|
output("parent: \(hex: parent)")
|
||||||
|
}
|
||||||
|
|
||||||
|
if let first = symbolicatedBacktrace.first {
|
||||||
|
output("async backtrace: \(first)")
|
||||||
|
for entry in symbolicatedBacktrace.dropFirst() {
|
||||||
|
output(" \(entry)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output("resume function: \(symbolicateBacktracePointer(ptr: task.runJob)) in \(runJobLibrary)")
|
||||||
|
output("task allocator: \(task.allocatorTotalSize) bytes in \(task.allocatorTotalChunks) chunks")
|
||||||
|
|
||||||
|
if task.childTasks.count > 0 {
|
||||||
|
let s = task.childTasks.count > 1 ? "s" : ""
|
||||||
|
output("* \(task.childTasks.count) child task\(s)")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task.childTasks.isEmpty) && i < hierarchy.count - 1 {
|
||||||
|
print(prefix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func dumpActors() {
|
||||||
|
print("ACTORS")
|
||||||
|
|
||||||
|
for actor in actors {
|
||||||
|
let metadata = swift_reflection_metadataForObject(context, UInt(actor))
|
||||||
|
let metadataName = name(metadata: swift_reflection_ptr_t(metadata)) ?? "<unknown class name>"
|
||||||
|
let info = swift_reflection_actorInfo(context, actor);
|
||||||
|
|
||||||
|
let flags = decodeActorFlags(info.Flags)
|
||||||
|
|
||||||
|
print(" \(hex: actor) \(metadataName) status=\(flags.status) flags=\(flags.flags) maxPriority=\(hex: flags.maxPriority)")
|
||||||
|
|
||||||
|
func jobStr(_ job: swift_reflection_ptr_t) -> String {
|
||||||
|
if let task = tasks[job] {
|
||||||
|
return "Task \(hex: task.id) \(symbolicateBacktracePointer(ptr: task.runJob))"
|
||||||
|
}
|
||||||
|
return "<internal job \(hex: job)>"
|
||||||
|
}
|
||||||
|
|
||||||
|
var job = info.FirstJob
|
||||||
|
if job == 0 {
|
||||||
|
print(" no jobs queued")
|
||||||
|
} else {
|
||||||
|
print(" job queue: \(jobStr(job))")
|
||||||
|
while job != 0 {
|
||||||
|
job = swift_reflection_nextJob(context, job);
|
||||||
|
if job != 0 {
|
||||||
|
print(" \(jobStr(job))")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func dumpThreads() {
|
||||||
|
print("THREADS")
|
||||||
|
if threadCurrentTasks.isEmpty {
|
||||||
|
print(" no threads with active tasks")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for (thread, task) in threadCurrentTasks {
|
||||||
|
let taskStr: String
|
||||||
|
if let info = tasks[task] {
|
||||||
|
taskStr = "\(hex: info.id)"
|
||||||
|
} else {
|
||||||
|
taskStr = "<unknown task \(hex: task)>"
|
||||||
|
}
|
||||||
|
print(" Thread \(hex: thread) - current task: \(taskStr)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@ class Inspector {
|
|||||||
let task: task_t
|
let task: task_t
|
||||||
let symbolicator: CSTypeRef
|
let symbolicator: CSTypeRef
|
||||||
let swiftCore: CSTypeRef
|
let swiftCore: CSTypeRef
|
||||||
|
let swiftConcurrency: CSTypeRef
|
||||||
|
|
||||||
init?(pid: pid_t) {
|
init?(pid: pid_t) {
|
||||||
task = Self.findTask(pid, tryForkCorpse: false)
|
task = Self.findTask(pid, tryForkCorpse: false)
|
||||||
@@ -26,6 +27,8 @@ class Inspector {
|
|||||||
symbolicator = CSSymbolicatorCreateWithTask(task)
|
symbolicator = CSSymbolicatorCreateWithTask(task)
|
||||||
swiftCore = CSSymbolicatorGetSymbolOwnerWithNameAtTime(
|
swiftCore = CSSymbolicatorGetSymbolOwnerWithNameAtTime(
|
||||||
symbolicator, "libswiftCore.dylib", kCSNow)
|
symbolicator, "libswiftCore.dylib", kCSNow)
|
||||||
|
swiftConcurrency = CSSymbolicatorGetSymbolOwnerWithNameAtTime(
|
||||||
|
symbolicator, "libswift_Concurrency.dylib", kCSNow)
|
||||||
_ = task_start_peeking(task)
|
_ = task_start_peeking(task)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,8 +77,11 @@ class Inspector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getAddr(symbolName: String) -> swift_addr_t {
|
func getAddr(symbolName: String) -> swift_addr_t {
|
||||||
let symbol = CSSymbolOwnerGetSymbolWithMangledName(swiftCore,
|
let fullName = "_" + symbolName
|
||||||
"_" + symbolName)
|
var symbol = CSSymbolOwnerGetSymbolWithMangledName(swiftCore, fullName)
|
||||||
|
if CSIsNull(symbol) {
|
||||||
|
symbol = CSSymbolOwnerGetSymbolWithMangledName(swiftConcurrency, fullName)
|
||||||
|
}
|
||||||
let range = CSSymbolGetRange(symbol)
|
let range = CSSymbolGetRange(symbol)
|
||||||
return swift_addr_t(range.location)
|
return swift_addr_t(range.location)
|
||||||
}
|
}
|
||||||
@@ -106,6 +112,57 @@ class Inspector {
|
|||||||
return task_peek(task, address, mach_vm_size_t(size))
|
return task_peek(task, address, mach_vm_size_t(size))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func threadCurrentTasks() -> [(threadID: UInt64, currentTask: swift_addr_t)] {
|
||||||
|
var threadList: UnsafeMutablePointer<thread_t>? = nil
|
||||||
|
var threadCount: mach_msg_type_number_t = 0
|
||||||
|
|
||||||
|
var kr = task_threads(task, &threadList, &threadCount)
|
||||||
|
if kr != KERN_SUCCESS {
|
||||||
|
print("Unable to gather threads of remote process: \(machErrStr(kr))")
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
defer {
|
||||||
|
// Deallocate the port rights for the threads.
|
||||||
|
for i in 0..<threadCount {
|
||||||
|
mach_port_deallocate(mach_task_self_, threadList![Int(i)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deallocate the thread list.
|
||||||
|
let ptr = vm_address_t(bitPattern: threadList)
|
||||||
|
let size = vm_size_t(MemoryLayout<thread_t>.size) * vm_size_t(threadCount)
|
||||||
|
vm_deallocate(mach_task_self_, ptr, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
var results: [(threadID: UInt64, currentTask: swift_addr_t)] = []
|
||||||
|
for i in 0..<threadCount {
|
||||||
|
let THREAD_IDENTIFIER_INFO_COUNT = MemoryLayout<thread_identifier_info_data_t>.size / MemoryLayout<natural_t>.size
|
||||||
|
var info = thread_identifier_info_data_t()
|
||||||
|
var infoCount = mach_msg_type_number_t(THREAD_IDENTIFIER_INFO_COUNT)
|
||||||
|
withUnsafeMutablePointer(to: &info) {
|
||||||
|
$0.withMemoryRebound(to: integer_t.self, capacity: THREAD_IDENTIFIER_INFO_COUNT) {
|
||||||
|
kr = thread_info(threadList![Int(i)],
|
||||||
|
thread_flavor_t(THREAD_IDENTIFIER_INFO), $0,
|
||||||
|
&infoCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (kr != KERN_SUCCESS) {
|
||||||
|
print("Unable to get info for thread \(i): \(machErrStr(kr))")
|
||||||
|
} else {
|
||||||
|
let tlsStart = info.thread_handle
|
||||||
|
if tlsStart != 0 {
|
||||||
|
let SWIFT_CONCURRENCY_TASK_KEY = 103
|
||||||
|
let currentTaskPointer = tlsStart + UInt64(SWIFT_CONCURRENCY_TASK_KEY * MemoryLayout<UnsafeRawPointer>.size)
|
||||||
|
if let ptr = read(address: currentTaskPointer, size: MemoryLayout<UnsafeRawPointer>.size) {
|
||||||
|
let currentTask = ptr.load(as: UInt.self)
|
||||||
|
results.append((threadID: info.thread_id, currentTask: swift_addr_t(currentTask)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
enum Callbacks {
|
enum Callbacks {
|
||||||
static let QueryDataLayout: @convention(c)
|
static let QueryDataLayout: @convention(c)
|
||||||
(UnsafeMutableRawPointer?,
|
(UnsafeMutableRawPointer?,
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
import SwiftRemoteMirror
|
import SwiftRemoteMirror
|
||||||
|
|
||||||
extension DefaultStringInterpolation {
|
extension DefaultStringInterpolation {
|
||||||
mutating func appendInterpolation(hex: swift_reflection_ptr_t) {
|
mutating func appendInterpolation<T>(hex: T) where T: BinaryInteger {
|
||||||
appendInterpolation("0x")
|
appendInterpolation("0x")
|
||||||
appendInterpolation(String(hex, radix: 16))
|
appendInterpolation(String(hex, radix: 16))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -169,6 +169,7 @@ struct SwiftInspect: ParsableCommand {
|
|||||||
DumpGenericMetadata.self,
|
DumpGenericMetadata.self,
|
||||||
DumpCacheNodes.self,
|
DumpCacheNodes.self,
|
||||||
DumpArrays.self,
|
DumpArrays.self,
|
||||||
|
DumpConcurrency.self,
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,7 +261,7 @@ struct DumpCacheNodes: ParsableCommand {
|
|||||||
|
|
||||||
struct DumpArrays: ParsableCommand {
|
struct DumpArrays: ParsableCommand {
|
||||||
static let configuration = CommandConfiguration(
|
static let configuration = CommandConfiguration(
|
||||||
abstract: "Print the target's metadata cache nodes.")
|
abstract: "Print information about array objects in the target.")
|
||||||
|
|
||||||
@OptionGroup()
|
@OptionGroup()
|
||||||
var options: UniversalOptions
|
var options: UniversalOptions
|
||||||
@@ -272,4 +273,18 @@ struct DumpArrays: ParsableCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct DumpConcurrency: ParsableCommand {
|
||||||
|
static let configuration = CommandConfiguration(
|
||||||
|
abstract: "Print information about the target's concurrency runtime.")
|
||||||
|
|
||||||
|
@OptionGroup()
|
||||||
|
var options: UniversalOptions
|
||||||
|
|
||||||
|
func run() throws {
|
||||||
|
try withReflectionContext(nameOrPid: options.nameOrPid) {
|
||||||
|
try dumpConcurrency(context: $0, inspector: $1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SwiftInspect.main()
|
SwiftInspect.main()
|
||||||
|
|||||||
@@ -58,6 +58,13 @@ enum Sym {
|
|||||||
static let CSSymbolicatorGetSymbolWithAddressAtTime: @convention(c)
|
static let CSSymbolicatorGetSymbolWithAddressAtTime: @convention(c)
|
||||||
(CSSymbolicatorRef, mach_vm_address_t, CSMachineTime) -> CSSymbolRef =
|
(CSSymbolicatorRef, mach_vm_address_t, CSMachineTime) -> CSSymbolRef =
|
||||||
symbol(coreSymbolicationHandle, "CSSymbolicatorGetSymbolWithAddressAtTime")
|
symbol(coreSymbolicationHandle, "CSSymbolicatorGetSymbolWithAddressAtTime")
|
||||||
|
static let CSSymbolicatorForeachSymbolOwnerAtTime:
|
||||||
|
@convention(c) (CSSymbolicatorRef, CSMachineTime, @convention(block) (CSSymbolOwnerRef) -> Void) -> UInt =
|
||||||
|
symbol(coreSymbolicationHandle, "CSSymbolicatorForeachSymbolOwnerAtTime")
|
||||||
|
static let CSSymbolOwnerGetBaseAddress: @convention(c) (CSSymbolOwnerRef) -> mach_vm_address_t =
|
||||||
|
symbol(symbolicationHandle, "CSSymbolOwnerGetBaseAddress")
|
||||||
|
static let CSIsNull: @convention(c) (CSTypeRef) -> CBool =
|
||||||
|
symbol(coreSymbolicationHandle, "CSIsNull")
|
||||||
static let task_start_peeking: @convention(c) (task_t) -> kern_return_t =
|
static let task_start_peeking: @convention(c) (task_t) -> kern_return_t =
|
||||||
symbol(symbolicationHandle, "task_start_peeking")
|
symbol(symbolicationHandle, "task_start_peeking")
|
||||||
static let task_peek: @convention(c) (task_t, mach_vm_address_t, mach_vm_size_t,
|
static let task_peek: @convention(c) (task_t, mach_vm_address_t, mach_vm_size_t,
|
||||||
@@ -77,13 +84,6 @@ enum Sym {
|
|||||||
@convention(c) (task_t, UnsafeMutableRawPointer?, CUnsignedInt, vm_range_recorder_t)
|
@convention(c) (task_t, UnsafeMutableRawPointer?, CUnsignedInt, vm_range_recorder_t)
|
||||||
-> Void =
|
-> Void =
|
||||||
symbol(symbolicationHandle, "task_enumerate_malloc_blocks")
|
symbol(symbolicationHandle, "task_enumerate_malloc_blocks")
|
||||||
|
|
||||||
static let CSSymbolicatorForeachSymbolOwnerAtTime:
|
|
||||||
@convention(c) (CSSymbolicatorRef, CSMachineTime, @convention(block) (CSSymbolOwnerRef) -> Void) -> UInt =
|
|
||||||
symbol(coreSymbolicationHandle, "CSSymbolicatorForeachSymbolOwnerAtTime")
|
|
||||||
|
|
||||||
static let CSSymbolOwnerGetBaseAddress: @convention(c) (CSSymbolOwnerRef) -> mach_vm_address_t =
|
|
||||||
symbol(symbolicationHandle, "CSSymbolOwnerGetBaseAddress")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typealias CSMachineTime = UInt64
|
typealias CSMachineTime = UInt64
|
||||||
@@ -173,6 +173,10 @@ func CSSymbolOwnerGetBaseAddress(_ symbolOwner: CSSymbolOwnerRef) -> mach_vm_add
|
|||||||
return Sym.CSSymbolOwnerGetBaseAddress(symbolOwner)
|
return Sym.CSSymbolOwnerGetBaseAddress(symbolOwner)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CSIsNull(_ symbol: CSTypeRef) -> Bool {
|
||||||
|
Sym.CSIsNull(symbol)
|
||||||
|
}
|
||||||
|
|
||||||
func task_start_peeking(_ task: task_t) -> Bool {
|
func task_start_peeking(_ task: task_t) -> Bool {
|
||||||
let result = Sym.task_start_peeking(task)
|
let result = Sym.task_start_peeking(task)
|
||||||
if result == KERN_SUCCESS {
|
if result == KERN_SUCCESS {
|
||||||
@@ -189,7 +193,6 @@ func task_peek(
|
|||||||
var ptr: UnsafeRawPointer? = nil
|
var ptr: UnsafeRawPointer? = nil
|
||||||
let result = Sym.task_peek(task, start, size, &ptr)
|
let result = Sym.task_peek(task, start, size, &ptr)
|
||||||
if result != KERN_SUCCESS {
|
if result != KERN_SUCCESS {
|
||||||
print("Unable to read (\(start), \(size)): \(machErrStr(result))", to: &Std.err)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return ptr
|
return ptr
|
||||||
|
|||||||
Reference in New Issue
Block a user