From 0116da253e4f7308e047dc3a440562e5e432952c Mon Sep 17 00:00:00 2001 From: Mike Ash Date: Thu, 4 Sep 2025 14:33:49 -0400 Subject: [PATCH] [Concurrency] Emit async task running/waitingOn metadata inline into CPUTrace. This allows tracking async code execution throughout a task's lifetime. rdar://137230240 --- stdlib/public/Concurrency/TaskPrivate.h | 7 ++++--- stdlib/public/Concurrency/TaskStatus.cpp | 11 +++++----- stdlib/public/Concurrency/Tracing.h | 3 ++- stdlib/public/Concurrency/TracingSignpost.h | 23 ++++++++++++++++++++- stdlib/public/Concurrency/TracingStubs.h | 3 ++- 5 files changed, 36 insertions(+), 11 deletions(-) diff --git a/stdlib/public/Concurrency/TaskPrivate.h b/stdlib/public/Concurrency/TaskPrivate.h index ea6db1ae0649e..3587c68f70bea 100644 --- a/stdlib/public/Concurrency/TaskPrivate.h +++ b/stdlib/public/Concurrency/TaskPrivate.h @@ -728,10 +728,11 @@ class alignas(2 * sizeof(void*)) ActiveTaskStatus { return record_iterator::rangeBeginning(getInnermostRecord()); } - void traceStatusChanged(AsyncTask *task, bool isStarting) { + void traceStatusChanged(AsyncTask *task, bool isStarting, bool wasRunning) { concurrency::trace::task_status_changed( task, static_cast(getStoredPriority()), isCancelled(), - isStoredPriorityEscalated(), isStarting, isRunning(), isEnqueued()); + isStoredPriorityEscalated(), isStarting, isRunning(), isEnqueued(), + wasRunning); } }; @@ -971,7 +972,7 @@ inline void AsyncTask::flagAsRunning() { if (_private()._status().compare_exchange_weak(oldStatus, newStatus, /* success */ std::memory_order_relaxed, /* failure */ std::memory_order_relaxed)) { - newStatus.traceStatusChanged(this, true); + newStatus.traceStatusChanged(this, true, oldStatus.isRunning()); adoptTaskVoucher(this); swift_task_enterThreadLocalContext( (char *)&_private().ExclusivityAccessSet[0]); diff --git a/stdlib/public/Concurrency/TaskStatus.cpp b/stdlib/public/Concurrency/TaskStatus.cpp index a17dcaf00ce28..c4c5d849161c9 100644 --- a/stdlib/public/Concurrency/TaskStatus.cpp +++ b/stdlib/public/Concurrency/TaskStatus.cpp @@ -94,8 +94,9 @@ static void withStatusRecordLock( status, newStatus, /*success*/ SWIFT_MEMORY_ORDER_CONSUME, /*failure*/ std::memory_order_relaxed)) { + bool wasRunning = status.isRunning(); status = newStatus; - status.traceStatusChanged(task, false); + status.traceStatusChanged(task, false, wasRunning); break; } } @@ -130,7 +131,7 @@ static void withStatusRecordLock( status, newStatus, /*success*/ std::memory_order_relaxed, /*failure*/ std::memory_order_relaxed)) { - newStatus.traceStatusChanged(task, false); + newStatus.traceStatusChanged(task, false, status.isRunning()); break; } } @@ -196,7 +197,7 @@ bool swift::addStatusRecord(AsyncTask *task, TaskStatusRecord *newRecord, if (task->_private()._status().compare_exchange_weak(oldStatus, newStatus, /*success*/ std::memory_order_release, /*failure*/ std::memory_order_relaxed)) { - newStatus.traceStatusChanged(task, false); + newStatus.traceStatusChanged(task, false, oldStatus.isRunning()); return true; } else { // Retry @@ -302,7 +303,7 @@ void swift::removeStatusRecord(AsyncTask *task, TaskStatusRecord *record, oldStatus, newStatus, /*success*/ std::memory_order_relaxed, /*failure*/ std::memory_order_relaxed)) { - newStatus.traceStatusChanged(task, false); + newStatus.traceStatusChanged(task, false, oldStatus.isRunning()); return; } @@ -904,7 +905,7 @@ static void swift_task_cancelImpl(AsyncTask *task) { } } - newStatus.traceStatusChanged(task, false); + newStatus.traceStatusChanged(task, false, oldStatus.isRunning()); if (newStatus.getInnermostRecord() == nullptr) { // No records, nothing to propagate return; diff --git a/stdlib/public/Concurrency/Tracing.h b/stdlib/public/Concurrency/Tracing.h index 1e084ce6bd66c..b26122f402ebe 100644 --- a/stdlib/public/Concurrency/Tracing.h +++ b/stdlib/public/Concurrency/Tracing.h @@ -63,7 +63,8 @@ void task_create(AsyncTask *task, AsyncTask *parent, TaskGroup *group, void task_destroy(AsyncTask *task); void task_status_changed(AsyncTask *task, uint8_t maxPriority, bool isCancelled, - bool isEscalated, bool isStarting, bool isRunning, bool isEnqueued); + bool isEscalated, bool isStarting, bool isRunning, + bool isEnqueued, bool wasRunning); void task_flags_changed(AsyncTask *task, uint8_t jobPriority, bool isChildTask, bool isFuture, bool isGroupChildTask, diff --git a/stdlib/public/Concurrency/TracingSignpost.h b/stdlib/public/Concurrency/TracingSignpost.h index 61ae44602400b..b44304e28254d 100644 --- a/stdlib/public/Concurrency/TracingSignpost.h +++ b/stdlib/public/Concurrency/TracingSignpost.h @@ -28,6 +28,10 @@ #include #include +#if defined(__arm64__) && __has_include() +#include +#endif + // Compatibility notes: // // These signposts can be read by external software that isn't synced with the @@ -210,7 +214,24 @@ inline void task_destroy(AsyncTask *task) { inline void task_status_changed(AsyncTask *task, uint8_t maxPriority, bool isCancelled, bool isEscalated, - bool isStarting, bool isRunning, bool isEnqueued) { + bool isStarting, bool isRunning, + bool isEnqueued, bool wasRunning) { +#if !TARGET_OS_SIMULATOR +#if OS_APT_SPI_VERSION >= 20241023 + uint64_t taskId = task->getTaskId(); + if (isRunning) { + if (SWIFT_RUNTIME_WEAK_CHECK(os_apt_msg_async_task_running_4swift)) { + SWIFT_RUNTIME_WEAK_USE(os_apt_msg_async_task_running_4swift(taskId)); + } + } else if (!isRunning && wasRunning) { +#if OS_APT_SPI_VERSION >= 20250710 + if (SWIFT_RUNTIME_WEAK_CHECK(os_apt_msg_async_task_stopped_4swift)) { + SWIFT_RUNTIME_WEAK_USE(os_apt_msg_async_task_stopped_4swift(taskId)); + } +#endif // OS_APT_SPI_VERSION >= 20250710 + } +#endif // OS_APT_SPI_VERSION >= 20241023 +#endif // !TARGET_OS_SIMULATOR ENSURE_LOGS(); auto id = os_signpost_id_make_with_pointer(TaskLog, task); os_signpost_event_emit( diff --git a/stdlib/public/Concurrency/TracingStubs.h b/stdlib/public/Concurrency/TracingStubs.h index 179434e926be7..2c610e6db5cac 100644 --- a/stdlib/public/Concurrency/TracingStubs.h +++ b/stdlib/public/Concurrency/TracingStubs.h @@ -56,7 +56,8 @@ inline void task_resume(AsyncTask *task) {} inline void task_status_changed(AsyncTask *task, uint8_t maxPriority, bool isCancelled, bool isEscalated, - bool isStarting, bool isRunning, bool isEnqueued) {} + bool isStarting, bool isRunning, + bool isEnqueued, bool wasRunning) {} inline void task_flags_changed(AsyncTask *task, uint8_t jobPriority, bool isChildTask, bool isFuture,