Skip to content

[6.2][Concurrency] Fix task status and private storage sizes. #80644

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 2 commits into from
Apr 9, 2025
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
4 changes: 2 additions & 2 deletions include/swift/ABI/Task.h
Original file line number Diff line number Diff line change
Expand Up @@ -301,15 +301,15 @@ class AsyncTask : public Job {
#if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION && SWIFT_POINTER_IS_4_BYTES
static constexpr size_t ActiveTaskStatusSize = 4 * sizeof(void *);
#else
static constexpr size_t ActiveTaskStatusSize = 4 * sizeof(void *);
static constexpr size_t ActiveTaskStatusSize = 2 * sizeof(void *);
#endif

// Private storage is currently 6 pointers, 16 bytes of non-pointer data,
// the ActiveTaskStatus, and a RecursiveMutex.
static constexpr size_t PrivateStorageSize =
6 * sizeof(void *) + 16 + ActiveTaskStatusSize + sizeof(RecursiveMutex);

void *Storage[PrivateStorageSize];
char Storage[PrivateStorageSize];

/// Initialize this storage during the creation of a task.
void initialize(JobPriority basePri);
Expand Down
52 changes: 39 additions & 13 deletions include/swift/RemoteInspection/ReflectionContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,13 @@ class ReflectionContext
std::vector<std::tuple<RemoteAddress, RemoteAddress>> dataRanges;

bool setupTargetPointers = false;
typename super::StoredPointer target_asyncTaskMetadata = 0;
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;
bool supportsPriorityEscalation = false;
typename super::StoredSize asyncTaskSize = 0;

public:
using super::getBuilder;
Expand Down Expand Up @@ -1816,16 +1818,32 @@ class ReflectionContext
ChildTask = RecordObj->FirstChild;
}

while (ChildTask) {
while (ChildTask && ChildTaskLoopCount++ < ChildTaskLimit) {
// Read the child task.
auto ChildTaskObj = readObj<AsyncTaskType>(ChildTask);
if (!ChildTaskObj)
return {std::string("found unreadable child task pointer"), Info};

Info.ChildTasks.push_back(ChildTask);

StoredPointer ChildFragmentAddr = ChildTask + sizeof(*AsyncTaskObj);
auto ChildFragmentObj =
readObj<ChildFragment<Runtime>>(ChildFragmentAddr);
if (ChildFragmentObj)
ChildTask = ChildFragmentObj->NextChild;
else
swift::JobFlags ChildJobFlags(AsyncTaskObj->Flags);
if (ChildJobFlags.task_isChildTask()) {
if (asyncTaskSize == 0)
return {std::string("target async task size unknown, unable to "
"iterate child tasks"),
Info};

StoredPointer ChildFragmentAddr = ChildTask + asyncTaskSize;
auto ChildFragmentObj =
readObj<ChildFragment<Runtime>>(ChildFragmentAddr);
if (ChildFragmentObj)
ChildTask = ChildFragmentObj->NextChild;
else
ChildTask = 0;
} else {
// No child fragment, so we're done iterating.
ChildTask = 0;
}
}

RecordPtr = RecordObj->Parent;
Expand Down Expand Up @@ -1927,7 +1945,7 @@ class ReflectionContext
if (setupTargetPointers)
return;

auto getFunc = [&](const std::string &name) -> StoredPointer {
auto getPointer = [&](const std::string &name) -> StoredPointer {
auto Symbol = getReader().getSymbolAddress(name);
if (!Symbol)
return 0;
Expand All @@ -1936,19 +1954,27 @@ class ReflectionContext
return 0;
return Pointer->getResolvedAddress().getAddressData();
};
target_asyncTaskMetadata =
getPointer("_swift_concurrency_debug_asyncTaskMetadata");
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");
getPointer("_swift_concurrency_debug_non_future_adapter");
target_future_adapter =
getPointer("_swift_concurrency_debug_future_adapter");
target_task_wait_throwing_resume_adapter = getPointer(
"_swift_concurrency_debug_task_wait_throwing_resume_adapter");
target_task_future_wait_resume_adapter =
getFunc("_swift_concurrency_debug_task_future_wait_resume_adapter");
getPointer("_swift_concurrency_debug_task_future_wait_resume_adapter");
auto supportsPriorityEscalationAddr = getReader().getSymbolAddress(
"_swift_concurrency_debug_supportsPriorityEscalation");
if (supportsPriorityEscalationAddr) {
getReader().readInteger(supportsPriorityEscalationAddr,
&supportsPriorityEscalation);
}
auto asyncTaskSizeAddr =
getReader().getSymbolAddress("_swift_concurrency_debug_asyncTaskSize");
if (asyncTaskSizeAddr) {
getReader().readInteger(asyncTaskSizeAddr, &asyncTaskSize);
}

setupTargetPointers = true;
}
Expand Down
7 changes: 2 additions & 5 deletions include/swift/RemoteInspection/RuntimeInternals.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ template <typename Runtime> struct ConformanceCacheEntry {

template <typename Runtime>
struct HeapObject {
typename Runtime::StoredPointer Metadata;
typename Runtime::StoredSignedPointer Metadata;
typename Runtime::StoredSize RefCounts;
};

Expand Down Expand Up @@ -131,10 +131,7 @@ struct AsyncTask: Job<Runtime> {
// On 64-bit, there's a Reserved64 after ResumeContext.
typename Runtime::StoredPointer ResumeContextAndReserved[
sizeof(typename Runtime::StoredPointer) == 8 ? 2 : 1];
union {
AsyncTaskPrivateStorage<Runtime, ActiveTaskStatus> PrivateStorage;
typename Runtime::StoredPointer PrivateStorageRaw[14];
};
AsyncTaskPrivateStorage<Runtime, ActiveTaskStatus> PrivateStorage;
};

template <typename Runtime>
Expand Down
10 changes: 10 additions & 0 deletions stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ public enum InstanceKind: UInt8 {
case Enum
case EnumValue
case AsyncTask
case LogString
}

/// Represents a section in a loaded image in this process.
Expand Down Expand Up @@ -642,6 +643,15 @@ public func reflect(asyncTask: UInt) {
reflect(instanceAddress: asyncTask, kind: .AsyncTask)
}

/// Log a string to the test's output. Use instead of print, which gets
/// captured by the parent and read as commands.
public func reflectionLog(str: String) {
str.withCString {
let addr = UInt(bitPattern: $0)
reflect(instanceAddress: addr, kind: .LogString);
}
}

/// Call this function to indicate to the parent that there are
/// no more instances to look at.
public func doneReflecting() {
Expand Down
4 changes: 4 additions & 0 deletions stdlib/public/Concurrency/Debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ const void *const _swift_concurrency_debug_jobMetadata;
SWIFT_EXPORT_FROM(swift_Concurrency)
const void *const _swift_concurrency_debug_asyncTaskMetadata;

/// The size of an AsyncTask, in bytes.
SWIFT_EXPORT_FROM(swift_Concurrency)
const size_t _swift_concurrency_debug_asyncTaskSize;

/// A fake metadata pointer placed at the start of async task slab allocations.
SWIFT_EXPORT_FROM(swift_Concurrency)
const void *const _swift_concurrency_debug_asyncTaskSlabMetadata;
Expand Down
2 changes: 2 additions & 0 deletions stdlib/public/Concurrency/Task.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,8 @@ const void *const swift::_swift_concurrency_debug_jobMetadata =
const void *const swift::_swift_concurrency_debug_asyncTaskMetadata =
static_cast<Metadata *>(&taskHeapMetadata);

const size_t swift::_swift_concurrency_debug_asyncTaskSize = sizeof(AsyncTask);

const HeapMetadata *swift::jobHeapMetadataPtr =
static_cast<HeapMetadata *>(&jobHeapMetadata);
const HeapMetadata *swift::taskHeapMetadataPtr =
Expand Down
3 changes: 2 additions & 1 deletion stdlib/tools/swift-reflection-test/messages.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@ typedef enum InstanceKind {
Closure,
Enum,
EnumValue,
AsyncTask
AsyncTask,
LogString,
} InstanceKind;
110 changes: 97 additions & 13 deletions stdlib/tools/swift-reflection-test/swift-reflection-test.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
Expand Down Expand Up @@ -84,6 +85,20 @@ static void errnoAndExit(const char *message) {
#define DEBUG_LOG(fmt, ...) (void)0
#endif

#ifdef __clang__
__attribute((__format__(__printf__, 2, 3)))
#endif
static void
indented_printf(unsigned indentLevel, const char *fmt, ...) {
for (unsigned i = 0; i < indentLevel; i++)
fputs(" ", stdout);

va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
}

static const size_t ReadEnd = 0;
static const size_t WriteEnd = 1;

Expand Down Expand Up @@ -774,10 +789,40 @@ int reflectEnumValue(SwiftReflectionContextRef RC,

}

int reflectAsyncTask(SwiftReflectionContextRef RC,
const PipeMemoryReader *Reader) {
uintptr_t AsyncTaskInstance = PipeMemoryReader_receiveInstanceAddress(Reader);
printf("Async task %#" PRIx64 "\n", (uint64_t)AsyncTaskInstance);
static int reflectAsyncTaskInstance(SwiftReflectionContextRef RC,
uintptr_t AsyncTaskInstance,
const PipeMemoryReader *Reader,
unsigned indentLevel) {
indented_printf(indentLevel, "Async task %#" PRIx64 "\n",
(uint64_t)AsyncTaskInstance);

swift_async_task_info_t TaskInfo =
swift_reflection_asyncTaskInfo(RC, AsyncTaskInstance);
if (TaskInfo.Error) {
printf("swift_reflection_asyncTaskInfo failed: %s\n", TaskInfo.Error);
} else {
indented_printf(indentLevel, "id %" PRIu64 "\n", TaskInfo.Id);
indented_printf(indentLevel, "enqueuePriority %u\n",
TaskInfo.EnqueuePriority);
if (TaskInfo.ChildTaskCount > 0) {
indented_printf(indentLevel, "children = {\n");

// The memory for ChildTasks is only valid until the next Remote Mirror
// call, so we need to copy it.
swift_reflection_ptr_t *ChildTasks =
calloc(TaskInfo.ChildTaskCount, sizeof(swift_reflection_ptr_t));
memcpy(ChildTasks, TaskInfo.ChildTasks,
TaskInfo.ChildTaskCount * sizeof(swift_reflection_ptr_t));

for (unsigned i = 0; i < TaskInfo.ChildTaskCount; i++)
reflectAsyncTaskInstance(RC, ChildTasks[i], Reader, indentLevel + 1);

free(ChildTasks);
indented_printf(indentLevel, "}\n");
} else {
indented_printf(indentLevel, "children = {}\n");
}
}

swift_async_task_slab_return_t SlabPtrResult =
swift_reflection_asyncTaskSlabPointer(RC, AsyncTaskInstance);
Expand All @@ -787,33 +832,67 @@ int reflectAsyncTask(SwiftReflectionContextRef RC,
} else {
swift_reflection_ptr_t SlabPtr = SlabPtrResult.SlabPtr;
while (SlabPtr) {
printf(" Slab pointer %#" PRIx64 "\n", (uint64_t)SlabPtr);
indented_printf(indentLevel, " Slab pointer %#" PRIx64 "\n",
(uint64_t)SlabPtr);
swift_async_task_slab_allocations_return_t AllocationsResult =
swift_reflection_asyncTaskSlabAllocations(RC, SlabPtr);
if (AllocationsResult.Error) {
printf("swift_reflection_asyncTaskSlabAllocations failed: %s\n",
AllocationsResult.Error);
indented_printf(
indentLevel,
"swift_reflection_asyncTaskSlabAllocations failed: %s\n",
AllocationsResult.Error);
SlabPtr = 0;
} else {
printf(" Slab size %" PRIu64 "\n",
(uint64_t)AllocationsResult.SlabSize);
indented_printf(indentLevel, " Slab size %" PRIu64 "\n",
(uint64_t)AllocationsResult.SlabSize);
for (unsigned i = 0; i < AllocationsResult.ChunkCount; i++) {
swift_async_task_allocation_chunk_t Chunk =
AllocationsResult.Chunks[i];
printf(" Chunk at %#" PRIx64 " length %u kind %u\n",
(uint64_t)Chunk.Start, Chunk.Length, Chunk.Kind);
indented_printf(indentLevel,
" Chunk at %#" PRIx64 " length %u kind %u\n",
(uint64_t)Chunk.Start, Chunk.Length, Chunk.Kind);
}
SlabPtr = AllocationsResult.NextSlab;
}
}
}

printf("\n\n");
PipeMemoryReader_sendDoneMessage(Reader);
if (indentLevel == 0) {
printf("\n\n");
}
fflush(stdout);
return 1;
}

int reflectAsyncTask(SwiftReflectionContextRef RC,
const PipeMemoryReader *Reader) {
uintptr_t AsyncTaskInstance = PipeMemoryReader_receiveInstanceAddress(Reader);
int result = reflectAsyncTaskInstance(RC, AsyncTaskInstance, Reader, 0);
PipeMemoryReader_sendDoneMessage(Reader);
return result;
}

int logString(SwiftReflectionContextRef RC, const PipeMemoryReader *Reader) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wcast-qual"
void *Context = (void *)Reader;
#pragma clang diagnostic pop

swift_addr_t StringPointer = PipeMemoryReader_receiveInstanceAddress(Context);
uint64_t StringLength =
PipeMemoryReader_getStringLength(Context, StringPointer);

void *FreeContext;
// Read length+1 bytes to get the NUL terminator too.
const void *String = PipeMemoryReader_readBytes(
Context, StringPointer, StringLength + 1, &FreeContext);

printf("%s\n", (const char *)String);
PipeMemoryReader_freeBytes(Context, String, FreeContext);

PipeMemoryReader_sendDoneMessage(Context);
return 1;
}

int doDumpHeapInstance(const char *BinaryFilename, PipeMemoryReader *Reader) {
#if defined(_WIN32)
Expand Down Expand Up @@ -926,6 +1005,11 @@ int doDumpHeapInstance(const char *BinaryFilename, PipeMemoryReader *Reader) {
return EXIT_SUCCESS;
break;
}
case LogString: {
if (!logString(RC, Reader))
return EXIT_SUCCESS;
break;
}
case None:
swift_reflection_destroyReflectionContext(RC);
printf("Done.\n");
Expand Down
Loading