From 55d950d6ad84177f3c9809892affd8376a6948ef Mon Sep 17 00:00:00 2001 From: James Moschou Date: Wed, 1 Jul 2026 13:30:49 +0200 Subject: [PATCH 01/42] Add IAGNamedTraceEventID --- Sources/ComputeCxx/Graph/IAGGraph.cpp | 8 ++++---- Sources/ComputeCxx/Graph/TraceRecorder.cpp | 4 ++-- Sources/ComputeCxx/Graph/TraceRecorder.h | 4 ++-- Sources/ComputeCxx/Trace/ExternalTrace.cpp | 4 ++-- Sources/ComputeCxx/Trace/ExternalTrace.h | 4 ++-- Sources/ComputeCxx/Trace/Trace.h | 4 ++-- .../ComputeCxx/include/ComputeCxx/IAGGraphTracing.h | 10 ++++++---- .../ComputeTests/Shared/Graph/GraphTracingTests.swift | 2 +- 8 files changed, 21 insertions(+), 19 deletions(-) diff --git a/Sources/ComputeCxx/Graph/IAGGraph.cpp b/Sources/ComputeCxx/Graph/IAGGraph.cpp index c2f130a1..2c3be98e 100644 --- a/Sources/ComputeCxx/Graph/IAGGraph.cpp +++ b/Sources/ComputeCxx/Graph/IAGGraph.cpp @@ -1018,7 +1018,7 @@ void IAGGraphAddTraceEvent(IAGGraphRef graph, const char *event_name, const void }); } -void IAGGraphAddNamedTraceEvent(IAGGraphRef graph, uint32_t event_id, uint32_t event_arg_count, const void *event_args, +void IAGGraphAddNamedTraceEvent(IAGGraphRef graph, IAGNamedTraceEventID event_id, uint32_t event_arg_count, const void *event_args, CFDataRef data, uint32_t arg6) { auto graph_context = IAG::Graph::Context::from_cf(graph); graph_context->graph().foreach_trace( @@ -1034,7 +1034,7 @@ static IAG::vector, 0, uint32_t> *names; } // namespace NamedEvents -const char *IAGGraphGetTraceEventName(uint32_t event_id) { +const char *IAGGraphGetTraceEventName(IAGNamedTraceEventID event_id) { const char *event_name = nullptr; platform_lock_lock(&NamedEvents::lock); @@ -1046,7 +1046,7 @@ const char *IAGGraphGetTraceEventName(uint32_t event_id) { return event_name; } -const char *IAGGraphGetTraceEventSubsystem(uint32_t event_id) { +const char *IAGGraphGetTraceEventSubsystem(IAGNamedTraceEventID event_id) { const char *event_subsystem = nullptr; platform_lock_lock(&NamedEvents::lock); @@ -1058,7 +1058,7 @@ const char *IAGGraphGetTraceEventSubsystem(uint32_t event_id) { return event_subsystem; } -uint32_t IAGGraphRegisterNamedTraceEvent(const char *event_name, const char *event_subsystem) { +IAGNamedTraceEventID IAGGraphRegisterNamedTraceEvent(const char *event_name, const char *event_subsystem) { platform_lock_lock(&NamedEvents::lock); if (!NamedEvents::names) { diff --git a/Sources/ComputeCxx/Graph/TraceRecorder.cpp b/Sources/ComputeCxx/Graph/TraceRecorder.cpp index 13ebe072..87ebbe22 100644 --- a/Sources/ComputeCxx/Graph/TraceRecorder.cpp +++ b/Sources/ComputeCxx/Graph/TraceRecorder.cpp @@ -196,12 +196,12 @@ void Graph::TraceRecorder::custom_event(const Graph::Context &context, const cha // TODO: not implemented } -void Graph::TraceRecorder::named_event(const Graph::Context &context, uint32_t event_id, uint32_t event_arg_count, +void Graph::TraceRecorder::named_event(const Graph::Context &context, IAGNamedTraceEventID event_id, uint32_t event_arg_count, const void *event_args, CFDataRef data, uint32_t arg6) { // TODO: not implemented } -bool Graph::TraceRecorder::named_event_enabled(uint32_t event_id) { +bool Graph::TraceRecorder::named_event_enabled(IAGNamedTraceEventID event_id) { // TODO: not implemented return false; } diff --git a/Sources/ComputeCxx/Graph/TraceRecorder.h b/Sources/ComputeCxx/Graph/TraceRecorder.h index a6219368..15e92af6 100644 --- a/Sources/ComputeCxx/Graph/TraceRecorder.h +++ b/Sources/ComputeCxx/Graph/TraceRecorder.h @@ -87,9 +87,9 @@ class Graph::TraceRecorder : public Trace { void custom_event(const Graph::Context &context, const char *event_name, const void *value, const swift::metadata &type) override; - void named_event(const Graph::Context &context, uint32_t event_id, uint32_t event_arg_count, const void *event_args, + void named_event(const Graph::Context &context, IAGNamedTraceEventID event_id, uint32_t event_arg_count, const void *event_args, CFDataRef data, uint32_t arg6) override; - bool named_event_enabled(uint32_t event_id) override; + bool named_event_enabled(IAGNamedTraceEventID event_id) override; // compare_failed not overridden }; diff --git a/Sources/ComputeCxx/Trace/ExternalTrace.cpp b/Sources/ComputeCxx/Trace/ExternalTrace.cpp index 167a856a..ea227aad 100644 --- a/Sources/ComputeCxx/Trace/ExternalTrace.cpp +++ b/Sources/ComputeCxx/Trace/ExternalTrace.cpp @@ -263,7 +263,7 @@ void ExternalTrace::custom_event(const IAG::Graph::Context &context, const char } } -void ExternalTrace::named_event(const IAG::Graph::Context &context, uint32_t event_id, uint32_t event_arg_count, +void ExternalTrace::named_event(const IAG::Graph::Context &context, IAGNamedTraceEventID event_id, uint32_t event_arg_count, const void *event_args, CFDataRef data, uint32_t arg6) { if (_trace->version < IAGTraceTypeVersionNamed) { return; @@ -274,7 +274,7 @@ void ExternalTrace::named_event(const IAG::Graph::Context &context, uint32_t eve } } -bool ExternalTrace::named_event_enabled(uint32_t event_id) { +bool ExternalTrace::named_event_enabled(IAGNamedTraceEventID event_id) { if (_trace->version < IAGTraceTypeVersionNamed) { return false; } diff --git a/Sources/ComputeCxx/Trace/ExternalTrace.h b/Sources/ComputeCxx/Trace/ExternalTrace.h index 5d9c5ae9..186229c2 100644 --- a/Sources/ComputeCxx/Trace/ExternalTrace.h +++ b/Sources/ComputeCxx/Trace/ExternalTrace.h @@ -81,9 +81,9 @@ class ExternalTrace : public IAG::Trace { void custom_event(const IAG::Graph::Context &context, const char *event_name, const void *value, const IAG::swift::metadata &type) override; - void named_event(const IAG::Graph::Context &context, uint32_t event_id, uint32_t event_arg_count, + void named_event(const IAG::Graph::Context &context, IAGNamedTraceEventID event_id, uint32_t event_arg_count, const void *event_args, CFDataRef data, uint32_t arg6) override; - bool named_event_enabled(uint32_t event_id) override; + bool named_event_enabled(IAGNamedTraceEventID event_id) override; void set_deadline(uint64_t deadline) override; void passed_deadline() override; diff --git a/Sources/ComputeCxx/Trace/Trace.h b/Sources/ComputeCxx/Trace/Trace.h index 4525d0ef..c4bde40f 100644 --- a/Sources/ComputeCxx/Trace/Trace.h +++ b/Sources/ComputeCxx/Trace/Trace.h @@ -96,9 +96,9 @@ class Trace { virtual void custom_event(const Graph::Context &context, const char *event_name, const void *value, const swift::metadata &type) {}; - virtual void named_event(const Graph::Context &context, uint32_t event_id, uint32_t event_arg_count, + virtual void named_event(const Graph::Context &context, IAGNamedTraceEventID event_id, uint32_t event_arg_count, const void *event_args, CFDataRef data, uint32_t arg6) {}; - virtual bool named_event_enabled(uint32_t event_id) { return false; }; + virtual bool named_event_enabled(IAGNamedTraceEventID event_id) { return false; }; virtual void compare_failed(data::ptr node, const void *lhs, const void *rhs, size_t range_offset, size_t range_size, const swift::metadata *_Nullable type) {}; diff --git a/Sources/ComputeCxx/include/ComputeCxx/IAGGraphTracing.h b/Sources/ComputeCxx/include/ComputeCxx/IAGGraphTracing.h index b1db4401..13bc2b31 100644 --- a/Sources/ComputeCxx/include/ComputeCxx/IAGGraphTracing.h +++ b/Sources/ComputeCxx/include/ComputeCxx/IAGGraphTracing.h @@ -14,6 +14,8 @@ typedef IAG_OPTIONS(uint32_t, IAGGraphTraceOptions) { typedef struct IAGTraceType *IAGTraceTypeRef; +typedef uint32_t IAGNamedTraceEventID IAG_SWIFT_STRUCT IAG_SWIFT_NAME(Graph.NamedTraceEventID); + IAG_ASSUME_NONNULL_BEGIN IAG_EXTERN_C_BEGIN @@ -79,22 +81,22 @@ void IAGGraphAddTraceEvent(IAGGraphRef graph, const char *event_name, const void IAG_EXPORT IAG_REFINED_FOR_SWIFT -void IAGGraphAddNamedTraceEvent(IAGGraphRef graph, uint32_t event_id, uint32_t event_arg_count, const void *event_args, +void IAGGraphAddNamedTraceEvent(IAGGraphRef graph, IAGNamedTraceEventID event_id, uint32_t event_arg_count, const void *event_args, CFDataRef data, uint32_t arg6) IAG_SWIFT_NAME(IAGGraphRef.addNamedTraceEvent(self:eventID:eventArgCount:eventArgs:data:arg6:)); IAG_EXPORT IAG_REFINED_FOR_SWIFT -const char *_Nullable IAGGraphGetTraceEventName(uint32_t event_id) IAG_SWIFT_NAME(IAGGraphRef.traceEventName(for:)); +const char *_Nullable IAGGraphGetTraceEventName(IAGNamedTraceEventID event_id) IAG_SWIFT_NAME(IAGGraphRef.traceEventName(for:)); IAG_EXPORT IAG_REFINED_FOR_SWIFT -const char *_Nullable IAGGraphGetTraceEventSubsystem(uint32_t event_id) +const char *_Nullable IAGGraphGetTraceEventSubsystem(IAGNamedTraceEventID event_id) IAG_SWIFT_NAME(IAGGraphRef.traceEventSubsystem(for:)); IAG_EXPORT IAG_REFINED_FOR_SWIFT -uint32_t IAGGraphRegisterNamedTraceEvent(const char *event_name, const char *event_subsystem) +IAGNamedTraceEventID IAGGraphRegisterNamedTraceEvent(const char *event_name, const char *event_subsystem) IAG_SWIFT_NAME(IAGGraphRef.registerNamedTraceEvent(name:subsystem:)); IAG_EXTERN_C_END diff --git a/Tests/ComputeTests/Shared/Graph/GraphTracingTests.swift b/Tests/ComputeTests/Shared/Graph/GraphTracingTests.swift index b3e370a2..aa9fa81f 100644 --- a/Tests/ComputeTests/Shared/Graph/GraphTracingTests.swift +++ b/Tests/ComputeTests/Shared/Graph/GraphTracingTests.swift @@ -86,7 +86,7 @@ struct GraphTracingTests { @Test func namedEvents() throws { - let eventName = Graph.traceEventName(for: 0) + let eventName = Graph.traceEventName(for: Graph.NamedTraceEventID(rawValue: 0)) #expect(eventName == nil) let eventID = "testname".utf8CString.withUnsafeBufferPointer { namePointer in From 0821272ba3ff15d06fc8585e783a2785db59a98d Mon Sep 17 00:00:00 2001 From: James Moschou Date: Mon, 3 Aug 2026 18:35:29 +0200 Subject: [PATCH 02/42] Rename trace_options to trace_flags --- Sources/ComputeCxx/Graph/Graph.cpp | 36 +++++++++---------- Sources/ComputeCxx/Graph/Graph.h | 4 +-- Sources/ComputeCxx/Graph/IAGGraph.cpp | 8 ++--- Sources/ComputeCxx/Graph/TraceRecorder.cpp | 2 +- Sources/ComputeCxx/Graph/TraceRecorder.h | 2 +- .../include/ComputeCxx/IAGGraphTracing.h | 24 ++++++------- 6 files changed, 38 insertions(+), 38 deletions(-) diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 688d59e1..ebff015e 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -48,18 +48,18 @@ Graph::Graph() _types.push_back(nullptr); - static auto [trace_options, trace_subsystems] = + static auto [trace_flags, trace_subsystems] = []() -> std::tuple, 0, uint64_t>> { // TODO: debug server // TODO: profile vector, 0, uint64_t> trace_subsystems = {}; - IAGGraphTraceOptions trace_options = 0; + IAGGraphTraceFlags trace_flags = 0; const char *trace_string = getenv("IAG_TRACE"); if (trace_string) { char *endptr = nullptr; - trace_options = (uint32_t)strtol(trace_string, &endptr, 0); + trace_flags = (uint32_t)strtol(trace_string, &endptr, 0); if (endptr) { const char *c = endptr + strspn(endptr, ", \t\n\f\r"); @@ -71,22 +71,22 @@ Graph::Graph() option[option_length] = 0; if (strcasecmp(option, "enabled") == 0) { - trace_options |= IAGGraphTraceOptionsEnabled; + trace_flags |= IAGGraphTraceFlagsEnabled; free(option); } else if (strcasecmp(option, "full") == 0) { - trace_options |= IAGGraphTraceOptionsFull; + trace_flags |= IAGGraphTraceFlagsFull; free(option); } else if (strcasecmp(option, "backtrace") == 0) { - trace_options |= IAGGraphTraceOptionsBacktrace; + trace_flags |= IAGGraphTraceFlagsBacktrace; free(option); } else if (strcasecmp(option, "prepare") == 0) { - trace_options |= IAGGraphTraceOptionsPrepare; + trace_flags |= IAGGraphTraceFlagsPrepare; free(option); } else if (strcasecmp(option, "custom") == 0) { - trace_options |= IAGGraphTraceOptionsCustom; + trace_flags |= IAGGraphTraceFlagsCustom; free(option); } else if (strcasecmp(option, "all") == 0) { - trace_options |= IAGGraphTraceOptionsAll; + trace_flags |= IAGGraphTraceFlagsAll; free(option); } else { trace_subsystems.push_back(std::unique_ptr(option)); @@ -97,11 +97,11 @@ Graph::Graph() } } - return {trace_options, std::move(trace_subsystems)}; + return {trace_flags, std::move(trace_subsystems)}; }(); - if (trace_options && !trace_subsystems.empty()) { - start_tracing(trace_options, std::span((const char **)trace_subsystems.data(), trace_subsystems.size())); + if (trace_flags && !trace_subsystems.empty()) { + start_tracing(trace_flags, std::span((const char **)trace_subsystems.data(), trace_subsystems.size())); } // Prepend this graph @@ -1874,16 +1874,16 @@ void *Graph::output_value_ref(data::ptr node, const swift::metadata &value #pragma mark - Trace -void Graph::start_tracing(IAGGraphTraceOptions trace_options, std::span subsystems) { - if ((trace_options & IAGGraphTraceOptionsEnabled) == 0) { +void Graph::start_tracing(IAGGraphTraceFlags trace_flags, std::span subsystems) { + if ((trace_flags & IAGGraphTraceFlagsEnabled) == 0) { return; } if (_trace_recorder) { return; } - _trace_recorder = new TraceRecorder(this, trace_options, subsystems); - if (trace_options & IAGGraphTraceOptionsPrepare) { + _trace_recorder = new TraceRecorder(this, trace_flags, subsystems); + if (trace_flags & IAGGraphTraceFlagsPrepare) { prepare_trace(*_trace_recorder); } add_trace(_trace_recorder); @@ -2008,10 +2008,10 @@ void Graph::remove_trace(uint64_t trace_id) { } } -void Graph::all_start_tracing(IAGGraphTraceOptions trace_options, std::span span) { +void Graph::all_start_tracing(IAGGraphTraceFlags trace_flags, std::span span) { all_lock(); for (auto graph = _all_graphs; graph != nullptr; graph = graph->_next) { - graph->start_tracing(trace_options, span); + graph->start_tracing(trace_flags, span); } all_unlock(); } diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index 5f2cc064..64ec46ea 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -390,7 +390,7 @@ class Graph { // MARK: Trace - void start_tracing(IAGGraphTraceOptions trace_options, std::span subsystems); + void start_tracing(IAGGraphTraceFlags trace_flags, std::span subsystems); void stop_tracing(); void sync_tracing(); CFStringRef copy_trace_path(); @@ -400,7 +400,7 @@ class Graph { void add_trace(Trace *_Nullable trace); void remove_trace(uint64_t trace_id); - static void all_start_tracing(IAGGraphTraceOptions trace_options, std::span span); + static void all_start_tracing(IAGGraphTraceFlags trace_flags, std::span span); static void all_stop_tracing(); static void all_sync_tracing(); static CFStringRef all_copy_trace_path(); diff --git a/Sources/ComputeCxx/Graph/IAGGraph.cpp b/Sources/ComputeCxx/Graph/IAGGraph.cpp index 2c3be98e..7897d918 100644 --- a/Sources/ComputeCxx/Graph/IAGGraph.cpp +++ b/Sources/ComputeCxx/Graph/IAGGraph.cpp @@ -901,9 +901,9 @@ void IAGGraphSetOutputValue(const void *value, IAGTypeID type) { #pragma mark - Trace -void IAGGraphStartTracing(IAGGraphRef graph, IAGGraphTraceOptions trace_options) { IAGGraphStartTracing2(graph, trace_options, NULL); } +void IAGGraphStartTracing(IAGGraphRef graph, IAGGraphTraceFlags trace_flags) { IAGGraphStartTracing2(graph, trace_flags, NULL); } -void IAGGraphStartTracing2(IAGGraphRef graph, IAGGraphTraceOptions trace_options, CFArrayRef subsystems) { +void IAGGraphStartTracing2(IAGGraphRef graph, IAGGraphTraceFlags trace_flags, CFArrayRef subsystems) { auto subsystems_vector = IAG::vector, 0, uint64_t>(); if (subsystems) { auto subsystems_count = CFArrayGetCount(subsystems); @@ -928,12 +928,12 @@ void IAGGraphStartTracing2(IAGGraphRef graph, IAGGraphTraceOptions trace_options std::span((const char **)subsystems_vector.data(), subsystems_vector.size()); if (graph == nullptr) { - IAG::Graph::all_start_tracing(trace_options, subsystems_span); + IAG::Graph::all_start_tracing(trace_flags, subsystems_span); return; } auto graph_context = IAG::Graph::Context::from_cf(graph); - graph_context->graph().start_tracing(trace_options, subsystems_span); + graph_context->graph().start_tracing(trace_flags, subsystems_span); } void IAGGraphStopTracing(IAGGraphRef graph) { diff --git a/Sources/ComputeCxx/Graph/TraceRecorder.cpp b/Sources/ComputeCxx/Graph/TraceRecorder.cpp index 87ebbe22..7dd7b203 100644 --- a/Sources/ComputeCxx/Graph/TraceRecorder.cpp +++ b/Sources/ComputeCxx/Graph/TraceRecorder.cpp @@ -15,7 +15,7 @@ namespace IAG { -Graph::TraceRecorder::TraceRecorder(Graph *graph, IAGGraphTraceOptions trace_options, std::span subsystems) { +Graph::TraceRecorder::TraceRecorder(Graph *graph, IAGGraphTraceFlags trace_flags, std::span subsystems) { // TODO: not implemented } diff --git a/Sources/ComputeCxx/Graph/TraceRecorder.h b/Sources/ComputeCxx/Graph/TraceRecorder.h index 15e92af6..d350b2f7 100644 --- a/Sources/ComputeCxx/Graph/TraceRecorder.h +++ b/Sources/ComputeCxx/Graph/TraceRecorder.h @@ -12,7 +12,7 @@ namespace IAG { class Graph::TraceRecorder : public Trace { public: - TraceRecorder(Graph *graph, IAGGraphTraceOptions trace_options, std::span subsystems); + TraceRecorder(Graph *graph, IAGGraphTraceFlags trace_flags, std::span subsystems); ~TraceRecorder(); uint64_t id() { return _id; }; diff --git a/Sources/ComputeCxx/include/ComputeCxx/IAGGraphTracing.h b/Sources/ComputeCxx/include/ComputeCxx/IAGGraphTracing.h index 13bc2b31..a0f31a0f 100644 --- a/Sources/ComputeCxx/include/ComputeCxx/IAGGraphTracing.h +++ b/Sources/ComputeCxx/include/ComputeCxx/IAGGraphTracing.h @@ -3,14 +3,14 @@ #include #include -typedef IAG_OPTIONS(uint32_t, IAGGraphTraceOptions) { - IAGGraphTraceOptionsEnabled = 1 << 0, - IAGGraphTraceOptionsFull = 1 << 1, - IAGGraphTraceOptionsBacktrace = 1 << 2, - IAGGraphTraceOptionsPrepare = 1 << 3, - IAGGraphTraceOptionsCustom = 1 << 4, - IAGGraphTraceOptionsAll = 1 << 5, -} IAG_SWIFT_NAME(IAGGraphRef.TraceOptions); +typedef IAG_OPTIONS(uint32_t, IAGGraphTraceFlags) { + IAGGraphTraceFlagsEnabled = 1 << 0, + IAGGraphTraceFlagsFull = 1 << 1, + IAGGraphTraceFlagsBacktrace = 1 << 2, + IAGGraphTraceFlagsPrepare = 1 << 3, + IAGGraphTraceFlagsCustom = 1 << 4, + IAGGraphTraceFlagsAll = 1 << 5, +} IAG_SWIFT_NAME(IAGGraphRef.TraceFlags); typedef struct IAGTraceType *IAGTraceTypeRef; @@ -22,14 +22,14 @@ IAG_EXTERN_C_BEGIN IAG_EXPORT IAG_REFINED_FOR_SWIFT -void IAGGraphStartTracing(IAGGraphRef _Nullable graph, IAGGraphTraceOptions trace_options) - IAG_SWIFT_NAME(IAGGraphRef.startTracing(_:options:)); +void IAGGraphStartTracing(IAGGraphRef _Nullable graph, IAGGraphTraceFlags trace_flags) + IAG_SWIFT_NAME(IAGGraphRef.startTracing(_:flags:)); IAG_EXPORT IAG_REFINED_FOR_SWIFT -void IAGGraphStartTracing2(IAGGraphRef _Nullable graph, IAGGraphTraceOptions trace_options, +void IAGGraphStartTracing2(IAGGraphRef _Nullable graph, IAGGraphTraceFlags trace_flags, CFArrayRef _Nullable subsystems) - IAG_SWIFT_NAME(IAGGraphRef.startTracing(_:options:subsystems:)); + IAG_SWIFT_NAME(IAGGraphRef.startTracing(_:flags:subsystems:)); IAG_EXPORT IAG_REFINED_FOR_SWIFT From 159277912414e6a5cc78a2795277dbabaadfb5fd Mon Sep 17 00:00:00 2001 From: James Moschou Date: Mon, 3 Aug 2026 18:43:27 +0200 Subject: [PATCH 03/42] Add IAG::Encoder --- Sources/ComputeCxx/Protobuf/Encoder.cpp | 104 ++++++++++++++++++++++++ Sources/ComputeCxx/Protobuf/Encoder.h | 78 ++++++++++++++++++ 2 files changed, 182 insertions(+) create mode 100644 Sources/ComputeCxx/Protobuf/Encoder.cpp create mode 100644 Sources/ComputeCxx/Protobuf/Encoder.h diff --git a/Sources/ComputeCxx/Protobuf/Encoder.cpp b/Sources/ComputeCxx/Protobuf/Encoder.cpp new file mode 100644 index 00000000..a494d929 --- /dev/null +++ b/Sources/ComputeCxx/Protobuf/Encoder.cpp @@ -0,0 +1,104 @@ +#include "Encoder.h" + +#include "Errors/Errors.h" + +namespace IAG { + +Encoder::Encoder(Delegate *_Nullable delegate, uint64_t flush_interval) + : _delegate(delegate), _flush_interval(flush_interval) { + if (delegate == nullptr && flush_interval != 0) { + precondition_failure("need a delegate if flush interval is non-zero"); + } +} + +void Encoder::encode_varint(uint64_t value) { + uint64_t width = 0; + if (value <= 0x7f) { + if (_buffer.size() < _buffer.capacity()) { + _buffer.push_back(value); + return; + } + width = 1; + } else { + width = ((64 - std::countl_zero(value)) + 6) / 7; + } + + uint64_t index = _buffer.size(); + _buffer.resize(_buffer.size() + width); // TODO: how to resize without zeroing memory + + _buffer[index] = 0; + uint64_t remaining_value = value; + while (remaining_value) { + _buffer[index] = ((char)remaining_value & 0x7f) | (0x7f < remaining_value) << 7; + index += 1; + remaining_value = remaining_value >> 7; + } +} + +void Encoder::encode_fixed64(uint64_t value) { + uint64_t old_size = _buffer.size(); + _buffer.resize(_buffer.size() + sizeof(uint64_t)); + // Protobuf fixed64 is little-endian + char *dest = _buffer.data() + old_size; + for (int i = 0; i < 8; ++i) { + dest[i] = (char)(value >> (i * 8)); + } +} + +void Encoder::encode_data(const void *data, size_t length) { + encode_varint(length); + if (length == 0) { + return; + } + uint64_t old_size = _buffer.size(); + _buffer.resize(_buffer.size() + length); + std::memcpy(_buffer.data() + old_size, data, length); +} + +void Encoder::begin_length_delimited() { + // Reserve one byte for the length and store the position + uint64_t position = _buffer.size(); + _buffer.resize(_buffer.size() + 1); + _sections.push_back(position); +} + +void Encoder::end_length_delimited() { + assert(!_sections.empty()); + + uint64_t position = _sections.back(); + _sections.pop_back(); + + uint64_t length = _buffer.size() - (position + 1); + if (length <= 0x7f) { + _buffer[position] = length; + } else { + // The length requires more than one byte + uint64_t width = ((64 - std::countl_zero(length)) + 6) / 7; + _buffer.resize(_buffer.size() + width - 1); + + std::memmove(reinterpret_cast(_buffer.data() + position + width), + reinterpret_cast(_buffer.data() + position + 1), length); + + uint64_t remaining_value = length; + while (remaining_value) { + _buffer[position] = ((char)remaining_value & 0x7f) | (0x7f < remaining_value) << 7; + position += 1; + remaining_value = remaining_value >> 7; + } + } + + if (_sections.empty() && _flush_interval != 0 && _flush_interval <= _buffer.size()) { + flush(); + } +} + +void Encoder::flush() { + assert(_sections.empty()); + if (!_buffer.empty() && _delegate) { + if (_delegate->flush_encoder(*this) == 0) { + _buffer.resize(0); + } + } +} + +} // namespace IAG diff --git a/Sources/ComputeCxx/Protobuf/Encoder.h b/Sources/ComputeCxx/Protobuf/Encoder.h new file mode 100644 index 00000000..79b45397 --- /dev/null +++ b/Sources/ComputeCxx/Protobuf/Encoder.h @@ -0,0 +1,78 @@ +#pragma once + +#include "ComputeCxx/IAGBase.h" + +#include "Vector/Vector.h" + +IAG_ASSUME_NONNULL_BEGIN + +namespace IAG { + +class Encoder { + public: + struct Delegate { + virtual int flush_encoder(Encoder &encoder) { return 0; }; + }; + + private: + Delegate *_Nullable _delegate; + uint64_t _flush_interval; + vector _buffer; + vector _sections; + + void encode_varint(uint64_t value); + void encode_fixed64(uint64_t value); + void encode_data(const void *data, size_t length); + + void begin_length_delimited(); + void end_length_delimited(); + + enum class WireType : uint8_t { + VarInt = 0, + I64 = 1, + Len = 2, + }; + + void encode_tag(uint64_t field, WireType wire_type) { + encode_varint((field << 3) | static_cast(wire_type)); + } + + public: + Encoder(Delegate *_Nullable delegate, uint64_t flush_interval); + + const vector &buffer() const { return _buffer; }; + + void encode_field_varint(uint64_t field, uint64_t value) { + if (value) { + encode_tag(field, WireType::VarInt); + encode_varint(value); + } + } + + void encode_field_fixed64(uint64_t field, uint64_t value) { + if (value) { + encode_tag(field, WireType::I64); + encode_fixed64(value); + } + } + + void encode_field_data(uint64_t field, const void *data, size_t length) { + if (length > 0) { + encode_tag(field, WireType::Len); + encode_data(data, length); + } + } + + void encode_field_begin(uint64_t field) { + encode_tag(field, WireType::Len); + begin_length_delimited(); + } + + void encode_field_end() { end_length_delimited(); } + + void flush(); +}; + +} // namespace IAG + +IAG_ASSUME_NONNULL_END From 89eb139cb7d7fd570676dd2bcece8f970db82e99 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Tue, 4 Aug 2026 10:14:37 +0200 Subject: [PATCH 04/42] Add encode methods for Subgraph, Node, IndirectNode and TreeElement --- .../AttributeData/Node/IndirectNode.h | 1 + .../Attribute/AttributeData/Node/Node.h | 2 + Sources/ComputeCxx/Graph/Graph.cpp | 111 ++++++++++++++++++ Sources/ComputeCxx/Graph/Graph.h | 9 +- Sources/ComputeCxx/Subgraph/Subgraph.cpp | 51 ++++++++ Sources/ComputeCxx/Subgraph/Subgraph.h | 6 + 6 files changed, 179 insertions(+), 1 deletion(-) diff --git a/Sources/ComputeCxx/Attribute/AttributeData/Node/IndirectNode.h b/Sources/ComputeCxx/Attribute/AttributeData/Node/IndirectNode.h index 7d028100..8b35c51f 100644 --- a/Sources/ComputeCxx/Attribute/AttributeData/Node/IndirectNode.h +++ b/Sources/ComputeCxx/Attribute/AttributeData/Node/IndirectNode.h @@ -87,6 +87,7 @@ class MutableIndirectNode : public IndirectNode { uint32_t initial_offset() { return _initial_offset; }; data::vector &output_edges() { return _output_edges; }; + const data::vector &output_edges() const { return _output_edges; }; }; } // namespace IAG diff --git a/Sources/ComputeCxx/Attribute/AttributeData/Node/Node.h b/Sources/ComputeCxx/Attribute/AttributeData/Node/Node.h index 456298a7..4d7fffe0 100644 --- a/Sources/ComputeCxx/Attribute/AttributeData/Node/Node.h +++ b/Sources/ComputeCxx/Attribute/AttributeData/Node/Node.h @@ -153,6 +153,7 @@ class Node { void set_self_modified(bool value) { _self_modified = value; } data::vector &input_edges() { return _input_edges; }; + const data::vector &input_edges() const { return _input_edges; }; uint32_t insert_input_edge(data::zone *subgraph, InputEdge &input_edge) { if (_needs_sort_input_edges) { _input_edges.push_back(subgraph, input_edge); @@ -166,6 +167,7 @@ class Node { void remove_input_edge(uint32_t index) { _input_edges.erase(_input_edges.begin() + index); } data::vector &output_edges() { return _output_edges; }; + const data::vector &output_edges() const { return _output_edges; }; void *get_self(const AttributeType &type) const; void update_self(const Graph &graph, const void *new_self); diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index ebff015e..a7aa1a6c 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -25,6 +25,7 @@ #include "Context.h" #include "KeyTable.h" #include "Log/Log.h" +#include "Protobuf/Encoder.h" #include "Subgraph/Subgraph.h" #include "TraceRecorder.h" #include "UpdateStack.h" @@ -2081,6 +2082,116 @@ const char *Graph::key_name(uint32_t key_id) const { IAG::precondition_failure("invalid string key id: %u", key_id); } +#pragma mark - Encoding + +void Graph::encode_node(Encoder &encoder, const Node &node, bool encode_value) const { + encoder.encode_field_varint(1, node.type_id()); + if (encode_value) { + auto type = attribute_type(node.type_id()); + void *value = node.get_value(); + if (node.is_value_initialized()) { + void *value = node.get_value(); +#if TARGET_OS_MAC + if (auto description = type.value_description(value)) { + uint64_t length = CFStringGetLength(description); + CFRange range = CFRangeMake(0, length); + uint8_t buffer[1024]; + CFIndex used_buffer_length = 0; + CFStringGetBytes(description, range, kCFStringEncodingUTF8, 0x3f, true, buffer, 1024, + &used_buffer_length); + encoder.encode_field_data(2, buffer, used_buffer_length); + } +#else + if (auto description = type.copy_value_description(value)) { + uint64_t length = CFStringGetLength(description); + CFRange range = CFRangeMake(0, length); + uint8_t buffer[1024]; + CFIndex used_buffer_length = 0; + CFStringGetBytes(description, range, kCFStringEncodingUTF8, 0x3f, true, buffer, 1024, + &used_buffer_length); + encoder.encode_field_data(2, buffer, used_buffer_length); + CFRelease(description); + } +#endif + } + } + for (auto input_edge : node.input_edges()) { + encoder.encode_field_begin(3); + encoder.encode_field_varint(1, input_edge.attribute); + encoder.encode_field_varint(2, input_edge.options & IAGInputOptionsUnprefetched ? 1 : 0); + encoder.encode_field_varint(3, input_edge.options & IAGInputOptionsSyncMainRef ? 1 : 0); + encoder.encode_field_varint(4, input_edge.options & IAGInputOptionsAlwaysEnabled ? 1 : 0); + encoder.encode_field_varint(5, input_edge.options & IAGInputOptionsChanged ? 1 : 0); + encoder.encode_field_varint(6, input_edge.options & IAGInputOptionsEnabled ? 1 : 0); + encoder.encode_field_end(); + } + for (auto output_edge : node.output_edges()) { + encoder.encode_field_begin(4); + encoder.encode_field_varint(1, output_edge.attribute); + encoder.encode_field_end(); + } + encoder.encode_field_varint(5, node.is_dirty()); + encoder.encode_field_varint(6, node.is_pending()); + encoder.encode_field_varint(7, node.is_updating()); + encoder.encode_field_varint(8, node.subgraph_flags()); + encoder.encode_field_varint(9, node.is_main_thread()); + encoder.encode_field_varint(10, node.requires_main_thread()); + encoder.encode_field_varint(11, node.is_main_ref()); + encoder.encode_field_varint(12, node.is_value_initialized()); + encoder.encode_field_varint(13, node.is_self_initialized()); + encoder.encode_field_varint(14, node.is_cached()); + encoder.encode_field_varint(15, node.is_self_modified()); +} + +void Graph::encode_indirect_node(Encoder &encoder, const IndirectNode &indirect_node) const { + encoder.encode_field_varint(1, indirect_node.source().identifier()); + encoder.encode_field_varint(2, indirect_node.source().identifier().subgraph()->subgraph_id()); + encoder.encode_field_varint(3, indirect_node.offset()); + auto size = indirect_node.size(); + if (size.has_value()) { + encoder.encode_field_varint(4, size.value()); + } + if (indirect_node.is_mutable()) { + encoder.encode_field_varint(5, indirect_node.to_mutable().dependency()); + for (auto output_edge : indirect_node.to_mutable().output_edges()) { + encoder.encode_field_begin(6); + encoder.encode_field_varint(1, output_edge.attribute); + encoder.encode_field_end(); + } + } +} + +void Graph::encode_tree(Encoder &encoder, data::ptr tree) const { + encoder.encode_field_varint(2, tree->value); + encoder.encode_field_varint(3, tree->flags); + for (auto child = tree->first_child; child != nullptr; child = child->next_sibling) { + encoder.encode_field_begin(4); + encode_tree(encoder, child); + encoder.encode_field_end(); + } + for (auto value = tree->first_value; value != nullptr; value = value->next) { + encoder.encode_field_begin(5); + encoder.encode_field_varint(2, value->value); + encoder.encode_field_varint(3, value->key_id); + encoder.encode_field_varint(4, value->flags); + encoder.encode_field_end(); + } + + Subgraph *subgraph = reinterpret_cast(tree.page_ptr()->zone); + if (auto tree_data = tree_data_element_for_subgraph(subgraph)) { + auto &nodes = tree_data->nodes(); + std::pair, data::ptr> *found = + std::find_if(nodes.begin(), nodes.end(), [&tree](auto node) { return node.first == tree; }); + + for (auto node = found; node != nodes.end(); ++node) { + if (node->first != tree) { + break; + } + encoder.encode_field_varint(6, node->second.offset()); + } + } +} + #pragma mark - Printing #if !TARGET_OS_MAC diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index 64ec46ea..a7f50f49 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -27,6 +27,7 @@ IAG_ASSUME_NONNULL_BEGIN namespace IAG { +class Encoder; class Trace; class Graph { @@ -210,7 +211,7 @@ class Graph { bool has_tree_data() const { return _tree_data_elements_by_subgraph != nullptr; }; - TreeDataElement *_Nullable tree_data_element_for_subgraph(Subgraph *subgraph) { + TreeDataElement *_Nullable tree_data_element_for_subgraph(Subgraph *subgraph) const { if (!_tree_data_elements_by_subgraph) { return nullptr; } @@ -421,6 +422,12 @@ class Graph { uint32_t intern_key(const char *key); const char *key_name(uint32_t key_id) const; + + // MARK: Encoding + + void encode_node(Encoder &encoder, const Node &node, bool encode_value) const; + void encode_indirect_node(Encoder &encoder, const IndirectNode &indirect_node) const; + void encode_tree(Encoder &encoder, data::ptr tree) const; // MARK: Printing diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.cpp b/Sources/ComputeCxx/Subgraph/Subgraph.cpp index 6591fdd5..423c8892 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.cpp +++ b/Sources/ComputeCxx/Subgraph/Subgraph.cpp @@ -15,6 +15,7 @@ #include "Graph/UpdateStack.h" #include "IAGSubgraph-Private.h" #include "NodeCache.h" +#include "Protobuf/Encoder.h" #include "Trace/Trace.h" namespace IAG { @@ -1032,6 +1033,56 @@ Graph::TreeElementID Subgraph::tree_subgraph_child(Graph::TreeElementID tree_ele return first_tree_child; } +#pragma mark - Encoding + +void Subgraph::encode(Encoder &encoder) const { + encoder.encode_field_varint(1, subgraph_id()); + encoder.encode_field_varint(2, context_id()); + for (auto parent : _parents) { + encoder.encode_field_varint(3, parent->subgraph_id()); + } + for (auto child : _children) { + encoder.encode_field_varint(4, child.subgraph()->subgraph_id()); + } + encoder.encode_field_varint(5, is_valid() ? 0 : 1); + + for (uint32_t iteration = 0; iteration < 2; ++iteration) { + for (auto page : pages()) { + bool found_nil_attribute = false; + auto view = iteration == 0 ? const_attribute_view(page) : attribute_view(page); + for (auto attribute : view) { + if (auto node = attribute.get_node()) { + encoder.encode_field_begin(6); + encoder.encode_field_varint(1, attribute); + encoder.encode_field_begin(2); + graph()->encode_node(encoder, *node.get(), false); + encoder.encode_field_end(); + encoder.encode_field_end(); + } else if (auto indirect_node = attribute.get_indirect_node()) { + encoder.encode_field_begin(6); + encoder.encode_field_varint(1, attribute); + encoder.encode_field_begin(3); + graph()->encode_indirect_node(encoder, *indirect_node.get()); + encoder.encode_field_end(); + encoder.encode_field_end(); + } else if (attribute.is_nil()) { + found_nil_attribute = true; + break; + } + } + if (found_nil_attribute) { + break; + } + } + } + + if (_tree_root) { + encoder.encode_field_begin(7); + _graph->encode_tree(encoder, _tree_root); + encoder.encode_field_end(); + } +} + #pragma mark - Printing void Subgraph::print(uint32_t indent_level) { diff --git a/Sources/ComputeCxx/Subgraph/Subgraph.h b/Sources/ComputeCxx/Subgraph/Subgraph.h index dfc50b3f..f896e17b 100644 --- a/Sources/ComputeCxx/Subgraph/Subgraph.h +++ b/Sources/ComputeCxx/Subgraph/Subgraph.h @@ -16,6 +16,8 @@ struct IAGSubgraphStorage; namespace IAG { +class Encoder; + class SubgraphObject { private: IAGSubgraphStorage _storage; @@ -226,6 +228,10 @@ class Subgraph : public data::zone { AttributeID tree_node_at_index(Graph::TreeElementID tree_element, uint64_t index); Graph::TreeElementID tree_subgraph_child(Graph::TreeElementID tree_element); + + // MARK: Encoding + + void encode(Encoder &encoder) const; // MARK: Printing From 0b706c2ab2ae98846ada5beca6d28cdac7eea0ba Mon Sep 17 00:00:00 2001 From: James Moschou Date: Tue, 4 Aug 2026 10:18:06 +0200 Subject: [PATCH 05/42] Change type of Trace.id to IAGUniqueID --- Sources/ComputeCxx/Graph/Graph.cpp | 2 +- Sources/ComputeCxx/Graph/Graph.h | 2 +- Sources/ComputeCxx/Graph/IAGGraph.cpp | 4 ++-- Sources/ComputeCxx/Trace/ExternalTrace.h | 2 +- Sources/ComputeCxx/Trace/Trace.h | 4 ++-- Sources/ComputeCxx/include/ComputeCxx/IAGGraphTracing.h | 5 +++-- 6 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index a7aa1a6c..98a1ca19 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -1998,7 +1998,7 @@ void Graph::add_trace(Trace *_Nullable trace) { _traces.push_back(trace); } -void Graph::remove_trace(uint64_t trace_id) { +void Graph::remove_trace(IAGUniqueID trace_id) { auto iter = std::remove_if(_traces.begin(), _traces.end(), [&trace_id](auto trace) -> bool { return trace->id() == trace_id; }); if (iter) { diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index a7f50f49..1d07e598 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -399,7 +399,7 @@ class Graph { void prepare_trace(Trace &trace); void add_trace(Trace *_Nullable trace); - void remove_trace(uint64_t trace_id); + void remove_trace(IAGUniqueID trace_id); static void all_start_tracing(IAGGraphTraceFlags trace_flags, std::span span); static void all_stop_tracing(); diff --git a/Sources/ComputeCxx/Graph/IAGGraph.cpp b/Sources/ComputeCxx/Graph/IAGGraph.cpp index 7897d918..f59025ee 100644 --- a/Sources/ComputeCxx/Graph/IAGGraph.cpp +++ b/Sources/ComputeCxx/Graph/IAGGraph.cpp @@ -965,14 +965,14 @@ CFStringRef IAGGraphCopyTracePath(IAGGraphRef graph) { return graph_context->graph().copy_trace_path(); } -uint64_t IAGGraphAddTrace(IAGGraphRef graph, const IAGTraceTypeRef trace, void *context) { +IAGUniqueID IAGGraphAddTrace(IAGGraphRef graph, const IAGTraceTypeRef trace, void *context) { auto graph_context = IAG::Graph::Context::from_cf(graph); auto external_trace = new ExternalTrace(trace, context); graph_context->graph().add_trace(external_trace); return external_trace->id(); } -void IAGGraphRemoveTrace(IAGGraphRef graph, uint64_t trace_id) { +void IAGGraphRemoveTrace(IAGGraphRef graph, IAGUniqueID trace_id) { auto graph_context = IAG::Graph::Context::from_cf(graph); graph_context->graph().remove_trace(trace_id); } diff --git a/Sources/ComputeCxx/Trace/ExternalTrace.h b/Sources/ComputeCxx/Trace/ExternalTrace.h index 186229c2..863e6fc3 100644 --- a/Sources/ComputeCxx/Trace/ExternalTrace.h +++ b/Sources/ComputeCxx/Trace/ExternalTrace.h @@ -20,7 +20,7 @@ class ExternalTrace : public IAG::Trace { public: ExternalTrace(IAGTraceTypeRef trace, void *context) : _trace(trace), _context(context) {}; - ExternalTrace(uint64_t id, const IAGTraceTypeRef trace, void *context) + ExternalTrace(IAGUniqueID id, const IAGTraceTypeRef trace, void *context) : IAG::Trace(id), _trace(trace), _context(context) {}; void graph_destroyed() override; diff --git a/Sources/ComputeCxx/Trace/Trace.h b/Sources/ComputeCxx/Trace/Trace.h index c4bde40f..4afb45a3 100644 --- a/Sources/ComputeCxx/Trace/Trace.h +++ b/Sources/ComputeCxx/Trace/Trace.h @@ -21,10 +21,10 @@ class Subgraph; class Trace { protected: - uint64_t _id; + IAGUniqueID _id; public: - uint64_t id() { return _id; } + IAGUniqueID id() { return _id; } Trace() : _id(IAGMakeUniqueID()) {}; Trace(uint64_t id) : _id(id) {}; diff --git a/Sources/ComputeCxx/include/ComputeCxx/IAGGraphTracing.h b/Sources/ComputeCxx/include/ComputeCxx/IAGGraphTracing.h index a0f31a0f..a9b50b98 100644 --- a/Sources/ComputeCxx/include/ComputeCxx/IAGGraphTracing.h +++ b/Sources/ComputeCxx/include/ComputeCxx/IAGGraphTracing.h @@ -2,6 +2,7 @@ #include #include +#include typedef IAG_OPTIONS(uint32_t, IAGGraphTraceFlags) { IAGGraphTraceFlagsEnabled = 1 << 0, @@ -45,12 +46,12 @@ CFStringRef IAGGraphCopyTracePath(IAGGraphRef graph) IAG_SWIFT_NAME(getter:IAGGr IAG_EXPORT IAG_REFINED_FOR_SWIFT -uint64_t IAGGraphAddTrace(IAGGraphRef graph, const IAGTraceTypeRef trace, void *_Nullable context) +IAGUniqueID IAGGraphAddTrace(IAGGraphRef graph, const IAGTraceTypeRef trace, void *_Nullable context) IAG_SWIFT_NAME(IAGGraphRef.addTrace(self:_:context:)); IAG_EXPORT IAG_REFINED_FOR_SWIFT -void IAGGraphRemoveTrace(IAGGraphRef graph, uint64_t trace_id) IAG_SWIFT_NAME(IAGGraphRef.removeTrace(self:traceID:)); +void IAGGraphRemoveTrace(IAGGraphRef graph, IAGUniqueID trace_id) IAG_SWIFT_NAME(IAGGraphRef.removeTrace(self:traceID:)); IAG_EXPORT IAG_REFINED_FOR_SWIFT From d8ab8cfb65489502e2609de0c0270553ff102b7b Mon Sep 17 00:00:00 2001 From: James Moschou Date: Tue, 4 Aug 2026 14:32:35 +0200 Subject: [PATCH 06/42] Update signatures of trace methods --- Sources/ComputeCxx/Graph/IAGGraph.cpp | 2 +- Sources/ComputeCxx/Graph/TraceRecorder.cpp | 7 ++++--- Sources/ComputeCxx/Graph/TraceRecorder.h | 6 +++--- Sources/ComputeCxx/Trace/ExternalTrace.cpp | 21 ++++++++++--------- Sources/ComputeCxx/Trace/ExternalTrace.h | 4 ++-- Sources/ComputeCxx/Trace/Trace.h | 4 ++-- .../include/ComputeCxx/IAGGraphTracing.h | 2 +- .../include/ComputeCxx/IAGTraceType.h | 15 ++++++------- 8 files changed, 32 insertions(+), 29 deletions(-) diff --git a/Sources/ComputeCxx/Graph/IAGGraph.cpp b/Sources/ComputeCxx/Graph/IAGGraph.cpp index f59025ee..de51cee7 100644 --- a/Sources/ComputeCxx/Graph/IAGGraph.cpp +++ b/Sources/ComputeCxx/Graph/IAGGraph.cpp @@ -1018,7 +1018,7 @@ void IAGGraphAddTraceEvent(IAGGraphRef graph, const char *event_name, const void }); } -void IAGGraphAddNamedTraceEvent(IAGGraphRef graph, IAGNamedTraceEventID event_id, uint32_t event_arg_count, const void *event_args, +void IAGGraphAddNamedTraceEvent(IAGGraphRef graph, IAGNamedTraceEventID event_id, uint32_t event_arg_count, const void **event_args, CFDataRef data, uint32_t arg6) { auto graph_context = IAG::Graph::Context::from_cf(graph); graph_context->graph().foreach_trace( diff --git a/Sources/ComputeCxx/Graph/TraceRecorder.cpp b/Sources/ComputeCxx/Graph/TraceRecorder.cpp index 7dd7b203..5580ed95 100644 --- a/Sources/ComputeCxx/Graph/TraceRecorder.cpp +++ b/Sources/ComputeCxx/Graph/TraceRecorder.cpp @@ -147,7 +147,7 @@ void Graph::TraceRecorder::remove_edge(data::ptr node, uint32_t input_inde // TODO: not implemented } -void Graph::TraceRecorder::set_edge_pending(data::ptr node, AttributeID input, bool pending) { +void Graph::TraceRecorder::set_edge_pending(data::ptr node, uint32_t input_index, bool pending) { // TODO: not implemented } @@ -196,8 +196,9 @@ void Graph::TraceRecorder::custom_event(const Graph::Context &context, const cha // TODO: not implemented } -void Graph::TraceRecorder::named_event(const Graph::Context &context, IAGNamedTraceEventID event_id, uint32_t event_arg_count, - const void *event_args, CFDataRef data, uint32_t arg6) { +void Graph::TraceRecorder::named_event(const Graph::Context &context, IAGNamedTraceEventID event_id, + uint32_t event_arg_count, const void **event_args, CFDataRef data, + uint32_t arg6) { // TODO: not implemented } diff --git a/Sources/ComputeCxx/Graph/TraceRecorder.h b/Sources/ComputeCxx/Graph/TraceRecorder.h index d350b2f7..f880b692 100644 --- a/Sources/ComputeCxx/Graph/TraceRecorder.h +++ b/Sources/ComputeCxx/Graph/TraceRecorder.h @@ -67,7 +67,7 @@ class Graph::TraceRecorder : public Trace { void add_edge(data::ptr node, AttributeID input, IAGInputOptions input_options) override; void remove_edge(data::ptr node, uint32_t input_index) override; - void set_edge_pending(data::ptr node, AttributeID input, bool pending) override; + void set_edge_pending(data::ptr node, uint32_t input_index, bool pending) override; void set_dirty(data::ptr node, bool dirty) override; void set_pending(data::ptr node, bool pending) override; @@ -87,8 +87,8 @@ class Graph::TraceRecorder : public Trace { void custom_event(const Graph::Context &context, const char *event_name, const void *value, const swift::metadata &type) override; - void named_event(const Graph::Context &context, IAGNamedTraceEventID event_id, uint32_t event_arg_count, const void *event_args, - CFDataRef data, uint32_t arg6) override; + void named_event(const Graph::Context &context, IAGNamedTraceEventID event_id, uint32_t event_arg_count, + const void **event_args, CFDataRef data, uint32_t arg6) override; bool named_event_enabled(IAGNamedTraceEventID event_id) override; // compare_failed not overridden diff --git a/Sources/ComputeCxx/Trace/ExternalTrace.cpp b/Sources/ComputeCxx/Trace/ExternalTrace.cpp index ea227aad..bea93144 100644 --- a/Sources/ComputeCxx/Trace/ExternalTrace.cpp +++ b/Sources/ComputeCxx/Trace/ExternalTrace.cpp @@ -47,7 +47,7 @@ void ExternalTrace::begin_update(const IAG::Graph::UpdateStack &update_stack, IA void ExternalTrace::end_update(const IAG::Graph::UpdateStack &update_stack, IAG::data::ptr node, IAGGraphUpdateStatus update_status) { if (auto callback = _trace->end_node_update) { - callback(_context, update_status == IAGGraphUpdateStatusChanged); + callback(_context, IAGAttribute(IAG::AttributeID(node)), update_status == IAGGraphUpdateStatusChanged); } } @@ -194,10 +194,10 @@ void ExternalTrace::remove_edge(IAG::data::ptr node, uint32_t input_i } } -void ExternalTrace::set_edge_pending(IAG::data::ptr node, IAG::AttributeID input, bool pending) { +void ExternalTrace::set_edge_pending(IAG::data::ptr node, uint32_t input_index, bool pending) { if (auto callback = _trace->node_set_edge_pending) { if (IAG::AttributeID(node).subgraph()) { - callback(_context, IAGAttribute(IAG::AttributeID(node)), IAGAttribute(input), pending); + callback(_context, IAGAttribute(IAG::AttributeID(node)), input_index, pending); } } } @@ -216,7 +216,7 @@ void ExternalTrace::set_pending(IAG::data::ptr node, bool pending) { void ExternalTrace::set_value(IAG::data::ptr node, const void *value) { if (auto callback = _trace->node_set_value) { - callback(_context, IAGAttribute(IAG::AttributeID(node))); + callback(_context, IAGAttribute(IAG::AttributeID(node)), value); } } @@ -234,7 +234,8 @@ void ExternalTrace::added(IAG::data::ptr indirect_node) { void ExternalTrace::set_source(IAG::data::ptr indirect_node, IAG::AttributeID source) { if (auto callback = _trace->indirect_node_set_source) { - callback(_context, IAGAttribute(IAG::AttributeID(indirect_node)), IAGAttribute(source)); // TODO: check sets kind + callback(_context, IAGAttribute(IAG::AttributeID(indirect_node)), + IAGAttribute(source)); // TODO: check sets kind } } @@ -263,8 +264,8 @@ void ExternalTrace::custom_event(const IAG::Graph::Context &context, const char } } -void ExternalTrace::named_event(const IAG::Graph::Context &context, IAGNamedTraceEventID event_id, uint32_t event_arg_count, - const void *event_args, CFDataRef data, uint32_t arg6) { +void ExternalTrace::named_event(const IAG::Graph::Context &context, IAGNamedTraceEventID event_id, + uint32_t event_arg_count, const void **event_args, CFDataRef data, uint32_t arg6) { if (_trace->version < IAGTraceTypeVersionNamed) { return; } @@ -279,7 +280,7 @@ bool ExternalTrace::named_event_enabled(IAGNamedTraceEventID event_id) { return false; } if (auto callback = _trace->named_event_enabled) { - return callback(_context); + return callback(_context, event_id); } return _trace->named_event != nullptr; } @@ -302,8 +303,8 @@ void ExternalTrace::passed_deadline() { } } -void ExternalTrace::compare_failed(IAG::data::ptr node, const void *lhs, const void *rhs, size_t range_offset, - size_t range_size, const IAG::swift::metadata *_Nullable type) { +void ExternalTrace::compare_failed(IAG::data::ptr node, const void *lhs, const void *rhs, + size_t range_offset, size_t range_size, const IAG::swift::metadata *_Nullable type) { if (_trace->version < IAGTraceTypeVersionCompareFailed) { return; } diff --git a/Sources/ComputeCxx/Trace/ExternalTrace.h b/Sources/ComputeCxx/Trace/ExternalTrace.h index 863e6fc3..ec6abb43 100644 --- a/Sources/ComputeCxx/Trace/ExternalTrace.h +++ b/Sources/ComputeCxx/Trace/ExternalTrace.h @@ -65,7 +65,7 @@ class ExternalTrace : public IAG::Trace { void add_edge(IAG::data::ptr node, IAG::AttributeID input, IAGInputOptions input_options) override; void remove_edge(IAG::data::ptr node, uint32_t input_index) override; - void set_edge_pending(IAG::data::ptr node, IAG::AttributeID input, bool pending) override; + void set_edge_pending(IAG::data::ptr node, uint32_t input_index, bool pending) override; void set_dirty(IAG::data::ptr node, bool dirty) override; void set_pending(IAG::data::ptr node, bool pending) override; @@ -82,7 +82,7 @@ class ExternalTrace : public IAG::Trace { void custom_event(const IAG::Graph::Context &context, const char *event_name, const void *value, const IAG::swift::metadata &type) override; void named_event(const IAG::Graph::Context &context, IAGNamedTraceEventID event_id, uint32_t event_arg_count, - const void *event_args, CFDataRef data, uint32_t arg6) override; + const void **event_args, CFDataRef data, uint32_t arg6) override; bool named_event_enabled(IAGNamedTraceEventID event_id) override; void set_deadline(uint64_t deadline) override; diff --git a/Sources/ComputeCxx/Trace/Trace.h b/Sources/ComputeCxx/Trace/Trace.h index 4afb45a3..a15f278d 100644 --- a/Sources/ComputeCxx/Trace/Trace.h +++ b/Sources/ComputeCxx/Trace/Trace.h @@ -76,7 +76,7 @@ class Trace { virtual void add_edge(data::ptr node, AttributeID input, IAGInputOptions input_options) {}; virtual void remove_edge(data::ptr node, uint32_t input_index) {}; - virtual void set_edge_pending(data::ptr node, AttributeID input, bool pending) {}; + virtual void set_edge_pending(data::ptr node, uint32_t input_index, bool pending) {}; virtual void set_dirty(data::ptr node, bool dirty) {}; virtual void set_pending(data::ptr node, bool pending) {}; @@ -97,7 +97,7 @@ class Trace { virtual void custom_event(const Graph::Context &context, const char *event_name, const void *value, const swift::metadata &type) {}; virtual void named_event(const Graph::Context &context, IAGNamedTraceEventID event_id, uint32_t event_arg_count, - const void *event_args, CFDataRef data, uint32_t arg6) {}; + const void **event_args, CFDataRef data, uint32_t arg6) {}; virtual bool named_event_enabled(IAGNamedTraceEventID event_id) { return false; }; virtual void compare_failed(data::ptr node, const void *lhs, const void *rhs, size_t range_offset, diff --git a/Sources/ComputeCxx/include/ComputeCxx/IAGGraphTracing.h b/Sources/ComputeCxx/include/ComputeCxx/IAGGraphTracing.h index a9b50b98..f9c064d6 100644 --- a/Sources/ComputeCxx/include/ComputeCxx/IAGGraphTracing.h +++ b/Sources/ComputeCxx/include/ComputeCxx/IAGGraphTracing.h @@ -82,7 +82,7 @@ void IAGGraphAddTraceEvent(IAGGraphRef graph, const char *event_name, const void IAG_EXPORT IAG_REFINED_FOR_SWIFT -void IAGGraphAddNamedTraceEvent(IAGGraphRef graph, IAGNamedTraceEventID event_id, uint32_t event_arg_count, const void *event_args, +void IAGGraphAddNamedTraceEvent(IAGGraphRef graph, IAGNamedTraceEventID event_id, uint32_t event_arg_count, const void **event_args, CFDataRef data, uint32_t arg6) IAG_SWIFT_NAME(IAGGraphRef.addNamedTraceEvent(self:eventID:eventArgCount:eventArgs:data:arg6:)); diff --git a/Sources/ComputeCxx/include/ComputeCxx/IAGTraceType.h b/Sources/ComputeCxx/include/ComputeCxx/IAGTraceType.h index c36b7851..e3d59f11 100644 --- a/Sources/ComputeCxx/include/ComputeCxx/IAGTraceType.h +++ b/Sources/ComputeCxx/include/ComputeCxx/IAGTraceType.h @@ -3,6 +3,7 @@ #include #include #include +#include IAG_ASSUME_NONNULL_BEGIN @@ -25,7 +26,7 @@ typedef struct IAG_SWIFT_NAME(TraceType) IAGTraceType { void (*_Nullable begin_subgraph_update)(void *_Nullable context, IAGSubgraphRef subgraph, uint32_t options); void (*_Nullable end_subgraph_update)(void *_Nullable context, IAGSubgraphRef subgraph); void (*_Nullable begin_node_update)(void *_Nullable context, IAGAttribute attribute); - void (*_Nullable end_node_update)(void *_Nullable context, bool changed); + void (*_Nullable end_node_update)(void *_Nullable context, IAGAttribute attribute, bool changed); void (*_Nullable begin_value_update)(void *_Nullable context, IAGAttribute attribute); void (*_Nullable end_value_update)(void *_Nullable context, IAGAttribute attribute, bool changed); void (*_Nullable begin_graph_update)(void *_Nullable context, IAGGraphRef graph); @@ -51,12 +52,12 @@ typedef struct IAG_SWIFT_NAME(TraceType) IAGTraceType { void (*_Nullable node_added)(void *_Nullable context, IAGAttribute attribute); void (*_Nullable node_add_edge)(void *_Nullable context, IAGAttribute attribute, IAGAttribute input, IAGInputOptions input_options); - void (*_Nullable node_remove_edge)(void *_Nullable context, IAGAttribute attribute, uint32_t index); - void (*_Nullable node_set_edge_pending)(void *_Nullable context, IAGAttribute attribute, IAGAttribute input, bool pending); + void (*_Nullable node_remove_edge)(void *_Nullable context, IAGAttribute attribute, uint32_t input_index); + void (*_Nullable node_set_edge_pending)(void *_Nullable context, IAGAttribute attribute, uint32_t input_index, bool pending); void (*_Nullable node_set_dirty)(void *_Nullable context, IAGAttribute attribute, bool dirty); void (*_Nullable node_set_pending)(void *_Nullable context, IAGAttribute attribute, bool pending); - void (*_Nullable node_set_value)(void *_Nullable context, IAGAttribute attribute); + void (*_Nullable node_set_value)(void *_Nullable context, IAGAttribute attribute, const void *value); void (*_Nullable node_mark_value)(void *_Nullable context, IAGAttribute attribute); void (*_Nullable indirect_node_added)(void *_Nullable context, IAGAttribute attribute); @@ -67,9 +68,9 @@ typedef struct IAG_SWIFT_NAME(TraceType) IAGTraceType { void (*_Nullable custom_event)(void *_Nullable context, IAGGraphRef graph, const char *event_name, const void *value, IAGTypeID type); - void (*_Nullable named_event)(void *_Nullable context, IAGGraphRef graph, uint32_t eventID, uint32_t eventArgCount, - const void *eventArgs, CFDataRef data, uint32_t arg6); - bool (*_Nullable named_event_enabled)(void *_Nullable context); + void (*_Nullable named_event)(void *_Nullable context, IAGGraphRef graph, IAGNamedTraceEventID event_id, uint32_t event_arg_count, + const void **event_args, CFDataRef data, uint32_t arg6); + bool (*_Nullable named_event_enabled)(void *_Nullable context, IAGNamedTraceEventID event_id); void (*_Nullable set_deadline)(void *_Nullable context); void (*_Nullable passed_deadline)(void *_Nullable context); From 0fc67cb60ba5e2750a83991a72c27b9ea740002f Mon Sep 17 00:00:00 2001 From: James Moschou Date: Tue, 4 Aug 2026 14:34:38 +0200 Subject: [PATCH 07/42] Finish implementation of TraceRecorder --- Sources/ComputeCxx/Graph/Graph.cpp | 8 +- Sources/ComputeCxx/Graph/TraceRecorder.cpp | 896 +++++++++++++++++++-- Sources/ComputeCxx/Graph/TraceRecorder.h | 123 ++- 3 files changed, 964 insertions(+), 63 deletions(-) diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 98a1ca19..519d089a 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -1883,18 +1883,14 @@ void Graph::start_tracing(IAGGraphTraceFlags trace_flags, std::span #include +#include +#include +#include +#if TARGET_OS_MAC +#include +#endif #include +#include #include "Attribute/AttributeData/Node/IndirectNode.h" #include "Attribute/AttributeType/AttributeType.h" #include "ComputeCxx/IAGGraph.h" #include "ComputeCxx/IAGGraphTracing.h" -#include "ComputeCxx/IAGUniqueID.h" #include "Context.h" +#include "Graph/KeyTable.h" #include "Log/Log.h" #include "Subgraph/Subgraph.h" #include "Time/Time.h" +#include "UpdateStack.h" namespace IAG { -Graph::TraceRecorder::TraceRecorder(Graph *graph, IAGGraphTraceFlags trace_flags, std::span subsystems) { - // TODO: not implemented -} +namespace { + +uint64_t uuid_hash(const uuid_t key) { + uint64_t hash = 0; + std::memcpy(&hash, key, sizeof(hash)); + return hash; +} + +bool uuid_equal(const uuid_t a, const uuid_t b) { return uuid_compare(a, b) == 0; } + +} // namespace + +Graph::TraceRecorder::TraceRecorder(Graph &graph, IAGGraphTraceFlags trace_flags, std::span subsystems) + : _graph(graph), _encoder(this, 0x10000), _trace_flags(trace_flags), + _image_offset_cache( + uuid_hash, uuid_equal, [](const uuid_t key) { free((unsigned char *)key); }, nullptr, nullptr) { + for (auto subsystem : subsystems) { + _named_event_subsystems.push_back(std::unique_ptr(strdup(subsystem))); + } + + #if TARGET_OS_MAC + void *array[1] = {(void *)&IAGGraphCreate}; + image_offset image_offsets[1]; + backtrace_image_offsets(array, image_offsets, 1); + + uuid_copy(_stack_frame_uuid, image_offsets[0].uuid); + #endif +} + +Graph::TraceRecorder::~TraceRecorder() { _encoder.flush(); } + +#pragma mark - Encoder::Delegate + +int Graph::TraceRecorder::flush_encoder(Encoder &encoder) { + int fd = -1; + if (_trace_path_created) { + fd = open(_trace_path.get(), O_WRONLY | O_APPEND, 0666); + } else { + _trace_path_created = true; + + const char *trace_file = getenv("IAG_TRACE_FILE"); + if (!trace_file) { + trace_file = "trace"; + } + + const char *dir = getenv("TMPDIR"); + if (!dir || !*dir) { + dir = "/tmp"; + } + + const char *separator = dir[strlen(dir) - 1] == '/' ? "" : "/"; + + char *attempted_trace_path = nullptr; + for (int attempt = 1; attempt <= 999; ++attempt) { + asprintf(&attempted_trace_path, "%s%s%s-%04d.iag-trace", dir, separator, trace_file, attempt); + fd = open(attempted_trace_path, O_WRONLY | O_CREAT | O_EXCL, 0666); + if (fd != -1) { + break; + } + if (attempted_trace_path) { + free(attempted_trace_path); + attempted_trace_path = nullptr; + } + if (errno != EEXIST) { + break; + } + } + + _trace_path = std::unique_ptr(attempted_trace_path); + + if (_trace_path) { + platform_log_info(misc_log(), "created trace file %s", _trace_path.get()); + fprintf(stdout, "created trace file \"%s\" (pid %d)\n", _trace_path.get(), getpid()); + } else { + fprintf(stdout, "failed to create trace file: %s%s%s-XXXX.ag-trace\n", dir, separator, trace_file); + } + } + if (fd == -1) { + return -1; + } + + const char *buffer = encoder.buffer().data(); + size_t remaining = encoder.buffer().size(); + while (remaining > 0) { + ssize_t written = write(fd, buffer, remaining); + if (written < 0) { + if (errno == EINTR) { + // try again on interrupted error + continue; + } + unlink(_trace_path.get()); + break; + } + buffer += written; + remaining -= written; + } + return close(fd); +} + +#pragma mark - Top level fields + +#define MESSAGE_FIELD_EVENT 1 +#define MESSAGE_FIELD_SUBGRAPH 2 +#define MESSAGE_FIELD_TYPES 3 +#define MESSAGE_FIELD_KEYS 4 +#define MESSAGE_FIELD_STACK 5 +#define MESSAGE_FIELD_NAMED_EVENT 6 + +void Graph::TraceRecorder::encode_event_begin() { _encoder.encode_field_begin(MESSAGE_FIELD_EVENT); }; + +void Graph::TraceRecorder::encode_event_end() { _encoder.encode_field_end(); }; + +void Graph::TraceRecorder::encode_subgraph(const IAG::Subgraph &subgraph) { + _encoder.encode_field_begin(MESSAGE_FIELD_SUBGRAPH); + subgraph.encode(_encoder); + _encoder.encode_field_end(); +} + +void Graph::TraceRecorder::encode_types() { + while (_num_encoded_types < _graph._types.size()) { + auto attribute_type = _graph.attribute_type(_num_encoded_types); + + _encoder.encode_field_begin(MESSAGE_FIELD_TYPES); + _encoder.encode_field_varint(1, _num_encoded_types); + auto body_type_name = attribute_type.body_metadata().name(false); + _encoder.encode_field_data(2, body_type_name, strlen(body_type_name)); + auto value_type_name = attribute_type.value_metadata().name(false); + _encoder.encode_field_data(3, value_type_name, strlen(value_type_name)); + _encoder.encode_field_varint(4, attribute_type.body_metadata().vw_size()); + _encoder.encode_field_varint(5, attribute_type.value_metadata().vw_size()); + _encoder.encode_field_varint(6, attribute_type.flags()); + _encoder.encode_field_end(); + + _num_encoded_types += 1; + } +} + +void Graph::TraceRecorder::encode_keys() { + if (_graph._keys == nullptr) { + return; + } + while (_num_encoded_keys < _graph._keys->size()) { + if (auto key_name = _graph.key_name(_num_encoded_keys)) { + _encoder.encode_field_begin(MESSAGE_FIELD_KEYS); + _encoder.encode_field_varint(1, _num_encoded_keys); + _encoder.encode_field_data(2, key_name, strlen(key_name)); + _encoder.encode_field_end(); + } + _num_encoded_keys += 1; + } +} + +void Graph::TraceRecorder::encode_stack() { + auto first_update = current_update(); + if (first_update == 0) { + return; + } + + _encoder.encode_field_begin(MESSAGE_FIELD_STACK); + + for (auto update = first_update; update != nullptr; update = update.get()->next()) { + for (auto &frame : std::ranges::reverse_view(update.get()->frames())) { + _encoder.encode_field_begin(1); + _encoder.encode_field_varint(1, frame.attribute.offset()); + _encoder.encode_field_varint(2, frame.pending ? 1 : 0); + _encoder.encode_field_varint(3, frame.cyclic ? 1 : 0); + _encoder.encode_field_end(); + } + } + + _encoder.encode_field_end(); +} + +void Graph::TraceRecorder::encode_named_event(uint64_t event_id, const char *_Nonnull event_name, + const char *_Nonnull event_subsystem) { + _encoder.encode_field_begin(MESSAGE_FIELD_NAMED_EVENT); + _encoder.encode_field_varint(1, event_id); + auto event_name_length = strlen(event_name); + if (event_name_length > 0) { + _encoder.encode_field_data(2, event_name, event_name_length); + } + auto event_subsystem_length = strlen(event_subsystem); + if (event_subsystem_length > 0) { + _encoder.encode_field_data(3, event_subsystem, event_subsystem_length); + } + _encoder.encode_field_end(); +} + +void Graph::TraceRecorder::encode_snapshot() { + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + + encode_types(); + encode_keys(); + + encode_event_begin(); + field_event_type(_encoder, EventType::BeginSnapshot); + field_timestamp(_encoder); + encode_event_end(); + + for (auto subgraph : _graph.subgraphs()) { + if (subgraph->is_valid()) { + encode_subgraph(*subgraph); + } + } + + encode_stack(); + + encode_event_begin(); + field_event_type(_encoder, EventType::EndSnapshot); + field_timestamp(_encoder); + encode_event_end(); +} + +#pragma mark - Event fields + +#define EVENT_FIELD_EVENT_TYPE 1 +#define EVENT_FIELD_TIMESTAMP 2 +#define EVENT_FIELD_PAYLOAD_1 3 +#define EVENT_FIELD_PAYLOAD_2 4 +#define EVENT_FIELD_PAYLOAD_3 5 +#define EVENT_FIELD_PAYLOAD_4 6 +#define EVENT_FIELD_PAYLOAD_5 7 +#define EVENT_FIELD_BACKTRACE 8 +#define EVENT_FIELD_DATA 9 +#define EVENT_FIELD_NAMED_EVENT_ID 10 + +void Graph::TraceRecorder::field_event_type(Encoder &encoder, EventType event_type) { + encoder.encode_field_varint(EVENT_FIELD_EVENT_TYPE, static_cast(event_type)); +} + +void Graph::TraceRecorder::field_timestamp(Encoder &encoder) { + auto timestamp = current_time(); + encoder.encode_field_varint(EVENT_FIELD_TIMESTAMP, static_cast(timestamp)); +} + +void Graph::TraceRecorder::field_payload_1(Encoder &encoder, uint64_t payload) { + encoder.encode_field_varint(EVENT_FIELD_PAYLOAD_1, payload); +} + +void Graph::TraceRecorder::field_payload_2(Encoder &encoder, uint64_t payload) { + encoder.encode_field_varint(EVENT_FIELD_PAYLOAD_2, payload); +} + +void Graph::TraceRecorder::field_payload_3(Encoder &encoder, uint64_t payload) { + encoder.encode_field_varint(EVENT_FIELD_PAYLOAD_3, payload); +} + +void Graph::TraceRecorder::field_backtrace(Encoder &encoder) { + if (!(_trace_flags & IAGGraphTraceFlagsBacktrace)) { + return; + } + + static int trace_stack_frames = []() -> int { + char *result = getenv("IAG_TRACE_STACK_FRAMES"); + if (result) { + return atoi(result); + } + return 8; + }(); + if (trace_stack_frames == 0) { + return; + } + + #if TARGET_OS_MAC + void *stack_frames_buffer[8]; + int stack_frames_size = backtrace(stack_frames_buffer, std::size(stack_frames_buffer)); + + image_offset image_offsets[8]; + backtrace_image_offsets(stack_frames_buffer, image_offsets, stack_frames_size); + + int stack_frames = std::min(trace_stack_frames, stack_frames_size); + for (int frame_index = 0; frame_index < stack_frames; ++frame_index) { + image_offset image_offset = image_offsets[frame_index]; + if (image_offset.offset && !uuid_is_null(image_offset.uuid) && + uuid_compare(image_offset.uuid, _stack_frame_uuid)) { + + encoder.encode_field_begin(EVENT_FIELD_BACKTRACE); + + uuid_t cached_uuid = {}; + uint64_t image_offset_id = _image_offset_cache.lookup(image_offset.uuid, &cached_uuid); + if (uuid_is_null(cached_uuid)) { + uuid_t *key = (uuid_t *)malloc(sizeof(uuid_t)); + uuid_copy(*key, image_offset.uuid); + + image_offset_id = _image_offset_cache.count(); + _image_offset_cache.insert(*key, image_offset_id); + + encoder.encode_field_begin(3); + + // don't encode trailing NULL character + uuid_string_t uuid_string = {}; + uuid_unparse(*key, uuid_string); + encoder.encode_field_data(1, uuid_string, sizeof(uuid_string_t) - 1); + + Dl_info dl_info; + if (dladdr(stack_frames_buffer[frame_index], &dl_info)) { + if (dl_info.dli_fname) { + encoder.encode_field_data(2, dl_info.dli_fname, strlen(dl_info.dli_fname)); + } + if (dl_info.dli_fbase) { + encoder.encode_field_varint(3, reinterpret_cast(dl_info.dli_fbase)); -Graph::TraceRecorder::~TraceRecorder() { - // TODO: not implemented + // TODO: what is the correct ptrauth key? + mach_vm_address_t address = + (mach_vm_address_t)ptrauth_strip(dl_info.dli_fbase, ptrauth_key_process_independent_code); + mach_vm_size_t size = 0; + vm_region_basic_info_data_64_t info; + mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64; + mach_port_t object_name = MACH_PORT_NULL; + kern_return_t status = + mach_vm_region(mach_task_self(), &address, &size, VM_REGION_BASIC_INFO_64, + (vm_region_info_t)&info, &info_count, &object_name); + if (object_name) { + mach_port_deallocate(mach_task_self(), object_name); + } + if (status == KERN_SUCCESS) { + encoder.encode_field_varint(4, size); + } + } + } + + encoder.encode_field_end(); + } + + encoder.encode_field_varint(1, image_offset_id); + encoder.encode_field_varint(2, image_offset.offset); + encoder.encode_field_end(); + } + } + #endif +} + +void Graph::TraceRecorder::field_data(Encoder &encoder, const void *data, size_t length) { + encoder.encode_field_data(EVENT_FIELD_DATA, data, length); +} + +void Graph::TraceRecorder::field_named_event_id(Encoder &encoder, uint32_t event_id) { + encoder.encode_field_varint(EVENT_FIELD_NAMED_EVENT_ID, event_id); } #pragma mark - Trace methods @@ -30,181 +373,634 @@ void Graph::TraceRecorder::graph_destroyed() { delete this; }; void Graph::TraceRecorder::trace_removed() { delete this; }; void Graph::TraceRecorder::begin_trace(const Graph &graph) { - // TODO: not implemented + encode_event_begin(); + field_event_type(_encoder, EventType::BeginTrace); + field_timestamp(_encoder); + encode_event_end(); } void Graph::TraceRecorder::end_trace(const Graph &graph) { - // TODO: not implemented + encode_event_begin(); + field_event_type(_encoder, EventType::EndTrace); + field_timestamp(_encoder); + encode_event_end(); + + encode_snapshot(); } void Graph::TraceRecorder::sync_trace() { - // TODO: not implemented + encode_snapshot(); + _encoder.flush(); } void Graph::TraceRecorder::log_message_v(const char *format, va_list args) { - // TODO: not implemented + char *message = nullptr; + int result = vasprintf(&message, format, args); + if (result < 0) { + precondition_failure("vasprintf failure (%u)", errno); + } + + encode_event_begin(); + field_event_type(_encoder, EventType::LogMessage); + field_timestamp(_encoder); + field_backtrace(_encoder); + field_data(_encoder, message, strlen(message)); + encode_event_end(); + + encode_stack(); + + if (message) { + free(message); + } } void Graph::TraceRecorder::begin_update(const Subgraph &subgraph, uint32_t options) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::BeginSubgraphUpdate); + field_timestamp(_encoder); + field_payload_1(_encoder, subgraph.subgraph_id()); + field_payload_2(_encoder, options); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::end_update(const Subgraph &subgraph) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::EndSubgraphUpdate); + field_timestamp(_encoder); + field_payload_1(_encoder, subgraph.subgraph_id()); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::begin_update(const Graph::UpdateStack &update_stack, data::ptr node, uint32_t options) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::BeginNodeUpdate); + field_timestamp(_encoder); + field_payload_1(_encoder, node.offset()); + field_payload_2(_encoder, options); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::end_update(const Graph::UpdateStack &update_stack, data::ptr node, IAGGraphUpdateStatus update_status) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::EndNodeUpdate); + field_timestamp(_encoder); + field_payload_1(_encoder, node.offset()); + field_payload_2(_encoder, update_status); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::begin_update(data::ptr node) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::BeginValueUpdate); + field_timestamp(_encoder); + field_payload_1(_encoder, node.offset()); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::end_update(data::ptr node, bool changed) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::EndValueUpdate); + field_timestamp(_encoder); + field_payload_1(_encoder, node.offset()); + field_payload_2(_encoder, changed ? 1 : 0); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::begin_update(const Graph::Context &context) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::BeginGraphUpdate); + field_timestamp(_encoder); + field_payload_1(_encoder, context.id()); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::end_update(const Graph::Context &context) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::EndGraphUpdate); + field_timestamp(_encoder); + field_payload_1(_encoder, context.id()); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::begin_invalidation(const Graph::Context &context, AttributeID attribute) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + if (!(_trace_flags & IAGGraphTraceFlagsFull)) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::BeginGraphInvalidation); + field_timestamp(_encoder); + field_payload_1(_encoder, attribute); + field_payload_2(_encoder, context.id()); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::end_invalidation(const Graph::Context &context, AttributeID attribute) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + if (!(_trace_flags & IAGGraphTraceFlagsFull)) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::EndGraphInvalidation); + field_timestamp(_encoder); + field_payload_1(_encoder, attribute); + field_payload_2(_encoder, context.id()); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::begin_modify(data::ptr node) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + if (!(_trace_flags & IAGGraphTraceFlagsFull)) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::BeginModifyNode); + field_timestamp(_encoder); + field_payload_1(_encoder, node.offset()); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::end_modify(data::ptr node) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + if (!(_trace_flags & IAGGraphTraceFlagsFull)) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::EndModifyNode); + field_timestamp(_encoder); + if (node.offset()) { + field_payload_1(_encoder, 1); + } + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::begin_event(data::ptr node, uint32_t event_id) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::BeginEvent); + field_timestamp(_encoder); + field_payload_1(_encoder, node.offset()); + field_payload_2(_encoder, event_id); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::end_event(data::ptr node, uint32_t event_id) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::EndEvent); + field_timestamp(_encoder); + field_payload_1(_encoder, node.offset()); + field_payload_2(_encoder, event_id); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::created(const Graph::Context &context) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::GraphCreated); + field_payload_1(_encoder, context.id()); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::destroy(const Graph::Context &context) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::GraphDestroy); + field_payload_1(_encoder, context.id()); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::needs_update(const Graph::Context &context) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + if (!(_trace_flags & IAGGraphTraceFlagsFull)) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::GraphNeedsUpdate); + field_payload_1(_encoder, context.id()); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::created(const Subgraph &subgraph) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::SubgraphCreated); + field_payload_1(_encoder, subgraph.subgraph_id()); + field_payload_2(_encoder, subgraph.context_id()); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::invalidate(const Subgraph &subgraph) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::SubgraphInvalidate); + field_payload_1(_encoder, subgraph.subgraph_id()); + field_backtrace(_encoder); + encode_event_end(); + + encode_subgraph(subgraph); } void Graph::TraceRecorder::destroy(const Subgraph &subgraph) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::SubgraphDestroy); + field_payload_1(_encoder, subgraph.subgraph_id()); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::add_child(const Subgraph &subgraph, const Subgraph &child) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::SubgraphAddChild); + field_payload_1(_encoder, subgraph.subgraph_id()); + field_payload_2(_encoder, child.subgraph_id()); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::remove_child(const Subgraph &subgraph, const Subgraph &child) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::SubgraphRemoveChild); + field_payload_1(_encoder, subgraph.subgraph_id()); + field_payload_2(_encoder, child.subgraph_id()); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::added(data::ptr node) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::NodeAdded); + field_payload_1(_encoder, node.offset()); + field_payload_2(_encoder, AttributeID(node).subgraph()->subgraph_id()); + field_payload_3(_encoder, node->type_id()); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::add_edge(data::ptr node, AttributeID input, IAGInputOptions input_options) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::NodeAddEdge); + field_payload_1(_encoder, node.offset()); + field_payload_2(_encoder, input); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::remove_edge(data::ptr node, uint32_t input_index) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::NodeRemoveEdge); + field_payload_1(_encoder, node.offset()); + field_payload_2(_encoder, node->input_edges()[input_index].attribute); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::set_edge_pending(data::ptr node, uint32_t input_index, bool pending) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + if (!(_trace_flags & IAGGraphTraceFlagsFull)) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::NodeSetEdgePending); + field_payload_1(_encoder, node.offset()); + field_payload_2(_encoder, node->input_edges()[input_index].attribute); + field_payload_3(_encoder, pending ? 1 : 0); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::set_dirty(data::ptr node, bool dirty) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + if (!(_trace_flags & IAGGraphTraceFlagsFull)) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::NodeSetDirty); + field_payload_1(_encoder, node.offset()); + field_payload_2(_encoder, dirty ? 1 : 0); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::set_pending(data::ptr node, bool pending) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + if (!(_trace_flags & IAGGraphTraceFlagsFull)) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::NodeSetPending); + field_payload_1(_encoder, node.offset()); + field_payload_2(_encoder, pending ? 1 : 0); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::set_value(data::ptr node, const void *value) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + if (!(_trace_flags & IAGGraphTraceFlagsFull)) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::NodeSetValue); + field_payload_1(_encoder, node.offset()); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::mark_value(data::ptr node) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + if (!(_trace_flags & IAGGraphTraceFlagsFull)) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::NodeMarkValue); + field_payload_1(_encoder, node.offset()); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::added(data::ptr indirect_node) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::IndirectNodeAdded); + field_payload_1(_encoder, indirect_node.offset()); + field_payload_2(_encoder, AttributeID(indirect_node).subgraph()->subgraph_id()); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::set_source(data::ptr indirect_node, AttributeID source) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::IndirectNodeSetSource); + field_payload_1(_encoder, indirect_node.offset()); + // FIXME: This shadows param or is there no param> + field_payload_2(_encoder, indirect_node->source().identifier()); // TODO: identifier()? + field_payload_3(_encoder, AttributeID(source).subgraph()->subgraph_id()); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::set_dependency(data::ptr indirect_node, AttributeID dependency) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::IndirectNodeSetDependency); + field_payload_1(_encoder, indirect_node.offset()); + // FIXME: This shadows param or is there no param> + field_payload_2(_encoder, indirect_node->to_mutable().dependency()); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::set_deadline(uint64_t deadline) { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::SetDeadline); + field_timestamp(_encoder); + field_payload_1(_encoder, deadline & 0xffffffff); + field_payload_2(_encoder, deadline >> 32); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::passed_deadline() { - // TODO: not implemented + if (_trace_flags & IAGGraphTraceFlagsCustom) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::PassedDeadline); + field_timestamp(_encoder); + field_backtrace(_encoder); + encode_event_end(); + + encode_stack(); } void Graph::TraceRecorder::mark_profile(const Graph &graph, uint32_t options) { - // TODO: not implemented + encode_event_begin(); + field_event_type(_encoder, EventType::ProfileMark); + field_timestamp(_encoder); + field_payload_1(_encoder, options); + field_backtrace(_encoder); + encode_event_end(); } void Graph::TraceRecorder::custom_event(const Graph::Context &context, const char *event_name, const void *value, const swift::metadata &type) { - // TODO: not implemented + encode_event_begin(); + field_event_type(_encoder, EventType::CustomEvent); + field_timestamp(_encoder); + field_backtrace(_encoder); + field_data(_encoder, event_name, strlen(event_name)); + encode_event_end(); } void Graph::TraceRecorder::named_event(const Graph::Context &context, IAGNamedTraceEventID event_id, uint32_t event_arg_count, const void **event_args, CFDataRef data, uint32_t arg6) { - // TODO: not implemented + if (!named_event_enabled(event_id)) { + return; + } + + encode_event_begin(); + field_event_type(_encoder, EventType::NamedEvent); + field_named_event_id(_encoder, event_id); + field_timestamp(_encoder); + + if (arg6 & 0x80000000) { + field_backtrace(_encoder); + arg6 &= 0x7fffffff; + } + field_payload_1(_encoder, arg6); + for (uint32_t i = 0; i < std::min(event_arg_count, uint32_t(4)); ++i) { + const void *event_arg = event_args[i]; + _encoder.encode_field_varint(EVENT_FIELD_PAYLOAD_2 + i, reinterpret_cast(event_arg)); + } + if (data != nullptr) { + void *ptr = (void *)CFDataGetBytePtr(data); + uint64_t length = CFDataGetLength(data); + field_data(_encoder, ptr, length); + } + encode_event_end(); } bool Graph::TraceRecorder::named_event_enabled(IAGNamedTraceEventID event_id) { - // TODO: not implemented - return false; + uint32_t index = 0; + if (!_named_event_infos.empty()) { + auto pos = std::lower_bound( + _named_event_infos.begin(), _named_event_infos.end(), event_id, + [](const NamedEventInfo &info, uint32_t event_id) -> bool { return info.event_id < event_id; }); + if (pos != _named_event_infos.end() && pos->event_id == event_id) { + return pos->enabled; + } + // TODO: specify difference_type on AG::vector::iterator + index = (uint32_t)(pos - _named_event_infos.begin()); + } + + const char *event_name = IAGGraphGetTraceEventName(event_id); + if (event_name == nullptr) { + precondition_failure("invalid named trace event: %u", event_id); + } + + const char *event_subsystem = IAGGraphGetTraceEventSubsystem(event_id); + + bool enabled = false; + if (event_subsystem == nullptr || (_trace_flags & IAGGraphTraceFlagsAll)) { + enabled = true; + } else { + for (auto &stored_subsystem : _named_event_subsystems) { + if (!strcasecmp(stored_subsystem.get(), event_subsystem)) { + enabled = true; + break; + } + } + } + _named_event_infos.insert(_named_event_infos.begin() + index, {event_id, enabled}); + + if (!enabled) { + return false; + } + + encode_named_event(event_id, event_name, event_subsystem); + + return true; } } // namespace IAG diff --git a/Sources/ComputeCxx/Graph/TraceRecorder.h b/Sources/ComputeCxx/Graph/TraceRecorder.h index f880b692..158ebf47 100644 --- a/Sources/ComputeCxx/Graph/TraceRecorder.h +++ b/Sources/ComputeCxx/Graph/TraceRecorder.h @@ -1,27 +1,136 @@ #pragma once +#include + #include +#include +#include + #include "ComputeCxx/IAGBase.h" #include "ComputeCxx/IAGGraph.h" +#include "Protobuf/Encoder.h" #include "Trace/Trace.h" IAG_ASSUME_NONNULL_BEGIN namespace IAG { -class Graph::TraceRecorder : public Trace { +class Graph::TraceRecorder : public Trace, public Encoder::Delegate { + private: + Graph &_graph; + Encoder _encoder; + IAGGraphTraceFlags _trace_flags; + + vector, 0, uint64_t> _named_event_subsystems; + + util::Table _image_offset_cache; + uuid_t _stack_frame_uuid; + + std::unique_ptr _trace_path = nullptr; + bool _trace_path_created = false; + + uint32_t _num_encoded_types = 1; // skip IAGAttributeNullType + uint32_t _num_encoded_keys = 0; + + struct NamedEventInfo { + uint32_t event_id; + bool enabled; + }; + vector _named_event_infos; + public: - TraceRecorder(Graph *graph, IAGGraphTraceFlags trace_flags, std::span subsystems); + TraceRecorder(Graph &graph, IAGGraphTraceFlags trace_flags, std::span subsystems); ~TraceRecorder(); - uint64_t id() { return _id; }; - - const char *_Nullable trace_path() const { - // TODO: not implemented - return nullptr; + const char *_Nullable trace_path() const { return _trace_path.get(); }; + + // MARK: Delegate methods + + int flush_encoder(Encoder &encoder) override; + + // MARK: Top-level fields + + void encode_event_begin(); + void encode_event_end(); + void encode_subgraph(const Subgraph &subgraph); + void encode_types(); + void encode_keys(); + void encode_stack(); + void encode_named_event(uint64_t event_id, const char *event_name, const char *event_subsystem); + void encode_snapshot(); + + // MARK: Event fields + + enum class EventType : uint64_t { + Unknown = 0, + + BeginTrace = 1, + EndTrace = 2, + + BeginSubgraphUpdate = 3, + EndSubgraphUpdate = 4, + BeginNodeUpdate = 5, + EndNodeUpdate = 6, + BeginValueUpdate = 7, + EndValueUpdate = 8, + BeginGraphUpdate = 9, + EndGraphUpdate = 10, + + BeginGraphInvalidation = 11, + EndGraphInvalidation = 12, + + BeginModifyNode = 13, + EndModifyNode = 14, + + BeginEvent = 15, + EndEvent = 16, + + BeginSnapshot = 17, + EndSnapshot = 18, + + GraphCreated = 32, + GraphDestroy = 33, + GraphNeedsUpdate = 34, + + SubgraphCreated = 35, + SubgraphInvalidate = 36, + SubgraphAddChild = 37, + SubgraphRemoveChild = 38, + + NodeAdded = 39, + NodeSetDirty = 40, + NodeSetPending = 41, + NodeSetValue = 42, + NodeMarkValue = 43, + + IndirectNodeAdded = 44, + IndirectNodeSetSource = 45, + IndirectNodeSetDependency = 46, + + NodeAddEdge = 47, + NodeRemoveEdge = 48, + NodeSetEdgePending = 49, + + ProfileMark = 50, + LogMessage = 51, + + CustomEvent = 52, + SubgraphDestroy = 53, + NamedEvent = 54, + SetDeadline = 55, + PassedDeadline = 56 }; + void field_event_type(Encoder &encoder, EventType event_type); + void field_timestamp(Encoder &encoder); + void field_payload_1(Encoder &encoder, uint64_t payload); + void field_payload_2(Encoder &encoder, uint64_t payload); + void field_payload_3(Encoder &encoder, uint64_t payload); + void field_backtrace(Encoder &encoder); + void field_data(Encoder &encoder, const void *data, size_t length); + void field_named_event_id(Encoder &encoder, uint32_t event_id); + // MARK: Trace methods void graph_destroyed() override; From ecd84dd0ca2a381c3163871eef702542c001e35e Mon Sep 17 00:00:00 2001 From: James Moschou Date: Tue, 4 Aug 2026 14:36:27 +0200 Subject: [PATCH 08/42] Use to enumerate traces in reverse --- Sources/ComputeCxx/Graph/Graph.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Sources/ComputeCxx/Graph/Graph.h b/Sources/ComputeCxx/Graph/Graph.h index 1d07e598..8aeaf56c 100644 --- a/Sources/ComputeCxx/Graph/Graph.h +++ b/Sources/ComputeCxx/Graph/Graph.h @@ -3,6 +3,7 @@ #include "ComputeCxx/IAGBase.h" #include +#include #include #if TARGET_OS_MAC @@ -413,8 +414,8 @@ class Graph { template requires std::invocable void foreach_trace(T body) { - for (auto trace = _traces.rbegin(), end = _traces.rend(); trace != end; ++trace) { - body(**trace); + for (auto trace : std::ranges::reverse_view(_traces)) { + body(*trace); } }; From bbfff09066a2e78df744157e49a5f0e85a899d36 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Tue, 4 Aug 2026 14:58:52 +0200 Subject: [PATCH 09/42] Change description file extension to iag-gzon --- Sources/ComputeCxx/Graph/Graph.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/ComputeCxx/Graph/Graph.mm b/Sources/ComputeCxx/Graph/Graph.mm index ffe8dcaf..d30c0ded 100644 --- a/Sources/ComputeCxx/Graph/Graph.mm +++ b/Sources/ComputeCxx/Graph/Graph.mm @@ -1037,7 +1037,7 @@ int trap_cycles() { } if (filename == nullptr) { - filename = "graph.ag-gzon"; + filename = "graph.iag-gzon"; } NSData *data = [NSJSONSerialization dataWithJSONObject:json options:0 error:nil]; @@ -1048,7 +1048,7 @@ int trap_cycles() { } NSError *error = nil; - if ([[path pathExtension] isEqualToString:@"ag-gzon"]) { + if ([[path pathExtension] isEqualToString:@"iag-gzon"]) { // Disassembly writes compressed data directly using gzwrite instead of creating an intermediate NSData object data = [data compressedDataUsingAlgorithm:NSDataCompressionAlgorithmZlib error:&error]; if (!data) { From 8d0c722ac65ecb6976cea353895d35b4dd541855 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Tue, 4 Aug 2026 15:03:45 +0200 Subject: [PATCH 10/42] Add uuid-dev dependency to devcontainer.json --- .devcontainer/devcontainer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index fe0e1952..1a837b6f 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -12,7 +12,7 @@ "ppa": "false" }, "ghcr.io/devcontainers-extra/features/apt-get-packages:1": { - "packages": "libssl-dev" + "packages": "libssl-dev,uuid-dev" } }, "runArgs": [ From 11734326eccd5ff58d1d50aee7966f4eb10e1460 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Tue, 4 Aug 2026 15:04:39 +0200 Subject: [PATCH 11/42] Add uuid-dev dependency to GitHub Actions --- .github/workflows/swift.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 493e59ec..a89ace0a 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -46,7 +46,7 @@ jobs: - name: Install dependencies run: | apt-get update - apt-get install -y libssl-dev + apt-get install -y libssl-dev uuid-dev - uses: actions/checkout@v6 with: submodules: true From e17b128ef57b0268dc1b6f6ae1befd87f89954bb Mon Sep 17 00:00:00 2001 From: James Moschou Date: Tue, 4 Aug 2026 15:14:05 +0200 Subject: [PATCH 12/42] Update compatibility testing framework --- .../AttributeGraph.framework/Headers/AGBase.h | 16 ++++++++ .../Headers/AGGraphTracing.h | 39 ++++++++++--------- .../Headers/AGTraceType.h | 15 +++---- .../Versions/A/Headers/AGBase.h | 16 ++++++++ .../Versions/A/Headers/AGGraphTracing.h | 39 ++++++++++--------- .../Versions/A/Headers/AGTraceType.h | 15 +++---- 6 files changed, 90 insertions(+), 50 deletions(-) diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGBase.h b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGBase.h index b4435566..b9bf1613 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGBase.h +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGBase.h @@ -43,6 +43,22 @@ #define AG_INLINE static inline #endif +#ifndef AG_NOINLINE +#if __has_attribute(noinline) +#define AG_NOINLINE __attribute__((noinline)) +#else +#define AG_NOINLINE +#endif +#endif + +#ifndef AG_OPTNONE +#if __has_attribute(optnone) +#define AG_OPTNONE __attribute__((optnone)) +#else +#define AG_OPTNONE +#endif +#endif + #ifndef AG_RETURNS_RETAINED #if __has_feature(attribute_cf_returns_retained) #define AG_RETURNS_RETAINED __attribute__((cf_returns_retained)) diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGGraphTracing.h b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGGraphTracing.h index 314d6b41..91780c92 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGGraphTracing.h +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGGraphTracing.h @@ -2,32 +2,35 @@ #include #include +#include -typedef AG_OPTIONS(uint32_t, AGGraphTraceOptions) { - AGGraphTraceOptionsEnabled = 1 << 0, - AGGraphTraceOptionsFull = 1 << 1, - AGGraphTraceOptionsBacktrace = 1 << 2, - AGGraphTraceOptionsPrepare = 1 << 3, - AGGraphTraceOptionsCustom = 1 << 4, - AGGraphTraceOptionsAll = 1 << 5, -} AG_SWIFT_NAME(AGGraphRef.TraceOptions); +typedef AG_OPTIONS(uint32_t, AGGraphTraceFlags) { + AGGraphTraceFlagsEnabled = 1 << 0, + AGGraphTraceFlagsFull = 1 << 1, + AGGraphTraceFlagsBacktrace = 1 << 2, + AGGraphTraceFlagsPrepare = 1 << 3, + AGGraphTraceFlagsCustom = 1 << 4, + AGGraphTraceFlagsAll = 1 << 5, +} AG_SWIFT_NAME(AGGraphRef.TraceFlags); typedef struct AGTraceType *AGTraceTypeRef; +typedef uint32_t AGNamedTraceEventID AG_SWIFT_STRUCT AG_SWIFT_NAME(Graph.NamedTraceEventID); + AG_ASSUME_NONNULL_BEGIN AG_EXTERN_C_BEGIN AG_EXPORT AG_REFINED_FOR_SWIFT -void AGGraphStartTracing(AGGraphRef _Nullable graph, AGGraphTraceOptions trace_options) - AG_SWIFT_NAME(AGGraphRef.startTracing(_:options:)); +void AGGraphStartTracing(AGGraphRef _Nullable graph, AGGraphTraceFlags trace_flags) + AG_SWIFT_NAME(AGGraphRef.startTracing(_:flags:)); AG_EXPORT AG_REFINED_FOR_SWIFT -void AGGraphStartTracing2(AGGraphRef _Nullable graph, AGGraphTraceOptions trace_options, +void AGGraphStartTracing2(AGGraphRef _Nullable graph, AGGraphTraceFlags trace_flags, CFArrayRef _Nullable subsystems) - AG_SWIFT_NAME(AGGraphRef.startTracing(_:options:subsystems:)); + AG_SWIFT_NAME(AGGraphRef.startTracing(_:flags:subsystems:)); AG_EXPORT AG_REFINED_FOR_SWIFT @@ -43,12 +46,12 @@ CFStringRef AGGraphCopyTracePath(AGGraphRef graph) AG_SWIFT_NAME(getter:AGGraphR AG_EXPORT AG_REFINED_FOR_SWIFT -uint64_t AGGraphAddTrace(AGGraphRef graph, const AGTraceTypeRef trace, void *_Nullable context) +AGUniqueID AGGraphAddTrace(AGGraphRef graph, const AGTraceTypeRef trace, void *_Nullable context) AG_SWIFT_NAME(AGGraphRef.addTrace(self:_:context:)); AG_EXPORT AG_REFINED_FOR_SWIFT -void AGGraphRemoveTrace(AGGraphRef graph, uint64_t trace_id) AG_SWIFT_NAME(AGGraphRef.removeTrace(self:traceID:)); +void AGGraphRemoveTrace(AGGraphRef graph, AGUniqueID trace_id) AG_SWIFT_NAME(AGGraphRef.removeTrace(self:traceID:)); AG_EXPORT AG_REFINED_FOR_SWIFT @@ -79,22 +82,22 @@ void AGGraphAddTraceEvent(AGGraphRef graph, const char *event_name, const void * AG_EXPORT AG_REFINED_FOR_SWIFT -void AGGraphAddNamedTraceEvent(AGGraphRef graph, uint32_t event_id, uint32_t event_arg_count, const void *event_args, +void AGGraphAddNamedTraceEvent(AGGraphRef graph, AGNamedTraceEventID event_id, uint32_t event_arg_count, const void **event_args, CFDataRef data, uint32_t arg6) AG_SWIFT_NAME(AGGraphRef.addNamedTraceEvent(self:eventID:eventArgCount:eventArgs:data:arg6:)); AG_EXPORT AG_REFINED_FOR_SWIFT -const char *_Nullable AGGraphGetTraceEventName(uint32_t event_id) AG_SWIFT_NAME(AGGraphRef.traceEventName(for:)); +const char *_Nullable AGGraphGetTraceEventName(AGNamedTraceEventID event_id) AG_SWIFT_NAME(AGGraphRef.traceEventName(for:)); AG_EXPORT AG_REFINED_FOR_SWIFT -const char *_Nullable AGGraphGetTraceEventSubsystem(uint32_t event_id) +const char *_Nullable AGGraphGetTraceEventSubsystem(AGNamedTraceEventID event_id) AG_SWIFT_NAME(AGGraphRef.traceEventSubsystem(for:)); AG_EXPORT AG_REFINED_FOR_SWIFT -uint32_t AGGraphRegisterNamedTraceEvent(const char *event_name, const char *event_subsystem) +AGNamedTraceEventID AGGraphRegisterNamedTraceEvent(const char *event_name, const char *event_subsystem) AG_SWIFT_NAME(AGGraphRef.registerNamedTraceEvent(name:subsystem:)); AG_EXTERN_C_END diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGTraceType.h b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGTraceType.h index d22355b7..9ee64384 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGTraceType.h +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGTraceType.h @@ -3,6 +3,7 @@ #include #include #include +#include AG_ASSUME_NONNULL_BEGIN @@ -25,7 +26,7 @@ typedef struct AG_SWIFT_NAME(TraceType) AGTraceType { void (*_Nullable begin_subgraph_update)(void *_Nullable context, AGSubgraphRef subgraph, uint32_t options); void (*_Nullable end_subgraph_update)(void *_Nullable context, AGSubgraphRef subgraph); void (*_Nullable begin_node_update)(void *_Nullable context, AGAttribute attribute); - void (*_Nullable end_node_update)(void *_Nullable context, bool changed); + void (*_Nullable end_node_update)(void *_Nullable context, AGAttribute attribute, bool changed); void (*_Nullable begin_value_update)(void *_Nullable context, AGAttribute attribute); void (*_Nullable end_value_update)(void *_Nullable context, AGAttribute attribute, bool changed); void (*_Nullable begin_graph_update)(void *_Nullable context, AGGraphRef graph); @@ -51,12 +52,12 @@ typedef struct AG_SWIFT_NAME(TraceType) AGTraceType { void (*_Nullable node_added)(void *_Nullable context, AGAttribute attribute); void (*_Nullable node_add_edge)(void *_Nullable context, AGAttribute attribute, AGAttribute input, AGInputOptions input_options); - void (*_Nullable node_remove_edge)(void *_Nullable context, AGAttribute attribute, uint32_t index); - void (*_Nullable node_set_edge_pending)(void *_Nullable context, AGAttribute attribute, AGAttribute input, bool pending); + void (*_Nullable node_remove_edge)(void *_Nullable context, AGAttribute attribute, uint32_t input_index); + void (*_Nullable node_set_edge_pending)(void *_Nullable context, AGAttribute attribute, uint32_t input_index, bool pending); void (*_Nullable node_set_dirty)(void *_Nullable context, AGAttribute attribute, bool dirty); void (*_Nullable node_set_pending)(void *_Nullable context, AGAttribute attribute, bool pending); - void (*_Nullable node_set_value)(void *_Nullable context, AGAttribute attribute); + void (*_Nullable node_set_value)(void *_Nullable context, AGAttribute attribute, const void *value); void (*_Nullable node_mark_value)(void *_Nullable context, AGAttribute attribute); void (*_Nullable indirect_node_added)(void *_Nullable context, AGAttribute attribute); @@ -67,9 +68,9 @@ typedef struct AG_SWIFT_NAME(TraceType) AGTraceType { void (*_Nullable custom_event)(void *_Nullable context, AGGraphRef graph, const char *event_name, const void *value, AGTypeID type); - void (*_Nullable named_event)(void *_Nullable context, AGGraphRef graph, uint32_t eventID, uint32_t eventArgCount, - const void *eventArgs, CFDataRef data, uint32_t arg6); - bool (*_Nullable named_event_enabled)(void *_Nullable context); + void (*_Nullable named_event)(void *_Nullable context, AGGraphRef graph, AGNamedTraceEventID event_id, uint32_t event_arg_count, + const void **event_args, CFDataRef data, uint32_t arg6); + bool (*_Nullable named_event_enabled)(void *_Nullable context, AGNamedTraceEventID event_id); void (*_Nullable set_deadline)(void *_Nullable context); void (*_Nullable passed_deadline)(void *_Nullable context); diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGBase.h b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGBase.h index b4435566..b9bf1613 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGBase.h +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGBase.h @@ -43,6 +43,22 @@ #define AG_INLINE static inline #endif +#ifndef AG_NOINLINE +#if __has_attribute(noinline) +#define AG_NOINLINE __attribute__((noinline)) +#else +#define AG_NOINLINE +#endif +#endif + +#ifndef AG_OPTNONE +#if __has_attribute(optnone) +#define AG_OPTNONE __attribute__((optnone)) +#else +#define AG_OPTNONE +#endif +#endif + #ifndef AG_RETURNS_RETAINED #if __has_feature(attribute_cf_returns_retained) #define AG_RETURNS_RETAINED __attribute__((cf_returns_retained)) diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGGraphTracing.h b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGGraphTracing.h index 314d6b41..91780c92 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGGraphTracing.h +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGGraphTracing.h @@ -2,32 +2,35 @@ #include #include +#include -typedef AG_OPTIONS(uint32_t, AGGraphTraceOptions) { - AGGraphTraceOptionsEnabled = 1 << 0, - AGGraphTraceOptionsFull = 1 << 1, - AGGraphTraceOptionsBacktrace = 1 << 2, - AGGraphTraceOptionsPrepare = 1 << 3, - AGGraphTraceOptionsCustom = 1 << 4, - AGGraphTraceOptionsAll = 1 << 5, -} AG_SWIFT_NAME(AGGraphRef.TraceOptions); +typedef AG_OPTIONS(uint32_t, AGGraphTraceFlags) { + AGGraphTraceFlagsEnabled = 1 << 0, + AGGraphTraceFlagsFull = 1 << 1, + AGGraphTraceFlagsBacktrace = 1 << 2, + AGGraphTraceFlagsPrepare = 1 << 3, + AGGraphTraceFlagsCustom = 1 << 4, + AGGraphTraceFlagsAll = 1 << 5, +} AG_SWIFT_NAME(AGGraphRef.TraceFlags); typedef struct AGTraceType *AGTraceTypeRef; +typedef uint32_t AGNamedTraceEventID AG_SWIFT_STRUCT AG_SWIFT_NAME(Graph.NamedTraceEventID); + AG_ASSUME_NONNULL_BEGIN AG_EXTERN_C_BEGIN AG_EXPORT AG_REFINED_FOR_SWIFT -void AGGraphStartTracing(AGGraphRef _Nullable graph, AGGraphTraceOptions trace_options) - AG_SWIFT_NAME(AGGraphRef.startTracing(_:options:)); +void AGGraphStartTracing(AGGraphRef _Nullable graph, AGGraphTraceFlags trace_flags) + AG_SWIFT_NAME(AGGraphRef.startTracing(_:flags:)); AG_EXPORT AG_REFINED_FOR_SWIFT -void AGGraphStartTracing2(AGGraphRef _Nullable graph, AGGraphTraceOptions trace_options, +void AGGraphStartTracing2(AGGraphRef _Nullable graph, AGGraphTraceFlags trace_flags, CFArrayRef _Nullable subsystems) - AG_SWIFT_NAME(AGGraphRef.startTracing(_:options:subsystems:)); + AG_SWIFT_NAME(AGGraphRef.startTracing(_:flags:subsystems:)); AG_EXPORT AG_REFINED_FOR_SWIFT @@ -43,12 +46,12 @@ CFStringRef AGGraphCopyTracePath(AGGraphRef graph) AG_SWIFT_NAME(getter:AGGraphR AG_EXPORT AG_REFINED_FOR_SWIFT -uint64_t AGGraphAddTrace(AGGraphRef graph, const AGTraceTypeRef trace, void *_Nullable context) +AGUniqueID AGGraphAddTrace(AGGraphRef graph, const AGTraceTypeRef trace, void *_Nullable context) AG_SWIFT_NAME(AGGraphRef.addTrace(self:_:context:)); AG_EXPORT AG_REFINED_FOR_SWIFT -void AGGraphRemoveTrace(AGGraphRef graph, uint64_t trace_id) AG_SWIFT_NAME(AGGraphRef.removeTrace(self:traceID:)); +void AGGraphRemoveTrace(AGGraphRef graph, AGUniqueID trace_id) AG_SWIFT_NAME(AGGraphRef.removeTrace(self:traceID:)); AG_EXPORT AG_REFINED_FOR_SWIFT @@ -79,22 +82,22 @@ void AGGraphAddTraceEvent(AGGraphRef graph, const char *event_name, const void * AG_EXPORT AG_REFINED_FOR_SWIFT -void AGGraphAddNamedTraceEvent(AGGraphRef graph, uint32_t event_id, uint32_t event_arg_count, const void *event_args, +void AGGraphAddNamedTraceEvent(AGGraphRef graph, AGNamedTraceEventID event_id, uint32_t event_arg_count, const void **event_args, CFDataRef data, uint32_t arg6) AG_SWIFT_NAME(AGGraphRef.addNamedTraceEvent(self:eventID:eventArgCount:eventArgs:data:arg6:)); AG_EXPORT AG_REFINED_FOR_SWIFT -const char *_Nullable AGGraphGetTraceEventName(uint32_t event_id) AG_SWIFT_NAME(AGGraphRef.traceEventName(for:)); +const char *_Nullable AGGraphGetTraceEventName(AGNamedTraceEventID event_id) AG_SWIFT_NAME(AGGraphRef.traceEventName(for:)); AG_EXPORT AG_REFINED_FOR_SWIFT -const char *_Nullable AGGraphGetTraceEventSubsystem(uint32_t event_id) +const char *_Nullable AGGraphGetTraceEventSubsystem(AGNamedTraceEventID event_id) AG_SWIFT_NAME(AGGraphRef.traceEventSubsystem(for:)); AG_EXPORT AG_REFINED_FOR_SWIFT -uint32_t AGGraphRegisterNamedTraceEvent(const char *event_name, const char *event_subsystem) +AGNamedTraceEventID AGGraphRegisterNamedTraceEvent(const char *event_name, const char *event_subsystem) AG_SWIFT_NAME(AGGraphRef.registerNamedTraceEvent(name:subsystem:)); AG_EXTERN_C_END diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGTraceType.h b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGTraceType.h index d22355b7..9ee64384 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGTraceType.h +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGTraceType.h @@ -3,6 +3,7 @@ #include #include #include +#include AG_ASSUME_NONNULL_BEGIN @@ -25,7 +26,7 @@ typedef struct AG_SWIFT_NAME(TraceType) AGTraceType { void (*_Nullable begin_subgraph_update)(void *_Nullable context, AGSubgraphRef subgraph, uint32_t options); void (*_Nullable end_subgraph_update)(void *_Nullable context, AGSubgraphRef subgraph); void (*_Nullable begin_node_update)(void *_Nullable context, AGAttribute attribute); - void (*_Nullable end_node_update)(void *_Nullable context, bool changed); + void (*_Nullable end_node_update)(void *_Nullable context, AGAttribute attribute, bool changed); void (*_Nullable begin_value_update)(void *_Nullable context, AGAttribute attribute); void (*_Nullable end_value_update)(void *_Nullable context, AGAttribute attribute, bool changed); void (*_Nullable begin_graph_update)(void *_Nullable context, AGGraphRef graph); @@ -51,12 +52,12 @@ typedef struct AG_SWIFT_NAME(TraceType) AGTraceType { void (*_Nullable node_added)(void *_Nullable context, AGAttribute attribute); void (*_Nullable node_add_edge)(void *_Nullable context, AGAttribute attribute, AGAttribute input, AGInputOptions input_options); - void (*_Nullable node_remove_edge)(void *_Nullable context, AGAttribute attribute, uint32_t index); - void (*_Nullable node_set_edge_pending)(void *_Nullable context, AGAttribute attribute, AGAttribute input, bool pending); + void (*_Nullable node_remove_edge)(void *_Nullable context, AGAttribute attribute, uint32_t input_index); + void (*_Nullable node_set_edge_pending)(void *_Nullable context, AGAttribute attribute, uint32_t input_index, bool pending); void (*_Nullable node_set_dirty)(void *_Nullable context, AGAttribute attribute, bool dirty); void (*_Nullable node_set_pending)(void *_Nullable context, AGAttribute attribute, bool pending); - void (*_Nullable node_set_value)(void *_Nullable context, AGAttribute attribute); + void (*_Nullable node_set_value)(void *_Nullable context, AGAttribute attribute, const void *value); void (*_Nullable node_mark_value)(void *_Nullable context, AGAttribute attribute); void (*_Nullable indirect_node_added)(void *_Nullable context, AGAttribute attribute); @@ -67,9 +68,9 @@ typedef struct AG_SWIFT_NAME(TraceType) AGTraceType { void (*_Nullable custom_event)(void *_Nullable context, AGGraphRef graph, const char *event_name, const void *value, AGTypeID type); - void (*_Nullable named_event)(void *_Nullable context, AGGraphRef graph, uint32_t eventID, uint32_t eventArgCount, - const void *eventArgs, CFDataRef data, uint32_t arg6); - bool (*_Nullable named_event_enabled)(void *_Nullable context); + void (*_Nullable named_event)(void *_Nullable context, AGGraphRef graph, AGNamedTraceEventID event_id, uint32_t event_arg_count, + const void **event_args, CFDataRef data, uint32_t arg6); + bool (*_Nullable named_event_enabled)(void *_Nullable context, AGNamedTraceEventID event_id); void (*_Nullable set_deadline)(void *_Nullable context); void (*_Nullable passed_deadline)(void *_Nullable context); From b6629ff8de0cd3e109a44a16123afd5cc2fea418 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Tue, 4 Aug 2026 15:32:50 +0200 Subject: [PATCH 13/42] Only implement backtrace on macOS --- Sources/ComputeCxx/Graph/TraceRecorder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/ComputeCxx/Graph/TraceRecorder.cpp b/Sources/ComputeCxx/Graph/TraceRecorder.cpp index f4ffd61f..b5617cc7 100644 --- a/Sources/ComputeCxx/Graph/TraceRecorder.cpp +++ b/Sources/ComputeCxx/Graph/TraceRecorder.cpp @@ -5,7 +5,7 @@ #include #include #include -#if TARGET_OS_MAC +#if TARGET_OS_MAC && TARGET_OS_OSX #include #endif #include @@ -289,7 +289,7 @@ void Graph::TraceRecorder::field_backtrace(Encoder &encoder) { return; } - #if TARGET_OS_MAC + #if TARGET_OS_MAC && TARGET_OS_OSX void *stack_frames_buffer[8]; int stack_frames_size = backtrace(stack_frames_buffer, std::size(stack_frames_buffer)); From bbafbe3bef65cce444db012755d328caf80f5db5 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Tue, 4 Aug 2026 19:57:33 +0200 Subject: [PATCH 14/42] Refine IAGUniqueID to UniqueID in Swift --- .../AttributeGraph.framework/Headers/AGUniqueID.h | 2 +- .../AttributeGraph.framework/Versions/A/Headers/AGUniqueID.h | 2 +- Sources/ComputeCxx/include/ComputeCxx/IAGUniqueID.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGUniqueID.h b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGUniqueID.h index cee96998..d8dc2f31 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGUniqueID.h +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGUniqueID.h @@ -6,7 +6,7 @@ AG_ASSUME_NONNULL_BEGIN AG_EXTERN_C_BEGIN -typedef long AGUniqueID; +typedef long AGUniqueID AG_SWIFT_NAME(UniqueID); AGUniqueID AGMakeUniqueID(void) AG_SWIFT_NAME(makeUniqueID()); diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGUniqueID.h b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGUniqueID.h index cee96998..d8dc2f31 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGUniqueID.h +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGUniqueID.h @@ -6,7 +6,7 @@ AG_ASSUME_NONNULL_BEGIN AG_EXTERN_C_BEGIN -typedef long AGUniqueID; +typedef long AGUniqueID AG_SWIFT_NAME(UniqueID); AGUniqueID AGMakeUniqueID(void) AG_SWIFT_NAME(makeUniqueID()); diff --git a/Sources/ComputeCxx/include/ComputeCxx/IAGUniqueID.h b/Sources/ComputeCxx/include/ComputeCxx/IAGUniqueID.h index 6e62a2f5..cd94afcc 100644 --- a/Sources/ComputeCxx/include/ComputeCxx/IAGUniqueID.h +++ b/Sources/ComputeCxx/include/ComputeCxx/IAGUniqueID.h @@ -6,7 +6,7 @@ IAG_ASSUME_NONNULL_BEGIN IAG_EXTERN_C_BEGIN -typedef long IAGUniqueID; +typedef long IAGUniqueID IAG_SWIFT_NAME(UniqueID); IAGUniqueID IAGMakeUniqueID(void) IAG_SWIFT_NAME(makeUniqueID()); From e0b330a77f212edcd7a1ece837aa70657a116fde Mon Sep 17 00:00:00 2001 From: James Moschou Date: Tue, 4 Aug 2026 20:07:26 +0200 Subject: [PATCH 15/42] Ensure deadline value is passed through SetDeadline trace event --- .../Headers/AGTraceType.h | 6 +- .../Versions/A/Headers/AGTraceType.h | 6 +- Sources/ComputeCxx/Trace/ExternalTrace.cpp | 2 +- .../include/ComputeCxx/IAGTraceType.h | 6 +- .../Shared/Graph/GraphDeadlineTests.swift | 43 +++ .../Shared/Graph/GraphTracingTests.swift | 96 +++++- .../Shared/Subgraph/SubgraphTests.swift | 45 +-- .../Shared/TestSupport/TestTrace.swift | 287 ++++++++++++++++++ 8 files changed, 445 insertions(+), 46 deletions(-) create mode 100644 Tests/ComputeTests/Shared/Graph/GraphDeadlineTests.swift create mode 100644 Tests/ComputeTests/Shared/TestSupport/TestTrace.swift diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGTraceType.h b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGTraceType.h index 9ee64384..0d91f577 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGTraceType.h +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGTraceType.h @@ -15,9 +15,9 @@ typedef AG_ENUM(uint64_t, AGTraceTypeVersion) { AGTraceTypeVersionNamed = 2, AGTraceTypeVersionDeadline = 3, AGTraceTypeVersionCompareFailed = 4, -}; +} AG_SWIFT_NAME(Graph.TraceType.Version); -typedef struct AG_SWIFT_NAME(TraceType) AGTraceType { +typedef struct AG_SWIFT_NAME(Graph.TraceType) AGTraceType { AGTraceTypeVersion version; void (*_Nullable begin_trace)(void *_Nullable context, AGGraphRef graph); @@ -72,7 +72,7 @@ typedef struct AG_SWIFT_NAME(TraceType) AGTraceType { const void **event_args, CFDataRef data, uint32_t arg6); bool (*_Nullable named_event_enabled)(void *_Nullable context, AGNamedTraceEventID event_id); - void (*_Nullable set_deadline)(void *_Nullable context); + void (*_Nullable set_deadline)(void *_Nullable context, uint64_t deadline); void (*_Nullable passed_deadline)(void *_Nullable context); void (*_Nullable compare_failed)(void *_Nullable context, AGAttribute attribute, AGComparisonState comparisonState); diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGTraceType.h b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGTraceType.h index 9ee64384..0d91f577 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGTraceType.h +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGTraceType.h @@ -15,9 +15,9 @@ typedef AG_ENUM(uint64_t, AGTraceTypeVersion) { AGTraceTypeVersionNamed = 2, AGTraceTypeVersionDeadline = 3, AGTraceTypeVersionCompareFailed = 4, -}; +} AG_SWIFT_NAME(Graph.TraceType.Version); -typedef struct AG_SWIFT_NAME(TraceType) AGTraceType { +typedef struct AG_SWIFT_NAME(Graph.TraceType) AGTraceType { AGTraceTypeVersion version; void (*_Nullable begin_trace)(void *_Nullable context, AGGraphRef graph); @@ -72,7 +72,7 @@ typedef struct AG_SWIFT_NAME(TraceType) AGTraceType { const void **event_args, CFDataRef data, uint32_t arg6); bool (*_Nullable named_event_enabled)(void *_Nullable context, AGNamedTraceEventID event_id); - void (*_Nullable set_deadline)(void *_Nullable context); + void (*_Nullable set_deadline)(void *_Nullable context, uint64_t deadline); void (*_Nullable passed_deadline)(void *_Nullable context); void (*_Nullable compare_failed)(void *_Nullable context, AGAttribute attribute, AGComparisonState comparisonState); diff --git a/Sources/ComputeCxx/Trace/ExternalTrace.cpp b/Sources/ComputeCxx/Trace/ExternalTrace.cpp index bea93144..6b866860 100644 --- a/Sources/ComputeCxx/Trace/ExternalTrace.cpp +++ b/Sources/ComputeCxx/Trace/ExternalTrace.cpp @@ -290,7 +290,7 @@ void ExternalTrace::set_deadline(uint64_t deadline) { return; } if (auto callback = _trace->set_deadline) { - callback(_context); + callback(_context, deadline); } } diff --git a/Sources/ComputeCxx/include/ComputeCxx/IAGTraceType.h b/Sources/ComputeCxx/include/ComputeCxx/IAGTraceType.h index e3d59f11..8dac6fe5 100644 --- a/Sources/ComputeCxx/include/ComputeCxx/IAGTraceType.h +++ b/Sources/ComputeCxx/include/ComputeCxx/IAGTraceType.h @@ -15,9 +15,9 @@ typedef IAG_ENUM(uint64_t, IAGTraceTypeVersion) { IAGTraceTypeVersionNamed = 2, IAGTraceTypeVersionDeadline = 3, IAGTraceTypeVersionCompareFailed = 4, -}; +} IAG_SWIFT_NAME(Graph.TraceType.Version); -typedef struct IAG_SWIFT_NAME(TraceType) IAGTraceType { +typedef struct IAG_SWIFT_NAME(Graph.TraceType) IAGTraceType { IAGTraceTypeVersion version; void (*_Nullable begin_trace)(void *_Nullable context, IAGGraphRef graph); @@ -72,7 +72,7 @@ typedef struct IAG_SWIFT_NAME(TraceType) IAGTraceType { const void **event_args, CFDataRef data, uint32_t arg6); bool (*_Nullable named_event_enabled)(void *_Nullable context, IAGNamedTraceEventID event_id); - void (*_Nullable set_deadline)(void *_Nullable context); + void (*_Nullable set_deadline)(void *_Nullable context, uint64_t deadline); void (*_Nullable passed_deadline)(void *_Nullable context); void (*_Nullable compare_failed)(void *_Nullable context, IAGAttribute attribute, IAGComparisonState comparisonState); diff --git a/Tests/ComputeTests/Shared/Graph/GraphDeadlineTests.swift b/Tests/ComputeTests/Shared/Graph/GraphDeadlineTests.swift new file mode 100644 index 00000000..0cfe4f64 --- /dev/null +++ b/Tests/ComputeTests/Shared/Graph/GraphDeadlineTests.swift @@ -0,0 +1,43 @@ +import Testing +import _ComputeTestSupport + +@Suite +struct GraphDeadlineTests { + @Test + func withDeadline() { + let graph = Graph() + + let trace = TestTrace() + trace.register(graph: graph) + + #expect(graph.deadline == UInt64.max) + graph.withDeadline(0) { + #expect(graph.deadline == 0) + } + #expect(graph.deadline == UInt64.max) + + let setDeadlineEvents = trace.events(of: .setDeadline) + #expect(setDeadlineEvents.count == 2) + #expect(setDeadlineEvents[0].message == "deadline = 0") + #expect(setDeadlineEvents[1].message == "deadline = \(UInt64.max)") + } + + @Test + func withDeadlineNonzero() { + let graph = Graph() + + let trace = TestTrace() + trace.register(graph: graph) + + #expect(graph.deadline == UInt64.max) + graph.withDeadline(100) { + #expect(graph.deadline == 100) + } + #expect(graph.deadline == UInt64.max) + + let setDeadlineEvents = trace.events(of: .setDeadline) + #expect(setDeadlineEvents.count == 2) + #expect(setDeadlineEvents[0].message == "deadline = 100") + #expect(setDeadlineEvents[1].message == "deadline = \(UInt64.max)") + } +} diff --git a/Tests/ComputeTests/Shared/Graph/GraphTracingTests.swift b/Tests/ComputeTests/Shared/Graph/GraphTracingTests.swift index aa9fa81f..9dc776a9 100644 --- a/Tests/ComputeTests/Shared/Graph/GraphTracingTests.swift +++ b/Tests/ComputeTests/Shared/Graph/GraphTracingTests.swift @@ -2,13 +2,105 @@ import Testing @Suite struct GraphTracingTests { + @Test + func initializeTraceType() { + Graph.TraceType(version: .compareFailed) { ctx, graph in + print("[begin_trace] graph = \(graph)") + } end_trace: { ctx, graph in + print("[end_trace] graph = \(graph)") + } begin_subgraph_update: { ctx, subgraph, options in + print("[begin_subgraph_update] subgraph = \(subgraph), options = \(options)") + } end_subgraph_update: { ctx, subgraph in + print("[end_subgraph_update] subgraph = \(subgraph)") + } begin_node_update: { ctx, attribute in + print("[begin_node_update] attribute = \(attribute)") + } end_node_update: { ctx, attribute, changed in + print("[end_node_update] attribute = \(attribute), changed = \(changed)") + } begin_value_update: { ctx, attribute in + print("[begin_value_update] attribute = \(attribute)") + } end_value_update: { ctx, attribute, changed in + print("[end_value_update] attribute = \(attribute), changed = \(changed)") + } begin_graph_update: { ctx, graph in + print("[begin_graph_update] graph = \(graph)") + } end_graph_update: { ctx, graph in + print("[end_graph_update] graph = \(graph)") + } begin_graph_invalidation: { ctx, graph, attribute in + print("[begin_graph_invalidation] graph = \(graph), attribute = \(attribute)") + } end_graph_invalidation: { ctx, graph, attribute in + print("[end_graph_invalidation] graph = \(graph), attribute = \(attribute)") + } begin_modify_node: { ctx, attribute in + print("[begin_modify_node] attribute = \(attribute)") + } end_modify_node: { ctx, attribute in + print("[end_modify_node] attribute = \(attribute)") + } begin_event: { ctx, attribute, event_name in + print("[begin_event] attribute = \(attribute), event_name = \(String(cString: event_name))") + } end_event: { ctx, attribute, event_name in + print("[end_event] attribute = \(attribute), event_name = \(String(cString: event_name))") + } graph_created: { ctx, graph in + print("[graph_created] graph = \(graph)") + } graph_destroy: { ctx, graph in + print("[graph_destroy] graph = \(graph)") + } graph_needs_update: { ctx, graph in + print("[graph_needs_update] graph = \(graph)") + } subgraph_created: { ctx, subgraph in + print("[subgraph_created] subgraph = \(subgraph)") + } subgraph_destroy: { ctx, subgraph in + print("[subgraph_destroy] subgraph = \(subgraph)") + } subgraph_add_child: { ctx, subgraph, child_subgraph in + print("[subgraph_add_child] subgraph = \(subgraph), child_subgraph = \(child_subgraph)") + } subgraph_remove_child: { ctx, subgraph, child_subgraph in + print("[subgraph_remove_child] subgraph = \(subgraph), child_subgraph = \(child_subgraph)") + } node_added: { ctx, attribute in + print("[node_added] attribute = \(attribute)") + } node_add_edge: { ctx, attribute, input, options in + print("[node_add_edge] attribute = \(attribute), input = \(input), options = \(options)") + } node_remove_edge: { ctx, attribute, input_index in + print("[node_remove_edge] attribute = \(attribute), input_index = \(input_index)") + } node_set_edge_pending: { ctx, attribute, input_index, pending in + print( + "[node_set_edge_pending] attribute = \(attribute), input_index = \(input_index), pending = \(pending)" + ) + } node_set_dirty: { ctx, attribute, dirty in + print("[node_set_dirty] attribute = \(attribute), dirty = \(dirty)") + } node_set_pending: { ctx, attribute, pending in + print("[node_set_pending] attribute = \(attribute), pending = \(pending)") + } node_set_value: { ctx, attribute, value in + print("[node_set_value] attribute = \(attribute), value = \(value)") + } node_mark_value: { ctx, attribute in + print("[node_mark_value] attribute = \(attribute)") + } indirect_node_added: { ctx, attribute in + print("[indirect_node_added] attribute = \(attribute)") + } indirect_node_set_source: { ctx, attribute, source in + print("[indirect_node_set_source] attribute = \(attribute), source = \(source)") + } indirect_node_set_dependency: { ctx, attribute, dependency in + print("[indirect_node_set_dependency] attribute = \(attribute), dependency = \(dependency)") + } profile_mark: { ctx, event_name in + print("[profile_mark] event_name = \(String(cString: event_name))") + } custom_event: { ctx, graph, event_name, value, type in + print( + "[custom_event] graph = \(graph), event_name = \(String(cString: event_name)), value = \(value), type = \(type.type)" + ) + } named_event: { ctx, graph, event_id, event_arg_count, event_args, data, flags in + print("[named_event] graph = \(graph), event_id = \(event_id)") + } named_event_enabled: { ctx, event_id in + print("[named_event_enabled] event_id = \(event_id)") + return true + } set_deadline: { ctx, deadline in + print("[set_deadline] deadline = \(deadline)") + } passed_deadline: { ctx in + print("[passed_deadline]") + } compare_failed: { ctx, attribute, comparison_state in + print("[compare_failed] attribute = \(attribute), comparison_state = \(comparison_state)") + } + } + @Test func addTrace() { class Context { var traceCalls: [(name: String, graph: Graph)] = [] } - var trace = TraceType() + var trace = Graph.TraceType() trace.begin_trace = { contextPointer, graph in if let context = contextPointer?.assumingMemoryBound(to: Context.self).pointee { context.traceCalls.append((name: "beginTrace", graph: graph)) @@ -52,7 +144,7 @@ struct GraphTracingTests { var traceCalls: [(name: String, graph: Graph)] = [] } - var trace = TraceType() + var trace = Graph.TraceType() trace.begin_trace = { contextPointer, graph in if let context = contextPointer?.assumingMemoryBound(to: Context.self).pointee { context.traceCalls.append((name: "beginTrace", graph: graph)) diff --git a/Tests/ComputeTests/Shared/Subgraph/SubgraphTests.swift b/Tests/ComputeTests/Shared/Subgraph/SubgraphTests.swift index 0d34af4c..a1739c21 100644 --- a/Tests/ComputeTests/Shared/Subgraph/SubgraphTests.swift +++ b/Tests/ComputeTests/Shared/Subgraph/SubgraphTests.swift @@ -1,5 +1,6 @@ import Foundation import Testing +import _ComputeTestSupport @Suite struct SubgraphTests { @@ -181,49 +182,25 @@ struct SubgraphTests { @Suite struct TraceTests { - class TraceReporter { - var createdSubgraphCount = 0 - var invalidateSubgraphCount = 0 - } - @Test func invalidateSubgraph() async throws { - var trace = TraceType() - trace.subgraph_created = { context, graph in - guard let reporter = context?.assumingMemoryBound(to: TraceReporter.self).pointee else { - return - } - reporter.createdSubgraphCount += 1 - } - trace.subgraph_destroy = { context, graph in - guard let reporter = context?.assumingMemoryBound(to: TraceReporter.self).pointee else { - return - } - reporter.invalidateSubgraphCount += 1 - } - - var reporter = TraceReporter() - let graph = Graph() - - #expect(reporter.createdSubgraphCount == 0) - #expect(reporter.invalidateSubgraphCount == 0) - - let _ = withUnsafeMutablePointer(to: &trace) { tracePointer in - withUnsafeMutablePointer(to: &reporter) { reporterPointer in - graph.addTrace(tracePointer, context: reporterPointer) - } - } + + let testTrace = TestTrace() + testTrace.register(graph: graph) + + #expect(testTrace.events(of: .subgraphCreated).count == 0) + #expect(testTrace.events(of: .subgraphDestroy).count == 0) let subgraph = Subgraph(graph: graph) - #expect(reporter.createdSubgraphCount == 1) - #expect(reporter.invalidateSubgraphCount == 0) + #expect(testTrace.events(of: .subgraphCreated).count == 1) + #expect(testTrace.events(of: .subgraphDestroy).count == 0) subgraph.invalidate() - #expect(reporter.createdSubgraphCount == 1) - #expect(reporter.invalidateSubgraphCount == 1) + #expect(testTrace.events(of: .subgraphCreated).count == 1) + #expect(testTrace.events(of: .subgraphDestroy).count == 1) } } diff --git a/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift b/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift new file mode 100644 index 00000000..8acf086b --- /dev/null +++ b/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift @@ -0,0 +1,287 @@ +public class TestTrace { + public enum EventType: Hashable { + case beginTrace + case endTrace + + case beginSubgraphUpdate + case endSubgraphUpdate + case beginNodeUpdate + case endNodeUpdate + case beginValueUpdate + case endValueUpdate + case beginGraphUpdate + case endGraphUpdate + + case beginGraphInvalidation + case endGraphInvalidation + + case beginModifyNode + case endModifyNode + + case beginEvent + case endEvent + + case graphCreated + case graphDestroy + case graphNeedsUpdate + + case subgraphCreated + case subgraphDestroy + case subgraphAddChild + case subgraphRemoveChild + + case nodeAdded + case nodeAddEdge + case nodeRemoveEdge + case nodeSetEdgePending + + case nodeSetDirty + case nodeSetPending + case nodeSetValue + case nodeMarkValue + + case indirectNodeAdded + case indirectNodeSetSource + case indirectNodeSetDependency + + case profileMark + case customEvent + case namedEvent + case namedEventEnabled + + case setDeadline + case passedDeadline + + case compareFailed + } + + var trace: UnsafeMutablePointer + var handle: (Graph, UniqueID)? + + public func register(graph: Graph) { + guard handle == nil else { + return + } + let id = graph.addTrace(trace, context: Unmanaged.passRetained(self as AnyObject).toOpaque()) + self.handle = (graph, id) + } + + public func unregister() { + guard let handle else { + return + } + handle.0.removeTrace(traceID: handle.1) + } + + public struct Event: Hashable { + public var type: EventType + public var message: String + } + + public var events: [Event] = [] + + public func events(of type: EventType) -> [Event] { + events.filter { $0.type == type } + } + + func handleEvent(_ type: EventType, message: String) { + events.append(Event(type: type, message: message)) + } + + public init() { + self.trace = UnsafeMutablePointer.allocate(capacity: 1) + self.trace.initialize( + to: Graph.TraceType(version: .compareFailed) { ctx, graph in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent(.beginTrace, message: "graph = \(graph)") + } end_trace: { ctx, graph in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent(.endTrace, message: "graph = \(graph)") + + // Check this calls deinit + Unmanaged.fromOpaque(ctx!).release() + } begin_subgraph_update: { ctx, subgraph, options in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent( + .beginSubgraphUpdate, + message: "subgraph = \(subgraph), options = \(options)" + ) + } end_subgraph_update: { ctx, subgraph in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent(.endSubgraphUpdate, message: "subgraph = \(subgraph)") + } begin_node_update: { ctx, attribute in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent(.beginNodeUpdate, message: "attribute = \(attribute)") + } end_node_update: { ctx, attribute, changed in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent( + .endNodeUpdate, + message: "attribute = \(attribute), changed = \(changed)" + ) + } begin_value_update: { ctx, attribute in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent(.beginValueUpdate, message: "attribute = \(attribute)") + } end_value_update: { ctx, attribute, changed in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent( + .endValueUpdate, + message: "attribute = \(attribute), changed = \(changed)" + ) + } begin_graph_update: { ctx, graph in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent(.beginGraphUpdate, message: "graph = \(graph)") + } end_graph_update: { ctx, graph in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent(.endGraphUpdate, message: "graph = \(graph)") + } begin_graph_invalidation: { ctx, graph, attribute in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent( + .beginGraphInvalidation, + message: "graph = \(graph), attribute = \(attribute)" + ) + } end_graph_invalidation: { ctx, graph, attribute in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent( + .endGraphInvalidation, + message: "graph = \(graph), attribute = \(attribute)" + ) + } begin_modify_node: { ctx, attribute in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent(.beginModifyNode, message: "attribute = \(attribute)") + } end_modify_node: { ctx, attribute in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent(.endModifyNode, message: "attribute = \(attribute)") + } begin_event: { ctx, attribute, event_name in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent( + .beginEvent, + message: "attribute = \(attribute), event_name = \(String(cString: event_name))" + ) + } end_event: { ctx, attribute, event_name in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent( + .endEvent, + message: "attribute = \(attribute), event_name = \(String(cString: event_name))" + ) + } graph_created: { ctx, graph in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent(.graphCreated, message: "graph = \(graph)") + } graph_destroy: { ctx, graph in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent(.graphDestroy, message: "graph = \(graph)") + } graph_needs_update: { ctx, graph in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent(.graphNeedsUpdate, message: "graph = \(graph)") + } subgraph_created: { ctx, subgraph in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent(.subgraphCreated, message: "subgraph = \(subgraph)") + } subgraph_destroy: { ctx, subgraph in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent(.subgraphDestroy, message: "subgraph = \(subgraph)") + } subgraph_add_child: { ctx, subgraph, child_subgraph in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent( + .subgraphAddChild, + message: "subgraph = \(subgraph), child_subgraph = \(child_subgraph)" + ) + } subgraph_remove_child: { ctx, subgraph, child_subgraph in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent( + .subgraphRemoveChild, + message: "subgraph = \(subgraph), child_subgraph = \(child_subgraph)" + ) + } node_added: { ctx, attribute in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent(.nodeAdded, message: "attribute = \(attribute)") + } node_add_edge: { ctx, attribute, input, options in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent( + .nodeAddEdge, + message: "attribute = \(attribute), input = \(input), options = \(options)" + ) + } node_remove_edge: { ctx, attribute, input_index in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent( + .nodeRemoveEdge, + message: "attribute = \(attribute), input_index = \(input_index)" + ) + } node_set_edge_pending: { ctx, attribute, input_index, pending in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent( + .nodeSetEdgePending, + message: + "attribute = \(attribute), input_index = \(input_index), pending = \(pending)" + ) + } node_set_dirty: { ctx, attribute, dirty in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent( + .nodeSetDirty, + message: "attribute = \(attribute), dirty = \(dirty)" + ) + } node_set_pending: { ctx, attribute, pending in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent( + .nodeSetPending, + message: "attribute = \(attribute), pending = \(pending)" + ) + } node_set_value: { ctx, attribute, value in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent( + .nodeSetValue, + message: "attribute = \(attribute), value = \(value)" + ) + } node_mark_value: { ctx, attribute in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent(.nodeMarkValue, message: "attribute = \(attribute)") + } indirect_node_added: { ctx, attribute in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent(.indirectNodeAdded, message: "attribute = \(attribute)") + } indirect_node_set_source: { ctx, attribute, source in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent( + .indirectNodeSetSource, + message: "attribute = \(attribute), source = \(source)" + ) + } indirect_node_set_dependency: { ctx, attribute, dependency in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent( + .indirectNodeSetDependency, + message: "attribute = \(attribute), dependency = \(dependency)" + ) + } profile_mark: { ctx, event_name in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent(.profileMark, message: "event_name = \(String(cString: event_name))") + } custom_event: { ctx, graph, event_name, value, type in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent( + .customEvent, + message: + "graph = \(graph), event_name = \(String(cString: event_name)), value = \(value), type = \(type.type)" + ) + } named_event: { ctx, graph, event_id, event_arg_count, event_args, data, flags in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent(.namedEvent, message: "graph = \(graph), event_id = \(event_id)") + } named_event_enabled: { ctx, event_id in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent(.namedEventEnabled, message: "event_id = \(event_id)") + return true + } set_deadline: { ctx, deadline in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent(.setDeadline, message: "deadline = \(deadline)") + } passed_deadline: { ctx in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent(.passedDeadline, message: "") + } compare_failed: { ctx, attribute, comparison_state in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.handleEvent( + .compareFailed, + message: "attribute = \(attribute), comparison_state = \(comparison_state)" + ) + } + ) + } + + deinit { + self.trace.deallocate() + } +} From 9f72114229cd10ea64f6092f14033910125f14e1 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Wed, 5 Aug 2026 12:27:33 +0200 Subject: [PATCH 16/42] Implement Graph.addTraceEvent Swift refinements --- Sources/Compute/Graph/Graph.swift | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/Sources/Compute/Graph/Graph.swift b/Sources/Compute/Graph/Graph.swift index d8a3594f..ad269771 100644 --- a/Sources/Compute/Graph/Graph.swift +++ b/Sources/Compute/Graph/Graph.swift @@ -47,7 +47,6 @@ extension Graph { func IAGGraphSetOutputValue(_ value: UnsafeRawPointer, of type: Metadata) extension Graph { - @inline(__always) @inlinable public static func setOutputValue(_ value: UnsafePointer) { @@ -57,16 +56,13 @@ extension Graph { @_transparent @inline(__always) public var mainUpdates: Int { numericCast(counter(for: .mainThreadUpdates)) } - } extension Graph { - @_transparent public static func anyInputsChanged(excluding excludedAttributes: [AnyAttribute]) -> Bool { return __IAGGraphAnyInputsChanged(excludedAttributes, excludedAttributes.count) } - } @_silgen_name("IAGGraphSetUpdateCallback") @@ -89,7 +85,6 @@ func IAGGraphWithMainThreadHandler( ) extension Graph { - public func onUpdate(_ handler: @escaping () -> Void) { IAGGraphSetUpdateCallback(unsafeBitCast(self, to: UnsafeRawPointer.self), callback: handler) } @@ -128,11 +123,9 @@ extension Graph { mainThreadHandler: mainThreadHandler ) } - } extension Graph { - public static func startProfiling(_ graph: Graph?) { fatalError("not implemented") } @@ -149,23 +142,21 @@ extension Graph { public static func resetProfile() { fatalError("not implemented") } - } extension Graph { - - public func addTraceEvent(_ event: UnsafePointer, value: T) { - fatalError("not implemented") + public func addTraceEvent(_ event: UnsafePointer, value: Value) { + withUnsafePointer(to: value) { valuePointer in + self.addTraceEvent(name: event, value: valuePointer, type: Metadata(Value.self)) + } } - public func addTraceEvent(_ event: UnsafePointer, context: UnsafePointer) { - fatalError("not implemented") + public func addTraceEvent(_ event: UnsafePointer, context: UnsafePointer) { + self.addTraceEvent(name: event, value: context, type: Metadata(Value.self)) } - } extension Graph { - public func print(includeValues: Bool) { fatalError("not implemented") } @@ -185,5 +176,4 @@ extension Graph { public static func stackDescription(maxFrames: Int) -> String { fatalError("not implemented") } - } From c4ac7978bbf2b9af05a08cb33890fc22bfb5135f Mon Sep 17 00:00:00 2001 From: James Moschou Date: Wed, 5 Aug 2026 15:00:13 +0200 Subject: [PATCH 17/42] Guard against null objects in ExternalTrace --- Sources/ComputeCxx/Trace/ExternalTrace.cpp | 111 ++++++++++++--------- 1 file changed, 64 insertions(+), 47 deletions(-) diff --git a/Sources/ComputeCxx/Trace/ExternalTrace.cpp b/Sources/ComputeCxx/Trace/ExternalTrace.cpp index 6b866860..ab63eab0 100644 --- a/Sources/ComputeCxx/Trace/ExternalTrace.cpp +++ b/Sources/ComputeCxx/Trace/ExternalTrace.cpp @@ -24,16 +24,18 @@ void ExternalTrace::end_trace(const IAG::Graph &graph) { } void ExternalTrace::begin_update(const IAG::Subgraph &subgraph, uint32_t options) { - auto cf_subgraph = subgraph.to_cf(); - if (auto callback = _trace->begin_subgraph_update) { - callback(_context, cf_subgraph, options); + if (auto cf_subgraph = subgraph.to_cf()) { + if (auto callback = _trace->begin_subgraph_update) { + callback(_context, cf_subgraph, options); + } } } void ExternalTrace::end_update(const IAG::Subgraph &subgraph) { - auto cf_subgraph = subgraph.to_cf(); - if (auto callback = _trace->end_subgraph_update) { - callback(_context, cf_subgraph); + if (auto cf_subgraph = subgraph.to_cf()) { + if (auto callback = _trace->end_subgraph_update) { + callback(_context, cf_subgraph); + } } } @@ -64,30 +66,34 @@ void ExternalTrace::end_update(IAG::data::ptr node, bool changed) { } void ExternalTrace::begin_update(const IAG::Graph::Context &context) { - auto cf_context = context.to_cf(); - if (auto callback = _trace->begin_graph_update) { - callback(_context, cf_context); + if (auto cf_context = context.to_cf()) { + if (auto callback = _trace->begin_graph_update) { + callback(_context, cf_context); + } } } void ExternalTrace::end_update(const IAG::Graph::Context &context) { - auto cf_context = context.to_cf(); - if (auto callback = _trace->end_graph_update) { - callback(_context, cf_context); + if (auto cf_context = context.to_cf()) { + if (auto callback = _trace->end_graph_update) { + callback(_context, cf_context); + } } } void ExternalTrace::begin_invalidation(const IAG::Graph::Context &context, IAG::AttributeID attribute) { - auto cf_context = context.to_cf(); - if (auto callback = _trace->begin_graph_invalidation) { - callback(_context, cf_context, IAGAttribute(attribute)); + if (auto cf_context = context.to_cf()) { + if (auto callback = _trace->begin_graph_invalidation) { + callback(_context, cf_context, IAGAttribute(attribute)); + } } } void ExternalTrace::end_invalidation(const IAG::Graph::Context &context, IAG::AttributeID attribute) { - auto cf_context = context.to_cf(); - if (auto callback = _trace->end_graph_invalidation) { - callback(_context, cf_context, IAGAttribute(attribute)); + if (auto cf_context = context.to_cf()) { + if (auto callback = _trace->end_graph_invalidation) { + callback(_context, cf_context, IAGAttribute(attribute)); + } } } @@ -122,55 +128,64 @@ void ExternalTrace::end_event(IAG::data::ptr node, uint32_t event_id) } void ExternalTrace::created(const IAG::Graph::Context &context) { - auto cf_context = context.to_cf(); - if (auto callback = _trace->graph_created) { - callback(_context, cf_context); + if (auto cf_context = context.to_cf()) { + if (auto callback = _trace->graph_created) { + callback(_context, cf_context); + } } } void ExternalTrace::destroy(const IAG::Graph::Context &context) { - auto cf_context = context.to_cf(); - if (auto callback = _trace->graph_destroy) { - callback(_context, cf_context); + if (auto cf_context = context.to_cf()) { + if (auto callback = _trace->graph_destroy) { + callback(_context, cf_context); + } } } void ExternalTrace::needs_update(const IAG::Graph::Context &context) { - auto cf_context = context.to_cf(); - if (auto callback = _trace->graph_needs_update) { - callback(_context, cf_context); + if (auto cf_context = context.to_cf()) { + if (auto callback = _trace->graph_needs_update) { + callback(_context, cf_context); + } } } void ExternalTrace::created(const IAG::Subgraph &subgraph) { - auto cf_subgraph = subgraph.to_cf(); - if (auto callback = _trace->subgraph_created) { - callback(_context, cf_subgraph); + if (auto cf_subgraph = subgraph.to_cf()) { + if (auto callback = _trace->subgraph_created) { + callback(_context, cf_subgraph); + } } } void ExternalTrace::invalidate(const IAG::Subgraph &subgraph) { - auto cf_subgraph = subgraph.to_cf(); - if (auto callback = _trace->subgraph_destroy) { - callback(_context, cf_subgraph); + if (auto cf_subgraph = subgraph.to_cf()) { + if (auto callback = _trace->subgraph_destroy) { + callback(_context, cf_subgraph); + } } } void ExternalTrace::destroy(const IAG::Subgraph &subgraph) {} void ExternalTrace::add_child(const IAG::Subgraph &subgraph, const IAG::Subgraph &child) { - auto cf_subgraph = subgraph.to_cf(); - auto cf_child = subgraph.to_cf(); - if (auto callback = _trace->subgraph_add_child) { - callback(_context, cf_subgraph, cf_child); + if (auto cf_subgraph = subgraph.to_cf()) { + if (auto cf_child = child.to_cf()) { + if (auto callback = _trace->subgraph_add_child) { + callback(_context, cf_subgraph, cf_child); + } + } } } void ExternalTrace::remove_child(const IAG::Subgraph &subgraph, const IAG::Subgraph &child) { - auto cf_subgraph = subgraph.to_cf(); - auto cf_child = subgraph.to_cf(); - if (auto callback = _trace->subgraph_remove_child) { - callback(_context, cf_subgraph, cf_child); + if (auto cf_subgraph = subgraph.to_cf()) { + if (auto cf_child = child.to_cf()) { + if (auto callback = _trace->subgraph_remove_child) { + callback(_context, cf_subgraph, cf_child); + } + } } } @@ -258,9 +273,10 @@ void ExternalTrace::custom_event(const IAG::Graph::Context &context, const char if (_trace->version < IAGTraceTypeVersionCustom) { return; } - auto cf_context = context.to_cf(); - if (auto callback = _trace->custom_event) { - callback(_context, cf_context, event_name, value, IAGTypeID(&type)); + if (auto cf_context = context.to_cf()) { + if (auto callback = _trace->custom_event) { + callback(_context, cf_context, event_name, value, IAGTypeID(&type)); + } } } @@ -269,9 +285,10 @@ void ExternalTrace::named_event(const IAG::Graph::Context &context, IAGNamedTrac if (_trace->version < IAGTraceTypeVersionNamed) { return; } - auto cf_context = context.to_cf(); - if (auto callback = _trace->named_event) { - callback(_context, cf_context, event_id, event_arg_count, event_args, data, arg6); + if (auto cf_context = context.to_cf()) { + if (auto callback = _trace->named_event) { + callback(_context, cf_context, event_id, event_arg_count, event_args, data, arg6); + } } } From 73a0ce5ca83fa073ab8437aebad678f8d499d4d4 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Wed, 5 Aug 2026 16:20:32 +0200 Subject: [PATCH 18/42] Update .swiftinterface files in CompatibilityTesting --- .../arm64-apple-ios-macabi.swiftinterface | 4 ++-- .../arm64e-apple-ios-macabi.swiftinterface | 4 ++-- .../x86_64-apple-ios-macabi.swiftinterface | 4 ++-- .../arm64-apple-macos.swiftinterface | 4 ++-- .../arm64e-apple-macos.swiftinterface | 4 ++-- .../x86_64-apple-macos.swiftinterface | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/arm64-apple-ios-macabi.swiftinterface b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/arm64-apple-ios-macabi.swiftinterface index 58bfcd05..1ebba0f1 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/arm64-apple-ios-macabi.swiftinterface +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/arm64-apple-ios-macabi.swiftinterface @@ -579,8 +579,8 @@ extension AttributeGraph.Graph { public static func resetProfile() } extension AttributeGraph.Graph { - public func addTraceEvent(_ event: Swift.UnsafePointer, value: T) - public func addTraceEvent(_ event: Swift.UnsafePointer, context: Swift.UnsafePointer) + public func addTraceEvent(_ event: Swift.UnsafePointer, value: Value) + public func addTraceEvent(_ event: Swift.UnsafePointer, context: Swift.UnsafePointer) } extension AttributeGraph.Graph { public func print(includeValues: Swift.Bool) diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/arm64e-apple-ios-macabi.swiftinterface b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/arm64e-apple-ios-macabi.swiftinterface index f235eb72..a4275a23 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/arm64e-apple-ios-macabi.swiftinterface +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/arm64e-apple-ios-macabi.swiftinterface @@ -579,8 +579,8 @@ extension AttributeGraph.Graph { public static func resetProfile() } extension AttributeGraph.Graph { - public func addTraceEvent(_ event: Swift.UnsafePointer, value: T) - public func addTraceEvent(_ event: Swift.UnsafePointer, context: Swift.UnsafePointer) + public func addTraceEvent(_ event: Swift.UnsafePointer, value: Value) + public func addTraceEvent(_ event: Swift.UnsafePointer, context: Swift.UnsafePointer) } extension AttributeGraph.Graph { public func print(includeValues: Swift.Bool) diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/x86_64-apple-ios-macabi.swiftinterface b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/x86_64-apple-ios-macabi.swiftinterface index ae0b814d..7445f795 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/x86_64-apple-ios-macabi.swiftinterface +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/x86_64-apple-ios-macabi.swiftinterface @@ -579,8 +579,8 @@ extension AttributeGraph.Graph { public static func resetProfile() } extension AttributeGraph.Graph { - public func addTraceEvent(_ event: Swift.UnsafePointer, value: T) - public func addTraceEvent(_ event: Swift.UnsafePointer, context: Swift.UnsafePointer) + public func addTraceEvent(_ event: Swift.UnsafePointer, value: Value) + public func addTraceEvent(_ event: Swift.UnsafePointer, context: Swift.UnsafePointer) } extension AttributeGraph.Graph { public func print(includeValues: Swift.Bool) diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/arm64-apple-macos.swiftinterface b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/arm64-apple-macos.swiftinterface index ec134406..77db4243 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/arm64-apple-macos.swiftinterface +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/arm64-apple-macos.swiftinterface @@ -579,8 +579,8 @@ extension AttributeGraph.Graph { public static func resetProfile() } extension AttributeGraph.Graph { - public func addTraceEvent(_ event: Swift.UnsafePointer, value: T) - public func addTraceEvent(_ event: Swift.UnsafePointer, context: Swift.UnsafePointer) + public func addTraceEvent(_ event: Swift.UnsafePointer, value: Value) + public func addTraceEvent(_ event: Swift.UnsafePointer, context: Swift.UnsafePointer) } extension AttributeGraph.Graph { public func print(includeValues: Swift.Bool) diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/arm64e-apple-macos.swiftinterface b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/arm64e-apple-macos.swiftinterface index 655340ae..3ff6fe31 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/arm64e-apple-macos.swiftinterface +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/arm64e-apple-macos.swiftinterface @@ -579,8 +579,8 @@ extension AttributeGraph.Graph { public static func resetProfile() } extension AttributeGraph.Graph { - public func addTraceEvent(_ event: Swift.UnsafePointer, value: T) - public func addTraceEvent(_ event: Swift.UnsafePointer, context: Swift.UnsafePointer) + public func addTraceEvent(_ event: Swift.UnsafePointer, value: Value) + public func addTraceEvent(_ event: Swift.UnsafePointer, context: Swift.UnsafePointer) } extension AttributeGraph.Graph { public func print(includeValues: Swift.Bool) diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/x86_64-apple-macos.swiftinterface b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/x86_64-apple-macos.swiftinterface index 7d8f6905..0316b771 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/x86_64-apple-macos.swiftinterface +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/x86_64-apple-macos.swiftinterface @@ -579,8 +579,8 @@ extension AttributeGraph.Graph { public static func resetProfile() } extension AttributeGraph.Graph { - public func addTraceEvent(_ event: Swift.UnsafePointer, value: T) - public func addTraceEvent(_ event: Swift.UnsafePointer, context: Swift.UnsafePointer) + public func addTraceEvent(_ event: Swift.UnsafePointer, value: Value) + public func addTraceEvent(_ event: Swift.UnsafePointer, context: Swift.UnsafePointer) } extension AttributeGraph.Graph { public func print(includeValues: Swift.Bool) From bc1e7fffa2352f8a95c2c88930266e8a3d2de1c0 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Wed, 5 Aug 2026 16:21:46 +0200 Subject: [PATCH 19/42] Add Swift refinement for IAGInputOptions --- .../AttributeGraph.framework/Headers/AGInputOptions.h | 2 +- .../arm64-apple-ios-macabi.swiftinterface | 8 ++++---- .../arm64e-apple-ios-macabi.swiftinterface | 8 ++++---- .../x86_64-apple-ios-macabi.swiftinterface | 8 ++++---- .../Versions/A/Headers/AGInputOptions.h | 2 +- .../arm64-apple-macos.swiftinterface | 8 ++++---- .../arm64e-apple-macos.swiftinterface | 8 ++++---- .../x86_64-apple-macos.swiftinterface | 8 ++++---- Sources/Compute/Attribute/AnyAttribute.swift | 4 ++-- Sources/Compute/Attribute/Attribute.swift | 4 ++-- Sources/ComputeCxx/include/ComputeCxx/IAGInputOptions.h | 2 +- 11 files changed, 31 insertions(+), 31 deletions(-) diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGInputOptions.h b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGInputOptions.h index 04f74904..673a7ec2 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGInputOptions.h +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGInputOptions.h @@ -13,7 +13,7 @@ typedef AG_OPTIONS(uint8_t, AGInputOptions) { AGInputOptionsAlwaysEnabled = 1 << 2, AGInputOptionsChanged = 1 << 3, AGInputOptionsEnabled = 1 << 4, -}; +} AG_SWIFT_NAME(InputOptions); AG_EXTERN_C_END diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/arm64-apple-ios-macabi.swiftinterface b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/arm64-apple-ios-macabi.swiftinterface index 1ebba0f1..bb4dd4e6 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/arm64-apple-ios-macabi.swiftinterface +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/arm64-apple-ios-macabi.swiftinterface @@ -17,8 +17,8 @@ extension AttributeGraph.AnyAttribute { public func visitBody(_ visitor: inout Visitor) where Visitor : AttributeGraph.AttributeBodyVisitor public func mutateBody(as type: Body.Type, invalidating: Swift.Bool, _ mutator: (inout Body) -> Swift.Void) public func setFlags(_ newFlags: AttributeGraph.Subgraph.Flags, mask: AttributeGraph.Subgraph.Flags) - public func addInput(_ input: AttributeGraph.AnyAttribute, options: AttributeGraph.AGInputOptions, token: Swift.Int) - public func addInput(_ input: AttributeGraph.Attribute, options: AttributeGraph.AGInputOptions, token: Swift.Int) + public func addInput(_ input: AttributeGraph.AnyAttribute, options: AttributeGraph.InputOptions, token: Swift.Int) + public func addInput(_ input: AttributeGraph.Attribute, options: AttributeGraph.InputOptions, token: Swift.Int) public func unsafeOffset(at offset: Swift.Int) -> AttributeGraph.AnyAttribute public var indirectDependency: AttributeGraph.AnyAttribute? { get @@ -71,8 +71,8 @@ extension AttributeGraph.AnyAttribute { public func setFlags(_ newFlags: AttributeGraph.Subgraph.Flags, mask: AttributeGraph.Subgraph.Flags) public func applying(offset: AttributeGraph.PointerOffset) -> AttributeGraph.Attribute public func mutateBody(as bodyType: Body.Type, invalidating: Swift.Bool, _ mutator: (inout Body) -> Swift.Void) - public func addInput(_ input: AttributeGraph.Attribute, options: AttributeGraph.AGInputOptions, token: Swift.Int) - public func addInput(_ input: AttributeGraph.AnyAttribute, options: AttributeGraph.AGInputOptions, token: Swift.Int) + public func addInput(_ input: AttributeGraph.Attribute, options: AttributeGraph.InputOptions, token: Swift.Int) + public func addInput(_ input: AttributeGraph.AnyAttribute, options: AttributeGraph.InputOptions, token: Swift.Int) public func breadthFirstSearch(options: AttributeGraph.SearchOptions, _ predicate: (AttributeGraph.AnyAttribute) -> Swift.Bool) -> Swift.Bool public func validate() public var value: Value { diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/arm64e-apple-ios-macabi.swiftinterface b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/arm64e-apple-ios-macabi.swiftinterface index a4275a23..6d0cac33 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/arm64e-apple-ios-macabi.swiftinterface +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/arm64e-apple-ios-macabi.swiftinterface @@ -17,8 +17,8 @@ extension AttributeGraph.AnyAttribute { public func visitBody(_ visitor: inout Visitor) where Visitor : AttributeGraph.AttributeBodyVisitor public func mutateBody(as type: Body.Type, invalidating: Swift.Bool, _ mutator: (inout Body) -> Swift.Void) public func setFlags(_ newFlags: AttributeGraph.Subgraph.Flags, mask: AttributeGraph.Subgraph.Flags) - public func addInput(_ input: AttributeGraph.AnyAttribute, options: AttributeGraph.AGInputOptions, token: Swift.Int) - public func addInput(_ input: AttributeGraph.Attribute, options: AttributeGraph.AGInputOptions, token: Swift.Int) + public func addInput(_ input: AttributeGraph.AnyAttribute, options: AttributeGraph.InputOptions, token: Swift.Int) + public func addInput(_ input: AttributeGraph.Attribute, options: AttributeGraph.InputOptions, token: Swift.Int) public func unsafeOffset(at offset: Swift.Int) -> AttributeGraph.AnyAttribute public var indirectDependency: AttributeGraph.AnyAttribute? { get @@ -71,8 +71,8 @@ extension AttributeGraph.AnyAttribute { public func setFlags(_ newFlags: AttributeGraph.Subgraph.Flags, mask: AttributeGraph.Subgraph.Flags) public func applying(offset: AttributeGraph.PointerOffset) -> AttributeGraph.Attribute public func mutateBody(as bodyType: Body.Type, invalidating: Swift.Bool, _ mutator: (inout Body) -> Swift.Void) - public func addInput(_ input: AttributeGraph.Attribute, options: AttributeGraph.AGInputOptions, token: Swift.Int) - public func addInput(_ input: AttributeGraph.AnyAttribute, options: AttributeGraph.AGInputOptions, token: Swift.Int) + public func addInput(_ input: AttributeGraph.Attribute, options: AttributeGraph.InputOptions, token: Swift.Int) + public func addInput(_ input: AttributeGraph.AnyAttribute, options: AttributeGraph.InputOptions, token: Swift.Int) public func breadthFirstSearch(options: AttributeGraph.SearchOptions, _ predicate: (AttributeGraph.AnyAttribute) -> Swift.Bool) -> Swift.Bool public func validate() public var value: Value { diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/x86_64-apple-ios-macabi.swiftinterface b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/x86_64-apple-ios-macabi.swiftinterface index 7445f795..9a7a1484 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/x86_64-apple-ios-macabi.swiftinterface +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/x86_64-apple-ios-macabi.swiftinterface @@ -17,8 +17,8 @@ extension AttributeGraph.AnyAttribute { public func visitBody(_ visitor: inout Visitor) where Visitor : AttributeGraph.AttributeBodyVisitor public func mutateBody(as type: Body.Type, invalidating: Swift.Bool, _ mutator: (inout Body) -> Swift.Void) public func setFlags(_ newFlags: AttributeGraph.Subgraph.Flags, mask: AttributeGraph.Subgraph.Flags) - public func addInput(_ input: AttributeGraph.AnyAttribute, options: AttributeGraph.AGInputOptions, token: Swift.Int) - public func addInput(_ input: AttributeGraph.Attribute, options: AttributeGraph.AGInputOptions, token: Swift.Int) + public func addInput(_ input: AttributeGraph.AnyAttribute, options: AttributeGraph.InputOptions, token: Swift.Int) + public func addInput(_ input: AttributeGraph.Attribute, options: AttributeGraph.InputOptions, token: Swift.Int) public func unsafeOffset(at offset: Swift.Int) -> AttributeGraph.AnyAttribute public var indirectDependency: AttributeGraph.AnyAttribute? { get @@ -71,8 +71,8 @@ extension AttributeGraph.AnyAttribute { public func setFlags(_ newFlags: AttributeGraph.Subgraph.Flags, mask: AttributeGraph.Subgraph.Flags) public func applying(offset: AttributeGraph.PointerOffset) -> AttributeGraph.Attribute public func mutateBody(as bodyType: Body.Type, invalidating: Swift.Bool, _ mutator: (inout Body) -> Swift.Void) - public func addInput(_ input: AttributeGraph.Attribute, options: AttributeGraph.AGInputOptions, token: Swift.Int) - public func addInput(_ input: AttributeGraph.AnyAttribute, options: AttributeGraph.AGInputOptions, token: Swift.Int) + public func addInput(_ input: AttributeGraph.Attribute, options: AttributeGraph.InputOptions, token: Swift.Int) + public func addInput(_ input: AttributeGraph.AnyAttribute, options: AttributeGraph.InputOptions, token: Swift.Int) public func breadthFirstSearch(options: AttributeGraph.SearchOptions, _ predicate: (AttributeGraph.AnyAttribute) -> Swift.Bool) -> Swift.Bool public func validate() public var value: Value { diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGInputOptions.h b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGInputOptions.h index 04f74904..673a7ec2 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGInputOptions.h +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGInputOptions.h @@ -13,7 +13,7 @@ typedef AG_OPTIONS(uint8_t, AGInputOptions) { AGInputOptionsAlwaysEnabled = 1 << 2, AGInputOptionsChanged = 1 << 3, AGInputOptionsEnabled = 1 << 4, -}; +} AG_SWIFT_NAME(InputOptions); AG_EXTERN_C_END diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/arm64-apple-macos.swiftinterface b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/arm64-apple-macos.swiftinterface index 77db4243..59504c56 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/arm64-apple-macos.swiftinterface +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/arm64-apple-macos.swiftinterface @@ -17,8 +17,8 @@ extension AttributeGraph.AnyAttribute { public func visitBody(_ visitor: inout Visitor) where Visitor : AttributeGraph.AttributeBodyVisitor public func mutateBody(as type: Body.Type, invalidating: Swift.Bool, _ mutator: (inout Body) -> Swift.Void) public func setFlags(_ newFlags: AttributeGraph.Subgraph.Flags, mask: AttributeGraph.Subgraph.Flags) - public func addInput(_ input: AttributeGraph.AnyAttribute, options: AttributeGraph.AGInputOptions, token: Swift.Int) - public func addInput(_ input: AttributeGraph.Attribute, options: AttributeGraph.AGInputOptions, token: Swift.Int) + public func addInput(_ input: AttributeGraph.AnyAttribute, options: AttributeGraph.InputOptions, token: Swift.Int) + public func addInput(_ input: AttributeGraph.Attribute, options: AttributeGraph.InputOptions, token: Swift.Int) public func unsafeOffset(at offset: Swift.Int) -> AttributeGraph.AnyAttribute public var indirectDependency: AttributeGraph.AnyAttribute? { get @@ -71,8 +71,8 @@ extension AttributeGraph.AnyAttribute { public func setFlags(_ newFlags: AttributeGraph.Subgraph.Flags, mask: AttributeGraph.Subgraph.Flags) public func applying(offset: AttributeGraph.PointerOffset) -> AttributeGraph.Attribute public func mutateBody(as bodyType: Body.Type, invalidating: Swift.Bool, _ mutator: (inout Body) -> Swift.Void) - public func addInput(_ input: AttributeGraph.Attribute, options: AttributeGraph.AGInputOptions, token: Swift.Int) - public func addInput(_ input: AttributeGraph.AnyAttribute, options: AttributeGraph.AGInputOptions, token: Swift.Int) + public func addInput(_ input: AttributeGraph.Attribute, options: AttributeGraph.InputOptions, token: Swift.Int) + public func addInput(_ input: AttributeGraph.AnyAttribute, options: AttributeGraph.InputOptions, token: Swift.Int) public func breadthFirstSearch(options: AttributeGraph.SearchOptions, _ predicate: (AttributeGraph.AnyAttribute) -> Swift.Bool) -> Swift.Bool public func validate() public var value: Value { diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/arm64e-apple-macos.swiftinterface b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/arm64e-apple-macos.swiftinterface index 3ff6fe31..d26be768 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/arm64e-apple-macos.swiftinterface +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/arm64e-apple-macos.swiftinterface @@ -17,8 +17,8 @@ extension AttributeGraph.AnyAttribute { public func visitBody(_ visitor: inout Visitor) where Visitor : AttributeGraph.AttributeBodyVisitor public func mutateBody(as type: Body.Type, invalidating: Swift.Bool, _ mutator: (inout Body) -> Swift.Void) public func setFlags(_ newFlags: AttributeGraph.Subgraph.Flags, mask: AttributeGraph.Subgraph.Flags) - public func addInput(_ input: AttributeGraph.AnyAttribute, options: AttributeGraph.AGInputOptions, token: Swift.Int) - public func addInput(_ input: AttributeGraph.Attribute, options: AttributeGraph.AGInputOptions, token: Swift.Int) + public func addInput(_ input: AttributeGraph.AnyAttribute, options: AttributeGraph.InputOptions, token: Swift.Int) + public func addInput(_ input: AttributeGraph.Attribute, options: AttributeGraph.InputOptions, token: Swift.Int) public func unsafeOffset(at offset: Swift.Int) -> AttributeGraph.AnyAttribute public var indirectDependency: AttributeGraph.AnyAttribute? { get @@ -71,8 +71,8 @@ extension AttributeGraph.AnyAttribute { public func setFlags(_ newFlags: AttributeGraph.Subgraph.Flags, mask: AttributeGraph.Subgraph.Flags) public func applying(offset: AttributeGraph.PointerOffset) -> AttributeGraph.Attribute public func mutateBody(as bodyType: Body.Type, invalidating: Swift.Bool, _ mutator: (inout Body) -> Swift.Void) - public func addInput(_ input: AttributeGraph.Attribute, options: AttributeGraph.AGInputOptions, token: Swift.Int) - public func addInput(_ input: AttributeGraph.AnyAttribute, options: AttributeGraph.AGInputOptions, token: Swift.Int) + public func addInput(_ input: AttributeGraph.Attribute, options: AttributeGraph.InputOptions, token: Swift.Int) + public func addInput(_ input: AttributeGraph.AnyAttribute, options: AttributeGraph.InputOptions, token: Swift.Int) public func breadthFirstSearch(options: AttributeGraph.SearchOptions, _ predicate: (AttributeGraph.AnyAttribute) -> Swift.Bool) -> Swift.Bool public func validate() public var value: Value { diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/x86_64-apple-macos.swiftinterface b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/x86_64-apple-macos.swiftinterface index 0316b771..f163b7b8 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/x86_64-apple-macos.swiftinterface +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/x86_64-apple-macos.swiftinterface @@ -17,8 +17,8 @@ extension AttributeGraph.AnyAttribute { public func visitBody(_ visitor: inout Visitor) where Visitor : AttributeGraph.AttributeBodyVisitor public func mutateBody(as type: Body.Type, invalidating: Swift.Bool, _ mutator: (inout Body) -> Swift.Void) public func setFlags(_ newFlags: AttributeGraph.Subgraph.Flags, mask: AttributeGraph.Subgraph.Flags) - public func addInput(_ input: AttributeGraph.AnyAttribute, options: AttributeGraph.AGInputOptions, token: Swift.Int) - public func addInput(_ input: AttributeGraph.Attribute, options: AttributeGraph.AGInputOptions, token: Swift.Int) + public func addInput(_ input: AttributeGraph.AnyAttribute, options: AttributeGraph.InputOptions, token: Swift.Int) + public func addInput(_ input: AttributeGraph.Attribute, options: AttributeGraph.InputOptions, token: Swift.Int) public func unsafeOffset(at offset: Swift.Int) -> AttributeGraph.AnyAttribute public var indirectDependency: AttributeGraph.AnyAttribute? { get @@ -71,8 +71,8 @@ extension AttributeGraph.AnyAttribute { public func setFlags(_ newFlags: AttributeGraph.Subgraph.Flags, mask: AttributeGraph.Subgraph.Flags) public func applying(offset: AttributeGraph.PointerOffset) -> AttributeGraph.Attribute public func mutateBody(as bodyType: Body.Type, invalidating: Swift.Bool, _ mutator: (inout Body) -> Swift.Void) - public func addInput(_ input: AttributeGraph.Attribute, options: AttributeGraph.AGInputOptions, token: Swift.Int) - public func addInput(_ input: AttributeGraph.AnyAttribute, options: AttributeGraph.AGInputOptions, token: Swift.Int) + public func addInput(_ input: AttributeGraph.Attribute, options: AttributeGraph.InputOptions, token: Swift.Int) + public func addInput(_ input: AttributeGraph.AnyAttribute, options: AttributeGraph.InputOptions, token: Swift.Int) public func breadthFirstSearch(options: AttributeGraph.SearchOptions, _ predicate: (AttributeGraph.AnyAttribute) -> Swift.Bool) -> Swift.Bool public func validate() public var value: Value { diff --git a/Sources/Compute/Attribute/AnyAttribute.swift b/Sources/Compute/Attribute/AnyAttribute.swift index 6b6677d3..2f68e170 100644 --- a/Sources/Compute/Attribute/AnyAttribute.swift +++ b/Sources/Compute/Attribute/AnyAttribute.swift @@ -54,11 +54,11 @@ extension AnyAttribute { flags = flags.subtracting(mask).union(newFlags.intersection(mask)) } - public func addInput(_ input: AnyAttribute, options: IAGInputOptions, token: Int) { + public func addInput(_ input: AnyAttribute, options: InputOptions, token: Int) { addInput(input, options: options) } - public func addInput(_ input: Attribute, options: IAGInputOptions, token: Int) { + public func addInput(_ input: Attribute, options: InputOptions, token: Int) { addInput(input.identifier, options: options, token: token) } diff --git a/Sources/Compute/Attribute/Attribute.swift b/Sources/Compute/Attribute/Attribute.swift index 2fcc162a..e96fa147 100644 --- a/Sources/Compute/Attribute/Attribute.swift +++ b/Sources/Compute/Attribute/Attribute.swift @@ -90,11 +90,11 @@ public struct Attribute { identifier.mutateBody(as: bodyType, invalidating: invalidating, mutator) } - public func addInput(_ input: Attribute, options: IAGInputOptions, token: Int) { + public func addInput(_ input: Attribute, options: InputOptions, token: Int) { identifier.addInput(input, options: options, token: token) } - public func addInput(_ input: AnyAttribute, options: IAGInputOptions, token: Int) { + public func addInput(_ input: AnyAttribute, options: InputOptions, token: Int) { identifier.addInput(input, options: options, token: token) } diff --git a/Sources/ComputeCxx/include/ComputeCxx/IAGInputOptions.h b/Sources/ComputeCxx/include/ComputeCxx/IAGInputOptions.h index 87d893ce..fce05d38 100644 --- a/Sources/ComputeCxx/include/ComputeCxx/IAGInputOptions.h +++ b/Sources/ComputeCxx/include/ComputeCxx/IAGInputOptions.h @@ -13,7 +13,7 @@ typedef IAG_OPTIONS(uint8_t, IAGInputOptions) { IAGInputOptionsAlwaysEnabled = 1 << 2, IAGInputOptionsChanged = 1 << 3, IAGInputOptionsEnabled = 1 << 4, -}; +} IAG_SWIFT_NAME(InputOptions); IAG_EXTERN_C_END From b2ea42ace84ed0972eac5ff8b04fe9b15bbdce62 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Wed, 5 Aug 2026 17:44:05 +0200 Subject: [PATCH 20/42] Add separate SubgraphLifecycleTests --- .../Shared/Graph/GraphDeadlineTests.swift | 42 +- .../Subgraph/SubgraphLifecycleTests.swift | 202 ++++++++ .../Shared/Subgraph/SubgraphTests.swift | 54 -- .../Shared/{ => TestSupport}/GraphHost.swift | 0 .../Shared/TestSupport/TestTrace.swift | 285 ++++------- .../TestSupport/TestTraceRecorder.swift | 461 ++++++++++++++++++ 6 files changed, 793 insertions(+), 251 deletions(-) create mode 100644 Tests/ComputeTests/Shared/Subgraph/SubgraphLifecycleTests.swift rename Tests/ComputeTests/Shared/{ => TestSupport}/GraphHost.swift (100%) create mode 100644 Tests/ComputeTests/Shared/TestSupport/TestTraceRecorder.swift diff --git a/Tests/ComputeTests/Shared/Graph/GraphDeadlineTests.swift b/Tests/ComputeTests/Shared/Graph/GraphDeadlineTests.swift index 0cfe4f64..21f81d16 100644 --- a/Tests/ComputeTests/Shared/Graph/GraphDeadlineTests.swift +++ b/Tests/ComputeTests/Shared/Graph/GraphDeadlineTests.swift @@ -6,38 +6,42 @@ struct GraphDeadlineTests { @Test func withDeadline() { let graph = Graph() - - let trace = TestTrace() - trace.register(graph: graph) - + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + #expect(graph.deadline == UInt64.max) graph.withDeadline(0) { #expect(graph.deadline == 0) } #expect(graph.deadline == UInt64.max) - - let setDeadlineEvents = trace.events(of: .setDeadline) - #expect(setDeadlineEvents.count == 2) - #expect(setDeadlineEvents[0].message == "deadline = 0") - #expect(setDeadlineEvents[1].message == "deadline = \(UInt64.max)") + + let entries: [TestTraceRecorder.History.SetDeadlineEntry] = recorder.history.entries.compactMap { event in + guard case .setDeadline(let entry) = event else { return nil } + return entry + } + #expect(entries.count == 2) + #expect(entries[0].deadline == 0) + #expect(entries[1].deadline == UInt.max) } - + @Test func withDeadlineNonzero() { let graph = Graph() - - let trace = TestTrace() - trace.register(graph: graph) - + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + #expect(graph.deadline == UInt64.max) graph.withDeadline(100) { #expect(graph.deadline == 100) } #expect(graph.deadline == UInt64.max) - - let setDeadlineEvents = trace.events(of: .setDeadline) - #expect(setDeadlineEvents.count == 2) - #expect(setDeadlineEvents[0].message == "deadline = 100") - #expect(setDeadlineEvents[1].message == "deadline = \(UInt64.max)") + + let entries: [TestTraceRecorder.History.SetDeadlineEntry] = recorder.history.entries.compactMap { event in + guard case .setDeadline(let entry) = event else { return nil } + return entry + } + #expect(entries.count == 2) + #expect(entries[0].deadline == 100) + #expect(entries[1].deadline == UInt.max) } } diff --git a/Tests/ComputeTests/Shared/Subgraph/SubgraphLifecycleTests.swift b/Tests/ComputeTests/Shared/Subgraph/SubgraphLifecycleTests.swift new file mode 100644 index 00000000..b3c9146c --- /dev/null +++ b/Tests/ComputeTests/Shared/Subgraph/SubgraphLifecycleTests.swift @@ -0,0 +1,202 @@ +import Testing + +@Suite +struct SubgraphLifecycleTests { + @Suite + struct SubgraphCreatedTests { + @Test + func addedToGraph() { + let graph = Graph() + let subgraph = Subgraph(graph: graph) + #expect(subgraph.graph == graph) + } + + @Test + func graphCountersIncremented() { + let graph = Graph() + + #expect(graph.counter(for: .subgraphs) == 0) + #expect(graph.counter(for: .createdSubgraphs) == 0) + + let subgraph = Subgraph(graph: graph) + withExtendedLifetime(subgraph) { + #expect(graph.counter(for: .subgraphs) == 1) + #expect(graph.counter(for: .createdSubgraphs) == 1) + } + } + + @Test + func traceSubgraphCreatedCalled() { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + #expect(recorder.history.subgraphCreatedEntries.count == 0) + + let subgraph = Subgraph(graph: graph) + + let subgraphCreatedEntries = recorder.history.subgraphCreatedEntries + #expect(subgraphCreatedEntries.count == 1) + #expect(subgraphCreatedEntries[0].subgraph == subgraph) + } + } + + @Suite + struct SubgraphInvalidateTests { + @Test + func removedFromGraph() async { + await #expect(processExitsWith: .failure) { + let graph = Graph() + let subgraph = Subgraph(graph: graph) + + subgraph.invalidate() + + print(subgraph.graph) // will crash + } + } + + @Test + func graphCountersDecrementedOnSubgraphInvalidate() { + let graph = Graph() + let subgraph = Subgraph(graph: graph) + + #expect(graph.counter(for: .subgraphs) == 1) + #expect(graph.counter(for: .createdSubgraphs) == 1) + + subgraph.invalidate() + withExtendedLifetime(subgraph) { + #expect(graph.counter(for: .subgraphs) == 0) + #expect(graph.counter(for: .createdSubgraphs) == 1) + } + } + + @Test + func traceSubgraphDestroyCalledForOnSubgraphInvalidate() { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + + #expect(recorder.history.subgraphDestroyEntries.count == 0) + + subgraph.invalidate() + + let subgraphDestroyEntries = recorder.history.subgraphDestroyEntries + #expect(subgraphDestroyEntries.count == 1) + #expect(subgraphDestroyEntries[0].subgraph == subgraph) + } + } + + @Suite + struct SubgraphDestroyTests { + // This really just tests that Subgraph.deinit was triggered + @Test + func removedFromGraph() async { + let graph = Graph() + weak var weakSubgraph: Subgraph? = nil + autoreleasepool { + let subgraph = Subgraph(graph: graph) + weakSubgraph = subgraph + } + #expect(weakSubgraph == nil) + } + + @Test + func graphCountersDecrementedOnSubgraphDeinit() { + let graph = Graph() + + #expect(graph.counter(for: .subgraphs) == 0) + #expect(graph.counter(for: .createdSubgraphs) == 0) + + autoreleasepool { + let subgraph = Subgraph(graph: graph) + withExtendedLifetime(subgraph) { + #expect(graph.counter(for: .subgraphs) == 1) + #expect(graph.counter(for: .createdSubgraphs) == 1) + } + } + + #expect(graph.counter(for: .subgraphs) == 0) + #expect(graph.counter(for: .createdSubgraphs) == 1) + } + + @Test + func traceSubgraphDestroyNotCalledOnSubgraphDeinit() { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + #expect(recorder.history.subgraphDestroyEntries.count == 0) + + autoreleasepool { + let _ = Subgraph(graph: graph) + } + + let subgraphDestroyEntries = recorder.history.subgraphDestroyEntries + #expect(subgraphDestroyEntries.count == 0) // Not called when Subgraph deinit is called + } + } + + @Suite + struct SubgraphAddChildTests { + @Test + func traceSubgraphAddChildCalled() { + class SubgraphTrace: TestTraceRecorder { + override func subgraphAddChild(subgraph: Subgraph, childSubgraph: Subgraph) { + super.subgraphAddChild(subgraph: subgraph, childSubgraph: childSubgraph) + + #expect(subgraph.childCount == 0) // "will" semantics + #expect(childSubgraph.parentCount == 0) // "will" semantics + } + } + + let graph = Graph() + let recorder = SubgraphTrace() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let childSubgraph = Subgraph(graph: graph) + subgraph.addChild(childSubgraph) + + #expect(subgraph.childCount == 1) + #expect(childSubgraph.parentCount == 1) + + let subgraphAddChildEntries = recorder.history.subgraphAddChildEntries + #expect(subgraphAddChildEntries.count == 1) + #expect(subgraphAddChildEntries[0].subgraph == subgraph) + #expect(subgraphAddChildEntries[0].childSubgraph == childSubgraph) + } + } + + @Suite + struct SubgraphRemoveChildTests { + @Test + func traceSubgraphRemoveChildCalled() { + class SubgraphTrace: TestTraceRecorder { + override func subgraphRemoveChild(subgraph: Subgraph, childSubgraph: Subgraph) { + super.subgraphRemoveChild(subgraph: subgraph, childSubgraph: childSubgraph) + + #expect(subgraph.childCount == 1) // "will" semantics + #expect(childSubgraph.parentCount == 0) // "did" semantics + } + } + + let graph = Graph() + let recorder = SubgraphTrace() + recorder.install(graph: graph) + + #expect(recorder.history.subgraphRemoveChildEntries.count == 0) + + let subgraph = Subgraph(graph: graph) + let childSubgraph = Subgraph(graph: graph) + subgraph.addChild(childSubgraph) + subgraph.removeChild(childSubgraph) + + let subgraphRemoveChildEntries = recorder.history.subgraphRemoveChildEntries + #expect(subgraphRemoveChildEntries.count == 1) + #expect(subgraphRemoveChildEntries[0].subgraph == subgraph) + #expect(subgraphRemoveChildEntries[0].childSubgraph == childSubgraph) + } + } +} diff --git a/Tests/ComputeTests/Shared/Subgraph/SubgraphTests.swift b/Tests/ComputeTests/Shared/Subgraph/SubgraphTests.swift index a1739c21..72871fc9 100644 --- a/Tests/ComputeTests/Shared/Subgraph/SubgraphTests.swift +++ b/Tests/ComputeTests/Shared/Subgraph/SubgraphTests.swift @@ -28,36 +28,6 @@ struct SubgraphTests { } } - @Suite - struct LifecycleTests { - @Test - func subgraphAddedToGraph() { - let graph = Graph() - - let subgraph = Subgraph(graph: graph) - #expect(subgraph.graph == graph) - } - - @Test - func subgraphCounters() { - let graph = Graph() - - #expect(graph.counter(for: .subgraphs) == 0) - #expect(graph.counter(for: .createdSubgraphs) == 0) - - autoreleasepool { - let subgraph = Subgraph(graph: graph) - #expect(subgraph.graph == graph) - - #expect(graph.counter(for: .subgraphs) == 1) - #expect(graph.counter(for: .createdSubgraphs) == 1) - } - - #expect(graph.counter(for: .subgraphs) == 0) - #expect(graph.counter(for: .createdSubgraphs) == 1) - } - } - @Suite struct ObserverTests { @Test @@ -180,30 +150,6 @@ struct SubgraphTests { } } - @Suite - struct TraceTests { - @Test - func invalidateSubgraph() async throws { - let graph = Graph() - - let testTrace = TestTrace() - testTrace.register(graph: graph) - - #expect(testTrace.events(of: .subgraphCreated).count == 0) - #expect(testTrace.events(of: .subgraphDestroy).count == 0) - - let subgraph = Subgraph(graph: graph) - - #expect(testTrace.events(of: .subgraphCreated).count == 1) - #expect(testTrace.events(of: .subgraphDestroy).count == 0) - - subgraph.invalidate() - - #expect(testTrace.events(of: .subgraphCreated).count == 1) - #expect(testTrace.events(of: .subgraphDestroy).count == 1) - } - } - @Suite struct CurrentSubgraph { @Test diff --git a/Tests/ComputeTests/Shared/GraphHost.swift b/Tests/ComputeTests/Shared/TestSupport/GraphHost.swift similarity index 100% rename from Tests/ComputeTests/Shared/GraphHost.swift rename to Tests/ComputeTests/Shared/TestSupport/GraphHost.swift diff --git a/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift b/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift index 8acf086b..328f2f37 100644 --- a/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift +++ b/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift @@ -1,91 +1,21 @@ public class TestTrace { - public enum EventType: Hashable { - case beginTrace - case endTrace - - case beginSubgraphUpdate - case endSubgraphUpdate - case beginNodeUpdate - case endNodeUpdate - case beginValueUpdate - case endValueUpdate - case beginGraphUpdate - case endGraphUpdate - - case beginGraphInvalidation - case endGraphInvalidation - - case beginModifyNode - case endModifyNode - - case beginEvent - case endEvent - - case graphCreated - case graphDestroy - case graphNeedsUpdate - - case subgraphCreated - case subgraphDestroy - case subgraphAddChild - case subgraphRemoveChild - - case nodeAdded - case nodeAddEdge - case nodeRemoveEdge - case nodeSetEdgePending - - case nodeSetDirty - case nodeSetPending - case nodeSetValue - case nodeMarkValue - - case indirectNodeAdded - case indirectNodeSetSource - case indirectNodeSetDependency - - case profileMark - case customEvent - case namedEvent - case namedEventEnabled - - case setDeadline - case passedDeadline - - case compareFailed - } - var trace: UnsafeMutablePointer var handle: (Graph, UniqueID)? - public func register(graph: Graph) { + public func install(graph: Graph) { guard handle == nil else { return } - let id = graph.addTrace(trace, context: Unmanaged.passRetained(self as AnyObject).toOpaque()) - self.handle = (graph, id) + let traceID = graph.addTrace(trace, context: Unmanaged.passRetained(self as AnyObject).toOpaque()) + self.handle = (graph, traceID) } - public func unregister() { - guard let handle else { + public func uninstall() { + guard let (graph, traceID) = handle else { return } - handle.0.removeTrace(traceID: handle.1) - } - - public struct Event: Hashable { - public var type: EventType - public var message: String - } - - public var events: [Event] = [] - - public func events(of type: EventType) -> [Event] { - events.filter { $0.type == type } - } - - func handleEvent(_ type: EventType, message: String) { - events.append(Event(type: type, message: message)) + graph.removeTrace(traceID: traceID) + handle = nil } public init() { @@ -93,190 +23,135 @@ public class TestTrace { self.trace.initialize( to: Graph.TraceType(version: .compareFailed) { ctx, graph in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent(.beginTrace, message: "graph = \(graph)") + context.beginTrace(graph: graph) } end_trace: { ctx, graph in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent(.endTrace, message: "graph = \(graph)") + context.endTrace(graph: graph) - // Check this calls deinit + // TODO: Check this calls deinit Unmanaged.fromOpaque(ctx!).release() } begin_subgraph_update: { ctx, subgraph, options in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent( - .beginSubgraphUpdate, - message: "subgraph = \(subgraph), options = \(options)" - ) + // TODO: options + context.beginSubgraphUpdate(subgraph: subgraph) } end_subgraph_update: { ctx, subgraph in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent(.endSubgraphUpdate, message: "subgraph = \(subgraph)") + context.endSubgraphUpdate(subgraph: subgraph) } begin_node_update: { ctx, attribute in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent(.beginNodeUpdate, message: "attribute = \(attribute)") + context.beginNodeUpdate(attribute: attribute) } end_node_update: { ctx, attribute, changed in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent( - .endNodeUpdate, - message: "attribute = \(attribute), changed = \(changed)" - ) + // TODO: test changed or update status + context.endNodeUpdate(attribute: attribute, changed: changed) } begin_value_update: { ctx, attribute in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent(.beginValueUpdate, message: "attribute = \(attribute)") + context.beginValueUpdate(attribute: attribute) } end_value_update: { ctx, attribute, changed in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent( - .endValueUpdate, - message: "attribute = \(attribute), changed = \(changed)" - ) + // TODO: test changed or update status + context.endValueUpdate(attribute: attribute, changed: changed) } begin_graph_update: { ctx, graph in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent(.beginGraphUpdate, message: "graph = \(graph)") + context.beginGraphUpdate(graph: graph) } end_graph_update: { ctx, graph in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent(.endGraphUpdate, message: "graph = \(graph)") + context.endGraphUpdate(graph: graph) } begin_graph_invalidation: { ctx, graph, attribute in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent( - .beginGraphInvalidation, - message: "graph = \(graph), attribute = \(attribute)" - ) + context.beginGraphInvalidation(graph: graph, attribute: attribute) } end_graph_invalidation: { ctx, graph, attribute in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent( - .endGraphInvalidation, - message: "graph = \(graph), attribute = \(attribute)" - ) + context.endGraphInvalidation(graph: graph, attribute: attribute) } begin_modify_node: { ctx, attribute in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent(.beginModifyNode, message: "attribute = \(attribute)") + context.beginModifyNode(attribute: attribute) } end_modify_node: { ctx, attribute in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent(.endModifyNode, message: "attribute = \(attribute)") + context.endModifyNode(attribute: attribute) } begin_event: { ctx, attribute, event_name in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent( - .beginEvent, - message: "attribute = \(attribute), event_name = \(String(cString: event_name))" - ) + context.beginEvent(attribute: attribute, eventName: String(cString: event_name)) } end_event: { ctx, attribute, event_name in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent( - .endEvent, - message: "attribute = \(attribute), event_name = \(String(cString: event_name))" - ) + context.endEvent(attribute: attribute, eventName: String(cString: event_name)) } graph_created: { ctx, graph in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent(.graphCreated, message: "graph = \(graph)") + context.graphCreated(graph: graph) } graph_destroy: { ctx, graph in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent(.graphDestroy, message: "graph = \(graph)") + context.graphDestroy(graph: graph) } graph_needs_update: { ctx, graph in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent(.graphNeedsUpdate, message: "graph = \(graph)") + context.graphNeedsUpdate(graph: graph) } subgraph_created: { ctx, subgraph in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent(.subgraphCreated, message: "subgraph = \(subgraph)") + context.subgraphCreated(subgraph: subgraph) } subgraph_destroy: { ctx, subgraph in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent(.subgraphDestroy, message: "subgraph = \(subgraph)") + context.subgraphDestroy(subgraph: subgraph) } subgraph_add_child: { ctx, subgraph, child_subgraph in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent( - .subgraphAddChild, - message: "subgraph = \(subgraph), child_subgraph = \(child_subgraph)" - ) + context.subgraphAddChild(subgraph: subgraph, childSubgraph: child_subgraph) } subgraph_remove_child: { ctx, subgraph, child_subgraph in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent( - .subgraphRemoveChild, - message: "subgraph = \(subgraph), child_subgraph = \(child_subgraph)" - ) + context.subgraphRemoveChild(subgraph: subgraph, childSubgraph: child_subgraph) } node_added: { ctx, attribute in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent(.nodeAdded, message: "attribute = \(attribute)") + context.nodeAdded(attribute: attribute) } node_add_edge: { ctx, attribute, input, options in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent( - .nodeAddEdge, - message: "attribute = \(attribute), input = \(input), options = \(options)" - ) + context.nodeAddEdge(attribute: attribute, inputAttribute: input, options: options) } node_remove_edge: { ctx, attribute, input_index in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent( - .nodeRemoveEdge, - message: "attribute = \(attribute), input_index = \(input_index)" - ) + context.nodeRemoveEdge(attribute: attribute, inputIndex: Int(input_index)) } node_set_edge_pending: { ctx, attribute, input_index, pending in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent( - .nodeSetEdgePending, - message: - "attribute = \(attribute), input_index = \(input_index), pending = \(pending)" - ) + context.nodeSetEdgePending(attribute: attribute, inputIndex: Int(input_index), pending: pending) } node_set_dirty: { ctx, attribute, dirty in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent( - .nodeSetDirty, - message: "attribute = \(attribute), dirty = \(dirty)" - ) + context.nodeSetDirty(attribute: attribute, dirty: dirty) } node_set_pending: { ctx, attribute, pending in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent( - .nodeSetPending, - message: "attribute = \(attribute), pending = \(pending)" - ) + context.nodeSetPending(attribute: attribute, pending: pending) } node_set_value: { ctx, attribute, value in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent( - .nodeSetValue, - message: "attribute = \(attribute), value = \(value)" - ) + context.nodeSetValue(attribute: attribute, value: value) } node_mark_value: { ctx, attribute in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent(.nodeMarkValue, message: "attribute = \(attribute)") + context.nodeMarkValue(attribute: attribute) } indirect_node_added: { ctx, attribute in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent(.indirectNodeAdded, message: "attribute = \(attribute)") + context.indirectNodeAdded(attribute: attribute) } indirect_node_set_source: { ctx, attribute, source in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent( - .indirectNodeSetSource, - message: "attribute = \(attribute), source = \(source)" - ) + context.indirectNodeSetSource(attribute: attribute, source: source) } indirect_node_set_dependency: { ctx, attribute, dependency in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent( - .indirectNodeSetDependency, - message: "attribute = \(attribute), dependency = \(dependency)" - ) + context.indirectNodeSetDependency(attribute: attribute, dependency: dependency) } profile_mark: { ctx, event_name in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent(.profileMark, message: "event_name = \(String(cString: event_name))") + context.profileMark(eventName: String(cString: event_name)) } custom_event: { ctx, graph, event_name, value, type in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent( - .customEvent, - message: - "graph = \(graph), event_name = \(String(cString: event_name)), value = \(value), type = \(type.type)" - ) + context.customEvent(graph: graph, eventName: String(cString: event_name), value: value, type: type.type) } named_event: { ctx, graph, event_id, event_arg_count, event_args, data, flags in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent(.namedEvent, message: "graph = \(graph), event_id = \(event_id)") + // TODO: remaining arguments + context.namedEvent(graph: graph, eventID: event_id) } named_event_enabled: { ctx, event_id in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent(.namedEventEnabled, message: "event_id = \(event_id)") + context.namedEventEnabled(eventID: event_id) return true } set_deadline: { ctx, deadline in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent(.setDeadline, message: "deadline = \(deadline)") + context.setDeadline(deadline: UInt(deadline)) } passed_deadline: { ctx in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent(.passedDeadline, message: "") + context.passedDeadline() } compare_failed: { ctx, attribute, comparison_state in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.handleEvent( - .compareFailed, - message: "attribute = \(attribute), comparison_state = \(comparison_state)" - ) + context.compareFailed(attribute: attribute, comparisonState: comparison_state) } ) } @@ -284,4 +159,58 @@ public class TestTrace { deinit { self.trace.deallocate() } + + public func beginTrace(graph: Graph) {} + public func endTrace(graph: Graph) {} + + public func beginSubgraphUpdate(subgraph: Subgraph) {} + public func endSubgraphUpdate(subgraph: Subgraph) {} + public func beginNodeUpdate(attribute: AnyAttribute) {} + public func endNodeUpdate(attribute: AnyAttribute, changed: Bool) {} + public func beginValueUpdate(attribute: AnyAttribute) {} + public func endValueUpdate(attribute: AnyAttribute, changed: Bool) {} + public func beginGraphUpdate(graph: Graph) {} + public func endGraphUpdate(graph: Graph) {} + + public func beginGraphInvalidation(graph: Graph, attribute: AnyAttribute) {} + public func endGraphInvalidation(graph: Graph, attribute: AnyAttribute) {} + + public func beginModifyNode(attribute: AnyAttribute) {} + public func endModifyNode(attribute: AnyAttribute) {} + + public func beginEvent(attribute: AnyAttribute, eventName: String) {} + public func endEvent(attribute: AnyAttribute, eventName: String) {} + + public func graphCreated(graph: Graph) {} + public func graphDestroy(graph: Graph) {} + public func graphNeedsUpdate(graph: Graph) {} + + public func subgraphCreated(subgraph: Subgraph) {} + public func subgraphDestroy(subgraph: Subgraph) {} + public func subgraphAddChild(subgraph: Subgraph, childSubgraph: Subgraph) {} + public func subgraphRemoveChild(subgraph: Subgraph, childSubgraph: Subgraph) {} + + public func nodeAdded(attribute: AnyAttribute) {} + public func nodeAddEdge(attribute: AnyAttribute, inputAttribute: AnyAttribute, options: InputOptions) {} + public func nodeRemoveEdge(attribute: AnyAttribute, inputIndex: Int) {} + public func nodeSetEdgePending(attribute: AnyAttribute, inputIndex: Int, pending: Bool) {} + + public func nodeSetDirty(attribute: AnyAttribute, dirty: Bool) {} + public func nodeSetPending(attribute: AnyAttribute, pending: Bool) {} + public func nodeSetValue(attribute: AnyAttribute, value: UnsafeRawPointer) {} + public func nodeMarkValue(attribute: AnyAttribute) {} + + public func indirectNodeAdded(attribute: AnyAttribute) {} + public func indirectNodeSetSource(attribute: AnyAttribute, source: AnyAttribute) {} + public func indirectNodeSetDependency(attribute: AnyAttribute, dependency: AnyAttribute) {} + + public func profileMark(eventName: String) {} + public func customEvent(graph: Graph, eventName: String, value: UnsafeRawPointer, type: Any.Type) {} + public func namedEvent(graph: Graph, eventID: Graph.NamedTraceEventID) {} + public func namedEventEnabled(eventID: Graph.NamedTraceEventID) {} + + public func setDeadline(deadline: UInt) {} + public func passedDeadline() {} + + public func compareFailed(attribute: AnyAttribute, comparisonState: ComparisonState) {} } diff --git a/Tests/ComputeTests/Shared/TestSupport/TestTraceRecorder.swift b/Tests/ComputeTests/Shared/TestSupport/TestTraceRecorder.swift new file mode 100644 index 00000000..4241f80f --- /dev/null +++ b/Tests/ComputeTests/Shared/TestSupport/TestTraceRecorder.swift @@ -0,0 +1,461 @@ +public class TestTraceRecorder: TestTrace { + public private(set) var history = History() + + public override func beginTrace(graph: Graph) { + history.entries.append(.beginTrace(.init(graph: graph))) + } + + public override func endTrace(graph: Graph) { + history.entries.append(.endTrace(.init(graph: graph))) + } + + public override func beginSubgraphUpdate(subgraph: Subgraph) { + history.entries.append(.beginSubgraphUpdate(.init(subgraph: subgraph))) + } + + public override func endSubgraphUpdate(subgraph: Subgraph) { + history.entries.append(.endSubgraphUpdate(.init(subgraph: subgraph))) + } + + public override func beginNodeUpdate(attribute: AnyAttribute) { + history.entries.append(.beginNodeUpdate(.init(attribute: attribute))) + } + + public override func endNodeUpdate(attribute: AnyAttribute, changed: Bool) { + history.entries.append(.endNodeUpdate(.init(attribute: attribute, changed: changed))) + } + + public override func beginValueUpdate(attribute: AnyAttribute) { + history.entries.append(.beginValueUpdate(.init(attribute: attribute))) + } + + public override func endValueUpdate(attribute: AnyAttribute, changed: Bool) { + history.entries.append(.endValueUpdate(.init(attribute: attribute, changed: changed))) + } + + public override func beginGraphUpdate(graph: Graph) { + history.entries.append(.beginGraphUpdate(.init(graph: graph))) + } + + public override func endGraphUpdate(graph: Graph) { + history.entries.append(.endGraphUpdate(.init(graph: graph))) + } + + public override func beginGraphInvalidation(graph: Graph, attribute: AnyAttribute) { + history.entries.append(.beginGraphInvalidation(.init(graph: graph, attribute: attribute))) + } + + public override func endGraphInvalidation(graph: Graph, attribute: AnyAttribute) { + history.entries.append(.endGraphInvalidation(.init(graph: graph, attribute: attribute))) + } + + public override func beginModifyNode(attribute: AnyAttribute) { + history.entries.append(.beginModifyNode(.init(attribute: attribute))) + } + + public override func endModifyNode(attribute: AnyAttribute) { + history.entries.append(.endModifyNode(.init(attribute: attribute))) + } + + public override func beginEvent(attribute: AnyAttribute, eventName: String) { + history.entries.append(.beginEvent(.init(attribute: attribute, eventName: eventName))) + } + + public override func endEvent(attribute: AnyAttribute, eventName: String) { + history.entries.append(.endEvent(.init(attribute: attribute, eventName: eventName))) + } + + public override func graphCreated(graph: Graph) { + history.entries.append(.graphCreated(.init(graph: graph))) + } + + public override func graphDestroy(graph: Graph) { + history.entries.append(.graphDestroy(.init(graph: graph))) + } + + public override func graphNeedsUpdate(graph: Graph) { + history.entries.append(.graphNeedsUpdate(.init(graph: graph))) + } + + public override func subgraphCreated(subgraph: Subgraph) { + history.entries.append(.subgraphCreated(.init(subgraph: subgraph))) + } + + public override func subgraphDestroy(subgraph: Subgraph) { + history.entries.append(.subgraphDestroy(.init(subgraph: subgraph))) + } + + public override func subgraphAddChild(subgraph: Subgraph, childSubgraph: Subgraph) { + history.entries.append(.subgraphAddChild(.init(subgraph: subgraph, childSubgraph: childSubgraph))) + } + + public override func subgraphRemoveChild(subgraph: Subgraph, childSubgraph: Subgraph) { + history.entries.append(.subgraphRemoveChild(.init(subgraph: subgraph, childSubgraph: childSubgraph))) + } + + public override func nodeAdded(attribute: AnyAttribute) { + history.entries.append(.nodeAdded(.init(attribute: attribute))) + } + + public override func nodeAddEdge(attribute: AnyAttribute, inputAttribute: AnyAttribute, options: InputOptions) { + history.entries.append(.nodeAddEdge(.init(attribute: attribute, inputAttribute: inputAttribute, options: options))) + } + + public override func nodeRemoveEdge(attribute: AnyAttribute, inputIndex: Int) { + history.entries.append(.nodeRemoveEdge(.init(attribute: attribute, inputIndex: inputIndex))) + } + + public override func nodeSetEdgePending(attribute: AnyAttribute, inputIndex: Int, pending: Bool) { + history.entries.append(.nodeSetEdgePending(.init(attribute: attribute, inputIndex: inputIndex, pending: pending))) + } + + public override func nodeSetDirty(attribute: AnyAttribute, dirty: Bool) { + history.entries.append(.nodeSetDirty(.init(attribute: attribute, dirty: dirty))) + } + + public override func nodeSetPending(attribute: AnyAttribute, pending: Bool) { + history.entries.append(.nodeSetPending(.init(attribute: attribute, pending: pending))) + } + + public override func nodeSetValue(attribute: AnyAttribute, value: UnsafeRawPointer) { + history.entries.append(.nodeSetValue(.init(attribute: attribute, value: value))) + } + + public override func nodeMarkValue(attribute: AnyAttribute) { + history.entries.append(.nodeMarkValue(.init(attribute: attribute))) + } + + public override func indirectNodeAdded(attribute: AnyAttribute) { + history.entries.append(.indirectNodeAdded(.init(attribute: attribute))) + } + + public override func indirectNodeSetSource(attribute: AnyAttribute, source: AnyAttribute) { + history.entries.append(.indirectNodeSetSource(.init(attribute: attribute, source: source))) + } + + public override func indirectNodeSetDependency(attribute: AnyAttribute, dependency: AnyAttribute) { + history.entries.append(.indirectNodeSetDependency(.init(attribute: attribute, dependency: dependency))) + } + + public override func profileMark(eventName: String) { + history.entries.append(.profileMark(.init(eventName: eventName))) + } + + public override func customEvent(graph: Graph, eventName: String, value: UnsafeRawPointer, type: Any.Type) { + history.entries.append(.customEvent(.init(graph: graph, eventName: eventName, value: value, type: type))) + } + + public override func namedEvent(graph: Graph, eventID: Graph.NamedTraceEventID) { + history.entries.append(.namedEvent(.init(graph: graph, eventID: eventID))) + } + + public override func namedEventEnabled(eventID: Graph.NamedTraceEventID) { + history.entries.append(.namedEventEnabled(.init(eventID: eventID))) + } + + public override func setDeadline(deadline: UInt) { + history.entries.append(.setDeadline(.init(deadline: deadline))) + } + + public override func passedDeadline() { + history.entries.append(.passedDeadline(.init())) + } + + public override func compareFailed(attribute: AnyAttribute, comparisonState: ComparisonState) { + history.entries.append(.compareFailed(.init(attribute: attribute, comparisonState: comparisonState))) + } +} + +extension TestTraceRecorder { + public struct History { + var entries: [Entry] + + init() { + self.entries = [] + } + + public enum Entry { + case beginTrace(BeginTraceEntry) + case endTrace(EndTraceEntry) + + case beginSubgraphUpdate(BeginSubgraphUpdateEntry) + case endSubgraphUpdate(EndSubgraphUpdateEntry) + case beginNodeUpdate(BeginNodeUpdateEntry) + case endNodeUpdate(EndNodeUpdateEntry) + case beginValueUpdate(BeginValueUpdateEntry) + case endValueUpdate(EndValueUpdateEntry) + case beginGraphUpdate(BeginGraphUpdateEntry) + case endGraphUpdate(EndGraphUpdateEntry) + + case beginGraphInvalidation(BeginGraphInvalidationEntry) + case endGraphInvalidation(EndGraphInvalidationEntry) + + case beginModifyNode(BeginModifyNodeEntry) + case endModifyNode(EndModifyNodeEntry) + + case beginEvent(BeginEventEntry) + case endEvent(EndEventEntry) + + case graphCreated(GraphCreatedEntry) + case graphDestroy(GraphDestroyEntry) + case graphNeedsUpdate(GraphNeedsUpdateEntry) + + case subgraphCreated(SubgraphCreatedEntry) + case subgraphDestroy(SubgraphDestroyEntry) + case subgraphAddChild(SubgraphAddChildEntry) + case subgraphRemoveChild(SubgraphRemoveChildEntry) + + case nodeAdded(NodeAddedEntry) + case nodeAddEdge(NodeAddEdgeEntry) + case nodeRemoveEdge(NodeRemoveEdgeEntry) + case nodeSetEdgePending(NodeSetEdgePendingEntry) + + case nodeSetDirty(NodeSetDirtyEntry) + case nodeSetPending(NodeSetPendingEntry) + case nodeSetValue(NodeSetValueEntry) + case nodeMarkValue(NodeMarkValueEntry) + + case indirectNodeAdded(IndirectNodeAddedEntry) + case indirectNodeSetSource(IndirectNodeSetSourceEntry) + case indirectNodeSetDependency(IndirectNodeSetDependencyEntry) + + case profileMark(ProfileMarkEntry) + case customEvent(CustomEventEntry) + case namedEvent(NamedEventEntry) + case namedEventEnabled(NamedEventEnabledEntry) + + case setDeadline(SetDeadlineEntry) + case passedDeadline(PassedDeadlineEntry) + + case compareFailed(CompareFailedEntry) + } + + public struct BeginTraceEntry { + public var graph: Graph + } + + public struct EndTraceEntry { + public var graph: Graph + } + + public struct BeginSubgraphUpdateEntry { + public var subgraph: Subgraph + } + + public struct EndSubgraphUpdateEntry { + public var subgraph: Subgraph + } + + public struct BeginNodeUpdateEntry { + public var attribute: AnyAttribute + } + + public struct EndNodeUpdateEntry { + public var attribute: AnyAttribute + public var changed: Bool + } + + public struct BeginValueUpdateEntry { + public var attribute: AnyAttribute + } + + public struct EndValueUpdateEntry { + public var attribute: AnyAttribute + public var changed: Bool + } + + public struct BeginGraphUpdateEntry { + public var graph: Graph + } + + public struct EndGraphUpdateEntry { + public var graph: Graph + } + + public struct BeginGraphInvalidationEntry { + public var graph: Graph + public var attribute: AnyAttribute + } + + public struct EndGraphInvalidationEntry { + public var graph: Graph + public var attribute: AnyAttribute + } + + public struct BeginModifyNodeEntry { + public var attribute: AnyAttribute + } + + public struct EndModifyNodeEntry { + public var attribute: AnyAttribute + } + + public struct BeginEventEntry { + public var attribute: AnyAttribute + public var eventName: String + } + + public struct EndEventEntry { + public var attribute: AnyAttribute + public var eventName: String + } + + public struct GraphCreatedEntry { + public var graph: Graph + } + + public struct GraphDestroyEntry { + public var graph: Graph + } + + public struct GraphNeedsUpdateEntry { + public var graph: Graph + } + + public struct SubgraphCreatedEntry { + public var subgraph: Subgraph + } + + public struct SubgraphDestroyEntry { + public var subgraph: Subgraph + } + + public struct SubgraphAddChildEntry { + public var subgraph: Subgraph + public var childSubgraph: Subgraph + } + + public struct SubgraphRemoveChildEntry { + public var subgraph: Subgraph + public var childSubgraph: Subgraph + } + + public struct NodeAddedEntry { + public var attribute: AnyAttribute + } + + public struct NodeAddEdgeEntry { + public var attribute: AnyAttribute + public var inputAttribute: AnyAttribute + public var options: InputOptions + } + + public struct NodeRemoveEdgeEntry { + public var attribute: AnyAttribute + public var inputIndex: Int + } + + public struct NodeSetEdgePendingEntry { + public var attribute: AnyAttribute + public var inputIndex: Int + public var pending: Bool + } + + public struct NodeSetDirtyEntry { + public var attribute: AnyAttribute + public var dirty: Bool + } + + public struct NodeSetPendingEntry { + public var attribute: AnyAttribute + public var pending: Bool + } + + public struct NodeSetValueEntry { + public var attribute: AnyAttribute + public var value: UnsafeRawPointer + } + + public struct NodeMarkValueEntry { + public var attribute: AnyAttribute + } + + public struct IndirectNodeAddedEntry { + public var attribute: AnyAttribute + } + + public struct IndirectNodeSetSourceEntry { + public var attribute: AnyAttribute + public var source: AnyAttribute + } + + public struct IndirectNodeSetDependencyEntry { + public var attribute: AnyAttribute + public var dependency: AnyAttribute + } + + public struct ProfileMarkEntry { + public var eventName: String + } + + public struct CustomEventEntry { + public var graph: Graph + public var eventName: String + public var value: UnsafeRawPointer + public var type: Any.Type + } + + public struct NamedEventEntry { + public var graph: Graph + public var eventID: Graph.NamedTraceEventID + } + + public struct NamedEventEnabledEntry { + public var eventID: Graph.NamedTraceEventID + } + + public struct SetDeadlineEntry { + public var deadline: UInt + } + + public struct PassedDeadlineEntry { + } + + public struct CompareFailedEntry { + public var attribute: AnyAttribute + public var comparisonState: ComparisonState + } + } +} + +extension TestTraceRecorder.History { + public var subgraphCreatedEntries: [TestTraceRecorder.History.SubgraphCreatedEntry] { + entries.compactMap { entry in + if case .subgraphCreated(let subgraphCreatedEntry) = entry { + subgraphCreatedEntry + } else { + nil + } + } + } + + public var subgraphDestroyEntries: [TestTraceRecorder.History.SubgraphDestroyEntry] { + entries.compactMap { entry in + if case .subgraphDestroy(let subgraphDestroyEntry) = entry { + subgraphDestroyEntry + } else { + nil + } + } + } + + public var subgraphAddChildEntries: [TestTraceRecorder.History.SubgraphAddChildEntry] { + entries.compactMap { entry in + if case .subgraphAddChild(let subgraphAddChildEntry) = entry { + subgraphAddChildEntry + } else { + nil + } + } + } + + public var subgraphRemoveChildEntries: [TestTraceRecorder.History.SubgraphRemoveChildEntry] { + entries.compactMap { entry in + if case .subgraphRemoveChild(let subgraphRemoveChildEntry) = entry { + subgraphRemoveChildEntry + } else { + nil + } + } + } +} From 1aba9e2b2b7da7040a589272d4e9663aacbb6a3e Mon Sep 17 00:00:00 2001 From: James Moschou Date: Wed, 5 Aug 2026 18:06:07 +0200 Subject: [PATCH 21/42] Make properties of TestTrace private --- Tests/ComputeTests/Shared/TestSupport/TestTrace.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift b/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift index 328f2f37..c973c91b 100644 --- a/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift +++ b/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift @@ -1,6 +1,6 @@ public class TestTrace { - var trace: UnsafeMutablePointer - var handle: (Graph, UniqueID)? + private var trace: UnsafeMutablePointer + private var handle: (Graph, UniqueID)? public func install(graph: Graph) { guard handle == nil else { From bd082d34cf21f68dfcce856df4f945b9faca0388 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 6 Aug 2026 13:44:30 +0200 Subject: [PATCH 22/42] Second parameter to begin_subgraph_update trace method is subgraph flags --- .../AttributeGraph.framework/Headers/AGTraceType.h | 2 +- .../Versions/A/Headers/AGTraceType.h | 2 +- Sources/ComputeCxx/Graph/TraceRecorder.cpp | 4 ++-- Sources/ComputeCxx/Graph/TraceRecorder.h | 2 +- Sources/ComputeCxx/Trace/ExternalTrace.cpp | 4 ++-- Sources/ComputeCxx/Trace/ExternalTrace.h | 2 +- Sources/ComputeCxx/Trace/Trace.h | 2 +- Sources/ComputeCxx/include/ComputeCxx/IAGTraceType.h | 2 +- Tests/ComputeTests/Shared/TestSupport/TestTrace.swift | 7 +++---- .../Shared/TestSupport/TestTraceRecorder.swift | 5 +++-- 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGTraceType.h b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGTraceType.h index 0d91f577..ee45c408 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGTraceType.h +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGTraceType.h @@ -23,7 +23,7 @@ typedef struct AG_SWIFT_NAME(Graph.TraceType) AGTraceType { void (*_Nullable begin_trace)(void *_Nullable context, AGGraphRef graph); void (*_Nullable end_trace)(void *_Nullable context, AGGraphRef graph); - void (*_Nullable begin_subgraph_update)(void *_Nullable context, AGSubgraphRef subgraph, uint32_t options); + void (*_Nullable begin_subgraph_update)(void *_Nullable context, AGSubgraphRef subgraph, AGAttributeFlags subgraph_flags); void (*_Nullable end_subgraph_update)(void *_Nullable context, AGSubgraphRef subgraph); void (*_Nullable begin_node_update)(void *_Nullable context, AGAttribute attribute); void (*_Nullable end_node_update)(void *_Nullable context, AGAttribute attribute, bool changed); diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGTraceType.h b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGTraceType.h index 0d91f577..ee45c408 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGTraceType.h +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGTraceType.h @@ -23,7 +23,7 @@ typedef struct AG_SWIFT_NAME(Graph.TraceType) AGTraceType { void (*_Nullable begin_trace)(void *_Nullable context, AGGraphRef graph); void (*_Nullable end_trace)(void *_Nullable context, AGGraphRef graph); - void (*_Nullable begin_subgraph_update)(void *_Nullable context, AGSubgraphRef subgraph, uint32_t options); + void (*_Nullable begin_subgraph_update)(void *_Nullable context, AGSubgraphRef subgraph, AGAttributeFlags subgraph_flags); void (*_Nullable end_subgraph_update)(void *_Nullable context, AGSubgraphRef subgraph); void (*_Nullable begin_node_update)(void *_Nullable context, AGAttribute attribute); void (*_Nullable end_node_update)(void *_Nullable context, AGAttribute attribute, bool changed); diff --git a/Sources/ComputeCxx/Graph/TraceRecorder.cpp b/Sources/ComputeCxx/Graph/TraceRecorder.cpp index b5617cc7..dabf9982 100644 --- a/Sources/ComputeCxx/Graph/TraceRecorder.cpp +++ b/Sources/ComputeCxx/Graph/TraceRecorder.cpp @@ -414,7 +414,7 @@ void Graph::TraceRecorder::log_message_v(const char *format, va_list args) { } } -void Graph::TraceRecorder::begin_update(const Subgraph &subgraph, uint32_t options) { +void Graph::TraceRecorder::begin_update(const Subgraph &subgraph, IAGAttributeFlags subgraph_flags) { if (_trace_flags & IAGGraphTraceFlagsCustom) { return; } @@ -423,7 +423,7 @@ void Graph::TraceRecorder::begin_update(const Subgraph &subgraph, uint32_t optio field_event_type(_encoder, EventType::BeginSubgraphUpdate); field_timestamp(_encoder); field_payload_1(_encoder, subgraph.subgraph_id()); - field_payload_2(_encoder, options); + field_payload_2(_encoder, subgraph_flags); field_backtrace(_encoder); encode_event_end(); } diff --git a/Sources/ComputeCxx/Graph/TraceRecorder.h b/Sources/ComputeCxx/Graph/TraceRecorder.h index 158ebf47..7dbacc1c 100644 --- a/Sources/ComputeCxx/Graph/TraceRecorder.h +++ b/Sources/ComputeCxx/Graph/TraceRecorder.h @@ -142,7 +142,7 @@ class Graph::TraceRecorder : public Trace, public Encoder::Delegate { void log_message_v(const char *format, va_list args) override; - void begin_update(const Subgraph &subgraph, uint32_t options) override; + void begin_update(const Subgraph &subgraph, IAGAttributeFlags subgraph_flags) override; void end_update(const Subgraph &subgraph) override; void begin_update(const Graph::UpdateStack &update_stack, data::ptr node, uint32_t options) override; void end_update(const Graph::UpdateStack &update_stack, data::ptr node, diff --git a/Sources/ComputeCxx/Trace/ExternalTrace.cpp b/Sources/ComputeCxx/Trace/ExternalTrace.cpp index ab63eab0..e24e11d2 100644 --- a/Sources/ComputeCxx/Trace/ExternalTrace.cpp +++ b/Sources/ComputeCxx/Trace/ExternalTrace.cpp @@ -23,10 +23,10 @@ void ExternalTrace::end_trace(const IAG::Graph &graph) { } } -void ExternalTrace::begin_update(const IAG::Subgraph &subgraph, uint32_t options) { +void ExternalTrace::begin_update(const IAG::Subgraph &subgraph, IAGAttributeFlags subgraph_flags) { if (auto cf_subgraph = subgraph.to_cf()) { if (auto callback = _trace->begin_subgraph_update) { - callback(_context, cf_subgraph, options); + callback(_context, cf_subgraph, subgraph_flags); } } } diff --git a/Sources/ComputeCxx/Trace/ExternalTrace.h b/Sources/ComputeCxx/Trace/ExternalTrace.h index ec6abb43..481fb1da 100644 --- a/Sources/ComputeCxx/Trace/ExternalTrace.h +++ b/Sources/ComputeCxx/Trace/ExternalTrace.h @@ -29,7 +29,7 @@ class ExternalTrace : public IAG::Trace { void begin_trace(const IAG::Graph &graph) override; void end_trace(const IAG::Graph &graph) override; - void begin_update(const IAG::Subgraph &subgraph, uint32_t options) override; + void begin_update(const IAG::Subgraph &subgraph, IAGAttributeFlags subgraph_flags) override; void end_update(const IAG::Subgraph &subgraph) override; void begin_update(const IAG::Graph::UpdateStack &update_stack, IAG::data::ptr node, diff --git a/Sources/ComputeCxx/Trace/Trace.h b/Sources/ComputeCxx/Trace/Trace.h index a15f278d..9546ccf6 100644 --- a/Sources/ComputeCxx/Trace/Trace.h +++ b/Sources/ComputeCxx/Trace/Trace.h @@ -42,7 +42,7 @@ class Trace { void log_message(const char *format, ...); // Updates - virtual void begin_update(const Subgraph &subgraph, uint32_t options) {}; + virtual void begin_update(const Subgraph &subgraph, IAGAttributeFlags subgraph_flags) {}; virtual void end_update(const Subgraph &subgraph) {}; virtual void begin_update(const Graph::UpdateStack &update_stack, data::ptr node, uint32_t options) {}; virtual void end_update(const Graph::UpdateStack &update_stack, data::ptr node, diff --git a/Sources/ComputeCxx/include/ComputeCxx/IAGTraceType.h b/Sources/ComputeCxx/include/ComputeCxx/IAGTraceType.h index 8dac6fe5..1966dc62 100644 --- a/Sources/ComputeCxx/include/ComputeCxx/IAGTraceType.h +++ b/Sources/ComputeCxx/include/ComputeCxx/IAGTraceType.h @@ -23,7 +23,7 @@ typedef struct IAG_SWIFT_NAME(Graph.TraceType) IAGTraceType { void (*_Nullable begin_trace)(void *_Nullable context, IAGGraphRef graph); void (*_Nullable end_trace)(void *_Nullable context, IAGGraphRef graph); - void (*_Nullable begin_subgraph_update)(void *_Nullable context, IAGSubgraphRef subgraph, uint32_t options); + void (*_Nullable begin_subgraph_update)(void *_Nullable context, IAGSubgraphRef subgraph, IAGAttributeFlags subgraph_flags); void (*_Nullable end_subgraph_update)(void *_Nullable context, IAGSubgraphRef subgraph); void (*_Nullable begin_node_update)(void *_Nullable context, IAGAttribute attribute); void (*_Nullable end_node_update)(void *_Nullable context, IAGAttribute attribute, bool changed); diff --git a/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift b/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift index c973c91b..20105ed3 100644 --- a/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift +++ b/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift @@ -30,10 +30,9 @@ public class TestTrace { // TODO: Check this calls deinit Unmanaged.fromOpaque(ctx!).release() - } begin_subgraph_update: { ctx, subgraph, options in + } begin_subgraph_update: { ctx, subgraph, subgraph_flags in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - // TODO: options - context.beginSubgraphUpdate(subgraph: subgraph) + context.beginSubgraphUpdate(subgraph: subgraph, flags: subgraph_flags) } end_subgraph_update: { ctx, subgraph in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() context.endSubgraphUpdate(subgraph: subgraph) @@ -163,7 +162,7 @@ public class TestTrace { public func beginTrace(graph: Graph) {} public func endTrace(graph: Graph) {} - public func beginSubgraphUpdate(subgraph: Subgraph) {} + public func beginSubgraphUpdate(subgraph: Subgraph, flags: Subgraph.Flags) {} public func endSubgraphUpdate(subgraph: Subgraph) {} public func beginNodeUpdate(attribute: AnyAttribute) {} public func endNodeUpdate(attribute: AnyAttribute, changed: Bool) {} diff --git a/Tests/ComputeTests/Shared/TestSupport/TestTraceRecorder.swift b/Tests/ComputeTests/Shared/TestSupport/TestTraceRecorder.swift index 4241f80f..b9a17436 100644 --- a/Tests/ComputeTests/Shared/TestSupport/TestTraceRecorder.swift +++ b/Tests/ComputeTests/Shared/TestSupport/TestTraceRecorder.swift @@ -9,8 +9,8 @@ public class TestTraceRecorder: TestTrace { history.entries.append(.endTrace(.init(graph: graph))) } - public override func beginSubgraphUpdate(subgraph: Subgraph) { - history.entries.append(.beginSubgraphUpdate(.init(subgraph: subgraph))) + public override func beginSubgraphUpdate(subgraph: Subgraph, flags: Subgraph.Flags) { + history.entries.append(.beginSubgraphUpdate(.init(subgraph: subgraph, flags: flags))) } public override func endSubgraphUpdate(subgraph: Subgraph) { @@ -240,6 +240,7 @@ extension TestTraceRecorder { public struct BeginSubgraphUpdateEntry { public var subgraph: Subgraph + public var flags: Subgraph.Flags } public struct EndSubgraphUpdateEntry { From bb119e60a71a8630a302274799e7fa047200b812 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 6 Aug 2026 15:57:36 +0200 Subject: [PATCH 23/42] Fix signatures of IAGGraphSyncTracing and IAGGraphCopyTracePath --- .../AttributeGraph.framework/Headers/AGGraphTracing.h | 6 ++++-- .../Versions/A/Headers/AGGraphTracing.h | 6 ++++-- Sources/ComputeCxx/include/ComputeCxx/IAGGraphTracing.h | 6 ++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGGraphTracing.h b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGGraphTracing.h index 91780c92..322dfb6a 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGGraphTracing.h +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGGraphTracing.h @@ -18,6 +18,7 @@ typedef struct AGTraceType *AGTraceTypeRef; typedef uint32_t AGNamedTraceEventID AG_SWIFT_STRUCT AG_SWIFT_NAME(Graph.NamedTraceEventID); AG_ASSUME_NONNULL_BEGIN +AG_IMPLICIT_BRIDGING_ENABLED AG_EXTERN_C_BEGIN @@ -38,11 +39,11 @@ void AGGraphStopTracing(AGGraphRef _Nullable graph) AG_SWIFT_NAME(AGGraphRef.sto AG_EXPORT AG_REFINED_FOR_SWIFT -void AGGraphSyncTracing(AGGraphRef graph) AG_SWIFT_NAME(AGGraphRef.syncTracing(self:)); +void AGGraphSyncTracing(AGGraphRef _Nullable graph) AG_SWIFT_NAME(AGGraphRef.syncTracing(self:)); AG_EXPORT AG_REFINED_FOR_SWIFT -CFStringRef AGGraphCopyTracePath(AGGraphRef graph) AG_SWIFT_NAME(getter:AGGraphRef.tracePath(self:)); +CFStringRef _Nullable AGGraphCopyTracePath(AGGraphRef _Nullable graph) AG_SWIFT_NAME(AGGraphRef.tracePath(_:)); AG_EXPORT AG_REFINED_FOR_SWIFT @@ -102,4 +103,5 @@ AGNamedTraceEventID AGGraphRegisterNamedTraceEvent(const char *event_name, const AG_EXTERN_C_END +AG_IMPLICIT_BRIDGING_DISABLED AG_ASSUME_NONNULL_END diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGGraphTracing.h b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGGraphTracing.h index 91780c92..322dfb6a 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGGraphTracing.h +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGGraphTracing.h @@ -18,6 +18,7 @@ typedef struct AGTraceType *AGTraceTypeRef; typedef uint32_t AGNamedTraceEventID AG_SWIFT_STRUCT AG_SWIFT_NAME(Graph.NamedTraceEventID); AG_ASSUME_NONNULL_BEGIN +AG_IMPLICIT_BRIDGING_ENABLED AG_EXTERN_C_BEGIN @@ -38,11 +39,11 @@ void AGGraphStopTracing(AGGraphRef _Nullable graph) AG_SWIFT_NAME(AGGraphRef.sto AG_EXPORT AG_REFINED_FOR_SWIFT -void AGGraphSyncTracing(AGGraphRef graph) AG_SWIFT_NAME(AGGraphRef.syncTracing(self:)); +void AGGraphSyncTracing(AGGraphRef _Nullable graph) AG_SWIFT_NAME(AGGraphRef.syncTracing(self:)); AG_EXPORT AG_REFINED_FOR_SWIFT -CFStringRef AGGraphCopyTracePath(AGGraphRef graph) AG_SWIFT_NAME(getter:AGGraphRef.tracePath(self:)); +CFStringRef _Nullable AGGraphCopyTracePath(AGGraphRef _Nullable graph) AG_SWIFT_NAME(AGGraphRef.tracePath(_:)); AG_EXPORT AG_REFINED_FOR_SWIFT @@ -102,4 +103,5 @@ AGNamedTraceEventID AGGraphRegisterNamedTraceEvent(const char *event_name, const AG_EXTERN_C_END +AG_IMPLICIT_BRIDGING_DISABLED AG_ASSUME_NONNULL_END diff --git a/Sources/ComputeCxx/include/ComputeCxx/IAGGraphTracing.h b/Sources/ComputeCxx/include/ComputeCxx/IAGGraphTracing.h index f9c064d6..d94de185 100644 --- a/Sources/ComputeCxx/include/ComputeCxx/IAGGraphTracing.h +++ b/Sources/ComputeCxx/include/ComputeCxx/IAGGraphTracing.h @@ -18,6 +18,7 @@ typedef struct IAGTraceType *IAGTraceTypeRef; typedef uint32_t IAGNamedTraceEventID IAG_SWIFT_STRUCT IAG_SWIFT_NAME(Graph.NamedTraceEventID); IAG_ASSUME_NONNULL_BEGIN +IAG_IMPLICIT_BRIDGING_ENABLED IAG_EXTERN_C_BEGIN @@ -38,11 +39,11 @@ void IAGGraphStopTracing(IAGGraphRef _Nullable graph) IAG_SWIFT_NAME(IAGGraphRef IAG_EXPORT IAG_REFINED_FOR_SWIFT -void IAGGraphSyncTracing(IAGGraphRef graph) IAG_SWIFT_NAME(IAGGraphRef.syncTracing(self:)); +void IAGGraphSyncTracing(IAGGraphRef _Nullable graph) IAG_SWIFT_NAME(IAGGraphRef.syncTracing(self:)); IAG_EXPORT IAG_REFINED_FOR_SWIFT -CFStringRef IAGGraphCopyTracePath(IAGGraphRef graph) IAG_SWIFT_NAME(getter:IAGGraphRef.tracePath(self:)); +CFStringRef _Nullable IAGGraphCopyTracePath(IAGGraphRef _Nullable graph) IAG_SWIFT_NAME(IAGGraphRef.tracePath(_:)); IAG_EXPORT IAG_REFINED_FOR_SWIFT @@ -102,4 +103,5 @@ IAGNamedTraceEventID IAGGraphRegisterNamedTraceEvent(const char *event_name, con IAG_EXTERN_C_END +IAG_IMPLICIT_BRIDGING_DISABLED IAG_ASSUME_NONNULL_END From 81dbbf16e25e460c57d829d193a64fa246fbe1fc Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 6 Aug 2026 17:32:39 +0200 Subject: [PATCH 24/42] Update signature of IAGGraphAddNamedTraceEvent and add Swift refinements --- .../Headers/AGGraphTracing.h | 39 ++++---- .../Headers/AGTraceType.h | 12 ++- .../arm64-apple-ios-macabi.swiftinterface | 9 ++ .../arm64e-apple-ios-macabi.swiftinterface | 9 ++ .../x86_64-apple-ios-macabi.swiftinterface | 9 ++ .../Versions/A/Headers/AGGraphTracing.h | 39 ++++---- .../Versions/A/Headers/AGTraceType.h | 12 ++- .../arm64-apple-macos.swiftinterface | 9 ++ .../arm64e-apple-macos.swiftinterface | 9 ++ .../x86_64-apple-macos.swiftinterface | 9 ++ Sources/Compute/Graph/Graph.swift | 17 ++++ Sources/ComputeCxx/Graph/IAGGraph.cpp | 94 ++++++++++--------- Sources/ComputeCxx/Graph/TraceRecorder.cpp | 16 ++-- Sources/ComputeCxx/Graph/TraceRecorder.h | 4 +- Sources/ComputeCxx/Trace/ExternalTrace.cpp | 5 +- Sources/ComputeCxx/Trace/ExternalTrace.h | 4 +- Sources/ComputeCxx/Trace/Trace.h | 4 +- .../include/ComputeCxx/IAGGraphTracing.h | 39 ++++---- .../include/ComputeCxx/IAGTraceType.h | 12 ++- .../Shared/TestSupport/TestTrace.swift | 26 ++++- .../TestSupport/TestTraceRecorder.swift | 25 ++++- 21 files changed, 269 insertions(+), 133 deletions(-) diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGGraphTracing.h b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGGraphTracing.h index 322dfb6a..d10bea3f 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGGraphTracing.h +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGGraphTracing.h @@ -17,6 +17,10 @@ typedef struct AGTraceType *AGTraceTypeRef; typedef uint32_t AGNamedTraceEventID AG_SWIFT_STRUCT AG_SWIFT_NAME(Graph.NamedTraceEventID); +typedef AG_OPTIONS(uint32_t, AGNamedTraceEventFlags) { + AGNamedTraceEventFlagsRecordBacktrace = 1ul << 31, +} AG_SWIFT_NAME(Graph.NamedTraceEventFlags); + AG_ASSUME_NONNULL_BEGIN AG_IMPLICIT_BRIDGING_ENABLED @@ -29,8 +33,7 @@ void AGGraphStartTracing(AGGraphRef _Nullable graph, AGGraphTraceFlags trace_fla AG_EXPORT AG_REFINED_FOR_SWIFT -void AGGraphStartTracing2(AGGraphRef _Nullable graph, AGGraphTraceFlags trace_flags, - CFArrayRef _Nullable subsystems) +void AGGraphStartTracing2(AGGraphRef _Nullable graph, AGGraphTraceFlags trace_flags, CFArrayRef _Nullable subsystems) AG_SWIFT_NAME(AGGraphRef.startTracing(_:flags:subsystems:)); AG_EXPORT @@ -47,21 +50,22 @@ CFStringRef _Nullable AGGraphCopyTracePath(AGGraphRef _Nullable graph) AG_SWIFT_ AG_EXPORT AG_REFINED_FOR_SWIFT -AGUniqueID AGGraphAddTrace(AGGraphRef graph, const AGTraceTypeRef trace, void *_Nullable context) - AG_SWIFT_NAME(AGGraphRef.addTrace(self:_:context:)); +void AGGraphSetTrace(AGGraphRef graph, const AGTraceTypeRef trace, void *_Nullable context) + AG_SWIFT_NAME(AGGraphRef.setTrace(self:_:context:)); AG_EXPORT AG_REFINED_FOR_SWIFT -void AGGraphRemoveTrace(AGGraphRef graph, AGUniqueID trace_id) AG_SWIFT_NAME(AGGraphRef.removeTrace(self:traceID:)); +void AGGraphResetTrace(AGGraphRef graph) AG_SWIFT_NAME(AGGraphRef.resetTrace(self:)); AG_EXPORT AG_REFINED_FOR_SWIFT -void AGGraphSetTrace(AGGraphRef graph, const AGTraceTypeRef trace, void *_Nullable context) - AG_SWIFT_NAME(AGGraphRef.setTrace(self:_:context:)); +AGUniqueID AGGraphAddTrace(AGGraphRef graph, const AGTraceTypeRef trace, void *_Nullable context) + AG_SWIFT_NAME(AGGraphRef.addTrace(self:_:context:)); AG_EXPORT AG_REFINED_FOR_SWIFT -void AGGraphResetTrace(AGGraphRef graph) AG_SWIFT_NAME(AGGraphRef.resetTrace(self:)); +void AGGraphRemoveTrace(AGGraphRef graph, AGUniqueID trace_id) + AG_SWIFT_NAME(AGGraphRef.removeTrace(self:traceID:)); AG_EXPORT AG_REFINED_FOR_SWIFT @@ -73,19 +77,18 @@ void AGGraphPrepareTrace(AGGraphRef graph, const AGTraceTypeRef trace, void *_Nu AG_EXPORT AG_REFINED_FOR_SWIFT -bool AGGraphTraceEventEnabled(AGGraphRef graph, uint32_t event_id) - AG_SWIFT_NAME(AGGraphRef.traceEventEnabled(self:for:)); +void AGGraphAddTraceEvent(AGGraphRef graph, const char *event_name, const void *value, AGTypeID type) + AG_SWIFT_NAME(AGGraphRef.addTraceEvent(self:name:value:type:)); AG_EXPORT AG_REFINED_FOR_SWIFT -void AGGraphAddTraceEvent(AGGraphRef graph, const char *event_name, const void *value, AGTypeID type) - AG_SWIFT_NAME(AGGraphRef.addTraceEvent(self:name:value:type:)); +bool AGGraphTraceEventEnabled(AGGraphRef graph, uint32_t event_id) + AG_SWIFT_NAME(AGGraphRef.traceEventEnabled(self:for:)); AG_EXPORT AG_REFINED_FOR_SWIFT -void AGGraphAddNamedTraceEvent(AGGraphRef graph, AGNamedTraceEventID event_id, uint32_t event_arg_count, const void **event_args, - CFDataRef data, uint32_t arg6) - AG_SWIFT_NAME(AGGraphRef.addNamedTraceEvent(self:eventID:eventArgCount:eventArgs:data:arg6:)); +AGNamedTraceEventID AGGraphRegisterNamedTraceEvent(const char *event_name, const char *event_subsystem) + AG_SWIFT_NAME(AGGraphRef.registerNamedTraceEvent(name:subsystem:)); AG_EXPORT AG_REFINED_FOR_SWIFT @@ -98,8 +101,10 @@ const char *_Nullable AGGraphGetTraceEventSubsystem(AGNamedTraceEventID event_id AG_EXPORT AG_REFINED_FOR_SWIFT -AGNamedTraceEventID AGGraphRegisterNamedTraceEvent(const char *event_name, const char *event_subsystem) - AG_SWIFT_NAME(AGGraphRef.registerNamedTraceEvent(name:subsystem:)); +void AGGraphAddNamedTraceEvent(AGGraphRef graph, AGNamedTraceEventID event_id, size_t event_arg_count, + const uint32_t *_Nullable AG_COUNTED_BY(event_arg_count) event_args, + CFDataRef _Nullable data, AGNamedTraceEventFlags flags) + AG_SWIFT_NAME(AGGraphRef.addNamedTraceEvent(self:eventID:eventArgCount:eventArgs:data:flags:)); AG_EXTERN_C_END diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGTraceType.h b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGTraceType.h index ee45c408..dd87f0eb 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGTraceType.h +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGTraceType.h @@ -2,8 +2,8 @@ #include #include -#include #include +#include AG_ASSUME_NONNULL_BEGIN @@ -66,10 +66,12 @@ typedef struct AG_SWIFT_NAME(Graph.TraceType) AGTraceType { void (*_Nullable profile_mark)(void *_Nullable context, const char *event_name); - void (*_Nullable custom_event)(void *_Nullable context, AGGraphRef graph, const char *event_name, const void *value, - AGTypeID type); - void (*_Nullable named_event)(void *_Nullable context, AGGraphRef graph, AGNamedTraceEventID event_id, uint32_t event_arg_count, - const void **event_args, CFDataRef data, uint32_t arg6); + void (*_Nullable custom_event)(void *_Nullable context, AGGraphRef graph, const char *event_name, + const void *value, AGTypeID type); + void (*_Nullable named_event)(void *_Nullable context, AGGraphRef graph, AGNamedTraceEventID event_id, + size_t event_arg_count, + const uint32_t *_Nullable AG_COUNTED_BY(event_arg_count) event_args, + CFDataRef _Nullable data, AGNamedTraceEventFlags flags); bool (*_Nullable named_event_enabled)(void *_Nullable context, AGNamedTraceEventID event_id); void (*_Nullable set_deadline)(void *_Nullable context, uint64_t deadline); diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/arm64-apple-ios-macabi.swiftinterface b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/arm64-apple-ios-macabi.swiftinterface index bb4dd4e6..813200a4 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/arm64-apple-ios-macabi.swiftinterface +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/arm64-apple-ios-macabi.swiftinterface @@ -581,6 +581,15 @@ extension AttributeGraph.Graph { extension AttributeGraph.Graph { public func addTraceEvent(_ event: Swift.UnsafePointer, value: Value) public func addTraceEvent(_ event: Swift.UnsafePointer, context: Swift.UnsafePointer) + @_transparent public func addNamedTraceEvent(_ eventID: AttributeGraph.Graph.NamedTraceEventID, eventArgs: [Swift.UInt32], data: Foundation.Data?, flags: AttributeGraph.Graph.NamedTraceEventFlags) { + addNamedTraceEvent( + eventID: eventID, + eventArgCount: eventArgs.count, + eventArgs: eventArgs, + data: data as CFData?, + flags: flags + ) + } } extension AttributeGraph.Graph { public func print(includeValues: Swift.Bool) diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/arm64e-apple-ios-macabi.swiftinterface b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/arm64e-apple-ios-macabi.swiftinterface index 6d0cac33..18b15cf9 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/arm64e-apple-ios-macabi.swiftinterface +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/arm64e-apple-ios-macabi.swiftinterface @@ -581,6 +581,15 @@ extension AttributeGraph.Graph { extension AttributeGraph.Graph { public func addTraceEvent(_ event: Swift.UnsafePointer, value: Value) public func addTraceEvent(_ event: Swift.UnsafePointer, context: Swift.UnsafePointer) + @_transparent public func addNamedTraceEvent(_ eventID: AttributeGraph.Graph.NamedTraceEventID, eventArgs: [Swift.UInt32], data: Foundation.Data?, flags: AttributeGraph.Graph.NamedTraceEventFlags) { + addNamedTraceEvent( + eventID: eventID, + eventArgCount: eventArgs.count, + eventArgs: eventArgs, + data: data as CFData?, + flags: flags + ) + } } extension AttributeGraph.Graph { public func print(includeValues: Swift.Bool) diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/x86_64-apple-ios-macabi.swiftinterface b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/x86_64-apple-ios-macabi.swiftinterface index 9a7a1484..3d777212 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/x86_64-apple-ios-macabi.swiftinterface +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/x86_64-apple-ios-macabi.swiftinterface @@ -581,6 +581,15 @@ extension AttributeGraph.Graph { extension AttributeGraph.Graph { public func addTraceEvent(_ event: Swift.UnsafePointer, value: Value) public func addTraceEvent(_ event: Swift.UnsafePointer, context: Swift.UnsafePointer) + @_transparent public func addNamedTraceEvent(_ eventID: AttributeGraph.Graph.NamedTraceEventID, eventArgs: [Swift.UInt32], data: Foundation.Data?, flags: AttributeGraph.Graph.NamedTraceEventFlags) { + addNamedTraceEvent( + eventID: eventID, + eventArgCount: eventArgs.count, + eventArgs: eventArgs, + data: data as CFData?, + flags: flags + ) + } } extension AttributeGraph.Graph { public func print(includeValues: Swift.Bool) diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGGraphTracing.h b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGGraphTracing.h index 322dfb6a..d10bea3f 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGGraphTracing.h +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGGraphTracing.h @@ -17,6 +17,10 @@ typedef struct AGTraceType *AGTraceTypeRef; typedef uint32_t AGNamedTraceEventID AG_SWIFT_STRUCT AG_SWIFT_NAME(Graph.NamedTraceEventID); +typedef AG_OPTIONS(uint32_t, AGNamedTraceEventFlags) { + AGNamedTraceEventFlagsRecordBacktrace = 1ul << 31, +} AG_SWIFT_NAME(Graph.NamedTraceEventFlags); + AG_ASSUME_NONNULL_BEGIN AG_IMPLICIT_BRIDGING_ENABLED @@ -29,8 +33,7 @@ void AGGraphStartTracing(AGGraphRef _Nullable graph, AGGraphTraceFlags trace_fla AG_EXPORT AG_REFINED_FOR_SWIFT -void AGGraphStartTracing2(AGGraphRef _Nullable graph, AGGraphTraceFlags trace_flags, - CFArrayRef _Nullable subsystems) +void AGGraphStartTracing2(AGGraphRef _Nullable graph, AGGraphTraceFlags trace_flags, CFArrayRef _Nullable subsystems) AG_SWIFT_NAME(AGGraphRef.startTracing(_:flags:subsystems:)); AG_EXPORT @@ -47,21 +50,22 @@ CFStringRef _Nullable AGGraphCopyTracePath(AGGraphRef _Nullable graph) AG_SWIFT_ AG_EXPORT AG_REFINED_FOR_SWIFT -AGUniqueID AGGraphAddTrace(AGGraphRef graph, const AGTraceTypeRef trace, void *_Nullable context) - AG_SWIFT_NAME(AGGraphRef.addTrace(self:_:context:)); +void AGGraphSetTrace(AGGraphRef graph, const AGTraceTypeRef trace, void *_Nullable context) + AG_SWIFT_NAME(AGGraphRef.setTrace(self:_:context:)); AG_EXPORT AG_REFINED_FOR_SWIFT -void AGGraphRemoveTrace(AGGraphRef graph, AGUniqueID trace_id) AG_SWIFT_NAME(AGGraphRef.removeTrace(self:traceID:)); +void AGGraphResetTrace(AGGraphRef graph) AG_SWIFT_NAME(AGGraphRef.resetTrace(self:)); AG_EXPORT AG_REFINED_FOR_SWIFT -void AGGraphSetTrace(AGGraphRef graph, const AGTraceTypeRef trace, void *_Nullable context) - AG_SWIFT_NAME(AGGraphRef.setTrace(self:_:context:)); +AGUniqueID AGGraphAddTrace(AGGraphRef graph, const AGTraceTypeRef trace, void *_Nullable context) + AG_SWIFT_NAME(AGGraphRef.addTrace(self:_:context:)); AG_EXPORT AG_REFINED_FOR_SWIFT -void AGGraphResetTrace(AGGraphRef graph) AG_SWIFT_NAME(AGGraphRef.resetTrace(self:)); +void AGGraphRemoveTrace(AGGraphRef graph, AGUniqueID trace_id) + AG_SWIFT_NAME(AGGraphRef.removeTrace(self:traceID:)); AG_EXPORT AG_REFINED_FOR_SWIFT @@ -73,19 +77,18 @@ void AGGraphPrepareTrace(AGGraphRef graph, const AGTraceTypeRef trace, void *_Nu AG_EXPORT AG_REFINED_FOR_SWIFT -bool AGGraphTraceEventEnabled(AGGraphRef graph, uint32_t event_id) - AG_SWIFT_NAME(AGGraphRef.traceEventEnabled(self:for:)); +void AGGraphAddTraceEvent(AGGraphRef graph, const char *event_name, const void *value, AGTypeID type) + AG_SWIFT_NAME(AGGraphRef.addTraceEvent(self:name:value:type:)); AG_EXPORT AG_REFINED_FOR_SWIFT -void AGGraphAddTraceEvent(AGGraphRef graph, const char *event_name, const void *value, AGTypeID type) - AG_SWIFT_NAME(AGGraphRef.addTraceEvent(self:name:value:type:)); +bool AGGraphTraceEventEnabled(AGGraphRef graph, uint32_t event_id) + AG_SWIFT_NAME(AGGraphRef.traceEventEnabled(self:for:)); AG_EXPORT AG_REFINED_FOR_SWIFT -void AGGraphAddNamedTraceEvent(AGGraphRef graph, AGNamedTraceEventID event_id, uint32_t event_arg_count, const void **event_args, - CFDataRef data, uint32_t arg6) - AG_SWIFT_NAME(AGGraphRef.addNamedTraceEvent(self:eventID:eventArgCount:eventArgs:data:arg6:)); +AGNamedTraceEventID AGGraphRegisterNamedTraceEvent(const char *event_name, const char *event_subsystem) + AG_SWIFT_NAME(AGGraphRef.registerNamedTraceEvent(name:subsystem:)); AG_EXPORT AG_REFINED_FOR_SWIFT @@ -98,8 +101,10 @@ const char *_Nullable AGGraphGetTraceEventSubsystem(AGNamedTraceEventID event_id AG_EXPORT AG_REFINED_FOR_SWIFT -AGNamedTraceEventID AGGraphRegisterNamedTraceEvent(const char *event_name, const char *event_subsystem) - AG_SWIFT_NAME(AGGraphRef.registerNamedTraceEvent(name:subsystem:)); +void AGGraphAddNamedTraceEvent(AGGraphRef graph, AGNamedTraceEventID event_id, size_t event_arg_count, + const uint32_t *_Nullable AG_COUNTED_BY(event_arg_count) event_args, + CFDataRef _Nullable data, AGNamedTraceEventFlags flags) + AG_SWIFT_NAME(AGGraphRef.addNamedTraceEvent(self:eventID:eventArgCount:eventArgs:data:flags:)); AG_EXTERN_C_END diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGTraceType.h b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGTraceType.h index ee45c408..dd87f0eb 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGTraceType.h +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGTraceType.h @@ -2,8 +2,8 @@ #include #include -#include #include +#include AG_ASSUME_NONNULL_BEGIN @@ -66,10 +66,12 @@ typedef struct AG_SWIFT_NAME(Graph.TraceType) AGTraceType { void (*_Nullable profile_mark)(void *_Nullable context, const char *event_name); - void (*_Nullable custom_event)(void *_Nullable context, AGGraphRef graph, const char *event_name, const void *value, - AGTypeID type); - void (*_Nullable named_event)(void *_Nullable context, AGGraphRef graph, AGNamedTraceEventID event_id, uint32_t event_arg_count, - const void **event_args, CFDataRef data, uint32_t arg6); + void (*_Nullable custom_event)(void *_Nullable context, AGGraphRef graph, const char *event_name, + const void *value, AGTypeID type); + void (*_Nullable named_event)(void *_Nullable context, AGGraphRef graph, AGNamedTraceEventID event_id, + size_t event_arg_count, + const uint32_t *_Nullable AG_COUNTED_BY(event_arg_count) event_args, + CFDataRef _Nullable data, AGNamedTraceEventFlags flags); bool (*_Nullable named_event_enabled)(void *_Nullable context, AGNamedTraceEventID event_id); void (*_Nullable set_deadline)(void *_Nullable context, uint64_t deadline); diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/arm64-apple-macos.swiftinterface b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/arm64-apple-macos.swiftinterface index 59504c56..e01bc2e7 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/arm64-apple-macos.swiftinterface +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/arm64-apple-macos.swiftinterface @@ -581,6 +581,15 @@ extension AttributeGraph.Graph { extension AttributeGraph.Graph { public func addTraceEvent(_ event: Swift.UnsafePointer, value: Value) public func addTraceEvent(_ event: Swift.UnsafePointer, context: Swift.UnsafePointer) + @_transparent public func addNamedTraceEvent(_ eventID: AttributeGraph.Graph.NamedTraceEventID, eventArgs: [Swift.UInt32], data: Foundation.Data?, flags: AttributeGraph.Graph.NamedTraceEventFlags) { + addNamedTraceEvent( + eventID: eventID, + eventArgCount: eventArgs.count, + eventArgs: eventArgs, + data: data as CFData?, + flags: flags + ) + } } extension AttributeGraph.Graph { public func print(includeValues: Swift.Bool) diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/arm64e-apple-macos.swiftinterface b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/arm64e-apple-macos.swiftinterface index d26be768..74e8eb61 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/arm64e-apple-macos.swiftinterface +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/arm64e-apple-macos.swiftinterface @@ -581,6 +581,15 @@ extension AttributeGraph.Graph { extension AttributeGraph.Graph { public func addTraceEvent(_ event: Swift.UnsafePointer, value: Value) public func addTraceEvent(_ event: Swift.UnsafePointer, context: Swift.UnsafePointer) + @_transparent public func addNamedTraceEvent(_ eventID: AttributeGraph.Graph.NamedTraceEventID, eventArgs: [Swift.UInt32], data: Foundation.Data?, flags: AttributeGraph.Graph.NamedTraceEventFlags) { + addNamedTraceEvent( + eventID: eventID, + eventArgCount: eventArgs.count, + eventArgs: eventArgs, + data: data as CFData?, + flags: flags + ) + } } extension AttributeGraph.Graph { public func print(includeValues: Swift.Bool) diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/x86_64-apple-macos.swiftinterface b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/x86_64-apple-macos.swiftinterface index f163b7b8..e9c03522 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/x86_64-apple-macos.swiftinterface +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/x86_64-apple-macos.swiftinterface @@ -581,6 +581,15 @@ extension AttributeGraph.Graph { extension AttributeGraph.Graph { public func addTraceEvent(_ event: Swift.UnsafePointer, value: Value) public func addTraceEvent(_ event: Swift.UnsafePointer, context: Swift.UnsafePointer) + @_transparent public func addNamedTraceEvent(_ eventID: AttributeGraph.Graph.NamedTraceEventID, eventArgs: [Swift.UInt32], data: Foundation.Data?, flags: AttributeGraph.Graph.NamedTraceEventFlags) { + addNamedTraceEvent( + eventID: eventID, + eventArgCount: eventArgs.count, + eventArgs: eventArgs, + data: data as CFData?, + flags: flags + ) + } } extension AttributeGraph.Graph { public func print(includeValues: Swift.Bool) diff --git a/Sources/Compute/Graph/Graph.swift b/Sources/Compute/Graph/Graph.swift index ad269771..36a43528 100644 --- a/Sources/Compute/Graph/Graph.swift +++ b/Sources/Compute/Graph/Graph.swift @@ -1,4 +1,5 @@ import ComputeCxx +import Foundation @_silgen_name("IAGGraphInternAttributeType") public func internAttributeType( @@ -154,6 +155,22 @@ extension Graph { public func addTraceEvent(_ event: UnsafePointer, context: UnsafePointer) { self.addTraceEvent(name: event, value: context, type: Metadata(Value.self)) } + + @_transparent + public func addNamedTraceEvent( + _ eventID: Graph.NamedTraceEventID, + eventArgs: [UInt32], + data: Data?, + flags: Graph.NamedTraceEventFlags + ) { + addNamedTraceEvent( + eventID: eventID, + eventArgCount: eventArgs.count, + eventArgs: eventArgs, + data: data as CFData?, + flags: flags + ) + } } extension Graph { diff --git a/Sources/ComputeCxx/Graph/IAGGraph.cpp b/Sources/ComputeCxx/Graph/IAGGraph.cpp index de51cee7..9794f99d 100644 --- a/Sources/ComputeCxx/Graph/IAGGraph.cpp +++ b/Sources/ComputeCxx/Graph/IAGGraph.cpp @@ -901,7 +901,9 @@ void IAGGraphSetOutputValue(const void *value, IAGTypeID type) { #pragma mark - Trace -void IAGGraphStartTracing(IAGGraphRef graph, IAGGraphTraceFlags trace_flags) { IAGGraphStartTracing2(graph, trace_flags, NULL); } +void IAGGraphStartTracing(IAGGraphRef graph, IAGGraphTraceFlags trace_flags) { + IAGGraphStartTracing2(graph, trace_flags, NULL); +} void IAGGraphStartTracing2(IAGGraphRef graph, IAGGraphTraceFlags trace_flags, CFArrayRef subsystems) { auto subsystems_vector = IAG::vector, 0, uint64_t>(); @@ -965,29 +967,29 @@ CFStringRef IAGGraphCopyTracePath(IAGGraphRef graph) { return graph_context->graph().copy_trace_path(); } -IAGUniqueID IAGGraphAddTrace(IAGGraphRef graph, const IAGTraceTypeRef trace, void *context) { +void IAGGraphSetTrace(IAGGraphRef graph, const IAGTraceTypeRef trace, void *context) { auto graph_context = IAG::Graph::Context::from_cf(graph); - auto external_trace = new ExternalTrace(trace, context); + graph_context->graph().remove_trace(0); + + auto external_trace = new ExternalTrace(0, trace, context); graph_context->graph().add_trace(external_trace); - return external_trace->id(); } -void IAGGraphRemoveTrace(IAGGraphRef graph, IAGUniqueID trace_id) { +void IAGGraphResetTrace(IAGGraphRef graph) { auto graph_context = IAG::Graph::Context::from_cf(graph); - graph_context->graph().remove_trace(trace_id); + graph_context->graph().remove_trace(0); } -void IAGGraphSetTrace(IAGGraphRef graph, const IAGTraceTypeRef trace, void *context) { +IAGUniqueID IAGGraphAddTrace(IAGGraphRef graph, const IAGTraceTypeRef trace, void *context) { auto graph_context = IAG::Graph::Context::from_cf(graph); - graph_context->graph().remove_trace(0); - - auto external_trace = new ExternalTrace(0, trace, context); + auto external_trace = new ExternalTrace(trace, context); graph_context->graph().add_trace(external_trace); + return external_trace->id(); } -void IAGGraphResetTrace(IAGGraphRef graph) { +void IAGGraphRemoveTrace(IAGGraphRef graph, IAGUniqueID trace_id) { auto graph_context = IAG::Graph::Context::from_cf(graph); - graph_context->graph().remove_trace(0); + graph_context->graph().remove_trace(trace_id); } bool IAGGraphIsTracingActive(IAGGraphRef graph) { @@ -1001,16 +1003,6 @@ void IAGGraphPrepareTrace(IAGGraphRef graph, const IAGTraceTypeRef trace, void * graph_context->graph().prepare_trace(*external_trace); } -bool IAGGraphTraceEventEnabled(IAGGraphRef graph, uint32_t event_id) { - auto graph_context = IAG::Graph::Context::from_cf(graph); - for (auto trace : graph_context->graph().traces()) { - if (trace->named_event_enabled(event_id)) { - return true; - } - } - return false; -} - void IAGGraphAddTraceEvent(IAGGraphRef graph, const char *event_name, const void *value, IAGTypeID type) { auto graph_context = IAG::Graph::Context::from_cf(graph); graph_context->graph().foreach_trace([&graph_context, &event_name, &value, &type](IAG::Trace &trace) { @@ -1018,13 +1010,14 @@ void IAGGraphAddTraceEvent(IAGGraphRef graph, const char *event_name, const void }); } -void IAGGraphAddNamedTraceEvent(IAGGraphRef graph, IAGNamedTraceEventID event_id, uint32_t event_arg_count, const void **event_args, - CFDataRef data, uint32_t arg6) { +bool IAGGraphTraceEventEnabled(IAGGraphRef graph, uint32_t event_id) { auto graph_context = IAG::Graph::Context::from_cf(graph); - graph_context->graph().foreach_trace( - [&graph_context, &event_id, &event_arg_count, &event_args, &data, &arg6](IAG::Trace &trace) { - trace.named_event(*graph_context, event_id, event_arg_count, event_args, data, arg6); - }); + for (auto trace : graph_context->graph().traces()) { + if (trace->named_event_enabled(event_id)) { + return true; + } + } + return false; } namespace NamedEvents { @@ -1034,6 +1027,26 @@ static IAG::vector, 0, uint32_t> *names; } // namespace NamedEvents +IAGNamedTraceEventID IAGGraphRegisterNamedTraceEvent(const char *event_name, const char *event_subsystem) { + platform_lock_lock(&NamedEvents::lock); + + if (!NamedEvents::names) { + NamedEvents::names = new IAG::vector, 0, uint32_t>(); + NamedEvents::names->push_back({0, 0}); // Disallow 0 as event ID + } + + uint32_t event_id = NamedEvents::names->size(); + if (event_subsystem != nullptr) { + event_subsystem = strdup(event_subsystem); + } + event_name = strdup(event_name); + NamedEvents::names->push_back({event_subsystem, event_name}); + + platform_lock_unlock(&NamedEvents::lock); + + return event_id; +} + const char *IAGGraphGetTraceEventName(IAGNamedTraceEventID event_id) { const char *event_name = nullptr; @@ -1058,24 +1071,13 @@ const char *IAGGraphGetTraceEventSubsystem(IAGNamedTraceEventID event_id) { return event_subsystem; } -IAGNamedTraceEventID IAGGraphRegisterNamedTraceEvent(const char *event_name, const char *event_subsystem) { - platform_lock_lock(&NamedEvents::lock); - - if (!NamedEvents::names) { - NamedEvents::names = new IAG::vector, 0, uint32_t>(); - NamedEvents::names->push_back({0, 0}); // Disallow 0 as event ID - } - - uint32_t event_id = NamedEvents::names->size(); - if (event_subsystem != nullptr) { - event_subsystem = strdup(event_subsystem); - } - event_name = strdup(event_name); - NamedEvents::names->push_back({event_subsystem, event_name}); - - platform_lock_unlock(&NamedEvents::lock); - - return event_id; +void IAGGraphAddNamedTraceEvent(IAGGraphRef graph, IAGNamedTraceEventID event_id, size_t event_arg_count, + const uint32_t *event_args, CFDataRef data, IAGNamedTraceEventFlags flags) { + auto graph_context = IAG::Graph::Context::from_cf(graph); + graph_context->graph().foreach_trace( + [&graph_context, &event_id, &event_arg_count, &event_args, &data, &flags](IAG::Trace &trace) { + trace.named_event(*graph_context, event_id, event_arg_count, event_args, data, flags); + }); } // MARK: Description diff --git a/Sources/ComputeCxx/Graph/TraceRecorder.cpp b/Sources/ComputeCxx/Graph/TraceRecorder.cpp index dabf9982..eede9e97 100644 --- a/Sources/ComputeCxx/Graph/TraceRecorder.cpp +++ b/Sources/ComputeCxx/Graph/TraceRecorder.cpp @@ -933,8 +933,8 @@ void Graph::TraceRecorder::custom_event(const Graph::Context &context, const cha } void Graph::TraceRecorder::named_event(const Graph::Context &context, IAGNamedTraceEventID event_id, - uint32_t event_arg_count, const void **event_args, CFDataRef data, - uint32_t arg6) { + size_t event_arg_count, const uint32_t *event_args, CFDataRef data, + IAGNamedTraceEventFlags flags) { if (!named_event_enabled(event_id)) { return; } @@ -944,14 +944,14 @@ void Graph::TraceRecorder::named_event(const Graph::Context &context, IAGNamedTr field_named_event_id(_encoder, event_id); field_timestamp(_encoder); - if (arg6 & 0x80000000) { + if (flags & IAGNamedTraceEventFlagsRecordBacktrace) { field_backtrace(_encoder); - arg6 &= 0x7fffffff; + flags &= ~IAGNamedTraceEventFlagsRecordBacktrace; } - field_payload_1(_encoder, arg6); - for (uint32_t i = 0; i < std::min(event_arg_count, uint32_t(4)); ++i) { - const void *event_arg = event_args[i]; - _encoder.encode_field_varint(EVENT_FIELD_PAYLOAD_2 + i, reinterpret_cast(event_arg)); + field_payload_1(_encoder, flags); + for (size_t i = 0; i < std::min(event_arg_count, size_t(4)); ++i) { + uint32_t event_arg = event_args[i]; + _encoder.encode_field_varint(EVENT_FIELD_PAYLOAD_2 + i, event_arg); } if (data != nullptr) { void *ptr = (void *)CFDataGetBytePtr(data); diff --git a/Sources/ComputeCxx/Graph/TraceRecorder.h b/Sources/ComputeCxx/Graph/TraceRecorder.h index 7dbacc1c..1e997cec 100644 --- a/Sources/ComputeCxx/Graph/TraceRecorder.h +++ b/Sources/ComputeCxx/Graph/TraceRecorder.h @@ -196,8 +196,8 @@ class Graph::TraceRecorder : public Trace, public Encoder::Delegate { void custom_event(const Graph::Context &context, const char *event_name, const void *value, const swift::metadata &type) override; - void named_event(const Graph::Context &context, IAGNamedTraceEventID event_id, uint32_t event_arg_count, - const void **event_args, CFDataRef data, uint32_t arg6) override; + void named_event(const Graph::Context &context, IAGNamedTraceEventID event_id, size_t event_arg_count, + const uint32_t *_Nullable event_args, CFDataRef data, IAGNamedTraceEventFlags flags) override; bool named_event_enabled(IAGNamedTraceEventID event_id) override; // compare_failed not overridden diff --git a/Sources/ComputeCxx/Trace/ExternalTrace.cpp b/Sources/ComputeCxx/Trace/ExternalTrace.cpp index e24e11d2..b9092e52 100644 --- a/Sources/ComputeCxx/Trace/ExternalTrace.cpp +++ b/Sources/ComputeCxx/Trace/ExternalTrace.cpp @@ -281,13 +281,14 @@ void ExternalTrace::custom_event(const IAG::Graph::Context &context, const char } void ExternalTrace::named_event(const IAG::Graph::Context &context, IAGNamedTraceEventID event_id, - uint32_t event_arg_count, const void **event_args, CFDataRef data, uint32_t arg6) { + size_t event_arg_count, const uint32_t *event_args, CFDataRef data, + IAGNamedTraceEventFlags flags) { if (_trace->version < IAGTraceTypeVersionNamed) { return; } if (auto cf_context = context.to_cf()) { if (auto callback = _trace->named_event) { - callback(_context, cf_context, event_id, event_arg_count, event_args, data, arg6); + callback(_context, cf_context, event_id, event_arg_count, event_args, data, flags); } } } diff --git a/Sources/ComputeCxx/Trace/ExternalTrace.h b/Sources/ComputeCxx/Trace/ExternalTrace.h index 481fb1da..438e950c 100644 --- a/Sources/ComputeCxx/Trace/ExternalTrace.h +++ b/Sources/ComputeCxx/Trace/ExternalTrace.h @@ -81,8 +81,8 @@ class ExternalTrace : public IAG::Trace { void custom_event(const IAG::Graph::Context &context, const char *event_name, const void *value, const IAG::swift::metadata &type) override; - void named_event(const IAG::Graph::Context &context, IAGNamedTraceEventID event_id, uint32_t event_arg_count, - const void **event_args, CFDataRef data, uint32_t arg6) override; + void named_event(const IAG::Graph::Context &context, IAGNamedTraceEventID event_id, size_t event_arg_count, + const uint32_t *_Nullable event_args, CFDataRef data, IAGNamedTraceEventFlags flags) override; bool named_event_enabled(IAGNamedTraceEventID event_id) override; void set_deadline(uint64_t deadline) override; diff --git a/Sources/ComputeCxx/Trace/Trace.h b/Sources/ComputeCxx/Trace/Trace.h index 9546ccf6..faf47dc4 100644 --- a/Sources/ComputeCxx/Trace/Trace.h +++ b/Sources/ComputeCxx/Trace/Trace.h @@ -96,8 +96,8 @@ class Trace { virtual void custom_event(const Graph::Context &context, const char *event_name, const void *value, const swift::metadata &type) {}; - virtual void named_event(const Graph::Context &context, IAGNamedTraceEventID event_id, uint32_t event_arg_count, - const void **event_args, CFDataRef data, uint32_t arg6) {}; + virtual void named_event(const Graph::Context &context, IAGNamedTraceEventID event_id, size_t event_arg_count, + const uint32_t *_Nullable event_args, CFDataRef data, IAGNamedTraceEventFlags flags) {}; virtual bool named_event_enabled(IAGNamedTraceEventID event_id) { return false; }; virtual void compare_failed(data::ptr node, const void *lhs, const void *rhs, size_t range_offset, diff --git a/Sources/ComputeCxx/include/ComputeCxx/IAGGraphTracing.h b/Sources/ComputeCxx/include/ComputeCxx/IAGGraphTracing.h index d94de185..2514493e 100644 --- a/Sources/ComputeCxx/include/ComputeCxx/IAGGraphTracing.h +++ b/Sources/ComputeCxx/include/ComputeCxx/IAGGraphTracing.h @@ -17,6 +17,10 @@ typedef struct IAGTraceType *IAGTraceTypeRef; typedef uint32_t IAGNamedTraceEventID IAG_SWIFT_STRUCT IAG_SWIFT_NAME(Graph.NamedTraceEventID); +typedef IAG_OPTIONS(uint32_t, IAGNamedTraceEventFlags) { + IAGNamedTraceEventFlagsRecordBacktrace = 1ul << 31, +} IAG_SWIFT_NAME(Graph.NamedTraceEventFlags); + IAG_ASSUME_NONNULL_BEGIN IAG_IMPLICIT_BRIDGING_ENABLED @@ -29,8 +33,7 @@ void IAGGraphStartTracing(IAGGraphRef _Nullable graph, IAGGraphTraceFlags trace_ IAG_EXPORT IAG_REFINED_FOR_SWIFT -void IAGGraphStartTracing2(IAGGraphRef _Nullable graph, IAGGraphTraceFlags trace_flags, - CFArrayRef _Nullable subsystems) +void IAGGraphStartTracing2(IAGGraphRef _Nullable graph, IAGGraphTraceFlags trace_flags, CFArrayRef _Nullable subsystems) IAG_SWIFT_NAME(IAGGraphRef.startTracing(_:flags:subsystems:)); IAG_EXPORT @@ -47,21 +50,22 @@ CFStringRef _Nullable IAGGraphCopyTracePath(IAGGraphRef _Nullable graph) IAG_SWI IAG_EXPORT IAG_REFINED_FOR_SWIFT -IAGUniqueID IAGGraphAddTrace(IAGGraphRef graph, const IAGTraceTypeRef trace, void *_Nullable context) - IAG_SWIFT_NAME(IAGGraphRef.addTrace(self:_:context:)); +void IAGGraphSetTrace(IAGGraphRef graph, const IAGTraceTypeRef trace, void *_Nullable context) + IAG_SWIFT_NAME(IAGGraphRef.setTrace(self:_:context:)); IAG_EXPORT IAG_REFINED_FOR_SWIFT -void IAGGraphRemoveTrace(IAGGraphRef graph, IAGUniqueID trace_id) IAG_SWIFT_NAME(IAGGraphRef.removeTrace(self:traceID:)); +void IAGGraphResetTrace(IAGGraphRef graph) IAG_SWIFT_NAME(IAGGraphRef.resetTrace(self:)); IAG_EXPORT IAG_REFINED_FOR_SWIFT -void IAGGraphSetTrace(IAGGraphRef graph, const IAGTraceTypeRef trace, void *_Nullable context) - IAG_SWIFT_NAME(IAGGraphRef.setTrace(self:_:context:)); +IAGUniqueID IAGGraphAddTrace(IAGGraphRef graph, const IAGTraceTypeRef trace, void *_Nullable context) + IAG_SWIFT_NAME(IAGGraphRef.addTrace(self:_:context:)); IAG_EXPORT IAG_REFINED_FOR_SWIFT -void IAGGraphResetTrace(IAGGraphRef graph) IAG_SWIFT_NAME(IAGGraphRef.resetTrace(self:)); +void IAGGraphRemoveTrace(IAGGraphRef graph, IAGUniqueID trace_id) + IAG_SWIFT_NAME(IAGGraphRef.removeTrace(self:traceID:)); IAG_EXPORT IAG_REFINED_FOR_SWIFT @@ -73,19 +77,18 @@ void IAGGraphPrepareTrace(IAGGraphRef graph, const IAGTraceTypeRef trace, void * IAG_EXPORT IAG_REFINED_FOR_SWIFT -bool IAGGraphTraceEventEnabled(IAGGraphRef graph, uint32_t event_id) - IAG_SWIFT_NAME(IAGGraphRef.traceEventEnabled(self:for:)); +void IAGGraphAddTraceEvent(IAGGraphRef graph, const char *event_name, const void *value, IAGTypeID type) + IAG_SWIFT_NAME(IAGGraphRef.addTraceEvent(self:name:value:type:)); IAG_EXPORT IAG_REFINED_FOR_SWIFT -void IAGGraphAddTraceEvent(IAGGraphRef graph, const char *event_name, const void *value, IAGTypeID type) - IAG_SWIFT_NAME(IAGGraphRef.addTraceEvent(self:name:value:type:)); +bool IAGGraphTraceEventEnabled(IAGGraphRef graph, uint32_t event_id) + IAG_SWIFT_NAME(IAGGraphRef.traceEventEnabled(self:for:)); IAG_EXPORT IAG_REFINED_FOR_SWIFT -void IAGGraphAddNamedTraceEvent(IAGGraphRef graph, IAGNamedTraceEventID event_id, uint32_t event_arg_count, const void **event_args, - CFDataRef data, uint32_t arg6) - IAG_SWIFT_NAME(IAGGraphRef.addNamedTraceEvent(self:eventID:eventArgCount:eventArgs:data:arg6:)); +IAGNamedTraceEventID IAGGraphRegisterNamedTraceEvent(const char *event_name, const char *event_subsystem) + IAG_SWIFT_NAME(IAGGraphRef.registerNamedTraceEvent(name:subsystem:)); IAG_EXPORT IAG_REFINED_FOR_SWIFT @@ -98,8 +101,10 @@ const char *_Nullable IAGGraphGetTraceEventSubsystem(IAGNamedTraceEventID event_ IAG_EXPORT IAG_REFINED_FOR_SWIFT -IAGNamedTraceEventID IAGGraphRegisterNamedTraceEvent(const char *event_name, const char *event_subsystem) - IAG_SWIFT_NAME(IAGGraphRef.registerNamedTraceEvent(name:subsystem:)); +void IAGGraphAddNamedTraceEvent(IAGGraphRef graph, IAGNamedTraceEventID event_id, size_t event_arg_count, + const uint32_t *_Nullable IAG_COUNTED_BY(event_arg_count) event_args, + CFDataRef _Nullable data, IAGNamedTraceEventFlags flags) + IAG_SWIFT_NAME(IAGGraphRef.addNamedTraceEvent(self:eventID:eventArgCount:eventArgs:data:flags:)); IAG_EXTERN_C_END diff --git a/Sources/ComputeCxx/include/ComputeCxx/IAGTraceType.h b/Sources/ComputeCxx/include/ComputeCxx/IAGTraceType.h index 1966dc62..e30d4cd2 100644 --- a/Sources/ComputeCxx/include/ComputeCxx/IAGTraceType.h +++ b/Sources/ComputeCxx/include/ComputeCxx/IAGTraceType.h @@ -2,8 +2,8 @@ #include #include -#include #include +#include IAG_ASSUME_NONNULL_BEGIN @@ -66,10 +66,12 @@ typedef struct IAG_SWIFT_NAME(Graph.TraceType) IAGTraceType { void (*_Nullable profile_mark)(void *_Nullable context, const char *event_name); - void (*_Nullable custom_event)(void *_Nullable context, IAGGraphRef graph, const char *event_name, const void *value, - IAGTypeID type); - void (*_Nullable named_event)(void *_Nullable context, IAGGraphRef graph, IAGNamedTraceEventID event_id, uint32_t event_arg_count, - const void **event_args, CFDataRef data, uint32_t arg6); + void (*_Nullable custom_event)(void *_Nullable context, IAGGraphRef graph, const char *event_name, + const void *value, IAGTypeID type); + void (*_Nullable named_event)(void *_Nullable context, IAGGraphRef graph, IAGNamedTraceEventID event_id, + size_t event_arg_count, + const uint32_t *_Nullable IAG_COUNTED_BY(event_arg_count) event_args, + CFDataRef _Nullable data, IAGNamedTraceEventFlags flags); bool (*_Nullable named_event_enabled)(void *_Nullable context, IAGNamedTraceEventID event_id); void (*_Nullable set_deadline)(void *_Nullable context, uint64_t deadline); diff --git a/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift b/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift index 20105ed3..e4557f0f 100644 --- a/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift +++ b/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift @@ -1,3 +1,5 @@ +import Foundation + public class TestTrace { private var trace: UnsafeMutablePointer private var handle: (Graph, UniqueID)? @@ -136,8 +138,20 @@ public class TestTrace { context.customEvent(graph: graph, eventName: String(cString: event_name), value: value, type: type.type) } named_event: { ctx, graph, event_id, event_arg_count, event_args, data, flags in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - // TODO: remaining arguments - context.namedEvent(graph: graph, eventID: event_id) + let eventArgs = event_args.map { pointer in + Array(capacity: event_arg_count) { span in + for i in 0...fromOpaque(ctx!).takeUnretainedValue() context.namedEventEnabled(eventID: event_id) @@ -205,7 +219,13 @@ public class TestTrace { public func profileMark(eventName: String) {} public func customEvent(graph: Graph, eventName: String, value: UnsafeRawPointer, type: Any.Type) {} - public func namedEvent(graph: Graph, eventID: Graph.NamedTraceEventID) {} + public func namedEvent( + graph: Graph, + eventID: Graph.NamedTraceEventID, + eventArgs: [UInt32], + data: Data?, + flags: Graph.NamedTraceEventFlags + ) {} public func namedEventEnabled(eventID: Graph.NamedTraceEventID) {} public func setDeadline(deadline: UInt) {} diff --git a/Tests/ComputeTests/Shared/TestSupport/TestTraceRecorder.swift b/Tests/ComputeTests/Shared/TestSupport/TestTraceRecorder.swift index b9a17436..a0e0c3c9 100644 --- a/Tests/ComputeTests/Shared/TestSupport/TestTraceRecorder.swift +++ b/Tests/ComputeTests/Shared/TestSupport/TestTraceRecorder.swift @@ -1,3 +1,5 @@ +import Foundation + public class TestTraceRecorder: TestTrace { public private(set) var history = History() @@ -145,8 +147,24 @@ public class TestTraceRecorder: TestTrace { history.entries.append(.customEvent(.init(graph: graph, eventName: eventName, value: value, type: type))) } - public override func namedEvent(graph: Graph, eventID: Graph.NamedTraceEventID) { - history.entries.append(.namedEvent(.init(graph: graph, eventID: eventID))) + public override func namedEvent( + graph: Graph, + eventID: Graph.NamedTraceEventID, + eventArgs: [UInt32], + data: Data?, + flags: Graph.NamedTraceEventFlags + ) { + history.entries.append( + .namedEvent( + .init( + graph: graph, + eventID: eventID, + eventArgs: eventArgs, + data: data, + flags: flags + ) + ) + ) } public override func namedEventEnabled(eventID: Graph.NamedTraceEventID) { @@ -399,6 +417,9 @@ extension TestTraceRecorder { public struct NamedEventEntry { public var graph: Graph public var eventID: Graph.NamedTraceEventID + public var eventArgs: [UInt32] + public var data: Data? + public var flags: Graph.NamedTraceEventFlags } public struct NamedEventEnabledEntry { From 51435a2abe2b9f279d7b00b36d12a05d4c7890ff Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 6 Aug 2026 18:11:19 +0200 Subject: [PATCH 25/42] Add remaining convenience properties to TestTraceRecorder.History --- .../TestSupport/TestTraceRecorder.swift | 392 +++++++++++++++++- 1 file changed, 381 insertions(+), 11 deletions(-) diff --git a/Tests/ComputeTests/Shared/TestSupport/TestTraceRecorder.swift b/Tests/ComputeTests/Shared/TestSupport/TestTraceRecorder.swift index a0e0c3c9..767b1dc3 100644 --- a/Tests/ComputeTests/Shared/TestSupport/TestTraceRecorder.swift +++ b/Tests/ComputeTests/Shared/TestSupport/TestTraceRecorder.swift @@ -441,40 +441,410 @@ extension TestTraceRecorder { } extension TestTraceRecorder.History { + public var beginTraceEntries: [TestTraceRecorder.History.BeginTraceEntry] { + entries.compactMap { entry in + if case .beginTrace(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var endTraceEntries: [TestTraceRecorder.History.EndTraceEntry] { + entries.compactMap { entry in + if case .endTrace(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var beginSubgraphUpdateEntries: [TestTraceRecorder.History.BeginSubgraphUpdateEntry] { + entries.compactMap { entry in + if case .beginSubgraphUpdate(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var endSubgraphUpdateEntries: [TestTraceRecorder.History.EndSubgraphUpdateEntry] { + entries.compactMap { entry in + if case .endSubgraphUpdate(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var beginNodeUpdateEntries: [TestTraceRecorder.History.BeginNodeUpdateEntry] { + entries.compactMap { entry in + if case .beginNodeUpdate(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var endNodeUpdateEntries: [TestTraceRecorder.History.EndNodeUpdateEntry] { + entries.compactMap { entry in + if case .endNodeUpdate(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var beginValueUpdateEntries: [TestTraceRecorder.History.BeginValueUpdateEntry] { + entries.compactMap { entry in + if case .beginValueUpdate(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var endValueUpdateEntries: [TestTraceRecorder.History.EndValueUpdateEntry] { + entries.compactMap { entry in + if case .endValueUpdate(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var beginGraphUpdateEntries: [TestTraceRecorder.History.BeginGraphUpdateEntry] { + entries.compactMap { entry in + if case .beginGraphUpdate(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var endGraphUpdateEntries: [TestTraceRecorder.History.EndGraphUpdateEntry] { + entries.compactMap { entry in + if case .endGraphUpdate(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var beginGraphInvalidationEntries: [TestTraceRecorder.History.BeginGraphInvalidationEntry] { + entries.compactMap { entry in + if case .beginGraphInvalidation(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var endGraphInvalidationEntries: [TestTraceRecorder.History.EndGraphInvalidationEntry] { + entries.compactMap { entry in + if case .endGraphInvalidation(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var beginModifyNodeEntries: [TestTraceRecorder.History.BeginModifyNodeEntry] { + entries.compactMap { entry in + if case .beginModifyNode(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var endModifyNodeEntries: [TestTraceRecorder.History.EndModifyNodeEntry] { + entries.compactMap { entry in + if case .endModifyNode(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var beginEventEntries: [TestTraceRecorder.History.BeginEventEntry] { + entries.compactMap { entry in + if case .beginEvent(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var endEventEntries: [TestTraceRecorder.History.EndEventEntry] { + entries.compactMap { entry in + if case .endEvent(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var graphCreatedEntries: [TestTraceRecorder.History.GraphCreatedEntry] { + entries.compactMap { entry in + if case .graphCreated(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var graphDestroyEntries: [TestTraceRecorder.History.GraphDestroyEntry] { + entries.compactMap { entry in + if case .graphDestroy(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var graphNeedsUpdateEntries: [TestTraceRecorder.History.GraphNeedsUpdateEntry] { + entries.compactMap { entry in + if case .graphNeedsUpdate(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + public var subgraphCreatedEntries: [TestTraceRecorder.History.SubgraphCreatedEntry] { entries.compactMap { entry in - if case .subgraphCreated(let subgraphCreatedEntry) = entry { - subgraphCreatedEntry + if case .subgraphCreated(let wrappedEntry) = entry { + wrappedEntry } else { nil } } } - + public var subgraphDestroyEntries: [TestTraceRecorder.History.SubgraphDestroyEntry] { entries.compactMap { entry in - if case .subgraphDestroy(let subgraphDestroyEntry) = entry { - subgraphDestroyEntry + if case .subgraphDestroy(let wrappedEntry) = entry { + wrappedEntry } else { nil } } } - + public var subgraphAddChildEntries: [TestTraceRecorder.History.SubgraphAddChildEntry] { entries.compactMap { entry in - if case .subgraphAddChild(let subgraphAddChildEntry) = entry { - subgraphAddChildEntry + if case .subgraphAddChild(let wrappedEntry) = entry { + wrappedEntry } else { nil } } } - + public var subgraphRemoveChildEntries: [TestTraceRecorder.History.SubgraphRemoveChildEntry] { entries.compactMap { entry in - if case .subgraphRemoveChild(let subgraphRemoveChildEntry) = entry { - subgraphRemoveChildEntry + if case .subgraphRemoveChild(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var nodeAddedEntries: [TestTraceRecorder.History.NodeAddedEntry] { + entries.compactMap { entry in + if case .nodeAdded(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var nodeAddEdgeEntries: [TestTraceRecorder.History.NodeAddEdgeEntry] { + entries.compactMap { entry in + if case .nodeAddEdge(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var nodeRemoveEdgeEntries: [TestTraceRecorder.History.NodeRemoveEdgeEntry] { + entries.compactMap { entry in + if case .nodeRemoveEdge(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var nodeSetEdgePendingEntries: [TestTraceRecorder.History.NodeSetEdgePendingEntry] { + entries.compactMap { entry in + if case .nodeSetEdgePending(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var nodeSetDirtyEntries: [TestTraceRecorder.History.NodeSetDirtyEntry] { + entries.compactMap { entry in + if case .nodeSetDirty(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var nodeSetPendingEntries: [TestTraceRecorder.History.NodeSetPendingEntry] { + entries.compactMap { entry in + if case .nodeSetPending(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var nodeSetValueEntries: [TestTraceRecorder.History.NodeSetValueEntry] { + entries.compactMap { entry in + if case .nodeSetValue(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var nodeMarkValueEntries: [TestTraceRecorder.History.NodeMarkValueEntry] { + entries.compactMap { entry in + if case .nodeMarkValue(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var indirectNodeAddedEntries: [TestTraceRecorder.History.IndirectNodeAddedEntry] { + entries.compactMap { entry in + if case .indirectNodeAdded(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var indirectNodeSetSourceEntries: [TestTraceRecorder.History.IndirectNodeSetSourceEntry] { + entries.compactMap { entry in + if case .indirectNodeSetSource(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var indirectNodeSetDependencyEntries: [TestTraceRecorder.History.IndirectNodeSetDependencyEntry] { + entries.compactMap { entry in + if case .indirectNodeSetDependency(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var profileMarkEntries: [TestTraceRecorder.History.ProfileMarkEntry] { + entries.compactMap { entry in + if case .profileMark(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var customEventEntries: [TestTraceRecorder.History.CustomEventEntry] { + entries.compactMap { entry in + if case .customEvent(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var namedEventEntries: [TestTraceRecorder.History.NamedEventEntry] { + entries.compactMap { entry in + if case .namedEvent(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var namedEventEnabledEntries: [TestTraceRecorder.History.NamedEventEnabledEntry] { + entries.compactMap { entry in + if case .namedEventEnabled(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var setDeadlineEntries: [TestTraceRecorder.History.SetDeadlineEntry] { + entries.compactMap { entry in + if case .setDeadline(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var passedDeadlineEntries: [TestTraceRecorder.History.PassedDeadlineEntry] { + entries.compactMap { entry in + if case .passedDeadline(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var compareFailedEntries: [TestTraceRecorder.History.CompareFailedEntry] { + entries.compactMap { entry in + if case .compareFailed(let wrappedEntry) = entry { + wrappedEntry } else { nil } From e028c45c06b7b44b31960b30e9015239f3f12feb Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 6 Aug 2026 18:12:27 +0200 Subject: [PATCH 26/42] Silence warning --- Tests/ComputeTests/Shared/Graph/GraphTracingTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/ComputeTests/Shared/Graph/GraphTracingTests.swift b/Tests/ComputeTests/Shared/Graph/GraphTracingTests.swift index 9dc776a9..16842458 100644 --- a/Tests/ComputeTests/Shared/Graph/GraphTracingTests.swift +++ b/Tests/ComputeTests/Shared/Graph/GraphTracingTests.swift @@ -4,7 +4,7 @@ import Testing struct GraphTracingTests { @Test func initializeTraceType() { - Graph.TraceType(version: .compareFailed) { ctx, graph in + let _ = Graph.TraceType(version: .compareFailed) { ctx, graph in print("[begin_trace] graph = \(graph)") } end_trace: { ctx, graph in print("[end_trace] graph = \(graph)") From d0bf79fcdd798ce2abd74f422c32cfbac098e2eb Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 6 Aug 2026 18:23:19 +0200 Subject: [PATCH 27/42] Add TracingCustomEventTests --- .../Tracing/TracingCustomEventTests.swift | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 Tests/ComputeTests/Shared/Tracing/TracingCustomEventTests.swift diff --git a/Tests/ComputeTests/Shared/Tracing/TracingCustomEventTests.swift b/Tests/ComputeTests/Shared/Tracing/TracingCustomEventTests.swift new file mode 100644 index 00000000..18b23dcb --- /dev/null +++ b/Tests/ComputeTests/Shared/Tracing/TracingCustomEventTests.swift @@ -0,0 +1,63 @@ +import Testing + +@Suite +struct TracingCustomEventTests { + @Suite + struct CustomEventTests { + @Test + func traceCustomEventCalled() throws { + class CustomEventTrace: TestTraceRecorder { + var capturedValue: Int? + var capturedType: Any.Type? + + override func customEvent(graph: Graph, eventName: String, value: UnsafeRawPointer, type: Any.Type) { + super.customEvent(graph: graph, eventName: eventName, value: value, type: type) + capturedValue = value.assumingMemoryBound(to: Int.self).pointee + capturedType = type + } + } + + let graph = Graph() + let recorder = CustomEventTrace() + recorder.install(graph: graph) + + try #require(recorder.history.customEventEntries.count == 0) + + "custom_event".withCString { eventName in + graph.addTraceEvent(eventName, value: 42) + } + + let customEventEntries = recorder.history.customEventEntries + try #require(customEventEntries.count == 1) + #expect(customEventEntries[0].graph == graph) + #expect(customEventEntries[0].eventName == "custom_event") + #expect(ObjectIdentifier(customEventEntries[0].type) == ObjectIdentifier(Int.self)) + // value pointer is no longer value here + #expect(recorder.capturedValue == 42) + #expect(recorder.capturedType == Int.self) + } + + @Test + func traceCustomEventCalledWithContext() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + try #require(recorder.history.customEventEntries.count == 0) + + let value: Int = 42 + try withUnsafePointer(to: value) { context in + "custom_event".withCString { eventName in + graph.addTraceEvent(eventName, context: context) + } + + let customEventEntries = recorder.history.customEventEntries + try #require(customEventEntries.count == 1) + #expect(customEventEntries[0].graph == graph) + #expect(customEventEntries[0].eventName == "custom_event") + #expect(customEventEntries[0].value == context) + #expect(ObjectIdentifier(customEventEntries[0].type) == ObjectIdentifier(Int.self)) + } + } + } +} From f5a00499355d2be4b14ac6b2b566b2d74787795c Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 6 Aug 2026 18:54:38 +0200 Subject: [PATCH 28/42] Add TracingNamedEventTests --- .../arm64-apple-ios-macabi.swiftinterface | 12 +- .../arm64e-apple-ios-macabi.swiftinterface | 12 +- .../x86_64-apple-ios-macabi.swiftinterface | 12 +- .../arm64-apple-macos.swiftinterface | 12 +- .../arm64e-apple-macos.swiftinterface | 12 +- .../x86_64-apple-macos.swiftinterface | 12 +- Sources/Compute/Graph/Graph.swift | 12 ++ .../Shared/TestSupport/TestTrace.swift | 5 +- .../TestSupport/TestTraceRecorder.swift | 3 +- .../Tracing/TracingNamedEventTests.swift | 155 ++++++++++++++++++ 10 files changed, 237 insertions(+), 10 deletions(-) create mode 100644 Tests/ComputeTests/Shared/Tracing/TracingNamedEventTests.swift diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/arm64-apple-ios-macabi.swiftinterface b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/arm64-apple-ios-macabi.swiftinterface index 813200a4..e31fbbda 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/arm64-apple-ios-macabi.swiftinterface +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/arm64-apple-ios-macabi.swiftinterface @@ -581,7 +581,17 @@ extension AttributeGraph.Graph { extension AttributeGraph.Graph { public func addTraceEvent(_ event: Swift.UnsafePointer, value: Value) public func addTraceEvent(_ event: Swift.UnsafePointer, context: Swift.UnsafePointer) - @_transparent public func addNamedTraceEvent(_ eventID: AttributeGraph.Graph.NamedTraceEventID, eventArgs: [Swift.UInt32], data: Foundation.Data?, flags: AttributeGraph.Graph.NamedTraceEventFlags) { + @_transparent public static func registerNamedTraceEvent(name: Swift.String, subsystem: Swift.String) -> AttributeGraph.Graph.NamedTraceEventID { + name.withCString { namePointer in + subsystem.withCString { subsystemPointer in + Graph.registerNamedTraceEvent( + name: namePointer, + subsystem: subsystemPointer + ) + } + } + } + @_transparent public func addNamedTraceEvent(_ eventID: AttributeGraph.Graph.NamedTraceEventID, eventArgs: [Swift.UInt32] = [], data: Foundation.Data? = nil, flags: AttributeGraph.Graph.NamedTraceEventFlags = []) { addNamedTraceEvent( eventID: eventID, eventArgCount: eventArgs.count, diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/arm64e-apple-ios-macabi.swiftinterface b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/arm64e-apple-ios-macabi.swiftinterface index 18b15cf9..ff23847d 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/arm64e-apple-ios-macabi.swiftinterface +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/arm64e-apple-ios-macabi.swiftinterface @@ -581,7 +581,17 @@ extension AttributeGraph.Graph { extension AttributeGraph.Graph { public func addTraceEvent(_ event: Swift.UnsafePointer, value: Value) public func addTraceEvent(_ event: Swift.UnsafePointer, context: Swift.UnsafePointer) - @_transparent public func addNamedTraceEvent(_ eventID: AttributeGraph.Graph.NamedTraceEventID, eventArgs: [Swift.UInt32], data: Foundation.Data?, flags: AttributeGraph.Graph.NamedTraceEventFlags) { + @_transparent public static func registerNamedTraceEvent(name: Swift.String, subsystem: Swift.String) -> AttributeGraph.Graph.NamedTraceEventID { + name.withCString { namePointer in + subsystem.withCString { subsystemPointer in + Graph.registerNamedTraceEvent( + name: namePointer, + subsystem: subsystemPointer + ) + } + } + } + @_transparent public func addNamedTraceEvent(_ eventID: AttributeGraph.Graph.NamedTraceEventID, eventArgs: [Swift.UInt32] = [], data: Foundation.Data? = nil, flags: AttributeGraph.Graph.NamedTraceEventFlags = []) { addNamedTraceEvent( eventID: eventID, eventArgCount: eventArgs.count, diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/x86_64-apple-ios-macabi.swiftinterface b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/x86_64-apple-ios-macabi.swiftinterface index 3d777212..00c100b1 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/x86_64-apple-ios-macabi.swiftinterface +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Modules/AttributeGraph.swiftmodule/x86_64-apple-ios-macabi.swiftinterface @@ -581,7 +581,17 @@ extension AttributeGraph.Graph { extension AttributeGraph.Graph { public func addTraceEvent(_ event: Swift.UnsafePointer, value: Value) public func addTraceEvent(_ event: Swift.UnsafePointer, context: Swift.UnsafePointer) - @_transparent public func addNamedTraceEvent(_ eventID: AttributeGraph.Graph.NamedTraceEventID, eventArgs: [Swift.UInt32], data: Foundation.Data?, flags: AttributeGraph.Graph.NamedTraceEventFlags) { + @_transparent public static func registerNamedTraceEvent(name: Swift.String, subsystem: Swift.String) -> AttributeGraph.Graph.NamedTraceEventID { + name.withCString { namePointer in + subsystem.withCString { subsystemPointer in + Graph.registerNamedTraceEvent( + name: namePointer, + subsystem: subsystemPointer + ) + } + } + } + @_transparent public func addNamedTraceEvent(_ eventID: AttributeGraph.Graph.NamedTraceEventID, eventArgs: [Swift.UInt32] = [], data: Foundation.Data? = nil, flags: AttributeGraph.Graph.NamedTraceEventFlags = []) { addNamedTraceEvent( eventID: eventID, eventArgCount: eventArgs.count, diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/arm64-apple-macos.swiftinterface b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/arm64-apple-macos.swiftinterface index e01bc2e7..d3cf0e72 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/arm64-apple-macos.swiftinterface +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/arm64-apple-macos.swiftinterface @@ -581,7 +581,17 @@ extension AttributeGraph.Graph { extension AttributeGraph.Graph { public func addTraceEvent(_ event: Swift.UnsafePointer, value: Value) public func addTraceEvent(_ event: Swift.UnsafePointer, context: Swift.UnsafePointer) - @_transparent public func addNamedTraceEvent(_ eventID: AttributeGraph.Graph.NamedTraceEventID, eventArgs: [Swift.UInt32], data: Foundation.Data?, flags: AttributeGraph.Graph.NamedTraceEventFlags) { + @_transparent public static func registerNamedTraceEvent(name: Swift.String, subsystem: Swift.String) -> AttributeGraph.Graph.NamedTraceEventID { + name.withCString { namePointer in + subsystem.withCString { subsystemPointer in + Graph.registerNamedTraceEvent( + name: namePointer, + subsystem: subsystemPointer + ) + } + } + } + @_transparent public func addNamedTraceEvent(_ eventID: AttributeGraph.Graph.NamedTraceEventID, eventArgs: [Swift.UInt32] = [], data: Foundation.Data? = nil, flags: AttributeGraph.Graph.NamedTraceEventFlags = []) { addNamedTraceEvent( eventID: eventID, eventArgCount: eventArgs.count, diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/arm64e-apple-macos.swiftinterface b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/arm64e-apple-macos.swiftinterface index 74e8eb61..6befb4c7 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/arm64e-apple-macos.swiftinterface +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/arm64e-apple-macos.swiftinterface @@ -581,7 +581,17 @@ extension AttributeGraph.Graph { extension AttributeGraph.Graph { public func addTraceEvent(_ event: Swift.UnsafePointer, value: Value) public func addTraceEvent(_ event: Swift.UnsafePointer, context: Swift.UnsafePointer) - @_transparent public func addNamedTraceEvent(_ eventID: AttributeGraph.Graph.NamedTraceEventID, eventArgs: [Swift.UInt32], data: Foundation.Data?, flags: AttributeGraph.Graph.NamedTraceEventFlags) { + @_transparent public static func registerNamedTraceEvent(name: Swift.String, subsystem: Swift.String) -> AttributeGraph.Graph.NamedTraceEventID { + name.withCString { namePointer in + subsystem.withCString { subsystemPointer in + Graph.registerNamedTraceEvent( + name: namePointer, + subsystem: subsystemPointer + ) + } + } + } + @_transparent public func addNamedTraceEvent(_ eventID: AttributeGraph.Graph.NamedTraceEventID, eventArgs: [Swift.UInt32] = [], data: Foundation.Data? = nil, flags: AttributeGraph.Graph.NamedTraceEventFlags = []) { addNamedTraceEvent( eventID: eventID, eventArgCount: eventArgs.count, diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/x86_64-apple-macos.swiftinterface b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/x86_64-apple-macos.swiftinterface index e9c03522..aaeda42b 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/x86_64-apple-macos.swiftinterface +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Modules/AttributeGraph.swiftmodule/x86_64-apple-macos.swiftinterface @@ -581,7 +581,17 @@ extension AttributeGraph.Graph { extension AttributeGraph.Graph { public func addTraceEvent(_ event: Swift.UnsafePointer, value: Value) public func addTraceEvent(_ event: Swift.UnsafePointer, context: Swift.UnsafePointer) - @_transparent public func addNamedTraceEvent(_ eventID: AttributeGraph.Graph.NamedTraceEventID, eventArgs: [Swift.UInt32], data: Foundation.Data?, flags: AttributeGraph.Graph.NamedTraceEventFlags) { + @_transparent public static func registerNamedTraceEvent(name: Swift.String, subsystem: Swift.String) -> AttributeGraph.Graph.NamedTraceEventID { + name.withCString { namePointer in + subsystem.withCString { subsystemPointer in + Graph.registerNamedTraceEvent( + name: namePointer, + subsystem: subsystemPointer + ) + } + } + } + @_transparent public func addNamedTraceEvent(_ eventID: AttributeGraph.Graph.NamedTraceEventID, eventArgs: [Swift.UInt32] = [], data: Foundation.Data? = nil, flags: AttributeGraph.Graph.NamedTraceEventFlags = []) { addNamedTraceEvent( eventID: eventID, eventArgCount: eventArgs.count, diff --git a/Sources/Compute/Graph/Graph.swift b/Sources/Compute/Graph/Graph.swift index 36a43528..4e312710 100644 --- a/Sources/Compute/Graph/Graph.swift +++ b/Sources/Compute/Graph/Graph.swift @@ -156,6 +156,18 @@ extension Graph { self.addTraceEvent(name: event, value: context, type: Metadata(Value.self)) } + @_transparent + public static func registerNamedTraceEvent(name: String, subsystem: String) -> Graph.NamedTraceEventID { + name.withCString { namePointer in + subsystem.withCString { subsystemPointer in + Graph.registerNamedTraceEvent( + name: namePointer, + subsystem: subsystemPointer + ) + } + } + } + @_transparent public func addNamedTraceEvent( _ eventID: Graph.NamedTraceEventID, diff --git a/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift b/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift index e4557f0f..0f8eb859 100644 --- a/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift +++ b/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift @@ -154,8 +154,7 @@ public class TestTrace { ) } named_event_enabled: { ctx, event_id in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.namedEventEnabled(eventID: event_id) - return true + return context.namedEventEnabled(eventID: event_id) } set_deadline: { ctx, deadline in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() context.setDeadline(deadline: UInt(deadline)) @@ -226,7 +225,7 @@ public class TestTrace { data: Data?, flags: Graph.NamedTraceEventFlags ) {} - public func namedEventEnabled(eventID: Graph.NamedTraceEventID) {} + public func namedEventEnabled(eventID: Graph.NamedTraceEventID) -> Bool { true } public func setDeadline(deadline: UInt) {} public func passedDeadline() {} diff --git a/Tests/ComputeTests/Shared/TestSupport/TestTraceRecorder.swift b/Tests/ComputeTests/Shared/TestSupport/TestTraceRecorder.swift index 767b1dc3..80ec5128 100644 --- a/Tests/ComputeTests/Shared/TestSupport/TestTraceRecorder.swift +++ b/Tests/ComputeTests/Shared/TestSupport/TestTraceRecorder.swift @@ -167,8 +167,9 @@ public class TestTraceRecorder: TestTrace { ) } - public override func namedEventEnabled(eventID: Graph.NamedTraceEventID) { + public override func namedEventEnabled(eventID: Graph.NamedTraceEventID) -> Bool { history.entries.append(.namedEventEnabled(.init(eventID: eventID))) + return true } public override func setDeadline(deadline: UInt) { diff --git a/Tests/ComputeTests/Shared/Tracing/TracingNamedEventTests.swift b/Tests/ComputeTests/Shared/Tracing/TracingNamedEventTests.swift new file mode 100644 index 00000000..54a3ff5a --- /dev/null +++ b/Tests/ComputeTests/Shared/Tracing/TracingNamedEventTests.swift @@ -0,0 +1,155 @@ +import Foundation +import Testing + +@Suite +struct TracingNamedEventTests { + @Suite + struct NamedEventTests { + @Test + func traceNamedEventCalled() throws { + let eventID = Graph.registerNamedTraceEvent(name: "test_event", subsystem: "test_subsystem") + + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + try #require(recorder.history.namedEventEntries.count == 0) + + graph.addNamedTraceEvent(eventID) + + let namedEventEntries = recorder.history.namedEventEntries + try #require(namedEventEntries.count == 1) + #expect(namedEventEntries[0].graph == graph) + #expect(namedEventEntries[0].eventID == eventID) + #expect(namedEventEntries[0].eventArgs == []) + #expect(namedEventEntries[0].data == nil) + #expect(namedEventEntries[0].flags == []) + } + + @Test + func traceNamedEventCalledWithData() throws { + let eventID = Graph.registerNamedTraceEvent(name: "test_event_with_data", subsystem: "test_subsystem") + + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let data = Data([0xDE, 0xAD, 0xBE, 0xEF]) + graph.addNamedTraceEvent(eventID, data: data) + + let namedEventEntries = recorder.history.namedEventEntries + try #require(namedEventEntries.count == 1) + #expect(namedEventEntries[0].graph == graph) + #expect(namedEventEntries[0].eventID == eventID) + #expect(namedEventEntries[0].eventArgs == []) + #expect(namedEventEntries[0].data == data) + #expect(namedEventEntries[0].flags == []) + } + + @Test + func traceNamedEventCalledWithEventArgs() throws { + let eventID = Graph.registerNamedTraceEvent(name: "test_event_with_args", subsystem: "test_subsystem") + + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + graph.addNamedTraceEvent(eventID, eventArgs: [111, 222, 333]) + + let namedEventEntries = recorder.history.namedEventEntries + try #require(namedEventEntries.count == 1) + #expect(namedEventEntries[0].graph == graph) + #expect(namedEventEntries[0].eventID == eventID) + #expect(namedEventEntries[0].eventArgs == [111, 222, 333]) + #expect(namedEventEntries[0].data == nil) + #expect(namedEventEntries[0].flags == []) + } + + @Test + func traceNamedEventCalledWithFlags() throws { + let eventID = Graph.registerNamedTraceEvent(name: "test_event_with_flags", subsystem: "test_subsystem") + + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + graph.addNamedTraceEvent(eventID, flags: .init(rawValue: 7)) + + let namedEventEntries = recorder.history.namedEventEntries + try #require(namedEventEntries.count == 1) + #expect(namedEventEntries[0].graph == graph) + #expect(namedEventEntries[0].eventID == eventID) + #expect(namedEventEntries[0].eventArgs == []) + #expect(namedEventEntries[0].data == nil) + #expect(namedEventEntries[0].flags == .init(rawValue: 7)) + } + + @Test + func traceNamedEventCalledWithRecordBacktraceFlag() throws { + let eventID = Graph.registerNamedTraceEvent(name: "test_event_with_flags", subsystem: "test_subsystem") + + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + graph.addNamedTraceEvent(eventID, flags: [.recordBacktrace, .init(rawValue: 7)]) + + let namedEventEntries = recorder.history.namedEventEntries + try #require(namedEventEntries.count == 1) + #expect(namedEventEntries[0].graph == graph) + #expect(namedEventEntries[0].eventID == eventID) + #expect(namedEventEntries[0].eventArgs == []) + #expect(namedEventEntries[0].data == nil) + #expect(namedEventEntries[0].flags == [.recordBacktrace, .init(rawValue: 7)]) + } + + // `named_event_enabled` only affects built-in trace types such as the trace recorder. + @Test + func traceNamedEventCalledWhenNotEnabled() throws { + class DisabledEvents: TestTraceRecorder { + override func namedEventEnabled(eventID: Graph.NamedTraceEventID) -> Bool { + let _ = super.namedEventEnabled(eventID: eventID) + return false + } + } + + let eventID = Graph.registerNamedTraceEvent(name: "test_event_disabled", subsystem: "test_subsystem") + + let graph = Graph() + let recorder = DisabledEvents() + recorder.install(graph: graph) + + try #require(recorder.history.namedEventEntries.count == 0) + + graph.addNamedTraceEvent(eventID) + + let namedEventEntries = recorder.history.namedEventEntries + try #require(namedEventEntries.count == 1) + #expect(namedEventEntries[0].graph == graph) + #expect(namedEventEntries[0].eventID == eventID) + #expect(namedEventEntries[0].eventArgs == []) + #expect(namedEventEntries[0].data == nil) + #expect(namedEventEntries[0].flags == []) + } + } + + @Suite + struct NamedEventEnabledTests { + @Test + func traceNamedEventEnabledCalled() throws { + let eventID = Graph.registerNamedTraceEvent(name: "test_event_enabled", subsystem: "test_subsystem") + + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + try #require(recorder.history.namedEventEnabledEntries.count == 0) + + let _ = graph.traceEventEnabled(for: eventID.rawValue) + + let namedEventEnabledEntries = recorder.history.namedEventEnabledEntries + try #require(namedEventEnabledEntries.count == 1) + #expect(namedEventEnabledEntries[0].eventID == eventID) + } + } +} From 0482b37d3962aaf297ba57a8146ce0b261f7a303 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 6 Aug 2026 20:20:02 +0200 Subject: [PATCH 29/42] Add TracingNodeLifecycleTests --- .../Headers/AGTraceType.h | 4 +- .../Versions/A/Headers/AGTraceType.h | 4 +- Sources/ComputeCxx/Graph/Graph.cpp | 14 +- Sources/ComputeCxx/Graph/UpdateStack.cpp | 4 +- Sources/ComputeCxx/Trace/ExternalTrace.cpp | 6 +- .../include/ComputeCxx/IAGTraceType.h | 4 +- .../Shared/Graph/GraphTracingTests.swift | 10 +- .../Shared/TestSupport/TestTrace.swift | 16 +- .../TestSupport/TestTraceRecorder.swift | 22 +- .../Tracing/TracingNodeLifecycleTests.swift | 722 ++++++++++++++++++ 10 files changed, 765 insertions(+), 41 deletions(-) create mode 100644 Tests/ComputeTests/Shared/Tracing/TracingNodeLifecycleTests.swift diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGTraceType.h b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGTraceType.h index dd87f0eb..03c0058c 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGTraceType.h +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGTraceType.h @@ -52,8 +52,8 @@ typedef struct AG_SWIFT_NAME(Graph.TraceType) AGTraceType { void (*_Nullable node_added)(void *_Nullable context, AGAttribute attribute); void (*_Nullable node_add_edge)(void *_Nullable context, AGAttribute attribute, AGAttribute input, AGInputOptions input_options); - void (*_Nullable node_remove_edge)(void *_Nullable context, AGAttribute attribute, uint32_t input_index); - void (*_Nullable node_set_edge_pending)(void *_Nullable context, AGAttribute attribute, uint32_t input_index, bool pending); + void (*_Nullable node_remove_edge)(void *_Nullable context, AGAttribute attribute, AGAttribute input); + void (*_Nullable node_set_edge_pending)(void *_Nullable context, AGAttribute attribute, AGAttribute input, bool pending); void (*_Nullable node_set_dirty)(void *_Nullable context, AGAttribute attribute, bool dirty); void (*_Nullable node_set_pending)(void *_Nullable context, AGAttribute attribute, bool pending); diff --git a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGTraceType.h b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGTraceType.h index dd87f0eb..03c0058c 100644 --- a/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGTraceType.h +++ b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGTraceType.h @@ -52,8 +52,8 @@ typedef struct AG_SWIFT_NAME(Graph.TraceType) AGTraceType { void (*_Nullable node_added)(void *_Nullable context, AGAttribute attribute); void (*_Nullable node_add_edge)(void *_Nullable context, AGAttribute attribute, AGAttribute input, AGInputOptions input_options); - void (*_Nullable node_remove_edge)(void *_Nullable context, AGAttribute attribute, uint32_t input_index); - void (*_Nullable node_set_edge_pending)(void *_Nullable context, AGAttribute attribute, uint32_t input_index, bool pending); + void (*_Nullable node_remove_edge)(void *_Nullable context, AGAttribute attribute, AGAttribute input); + void (*_Nullable node_set_edge_pending)(void *_Nullable context, AGAttribute attribute, AGAttribute input, bool pending); void (*_Nullable node_set_dirty)(void *_Nullable context, AGAttribute attribute, bool dirty); void (*_Nullable node_set_pending)(void *_Nullable context, AGAttribute attribute, bool pending); diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 519d089a..1a224a83 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -660,17 +660,17 @@ uint32_t Graph::add_input(data::ptr node, AttributeID input, bool allow_ni (node->is_dirty() ? IAGInputOptionsChanged : IAGInputOptionsNone)), }; - uint32_t index = node->insert_input_edge(subgraph, new_input_edge); + uint32_t input_index = node->insert_input_edge(subgraph, new_input_edge); add_input_dependencies(AttributeID(node), input); if (node->is_updating()) { reset_update(node); } if (node->is_dirty()) { - foreach_trace([&node, &input](Trace &trace) { trace.set_edge_pending(node, input, true); }); + foreach_trace([&node, &input_index](Trace &trace) { trace.set_edge_pending(node, input_index, true); }); } - return index; + return input_index; } void Graph::remove_input(data::ptr node, uint32_t index) { @@ -1282,8 +1282,8 @@ void Graph::mark_changed(AttributeID attribute, AttributeType *_Nullable type, c continue; } } - foreach_trace([&output_node, &input_edge](Trace &trace) { - trace.set_edge_pending(output_node, input_edge.attribute, true); + foreach_trace([&output_node, &input_index](Trace &trace) { + trace.set_edge_pending(output_node, input_index, true); }); input_edge.options |= IAGInputOptionsChanged; } @@ -1962,11 +1962,13 @@ void Graph::prepare_trace(Trace &trace) { auto view = iteration == 0 ? const_attribute_view(page) : attribute_view(page); for (auto attribute : view) { if (auto node = attribute.get_node()) { + uint32_t input_index = 0; for (auto input_edge : node->input_edges()) { trace.add_edge(node, input_edge.attribute, input_edge.options); if (input_edge.options & IAGInputOptionsChanged) { - trace.set_edge_pending(node, input_edge.attribute, true); + trace.set_edge_pending(node, input_index, true); } + ++input_index; } } else if (auto indirect_node = attribute.get_indirect_node()) { trace.set_source(indirect_node, indirect_node->source().identifier()); diff --git a/Sources/ComputeCxx/Graph/UpdateStack.cpp b/Sources/ComputeCxx/Graph/UpdateStack.cpp index 2958f962..f16bace6 100644 --- a/Sources/ComputeCxx/Graph/UpdateStack.cpp +++ b/Sources/ComputeCxx/Graph/UpdateStack.cpp @@ -295,8 +295,8 @@ Graph::UpdateStatus Graph::UpdateStack::update() { if (reset_edge_pending) { if (frame.pending && !frame.cancelled) { if (input_edge.options & IAGInputOptionsChanged) { - _graph->foreach_trace([&frame, &input_edge](Trace &trace) { - trace.set_edge_pending(frame.attribute, input_edge.attribute, false); + _graph->foreach_trace([&frame, &input_index](Trace &trace) { + trace.set_edge_pending(frame.attribute, input_index, false); }); input_edge.options &= ~IAGInputOptionsChanged; } diff --git a/Sources/ComputeCxx/Trace/ExternalTrace.cpp b/Sources/ComputeCxx/Trace/ExternalTrace.cpp index b9092e52..4922a726 100644 --- a/Sources/ComputeCxx/Trace/ExternalTrace.cpp +++ b/Sources/ComputeCxx/Trace/ExternalTrace.cpp @@ -204,7 +204,8 @@ void ExternalTrace::add_edge(IAG::data::ptr node, IAG::AttributeID in void ExternalTrace::remove_edge(IAG::data::ptr node, uint32_t input_index) { if (auto callback = _trace->node_remove_edge) { if (IAG::AttributeID(node).subgraph()) { - callback(_context, IAGAttribute(IAG::AttributeID(node)), input_index); + auto input = node->input_edges()[input_index].attribute; + callback(_context, IAGAttribute(IAG::AttributeID(node)), IAGAttribute(input)); } } } @@ -212,7 +213,8 @@ void ExternalTrace::remove_edge(IAG::data::ptr node, uint32_t input_i void ExternalTrace::set_edge_pending(IAG::data::ptr node, uint32_t input_index, bool pending) { if (auto callback = _trace->node_set_edge_pending) { if (IAG::AttributeID(node).subgraph()) { - callback(_context, IAGAttribute(IAG::AttributeID(node)), input_index, pending); + auto input = node->input_edges()[input_index].attribute; + callback(_context, IAGAttribute(IAG::AttributeID(node)), IAGAttribute(input), pending); } } } diff --git a/Sources/ComputeCxx/include/ComputeCxx/IAGTraceType.h b/Sources/ComputeCxx/include/ComputeCxx/IAGTraceType.h index e30d4cd2..1835d1ba 100644 --- a/Sources/ComputeCxx/include/ComputeCxx/IAGTraceType.h +++ b/Sources/ComputeCxx/include/ComputeCxx/IAGTraceType.h @@ -52,8 +52,8 @@ typedef struct IAG_SWIFT_NAME(Graph.TraceType) IAGTraceType { void (*_Nullable node_added)(void *_Nullable context, IAGAttribute attribute); void (*_Nullable node_add_edge)(void *_Nullable context, IAGAttribute attribute, IAGAttribute input, IAGInputOptions input_options); - void (*_Nullable node_remove_edge)(void *_Nullable context, IAGAttribute attribute, uint32_t input_index); - void (*_Nullable node_set_edge_pending)(void *_Nullable context, IAGAttribute attribute, uint32_t input_index, bool pending); + void (*_Nullable node_remove_edge)(void *_Nullable context, IAGAttribute attribute, IAGAttribute input); + void (*_Nullable node_set_edge_pending)(void *_Nullable context, IAGAttribute attribute, IAGAttribute input, bool pending); void (*_Nullable node_set_dirty)(void *_Nullable context, IAGAttribute attribute, bool dirty); void (*_Nullable node_set_pending)(void *_Nullable context, IAGAttribute attribute, bool pending); diff --git a/Tests/ComputeTests/Shared/Graph/GraphTracingTests.swift b/Tests/ComputeTests/Shared/Graph/GraphTracingTests.swift index 16842458..f30306d7 100644 --- a/Tests/ComputeTests/Shared/Graph/GraphTracingTests.swift +++ b/Tests/ComputeTests/Shared/Graph/GraphTracingTests.swift @@ -54,12 +54,10 @@ struct GraphTracingTests { print("[node_added] attribute = \(attribute)") } node_add_edge: { ctx, attribute, input, options in print("[node_add_edge] attribute = \(attribute), input = \(input), options = \(options)") - } node_remove_edge: { ctx, attribute, input_index in - print("[node_remove_edge] attribute = \(attribute), input_index = \(input_index)") - } node_set_edge_pending: { ctx, attribute, input_index, pending in - print( - "[node_set_edge_pending] attribute = \(attribute), input_index = \(input_index), pending = \(pending)" - ) + } node_remove_edge: { ctx, attribute, input in + print("[node_remove_edge] attribute = \(attribute), input = \(input)") + } node_set_edge_pending: { ctx, attribute, input, pending in + print("[node_set_edge_pending] attribute = \(attribute), input = \(input), pending = \(pending)") } node_set_dirty: { ctx, attribute, dirty in print("[node_set_dirty] attribute = \(attribute), dirty = \(dirty)") } node_set_pending: { ctx, attribute, pending in diff --git a/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift b/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift index 0f8eb859..713de9d0 100644 --- a/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift +++ b/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift @@ -102,13 +102,13 @@ public class TestTrace { context.nodeAdded(attribute: attribute) } node_add_edge: { ctx, attribute, input, options in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.nodeAddEdge(attribute: attribute, inputAttribute: input, options: options) - } node_remove_edge: { ctx, attribute, input_index in + context.nodeAddEdge(attribute: attribute, input: input, options: options) + } node_remove_edge: { ctx, attribute, input in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.nodeRemoveEdge(attribute: attribute, inputIndex: Int(input_index)) - } node_set_edge_pending: { ctx, attribute, input_index, pending in + context.nodeRemoveEdge(attribute: attribute, input: input) + } node_set_edge_pending: { ctx, attribute, input, pending in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() - context.nodeSetEdgePending(attribute: attribute, inputIndex: Int(input_index), pending: pending) + context.nodeSetEdgePending(attribute: attribute, input: input, pending: pending) } node_set_dirty: { ctx, attribute, dirty in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() context.nodeSetDirty(attribute: attribute, dirty: dirty) @@ -203,9 +203,9 @@ public class TestTrace { public func subgraphRemoveChild(subgraph: Subgraph, childSubgraph: Subgraph) {} public func nodeAdded(attribute: AnyAttribute) {} - public func nodeAddEdge(attribute: AnyAttribute, inputAttribute: AnyAttribute, options: InputOptions) {} - public func nodeRemoveEdge(attribute: AnyAttribute, inputIndex: Int) {} - public func nodeSetEdgePending(attribute: AnyAttribute, inputIndex: Int, pending: Bool) {} + public func nodeAddEdge(attribute: AnyAttribute, input: AnyAttribute, options: InputOptions) {} + public func nodeRemoveEdge(attribute: AnyAttribute, input: AnyAttribute) {} + public func nodeSetEdgePending(attribute: AnyAttribute, input: AnyAttribute, pending: Bool) {} public func nodeSetDirty(attribute: AnyAttribute, dirty: Bool) {} public func nodeSetPending(attribute: AnyAttribute, pending: Bool) {} diff --git a/Tests/ComputeTests/Shared/TestSupport/TestTraceRecorder.swift b/Tests/ComputeTests/Shared/TestSupport/TestTraceRecorder.swift index 80ec5128..3c9463eb 100644 --- a/Tests/ComputeTests/Shared/TestSupport/TestTraceRecorder.swift +++ b/Tests/ComputeTests/Shared/TestSupport/TestTraceRecorder.swift @@ -99,16 +99,16 @@ public class TestTraceRecorder: TestTrace { history.entries.append(.nodeAdded(.init(attribute: attribute))) } - public override func nodeAddEdge(attribute: AnyAttribute, inputAttribute: AnyAttribute, options: InputOptions) { - history.entries.append(.nodeAddEdge(.init(attribute: attribute, inputAttribute: inputAttribute, options: options))) + public override func nodeAddEdge(attribute: AnyAttribute, input: AnyAttribute, options: InputOptions) { + history.entries.append(.nodeAddEdge(.init(attribute: attribute, input: input, options: options))) } - - public override func nodeRemoveEdge(attribute: AnyAttribute, inputIndex: Int) { - history.entries.append(.nodeRemoveEdge(.init(attribute: attribute, inputIndex: inputIndex))) + + public override func nodeRemoveEdge(attribute: AnyAttribute, input: AnyAttribute) { + history.entries.append(.nodeRemoveEdge(.init(attribute: attribute, input: input))) } - - public override func nodeSetEdgePending(attribute: AnyAttribute, inputIndex: Int, pending: Bool) { - history.entries.append(.nodeSetEdgePending(.init(attribute: attribute, inputIndex: inputIndex, pending: pending))) + + public override func nodeSetEdgePending(attribute: AnyAttribute, input: AnyAttribute, pending: Bool) { + history.entries.append(.nodeSetEdgePending(.init(attribute: attribute, input: input, pending: pending))) } public override func nodeSetDirty(attribute: AnyAttribute, dirty: Bool) { @@ -356,18 +356,18 @@ extension TestTraceRecorder { public struct NodeAddEdgeEntry { public var attribute: AnyAttribute - public var inputAttribute: AnyAttribute + public var input: AnyAttribute public var options: InputOptions } public struct NodeRemoveEdgeEntry { public var attribute: AnyAttribute - public var inputIndex: Int + public var input: AnyAttribute } public struct NodeSetEdgePendingEntry { public var attribute: AnyAttribute - public var inputIndex: Int + public var input: AnyAttribute public var pending: Bool } diff --git a/Tests/ComputeTests/Shared/Tracing/TracingNodeLifecycleTests.swift b/Tests/ComputeTests/Shared/Tracing/TracingNodeLifecycleTests.swift new file mode 100644 index 00000000..f259217d --- /dev/null +++ b/Tests/ComputeTests/Shared/Tracing/TracingNodeLifecycleTests.swift @@ -0,0 +1,722 @@ +import Testing + +@Suite(.serialized(for: \Subgraph.Type.current)) +struct TracingNodeLifecycleTests { + @Suite + struct NodeAddedTests { + @Test + func traceNodeAddedCalled() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + + try #require(recorder.history.nodeAddedEntries.count == 0) + + let attribute = subgraph.apply { + Attribute(value: 1) + } + + let nodeAddedEntries = recorder.history.nodeAddedEntries + try #require(nodeAddedEntries.count == 1) + #expect(nodeAddedEntries[0].attribute == attribute.identifier) + } + } + + @Suite + struct NodeAddEdgeTests { + struct TestRule: Rule { + @OptionalAttribute var input: Int? + var value: Int { return (input ?? 0) + 1 } + } + + @Test + func traceNodeAddEdgeCalled() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let (attribute, input) = subgraph.apply { + let attribute = Attribute(value: 0) + let input = Attribute(value: 1) + return (attribute, input) + } + + try #require(recorder.history.nodeAddEdgeEntries.count == 0) + + attribute.addInput(input, options: [], token: 0) + + let nodeAddEdgeEntries = recorder.history.nodeAddEdgeEntries + try #require(nodeAddEdgeEntries.count == 1) + #expect(nodeAddEdgeEntries[0].attribute == attribute.identifier) + #expect(nodeAddEdgeEntries[0].input == input.identifier) + #expect(nodeAddEdgeEntries[0].options == []) + } + + @Test + func traceNodeAddEdgeCalledWithOptions() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let (attribute, input) = subgraph.apply { + let attribute = Attribute(value: 0) + let input = Attribute(value: 1) + return (attribute, input) + } + + try #require(recorder.history.nodeAddEdgeEntries.count == 0) + + let options: InputOptions = [.unprefetched, .alwaysEnabled] + attribute.addInput(input, options: options, token: 0) + + let nodeAddEdgeEntries = recorder.history.nodeAddEdgeEntries + try #require(nodeAddEdgeEntries.count == 1) + #expect(nodeAddEdgeEntries[0].attribute == attribute.identifier) + #expect(nodeAddEdgeEntries[0].input == input.identifier) + #expect(nodeAddEdgeEntries[0].options == options) + } + + @Test + func traceNodeAddEdgeCalledOnUpdate() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let (attribute, input) = subgraph.apply { + let input = Attribute(value: 1) + let attribute = Attribute(TestRule(input: OptionalAttribute(input))) + return (attribute, input) + } + + try #require(recorder.history.nodeAddEdgeEntries.count == 0) + + let _ = attribute.value + + let nodeAddEdgeEntries = recorder.history.nodeAddEdgeEntries + try #require(nodeAddEdgeEntries.count == 1) + #expect(nodeAddEdgeEntries[0].attribute == attribute.identifier) + #expect(nodeAddEdgeEntries[0].input == input.identifier) + #expect(nodeAddEdgeEntries[0].options == []) + } + } + + @Suite + struct NodeRemoveEdgeTests { + struct TestRule: Rule { + @OptionalAttribute var input: Int? + var value: Int { return (input ?? 0) + 1 } + } + + @Test + func traceNodeRemoveEdgeCalledOnUpdateWhenAddedExplicitly() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let (attribute, input) = subgraph.apply { + let input = Attribute(value: 1) + let attribute = Attribute(TestRule(input: OptionalAttribute(input))) + return (attribute, input) + } + + attribute.addInput(input, options: [], token: 0) + try #require(recorder.history.nodeAddEdgeEntries.count == 1) + try #require(recorder.history.nodeRemoveEdgeEntries.count == 0) + + attribute.mutateBody(as: TestRule.self, invalidating: true) { body in + body.$input = nil + } + let _ = attribute.value + + let nodeRemoveEdgeEntries = recorder.history.nodeRemoveEdgeEntries + try #require(nodeRemoveEdgeEntries.count == 1) + #expect(nodeRemoveEdgeEntries[0].attribute == attribute.identifier) + #expect(nodeRemoveEdgeEntries[0].input == input.identifier) + } + + @Test + func traceNodeRemoveEdgeNOtCalledOnUpdateWhenAddedExplicitlyWithAlwaysEnabled() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let (attribute, input) = subgraph.apply { + let input = Attribute(value: 1) + let attribute = Attribute(TestRule(input: OptionalAttribute(input))) + return (attribute, input) + } + + attribute.addInput(input, options: [.alwaysEnabled], token: 0) + try #require(recorder.history.nodeAddEdgeEntries.count == 1) + try #require(recorder.history.nodeRemoveEdgeEntries.count == 0) + + attribute.mutateBody(as: TestRule.self, invalidating: true) { body in + body.$input = nil + } + let _ = attribute.value + + let nodeRemoveEdgeEntries = recorder.history.nodeRemoveEdgeEntries + try #require(nodeRemoveEdgeEntries.count == 0) + } + + @Test + func traceNodeRemoveEdgeCalledOnUpdateWithAddedImplicitly() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let (attribute, input) = subgraph.apply { + let input = Attribute(value: 1) + let attribute = Attribute(TestRule(input: OptionalAttribute(input))) + return (attribute, input) + } + + let _ = attribute.value + try #require(recorder.history.nodeAddEdgeEntries.count == 1) + try #require(recorder.history.nodeRemoveEdgeEntries.count == 0) + + attribute.mutateBody(as: TestRule.self, invalidating: true) { body in + body.$input = nil + } + let _ = attribute.value + + let nodeRemoveEdgeEntries = recorder.history.nodeRemoveEdgeEntries + try #require(nodeRemoveEdgeEntries.count == 1) + #expect(nodeRemoveEdgeEntries[0].attribute == attribute.identifier) + #expect(nodeRemoveEdgeEntries[0].input == input.identifier) + } + + @Test + func traceNodeRemoveEdgeNotCalledOnSameSubgraphInvalidate() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let (attribute, _) = subgraph.apply { + let input = Attribute(value: 1) + let attribute = Attribute(TestRule(input: OptionalAttribute(input))) + return (attribute, input) + } + + let _ = attribute.value + try #require(recorder.history.nodeAddEdgeEntries.count == 1) + try #require(recorder.history.nodeRemoveEdgeEntries.count == 0) + + subgraph.invalidate() + + let nodeRemoveEdgeEntries = recorder.history.nodeRemoveEdgeEntries + try #require(nodeRemoveEdgeEntries.count == 0) + } + + @Test + func traceNodeRemoveEdgeCalledOnDifferentSubgraphInvalidate() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let inputSubgraph = Subgraph(graph: graph) + let input = inputSubgraph.apply { + Attribute(value: 1) + } + + let outputSubgraph = Subgraph(graph: graph) + let attribute = outputSubgraph.apply { + Attribute(TestRule(input: OptionalAttribute(input))) + } + + let _ = attribute.value + try #require(recorder.history.nodeAddEdgeEntries.count == 1) + try #require(recorder.history.nodeRemoveEdgeEntries.count == 0) + + inputSubgraph.invalidate() + + let nodeRemoveEdgeEntries = recorder.history.nodeRemoveEdgeEntries + try #require(nodeRemoveEdgeEntries.count == 1) + #expect(nodeRemoveEdgeEntries[0].attribute == attribute.identifier) + #expect(nodeRemoveEdgeEntries[0].input == input.identifier) + } + + @Test + func traceNodeRemoveEdgeReportsCorrectInputAttribute() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let inputSubgraph = Subgraph(graph: graph) + let (input1, input2) = inputSubgraph.apply { + let input1 = Attribute(value: 1) + let input2 = Attribute(value: 2) + return (input1, input2) + } + + let outputSubgraph = Subgraph(graph: graph) + let attribute = outputSubgraph.apply { + Attribute(value: 0) + } + + attribute.addInput(input1, options: [], token: 0) + attribute.addInput(input2, options: [], token: 1) + try #require(recorder.history.nodeAddEdgeEntries.count == 2) + try #require(recorder.history.nodeRemoveEdgeEntries.count == 0) + + inputSubgraph.invalidate() + + let nodeRemoveEdgeEntries = recorder.history.nodeRemoveEdgeEntries + try #require(nodeRemoveEdgeEntries.count == 2) + #expect(nodeRemoveEdgeEntries[0].attribute == attribute.identifier) + #expect(nodeRemoveEdgeEntries[0].input == input2.identifier) + #expect(nodeRemoveEdgeEntries[1].attribute == attribute.identifier) + #expect(nodeRemoveEdgeEntries[1].input == input1.identifier) + } + } + + @Suite + struct NodeSetEdgePendingTests { + struct TestRule: Rule { + var value: Int { return 0 } + } + + @Test + func traceNodeSetEdgePendingCalledOnInputAddedToDirtyNode() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let (attribute, input) = subgraph.apply { + let input = Attribute(value: 1) + let attribute = Attribute(TestRule()) + return (attribute, input) + } + + try #require(recorder.history.nodeSetEdgePendingEntries.count == 0) + + attribute.invalidateValue() + attribute.addInput(input, options: [], token: 0) + + let nodeSetEdgePendingEntries = recorder.history.nodeSetEdgePendingEntries + try #require(nodeSetEdgePendingEntries.count == 1) + #expect(nodeSetEdgePendingEntries[0].attribute == attribute.identifier) + #expect(nodeSetEdgePendingEntries[0].input == input.identifier) + #expect(nodeSetEdgePendingEntries[0].pending == true) + } + + @Test + func traceNodeSetEdgePendingCalledOnInputChanged() throws { + struct TestRule: Rule { + @Attribute var input: Int + var value: Int { input + 1 } + } + + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let (input, attribute) = subgraph.apply { + let input = Attribute(value: 1) + let attribute = Attribute(TestRule(input: input)) + return (input, attribute) + } + + let _ = attribute.value + try #require(recorder.history.nodeSetEdgePendingEntries.count == 2) + + input.value = 2 + + let nodeSetEdgePendingEntries = Array(recorder.history.nodeSetEdgePendingEntries) + try #require(nodeSetEdgePendingEntries.count == 3) + #expect(nodeSetEdgePendingEntries[2].attribute == attribute.identifier) + #expect(nodeSetEdgePendingEntries[2].input == input.identifier) + #expect(nodeSetEdgePendingEntries[2].pending == true) + } + + @Test + func traceNodeSetEdgePendingCalledOnUpdateFrameReset() throws { + struct TestRule: Rule { + @Attribute var input: Int + var value: Int { input + 1 } + } + + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let (input, attribute) = subgraph.apply { + let input = Attribute(value: 1) + let attribute = Attribute(TestRule(input: input)) + return (input, attribute) + } + + let _ = attribute.value + input.value = 2 + try #require(recorder.history.nodeSetEdgePendingEntries.count == 3) + + let _ = attribute.value + + let nodeSetEdgePendingEntries = Array(recorder.history.nodeSetEdgePendingEntries) + try #require(nodeSetEdgePendingEntries.count == 4) + #expect(nodeSetEdgePendingEntries[3].attribute == attribute.identifier) + #expect(nodeSetEdgePendingEntries[3].input == input.identifier) + #expect(nodeSetEdgePendingEntries[3].pending == false) + } + } + + @Suite + struct NodeSetDirtyTests { + struct TestRule: Rule { + var value: Int { 1 } + } + + @Test + func traceNodeSetDirtyCalledOnInvalidateInitializedAttribute() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let attribute = subgraph.apply { + Attribute(TestRule(), initialValue: 0) + } + + try #require(recorder.history.nodeSetDirtyEntries.count == 0) + + attribute.invalidateValue() + + let nodeSetDirtyEntries = recorder.history.nodeSetDirtyEntries + try #require(nodeSetDirtyEntries.count == 1) + #expect(nodeSetDirtyEntries[0].attribute == attribute.identifier) + #expect(nodeSetDirtyEntries[0].dirty == true) + } + + @Test + func traceNodeSetDirtyNotCalledOnInvalidateUninitializedAttribute() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let attribute = subgraph.apply { + Attribute(TestRule()) + } + + try #require(recorder.history.nodeSetDirtyEntries.count == 0) + + attribute.invalidateValue() + + let nodeSetDirtyEntries = recorder.history.nodeSetDirtyEntries + try #require(nodeSetDirtyEntries.count == 0) + } + + @Test + func traceNodeSetDirtyNotCalledOnInvalidateExternalAttribute() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let attribute = subgraph.apply { + Attribute(value: 1) + } + + try #require(recorder.history.nodeSetDirtyEntries.count == 0) + + attribute.invalidateValue() + + let nodeSetDirtyEntries = recorder.history.nodeSetDirtyEntries + try #require(nodeSetDirtyEntries.count == 0) + } + } + + @Suite + struct NodeSetPendingTests { + struct TestRule: Rule { + var value: Int { 1 } + } + + @Test + func traceNodeSetPendingCalledOnInvalidateInitializedAttribute() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let attribute = subgraph.apply { + Attribute(TestRule(), initialValue: 0) + } + + try #require(recorder.history.nodeSetPendingEntries.count == 0) + + attribute.invalidateValue() + + let nodeSetPendingEntries = recorder.history.nodeSetPendingEntries + try #require(nodeSetPendingEntries.count == 1) + #expect(nodeSetPendingEntries[0].attribute == attribute.identifier) + #expect(nodeSetPendingEntries[0].pending == true) + } + + @Test + func traceNodeSetPendingNotCalledOnInvalidateUninitializedAttribute() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let attribute = subgraph.apply { + Attribute(TestRule()) + } + + try #require(recorder.history.nodeSetPendingEntries.count == 0) + + attribute.invalidateValue() + + let nodeSetPendingEntries = recorder.history.nodeSetPendingEntries + try #require(nodeSetPendingEntries.count == 0) + } + + @Test + func traceNodeSetPendingNotCalledOnInvalidateExternalAttribute() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let attribute = subgraph.apply { + Attribute(value: 1) + } + + try #require(recorder.history.nodeSetPendingEntries.count == 0) + + attribute.invalidateValue() + + let nodeSetPendingEntries = recorder.history.nodeSetPendingEntries + try #require(nodeSetPendingEntries.count == 0) + } + } + + @Suite + struct NodeSetValueTests { + struct TestRule: Rule { + var value: Int { 1 } + } + + @Test + func traceNodeSetValueCalledOnCreateAttributeWithValue() throws { + class NodeSetValueTrace: TestTraceRecorder { + var capturedValue: Int? + + override func nodeSetValue(attribute: AnyAttribute, value: UnsafeRawPointer) { + super.nodeSetValue(attribute: attribute, value: value) + capturedValue = value.assumingMemoryBound(to: Int.self).pointee + } + } + + let graph = Graph() + let recorder = NodeSetValueTrace() + recorder.install(graph: graph) + + try #require(recorder.history.nodeSetValueEntries.count == 0) + + let subgraph = Subgraph(graph: graph) + let attribute = subgraph.apply { + Attribute(value: 42) + } + + let nodeSetValueEntries = recorder.history.nodeSetValueEntries + try #require(nodeSetValueEntries.count == 1) + #expect(nodeSetValueEntries[0].attribute == attribute.identifier) + // nodeSetValueEntries[0].value is not valid by this point + #expect(recorder.capturedValue == 42) + } + + @Test + func traceNodeSetValueNotCalledOnCreateAttributeWithType() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + try #require(recorder.history.nodeSetValueEntries.count == 0) + + let subgraph = Subgraph(graph: graph) + let _ = subgraph.apply { + Attribute(type: Int.self) + } + + let nodeSetValueEntries = recorder.history.nodeSetValueEntries + try #require(nodeSetValueEntries.count == 0) + } + + @Test + func traceNodeSetValueCalledOnCreateAttributeWithRuleAndInitialValue() throws { + class NodeSetValueTrace: TestTraceRecorder { + var capturedValue: Int? + + override func nodeSetValue(attribute: AnyAttribute, value: UnsafeRawPointer) { + super.nodeSetValue(attribute: attribute, value: value) + capturedValue = value.assumingMemoryBound(to: Int.self).pointee + } + } + + let graph = Graph() + let recorder = NodeSetValueTrace() + recorder.install(graph: graph) + + try #require(recorder.history.nodeSetValueEntries.count == 0) + + let subgraph = Subgraph(graph: graph) + let attribute = subgraph.apply { + Attribute(TestRule(), initialValue: 42) + } + + let nodeSetValueEntries = recorder.history.nodeSetValueEntries + try #require(nodeSetValueEntries.count == 1) + #expect(nodeSetValueEntries[0].attribute == attribute.identifier) + // nodeSetValueEntries[0].value is not valid by this point + #expect(recorder.capturedValue == 42) + } + + @Test + func traceNodeSetValueCalledOnCreateUnitializedAttributeWithRule() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + try #require(recorder.history.nodeSetValueEntries.count == 0) + + let subgraph = Subgraph(graph: graph) + let _ = subgraph.apply { + Attribute(TestRule()) + } + + let nodeSetValueEntries = recorder.history.nodeSetValueEntries + try #require(nodeSetValueEntries.count == 0) + } + + @Test + func traceNodeSetValueCalledOnSetValue() throws { + class NodeSetValueTrace: TestTraceRecorder { + var capturedValue: Int? + + override func nodeSetValue(attribute: AnyAttribute, value: UnsafeRawPointer) { + super.nodeSetValue(attribute: attribute, value: value) + capturedValue = value.assumingMemoryBound(to: Int.self).pointee + } + } + + let graph = Graph() + let recorder = NodeSetValueTrace() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let attribute = subgraph.apply { + Attribute(value: 0) + } + + try #require(recorder.history.nodeSetValueEntries.count == 1) + + attribute.value = 42 + + let nodeSetValueEntries = recorder.history.nodeSetValueEntries + try #require(nodeSetValueEntries.count == 2) + #expect(nodeSetValueEntries[0].attribute == attribute.identifier) + #expect(recorder.capturedValue == 42) + } + } + + @Suite + struct NodeMarkValueTests { + @Test + func traceNodeMarkValueCalledOnInvalidateValue() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let attribute = subgraph.apply { + Attribute(value: 0) + } + + try #require(recorder.history.nodeMarkValueEntries.count == 0) + + attribute.invalidateValue() + + let nodeMarkValueEntries = recorder.history.nodeMarkValueEntries + try #require(nodeMarkValueEntries.count == 1) + #expect(nodeMarkValueEntries[0].attribute == attribute.identifier) + } + } + + @Suite + struct BeginModifyNodeTests { + struct TestRule: Rule { + var flag: Int + var value: Int { + flag + } + } + + @Test + func traceBeginModifyNodeCalled() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let attribute = subgraph.apply { + Attribute(TestRule(flag: 1)) + } + + try #require(recorder.history.beginModifyNodeEntries.count == 0) + + attribute.mutateBody(as: TestRule.self, invalidating: false) { body in + body.flag = 2 + } + + let beginModifyNodeEntries = recorder.history.beginModifyNodeEntries + try #require(beginModifyNodeEntries.count == 1) + #expect(beginModifyNodeEntries[0].attribute == attribute.identifier) + } + } + + @Suite + struct EndModifyNodeTests { + struct TestRule: Rule { + var flag: Int + var value: Int { + flag + } + } + + @Test + func traceEndModifyNodeCalled() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let attribute = subgraph.apply { + Attribute(TestRule(flag: 1)) + } + + try #require(recorder.history.endModifyNodeEntries.count == 0) + + attribute.mutateBody(as: TestRule.self, invalidating: false) { body in + body.flag = 2 + } + + let endModifyNodeEntries = recorder.history.endModifyNodeEntries + try #require(endModifyNodeEntries.count == 1) + #expect(endModifyNodeEntries[0].attribute == attribute.identifier) + } + } +} From cd5be1badc4faa55c9e34f62447621c0fed49ecb Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 6 Aug 2026 20:28:27 +0200 Subject: [PATCH 30/42] Add TracingIndirectNodeLifecycleTests --- .../TracingIndirectNodeLifecycleTests.swift | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 Tests/ComputeTests/Shared/Tracing/TracingIndirectNodeLifecycleTests.swift diff --git a/Tests/ComputeTests/Shared/Tracing/TracingIndirectNodeLifecycleTests.swift b/Tests/ComputeTests/Shared/Tracing/TracingIndirectNodeLifecycleTests.swift new file mode 100644 index 00000000..033c98a3 --- /dev/null +++ b/Tests/ComputeTests/Shared/Tracing/TracingIndirectNodeLifecycleTests.swift @@ -0,0 +1,115 @@ +import Testing + +@Suite(.serialized(for: \Subgraph.Type.current)) +struct TracingIndirectNodeLifecycleTests { + @Suite + struct IndirectNodeAddedTests { + @Test + func traceIndirectNodeAddedCalled() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let source = subgraph.apply { + Attribute(value: 1) + } + + try #require(recorder.history.indirectNodeAddedEntries.count == 0) + + let indirect = subgraph.apply { + IndirectAttribute(source: source) + } + + let indirectNodeAddedEntries = recorder.history.indirectNodeAddedEntries + try #require(indirectNodeAddedEntries.count == 1) + #expect(indirectNodeAddedEntries[0].attribute == indirect.identifier) + } + } + + @Suite + struct IndirectNodeSetSourceTests { + @Test + func traceIndirectNodeSetSourceCalledOnSetSource() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let indirect = subgraph.apply { + let source1 = Attribute(value: 1) + return IndirectAttribute(source: source1) + } + + try #require(recorder.history.indirectNodeSetSourceEntries.count == 0) + + let source2 = subgraph.apply { + Attribute(value: 2) + } + + indirect.source = source2 + + let indirectNodeSetSourceEntries = recorder.history.indirectNodeSetSourceEntries + try #require(indirectNodeSetSourceEntries.count == 1) + #expect(indirectNodeSetSourceEntries[0].attribute == indirect.identifier) + #expect(indirectNodeSetSourceEntries[0].source == source2.identifier) + } + + @Test + func traceIndirectNodeSetSourceCalledOnResetSource() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let (indirect, source1) = subgraph.apply { + let source1 = Attribute(value: 1) + let indirect = IndirectAttribute(source: source1) + return (indirect, source1) + } + + let source2 = subgraph.apply { + Attribute(value: 2) + } + + indirect.source = source2 + try #require(recorder.history.indirectNodeSetSourceEntries.count == 1) + + indirect.resetSource() + + let indirectNodeSetSourceEntries = recorder.history.indirectNodeSetSourceEntries + try #require(indirectNodeSetSourceEntries.count == 2) + #expect(indirectNodeSetSourceEntries[1].attribute == indirect.identifier) + #expect(indirectNodeSetSourceEntries[1].source == source1.identifier) + } + } + + @Suite + struct IndirectNodeSetDependencyTests { + @Test + func traceIndirectNodeSetDependencyCalled() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let indirect = subgraph.apply { + let source = Attribute(value: 1) + return IndirectAttribute(source: source) + } + + let dependency = subgraph.apply { + Attribute(value: 2) + } + + try #require(recorder.history.indirectNodeSetDependencyEntries.count == 0) + + indirect.dependency = dependency.identifier + + let indirectNodeSetDependencyEntries = recorder.history.indirectNodeSetDependencyEntries + try #require(indirectNodeSetDependencyEntries.count == 1) + #expect(indirectNodeSetDependencyEntries[0].attribute == indirect.identifier) + #expect(indirectNodeSetDependencyEntries[0].dependency == dependency.identifier) + } + } +} From 67511442e9e621056ed52acf46e77ce0a98d1030 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 6 Aug 2026 20:29:14 +0200 Subject: [PATCH 31/42] Add TracingGraphLifecycleTests --- .../Tracing/TracingGraphLifecycleTests.swift | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 Tests/ComputeTests/Shared/Tracing/TracingGraphLifecycleTests.swift diff --git a/Tests/ComputeTests/Shared/Tracing/TracingGraphLifecycleTests.swift b/Tests/ComputeTests/Shared/Tracing/TracingGraphLifecycleTests.swift new file mode 100644 index 00000000..c4edad3d --- /dev/null +++ b/Tests/ComputeTests/Shared/Tracing/TracingGraphLifecycleTests.swift @@ -0,0 +1,58 @@ +import Testing + +@Suite +struct TracingGraphLifecycleTests { + @Suite + struct GraphCreatedTests { + @Test + func traceGraphCreatedCalledForSharedGraph() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + try #require(recorder.history.graphCreatedEntries.count == 0) + + let sharedGraph = Graph(shared: graph) + + let graphCreatedEntries = recorder.history.graphCreatedEntries + try #require(graphCreatedEntries.count == 1) + #expect(graphCreatedEntries[0].graph == sharedGraph) + } + } + + @Suite + struct GraphDestroyTests { + @Test + func traceGraphDestroyCalledOnInvalidate() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + try #require(recorder.history.graphDestroyEntries.count == 0) + + graph.invalidate() + + let graphDestroyEntries = recorder.history.graphDestroyEntries + try #require(graphDestroyEntries.count == 1) + #expect(graphDestroyEntries[0].graph == graph) + } + } + + @Suite + struct GraphNeedsUpdateTests { + @Test + func traceGraphNeedsUpdateCalled() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + try #require(recorder.history.graphNeedsUpdateEntries.count == 0) + + graph.setNeedsUpdate() + + let graphNeedsUpdateEntries = recorder.history.graphNeedsUpdateEntries + try #require(graphNeedsUpdateEntries.count == 1) + #expect(graphNeedsUpdateEntries[0].graph == graph) + } + } +} From f92ebdb83c3358e71814cb8278b8628bc39150e1 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 6 Aug 2026 20:31:56 +0200 Subject: [PATCH 32/42] Add TracingSubgraphLifecycleTests --- .../Subgraph/SubgraphLifecycleTests.swift | 110 ---------------- .../TracingSubgraphLifecycleTests.swift | 123 ++++++++++++++++++ 2 files changed, 123 insertions(+), 110 deletions(-) create mode 100644 Tests/ComputeTests/Shared/Tracing/TracingSubgraphLifecycleTests.swift diff --git a/Tests/ComputeTests/Shared/Subgraph/SubgraphLifecycleTests.swift b/Tests/ComputeTests/Shared/Subgraph/SubgraphLifecycleTests.swift index b3c9146c..42a7fd7c 100644 --- a/Tests/ComputeTests/Shared/Subgraph/SubgraphLifecycleTests.swift +++ b/Tests/ComputeTests/Shared/Subgraph/SubgraphLifecycleTests.swift @@ -24,21 +24,6 @@ struct SubgraphLifecycleTests { #expect(graph.counter(for: .createdSubgraphs) == 1) } } - - @Test - func traceSubgraphCreatedCalled() { - let graph = Graph() - let recorder = TestTraceRecorder() - recorder.install(graph: graph) - - #expect(recorder.history.subgraphCreatedEntries.count == 0) - - let subgraph = Subgraph(graph: graph) - - let subgraphCreatedEntries = recorder.history.subgraphCreatedEntries - #expect(subgraphCreatedEntries.count == 1) - #expect(subgraphCreatedEntries[0].subgraph == subgraph) - } } @Suite @@ -69,23 +54,6 @@ struct SubgraphLifecycleTests { #expect(graph.counter(for: .createdSubgraphs) == 1) } } - - @Test - func traceSubgraphDestroyCalledForOnSubgraphInvalidate() { - let graph = Graph() - let recorder = TestTraceRecorder() - recorder.install(graph: graph) - - let subgraph = Subgraph(graph: graph) - - #expect(recorder.history.subgraphDestroyEntries.count == 0) - - subgraph.invalidate() - - let subgraphDestroyEntries = recorder.history.subgraphDestroyEntries - #expect(subgraphDestroyEntries.count == 1) - #expect(subgraphDestroyEntries[0].subgraph == subgraph) - } } @Suite @@ -120,83 +88,5 @@ struct SubgraphLifecycleTests { #expect(graph.counter(for: .subgraphs) == 0) #expect(graph.counter(for: .createdSubgraphs) == 1) } - - @Test - func traceSubgraphDestroyNotCalledOnSubgraphDeinit() { - let graph = Graph() - let recorder = TestTraceRecorder() - recorder.install(graph: graph) - - #expect(recorder.history.subgraphDestroyEntries.count == 0) - - autoreleasepool { - let _ = Subgraph(graph: graph) - } - - let subgraphDestroyEntries = recorder.history.subgraphDestroyEntries - #expect(subgraphDestroyEntries.count == 0) // Not called when Subgraph deinit is called - } - } - - @Suite - struct SubgraphAddChildTests { - @Test - func traceSubgraphAddChildCalled() { - class SubgraphTrace: TestTraceRecorder { - override func subgraphAddChild(subgraph: Subgraph, childSubgraph: Subgraph) { - super.subgraphAddChild(subgraph: subgraph, childSubgraph: childSubgraph) - - #expect(subgraph.childCount == 0) // "will" semantics - #expect(childSubgraph.parentCount == 0) // "will" semantics - } - } - - let graph = Graph() - let recorder = SubgraphTrace() - recorder.install(graph: graph) - - let subgraph = Subgraph(graph: graph) - let childSubgraph = Subgraph(graph: graph) - subgraph.addChild(childSubgraph) - - #expect(subgraph.childCount == 1) - #expect(childSubgraph.parentCount == 1) - - let subgraphAddChildEntries = recorder.history.subgraphAddChildEntries - #expect(subgraphAddChildEntries.count == 1) - #expect(subgraphAddChildEntries[0].subgraph == subgraph) - #expect(subgraphAddChildEntries[0].childSubgraph == childSubgraph) - } - } - - @Suite - struct SubgraphRemoveChildTests { - @Test - func traceSubgraphRemoveChildCalled() { - class SubgraphTrace: TestTraceRecorder { - override func subgraphRemoveChild(subgraph: Subgraph, childSubgraph: Subgraph) { - super.subgraphRemoveChild(subgraph: subgraph, childSubgraph: childSubgraph) - - #expect(subgraph.childCount == 1) // "will" semantics - #expect(childSubgraph.parentCount == 0) // "did" semantics - } - } - - let graph = Graph() - let recorder = SubgraphTrace() - recorder.install(graph: graph) - - #expect(recorder.history.subgraphRemoveChildEntries.count == 0) - - let subgraph = Subgraph(graph: graph) - let childSubgraph = Subgraph(graph: graph) - subgraph.addChild(childSubgraph) - subgraph.removeChild(childSubgraph) - - let subgraphRemoveChildEntries = recorder.history.subgraphRemoveChildEntries - #expect(subgraphRemoveChildEntries.count == 1) - #expect(subgraphRemoveChildEntries[0].subgraph == subgraph) - #expect(subgraphRemoveChildEntries[0].childSubgraph == childSubgraph) - } } } diff --git a/Tests/ComputeTests/Shared/Tracing/TracingSubgraphLifecycleTests.swift b/Tests/ComputeTests/Shared/Tracing/TracingSubgraphLifecycleTests.swift new file mode 100644 index 00000000..880610f1 --- /dev/null +++ b/Tests/ComputeTests/Shared/Tracing/TracingSubgraphLifecycleTests.swift @@ -0,0 +1,123 @@ +import Testing + +@Suite +struct TracingSubgraphLifecycleTests { + @Suite + struct SubgraphCreatedTests { + @Test + func traceSubgraphCreatedCalled() { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + #expect(recorder.history.subgraphCreatedEntries.count == 0) + + let subgraph = Subgraph(graph: graph) + + let subgraphCreatedEntries = recorder.history.subgraphCreatedEntries + #expect(subgraphCreatedEntries.count == 1) + #expect(subgraphCreatedEntries[0].subgraph == subgraph) + } + } + + @Suite + struct SubgraphInvalidateTests { + @Test + func traceSubgraphDestroyCalledForOnSubgraphInvalidate() { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + + #expect(recorder.history.subgraphDestroyEntries.count == 0) + + subgraph.invalidate() + + let subgraphDestroyEntries = recorder.history.subgraphDestroyEntries + #expect(subgraphDestroyEntries.count == 1) + #expect(subgraphDestroyEntries[0].subgraph == subgraph) + } + } + + @Suite + struct SubgraphDestroyTests { + @Test + func traceSubgraphDestroyNotCalledOnSubgraphDeinit() { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + #expect(recorder.history.subgraphDestroyEntries.count == 0) + + autoreleasepool { + let _ = Subgraph(graph: graph) + } + + let subgraphDestroyEntries = recorder.history.subgraphDestroyEntries + #expect(subgraphDestroyEntries.count == 0) // Not called when Subgraph deinit is called + } + } + + @Suite + struct SubgraphAddChildTests { + @Test + func traceSubgraphAddChildCalled() { + class SubgraphTrace: TestTraceRecorder { + override func subgraphAddChild(subgraph: Subgraph, childSubgraph: Subgraph) { + super.subgraphAddChild(subgraph: subgraph, childSubgraph: childSubgraph) + + #expect(subgraph.childCount == 0) // "will" semantics + #expect(childSubgraph.parentCount == 0) // "will" semantics + } + } + + let graph = Graph() + let recorder = SubgraphTrace() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let childSubgraph = Subgraph(graph: graph) + subgraph.addChild(childSubgraph) + + #expect(subgraph.childCount == 1) + #expect(childSubgraph.parentCount == 1) + + let subgraphAddChildEntries = recorder.history.subgraphAddChildEntries + #expect(subgraphAddChildEntries.count == 1) + #expect(subgraphAddChildEntries[0].subgraph == subgraph) + #expect(subgraphAddChildEntries[0].childSubgraph == childSubgraph) + } + } + + @Suite + struct SubgraphRemoveChildTests { + @Test + func traceSubgraphRemoveChildCalled() { + class SubgraphTrace: TestTraceRecorder { + override func subgraphRemoveChild(subgraph: Subgraph, childSubgraph: Subgraph) { + super.subgraphRemoveChild(subgraph: subgraph, childSubgraph: childSubgraph) + + #expect(subgraph.childCount == 1) // "will" semantics + #expect(childSubgraph.parentCount == 0) // "did" semantics + } + } + + let graph = Graph() + let recorder = SubgraphTrace() + recorder.install(graph: graph) + + #expect(recorder.history.subgraphRemoveChildEntries.count == 0) + + let subgraph = Subgraph(graph: graph) + let childSubgraph = Subgraph(graph: graph) + subgraph.addChild(childSubgraph) + subgraph.removeChild(childSubgraph) + + let subgraphRemoveChildEntries = recorder.history.subgraphRemoveChildEntries + #expect(subgraphRemoveChildEntries.count == 1) + #expect(subgraphRemoveChildEntries[0].subgraph == subgraph) + #expect(subgraphRemoveChildEntries[0].childSubgraph == childSubgraph) + } + } +} From 349b7764b7fa7ad70cd2dc495c142e7e79445cf7 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 6 Aug 2026 20:50:12 +0200 Subject: [PATCH 33/42] Add default arguments to addNamedTraceEvent --- Sources/Compute/Graph/Graph.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/Compute/Graph/Graph.swift b/Sources/Compute/Graph/Graph.swift index 4e312710..ffe10f6b 100644 --- a/Sources/Compute/Graph/Graph.swift +++ b/Sources/Compute/Graph/Graph.swift @@ -171,9 +171,9 @@ extension Graph { @_transparent public func addNamedTraceEvent( _ eventID: Graph.NamedTraceEventID, - eventArgs: [UInt32], - data: Data?, - flags: Graph.NamedTraceEventFlags + eventArgs: [UInt32] = [], + data: Data? = nil, + flags: Graph.NamedTraceEventFlags = [] ) { addNamedTraceEvent( eventID: eventID, From c41aba5fbed94ef189bb0f93cd87e7cc59a60ddd Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 6 Aug 2026 21:26:22 +0200 Subject: [PATCH 34/42] Add TracingUpdateTests --- .../Shared/Tracing/TracingUpdateTests.swift | 376 ++++++++++++++++++ 1 file changed, 376 insertions(+) create mode 100644 Tests/ComputeTests/Shared/Tracing/TracingUpdateTests.swift diff --git a/Tests/ComputeTests/Shared/Tracing/TracingUpdateTests.swift b/Tests/ComputeTests/Shared/Tracing/TracingUpdateTests.swift new file mode 100644 index 00000000..bd98a017 --- /dev/null +++ b/Tests/ComputeTests/Shared/Tracing/TracingUpdateTests.swift @@ -0,0 +1,376 @@ +import Testing + +@Suite(.serialized(for: \Subgraph.Type.current)) +struct TracingUpdateTests { + struct TestRule: Rule { + @Attribute var input: Int + var value: Int { 1 } + } + + @Suite + struct BeginSubgraphUpdateTests { + @Test + func traceBeginSubgraphUpdateCalledOnSubgraphUpdateWithDirtyFlags() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let attribute = subgraph.apply { + let input = Attribute(value: 1) + return Attribute(TestRule(input: input), initialValue: 0) + } + + attribute.setFlags(Subgraph.Flags(rawValue: 1), mask: Subgraph.Flags(rawValue: 1)) + attribute.invalidateValue() + + try #require(recorder.history.beginSubgraphUpdateEntries.count == 0) + + subgraph.update(flags: Subgraph.Flags(rawValue: 1)) + + let beginSubgraphUpdateEntries = recorder.history.beginSubgraphUpdateEntries + try #require(beginSubgraphUpdateEntries.count == 1) + #expect(beginSubgraphUpdateEntries[0].subgraph == subgraph) + #expect(beginSubgraphUpdateEntries[0].flags == Subgraph.Flags(rawValue: 1)) + } + + @Test + func traceBeginSubgraphUpdateNotCalledOnSubgraphUpdateWithNonDirtyFlags() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let attribute = subgraph.apply { + let input = Attribute(value: 1) + return Attribute(TestRule(input: input), initialValue: 0) + } + + attribute.setFlags(Subgraph.Flags(rawValue: 1), mask: Subgraph.Flags(rawValue: 1)) + attribute.invalidateValue() + + try #require(recorder.history.beginSubgraphUpdateEntries.count == 0) + + subgraph.update(flags: Subgraph.Flags(rawValue: 2)) // won't match attribute + + try #require(recorder.history.beginSubgraphUpdateEntries.count == 0) + } + + @Test + func traceBeginSubgraphUpdateNotCalledOnSubgraphUpdateWithEmptyFlags() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let attribute = subgraph.apply { + let input = Attribute(value: 1) + return Attribute(TestRule(input: input), initialValue: 0) + } + + attribute.setFlags(Subgraph.Flags(rawValue: 1), mask: Subgraph.Flags(rawValue: 1)) + attribute.invalidateValue() + + try #require(recorder.history.beginSubgraphUpdateEntries.count == 0) + + subgraph.update(flags: []) + + try #require(recorder.history.beginSubgraphUpdateEntries.count == 0) + } + } + + @Suite + struct EndSubgraphUpdateTests { + @Test + func traceEndSubgraphUpdateCalledOnSubgraphUpdateWithDirtyFlags() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let attribute = subgraph.apply { + let input = Attribute(value: 1) + return Attribute(TestRule(input: input), initialValue: 0) + } + + attribute.setFlags(Subgraph.Flags(rawValue: 1), mask: Subgraph.Flags(rawValue: 1)) + attribute.invalidateValue() + + try #require(recorder.history.endSubgraphUpdateEntries.count == 0) + + subgraph.update(flags: Subgraph.Flags(rawValue: 1)) + + let endSubgraphUpdateEntries = recorder.history.endSubgraphUpdateEntries + try #require(endSubgraphUpdateEntries.count == 1) + #expect(endSubgraphUpdateEntries[0].subgraph == subgraph) + } + } + + @Suite + struct BeginNodeUpdateTests { + @Test + func traceBeginNodeUpdateCalledOnReadValue() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let attribute = subgraph.apply { + let input = Attribute(value: 1) + return Attribute(TestRule(input: input)) + } + + try #require(recorder.history.beginNodeUpdateEntries.count == 0) + + let _ = attribute.value + + let beginNodeUpdateEntries = recorder.history.beginNodeUpdateEntries + try #require(beginNodeUpdateEntries.count == 1) + #expect(beginNodeUpdateEntries[0].attribute == attribute.identifier) + } + } + + @Suite + struct EndNodeUpdateTests { + @Test + func traceEndNodeUpdateCalledOnReadValue() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let attribute = subgraph.apply { + let input = Attribute(value: 1) + return Attribute(TestRule(input: input)) + } + + try #require(recorder.history.endNodeUpdateEntries.count == 0) + + let _ = attribute.value + + let endNodeUpdateEntries = recorder.history.endNodeUpdateEntries + try #require(endNodeUpdateEntries.count == 1) + #expect(endNodeUpdateEntries[0].attribute == attribute.identifier) + #expect(endNodeUpdateEntries[0].changed == true) + } + + @Test + func traceEndNodeUpdateCalledOnReadValueWithAbortedUpdate() throws { + struct CancellingRule: Rule { + var value: Int { + Graph.cancelUpdate() + return 0 + } + } + + struct ReadingRule: Rule { + @Attribute var input: Int + var value: Int { input + 1 } + } + + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let (attribute, dependency) = subgraph.apply { + let source = Attribute(value: 42) + let indirect = IndirectAttribute(source: source) + let dependency = Attribute(CancellingRule()) + indirect.dependency = dependency.identifier + let attribute = Attribute(ReadingRule(input: indirect.attribute)) + return (attribute, dependency) + } + + // Establish node edges + let _ = attribute.value + var endNodeUpdateEntries = recorder.history.endNodeUpdateEntries.filter { + $0.attribute == attribute.identifier + } + try #require(endNodeUpdateEntries.count == 1) + #expect(endNodeUpdateEntries[0].changed == true) // The value was read with default update options + + dependency.invalidateValue() + attribute.prefetchValue() // Uses `.abortIfCancelled | .cancelIfPassedDeadline` + + endNodeUpdateEntries = recorder.history.endNodeUpdateEntries.filter { + $0.attribute == attribute.identifier + } + try #require(endNodeUpdateEntries.count == 2) + #expect(endNodeUpdateEntries[1].attribute == attribute.identifier) + #expect(endNodeUpdateEntries[1].changed == false) // The update was cancelled and `.abortIfCancelled` was specified + } + } + + @Suite + struct BeginValueUpdateTests { + @Test + func traceBeginValueUpdateCalledOnReadValue() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let attribute = subgraph.apply { + let input = Attribute(value: 1) + return Attribute(TestRule(input: input)) + } + + try #require(recorder.history.beginValueUpdateEntries.count == 0) + + let _ = attribute.value + + let beginValueUpdateEntries = recorder.history.beginValueUpdateEntries + try #require(beginValueUpdateEntries.count == 1) + #expect(beginValueUpdateEntries[0].attribute == attribute.identifier) + } + } + + @Suite + struct EndValueUpdateTests { + @Test + func traceEndValueUpdateCalledOnValueRead() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let attribute = subgraph.apply { + let input = Attribute(value: 1) + return Attribute(TestRule(input: input)) + } + + try #require(recorder.history.endValueUpdateEntries.count == 0) + + let _ = attribute.value + + let endValueUpdateEntries = recorder.history.endValueUpdateEntries + try #require(endValueUpdateEntries.count == 1) + #expect(endValueUpdateEntries[0].attribute == attribute.identifier) + #expect(endValueUpdateEntries[0].changed == true) + } + + @Test + func traceEndValueUpdateCalledOnReadValueWithNoChange() throws { + struct ConstantRule: Rule { + var value: Int { 42 } + } + + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let attribute = subgraph.apply { + Attribute(ConstantRule()) + } + + let _ = attribute.value + try #require(recorder.history.endValueUpdateEntries.count == 1) + try #require(recorder.history.endValueUpdateEntries[0].changed == true) + + // Trigger update with no change + attribute.invalidateValue() + let _ = attribute.value + + let endValueUpdateEntries = recorder.history.endValueUpdateEntries + try #require(endValueUpdateEntries.count == 2) + #expect(endValueUpdateEntries[1].attribute == attribute.identifier) + #expect(endValueUpdateEntries[1].changed == false) + } + } + + @Suite + struct BeginGraphUpdateTests { + @Test + func traceBeginGraphUpdateCalled() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let attribute = subgraph.apply { + let input = Attribute(value: 1) + return Attribute(TestRule(input: input)) + } + + graph.onUpdate {} + graph.setNeedsUpdate() + + try #require(recorder.history.beginGraphUpdateEntries.count == 0) + + let _ = attribute.value + + let beginGraphUpdateEntries = recorder.history.beginGraphUpdateEntries + try #require(beginGraphUpdateEntries.count == 1) + #expect(beginGraphUpdateEntries[0].graph == graph) + } + + @Test + func traceBeginGraphUpdateNotCalledWhenGraphHasNoUpdateHandler() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let attribute = subgraph.apply { + let input = Attribute(value: 1) + return Attribute(TestRule(input: input)) + } + + try #require(recorder.history.beginGraphUpdateEntries.count == 0) + + let _ = attribute.value + + let beginGraphUpdateEntries = recorder.history.beginGraphUpdateEntries + try #require(beginGraphUpdateEntries.count == 0) + } + } + + @Suite + struct EndGraphUpdateTests { + @Test + func traceEndGraphUpdateCalled() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let attribute = subgraph.apply { + let input = Attribute(value: 1) + return Attribute(TestRule(input: input)) + } + + graph.onUpdate {} + graph.setNeedsUpdate() + + try #require(recorder.history.endGraphUpdateEntries.count == 0) + + let _ = attribute.value + + let endGraphUpdateEntries = recorder.history.endGraphUpdateEntries + try #require(endGraphUpdateEntries.count == 1) + #expect(endGraphUpdateEntries[0].graph == graph) + } + + @Test + func traceEndGraphUpdateNotCalledWhenGraphHasNoUpdateHandler() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let attribute = subgraph.apply { + let input = Attribute(value: 1) + return Attribute(TestRule(input: input)) + } + + try #require(recorder.history.endGraphUpdateEntries.count == 0) + + let _ = attribute.value + + let endGraphUpdateEntries = recorder.history.endGraphUpdateEntries + try #require(endGraphUpdateEntries.count == 0) + } + } +} From 53bea0c3ca1094384321bca34ca7ba3e32243ca6 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 6 Aug 2026 21:27:02 +0200 Subject: [PATCH 35/42] Add TracingTraceTests --- .../Shared/Tracing/TracingTraceTests.swift | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 Tests/ComputeTests/Shared/Tracing/TracingTraceTests.swift diff --git a/Tests/ComputeTests/Shared/Tracing/TracingTraceTests.swift b/Tests/ComputeTests/Shared/Tracing/TracingTraceTests.swift new file mode 100644 index 00000000..0e1d56ab --- /dev/null +++ b/Tests/ComputeTests/Shared/Tracing/TracingTraceTests.swift @@ -0,0 +1,37 @@ +import Testing + +@Suite +struct TracingTraceTests { + @Suite + struct BeginTraceTests { + @Test + func traceBeginTraceCalled() throws { + let graph = Graph() + + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let beginTraceEntries = recorder.history.beginTraceEntries + try #require(beginTraceEntries.count == 1) + #expect(beginTraceEntries[0].graph == graph) + } + } + + @Suite + struct EndTraceTests { + @Test + func traceEndTraceCalled() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + try #require(recorder.history.endTraceEntries.count == 0) + + recorder.uninstall() + + let endTraceEntries = recorder.history.endTraceEntries + try #require(endTraceEntries.count == 1) + #expect(endTraceEntries[0].graph == graph) + } + } +} From 69b01fe80c002878129e5f98266726682f59e845 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 6 Aug 2026 21:38:36 +0200 Subject: [PATCH 36/42] Add TracingGraphInvalidationTests --- .../TracingGraphInvalidationTests.swift | 176 ++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 Tests/ComputeTests/Shared/Tracing/TracingGraphInvalidationTests.swift diff --git a/Tests/ComputeTests/Shared/Tracing/TracingGraphInvalidationTests.swift b/Tests/ComputeTests/Shared/Tracing/TracingGraphInvalidationTests.swift new file mode 100644 index 00000000..a270e628 --- /dev/null +++ b/Tests/ComputeTests/Shared/Tracing/TracingGraphInvalidationTests.swift @@ -0,0 +1,176 @@ +import Testing + +@Suite(.serialized(for: \Subgraph.Type.current)) +struct TracingGraphInvalidationTests { + struct TestRule: Rule { + @Attribute var input: Int + var value: Int { return input + 1 } + } + + @Suite + struct BeginGraphInvalidationTests { + // In order for `beginGraphInvalidation()` to be called: + // - The graph mush have an invalidation callback + // - The node's output edges must be non-empty + // - The node's inputs must traverse contexts + + @Test + func traceBeginGraphInvalidationCalled() throws { + let globalGraph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: globalGraph) + + let inputGraph = Graph(shared: globalGraph) + let inputSubgraph = Subgraph(graph: inputGraph) + let input = inputSubgraph.apply { + Attribute(value: 1) + } + + let outputGraph = Graph(shared: globalGraph) + let outputSubgraph = Subgraph(graph: outputGraph) + let output = outputSubgraph.apply { + let attribute = Attribute(TestRule(input: input)) + return Attribute(TestRule(input: attribute)) + } + + outputGraph.onInvalidation { _ in } + + // Establish node edges + let _ = output.value + + try #require(recorder.history.beginGraphInvalidationEntries.count == 0) + + input.invalidateValue() + + let beginGraphInvalidationEntries = recorder.history.beginGraphInvalidationEntries + try #require(beginGraphInvalidationEntries.count == 1) + #expect(beginGraphInvalidationEntries[0].graph == outputGraph) + #expect(beginGraphInvalidationEntries[0].attribute == input.identifier) + } + + @Test + func traceBeginGraphInvalidationNotCalledWhenNoInvalidationCallback() throws { + let globalGraph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: globalGraph) + + let inputGraph = Graph(shared: globalGraph) + let inputSubgraph = Subgraph(graph: inputGraph) + let input = inputSubgraph.apply { + Attribute(value: 1) + } + + let outputGraph = Graph(shared: globalGraph) + let outputSubgraph = Subgraph(graph: outputGraph) + let output = outputSubgraph.apply { + let attribute = Attribute(TestRule(input: input)) + return Attribute(TestRule(input: attribute)) + } + + // No invalidation callback + + // establish node edges + let _ = output.value + + try #require(recorder.history.beginGraphInvalidationEntries.count == 0) + + input.invalidateValue() + + #expect(recorder.history.beginGraphInvalidationEntries.count == 0) + } + + @Test + func traceBeginGraphInvalidationNotCalledWhenNoOutputEdge() throws { + let globalGraph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: globalGraph) + + let inputGraph = Graph(shared: globalGraph) + let inputSubgraph = Subgraph(graph: inputGraph) + let input = inputSubgraph.apply { + Attribute(value: 1) + } + + let outputGraph = Graph(shared: globalGraph) + let outputSubgraph = Subgraph(graph: outputGraph) + let attribute = outputSubgraph.apply { + Attribute(TestRule(input: input)) + // No output edge + } + + outputGraph.onInvalidation { _ in } + + // Establish node edges + let _ = attribute.value + + try #require(recorder.history.beginGraphInvalidationEntries.count == 0) + + input.invalidateValue() + + #expect(recorder.history.beginGraphInvalidationEntries.count == 0) + } + + @Test + func traceBeginGraphInvalidationNotCalledWhenInputsDoNotTraverseContexts() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + graph.onInvalidation { _ in } + + let subgraph = Subgraph(graph: graph) + let (input, output) = subgraph.apply { + let input = Attribute(value: 1) + let attribute = Attribute(TestRule(input: input)) + let output = Attribute(TestRule(input: attribute)) + return (input, output) + } + + // Establish node edges + let _ = output.value + + try #require(recorder.history.beginGraphInvalidationEntries.count == 0) + + input.invalidateValue() + + #expect(recorder.history.beginGraphInvalidationEntries.count == 0) + } + } + + @Suite + struct EndGraphInvalidationTests { + @Test + func traceEndGraphInvalidationCalled() throws { + let globalGraph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: globalGraph) + + let inputGraph = Graph(shared: globalGraph) + let inputSubgraph = Subgraph(graph: inputGraph) + let input = inputSubgraph.apply { + Attribute(value: 1) + } + + let outputGraph = Graph(shared: globalGraph) + let outputSubgraph = Subgraph(graph: outputGraph) + let output = outputSubgraph.apply { + let attribute = Attribute(TestRule(input: input)) + return Attribute(TestRule(input: attribute)) + } + + outputGraph.onInvalidation { _ in } + + // Establish node edges + let _ = output.value + + try #require(recorder.history.endGraphInvalidationEntries.count == 0) + + input.invalidateValue() + + let endGraphInvalidationEntries = recorder.history.endGraphInvalidationEntries + try #require(endGraphInvalidationEntries.count == 1) + #expect(endGraphInvalidationEntries[0].graph == outputGraph) + #expect(endGraphInvalidationEntries[0].attribute == input.identifier) + } + } +} From 3337becdce01024d9baa24fec88cff744ed167d8 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 6 Aug 2026 21:41:46 +0200 Subject: [PATCH 37/42] Add TracingDeadlineTests --- .../Shared/Graph/GraphDeadlineTests.swift | 20 ------ .../Shared/Tracing/TracingDeadlineTests.swift | 65 +++++++++++++++++++ 2 files changed, 65 insertions(+), 20 deletions(-) create mode 100644 Tests/ComputeTests/Shared/Tracing/TracingDeadlineTests.swift diff --git a/Tests/ComputeTests/Shared/Graph/GraphDeadlineTests.swift b/Tests/ComputeTests/Shared/Graph/GraphDeadlineTests.swift index 21f81d16..7e9e2db5 100644 --- a/Tests/ComputeTests/Shared/Graph/GraphDeadlineTests.swift +++ b/Tests/ComputeTests/Shared/Graph/GraphDeadlineTests.swift @@ -6,42 +6,22 @@ struct GraphDeadlineTests { @Test func withDeadline() { let graph = Graph() - let recorder = TestTraceRecorder() - recorder.install(graph: graph) #expect(graph.deadline == UInt64.max) graph.withDeadline(0) { #expect(graph.deadline == 0) } #expect(graph.deadline == UInt64.max) - - let entries: [TestTraceRecorder.History.SetDeadlineEntry] = recorder.history.entries.compactMap { event in - guard case .setDeadline(let entry) = event else { return nil } - return entry - } - #expect(entries.count == 2) - #expect(entries[0].deadline == 0) - #expect(entries[1].deadline == UInt.max) } @Test func withDeadlineNonzero() { let graph = Graph() - let recorder = TestTraceRecorder() - recorder.install(graph: graph) #expect(graph.deadline == UInt64.max) graph.withDeadline(100) { #expect(graph.deadline == 100) } #expect(graph.deadline == UInt64.max) - - let entries: [TestTraceRecorder.History.SetDeadlineEntry] = recorder.history.entries.compactMap { event in - guard case .setDeadline(let entry) = event else { return nil } - return entry - } - #expect(entries.count == 2) - #expect(entries[0].deadline == 100) - #expect(entries[1].deadline == UInt.max) } } diff --git a/Tests/ComputeTests/Shared/Tracing/TracingDeadlineTests.swift b/Tests/ComputeTests/Shared/Tracing/TracingDeadlineTests.swift new file mode 100644 index 00000000..e5c5aa64 --- /dev/null +++ b/Tests/ComputeTests/Shared/Tracing/TracingDeadlineTests.swift @@ -0,0 +1,65 @@ +import Testing + +@Suite(.serialized(for: \Subgraph.Type.current)) +struct TracingDeadlineTests { + @Suite + struct SetDeadlineTests { + @Test + func traceSetDeadlineCalled() { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + graph.withDeadline(0) {} + + let entries: [TestTraceRecorder.History.SetDeadlineEntry] = recorder.history.entries.compactMap { event in + guard case .setDeadline(let entry) = event else { return nil } + return entry + } + #expect(entries.count == 2) + #expect(entries[0].deadline == 0) + #expect(entries[1].deadline == UInt.max) + } + + @Test + func withDeadlineNonzero() { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + graph.withDeadline(100) {} + + let entries: [TestTraceRecorder.History.SetDeadlineEntry] = recorder.history.entries.compactMap { event in + guard case .setDeadline(let entry) = event else { return nil } + return entry + } + #expect(entries.count == 2) + #expect(entries[0].deadline == 100) + #expect(entries[1].deadline == UInt.max) + } + } + + @Suite + struct PassedDeadlineTests { + @Test + func tracePassedDeadlineCalled() throws { + let graph = Graph() + let subgraph = Subgraph(graph: graph) + Subgraph.current = subgraph + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let attribute = Attribute(value: 0) + + try #require(recorder.history.passedDeadlineEntries.count == 0) + + // Deadline of 1 will be in the past + graph.withDeadline(1) { + let _ = attribute.prefetchValue() + } + + let passedDeadlineEntries = recorder.history.passedDeadlineEntries + try #require(passedDeadlineEntries.count == 1) + } + } +} From 088c5e5acf29fc7422921c250d95ca63a35c1d19 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 6 Aug 2026 21:45:59 +0200 Subject: [PATCH 38/42] Add TracingComparisionTests --- .../Tracing/TracingComparisonTests.swift | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 Tests/ComputeTests/Shared/Tracing/TracingComparisonTests.swift diff --git a/Tests/ComputeTests/Shared/Tracing/TracingComparisonTests.swift b/Tests/ComputeTests/Shared/Tracing/TracingComparisonTests.swift new file mode 100644 index 00000000..a6ebc688 --- /dev/null +++ b/Tests/ComputeTests/Shared/Tracing/TracingComparisonTests.swift @@ -0,0 +1,45 @@ +import Testing + +@Suite(.serialized(for: \Subgraph.Type.current)) +struct TracingComparisonTests { + @Suite + struct CompareFailedTests { + struct CounterRule: StatefulRule { + var counter: Int = 0 + mutating func updateValue() { + counter += 1 + context.value = CounterRule.Value(text: "value-\(counter)") + } + struct Value: Equatable { + var text: String + } + } + + @Test + func traceCompareFailedCalledOnValueChanged() throws { + let graph = Graph() + let recorder = TestTraceRecorder() + recorder.install(graph: graph) + + let subgraph = Subgraph(graph: graph) + let attribute = subgraph.apply { + Attribute(CounterRule()) + } + + let firstValue = attribute.value + #expect(firstValue == CounterRule.Value(text: "value-1")) + + // Initial value does not perform comparison + try #require(recorder.history.compareFailedEntries.count == 0) + + attribute.invalidateValue() + let secondValue = attribute.value + #expect(secondValue == CounterRule.Value(text: "value-2")) + + // Subsequent value fails comparison + let compareFailedEntries = recorder.history.compareFailedEntries + try #require(compareFailedEntries.count == 1) + #expect(compareFailedEntries[0].attribute == attribute.identifier) + } + } +} From 3f6303f6da3abae7dc71a35689ad3d7ee4bdf688 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Thu, 6 Aug 2026 23:25:52 +0200 Subject: [PATCH 39/42] Run swift format --- .../Shared/Attribute/AttributeBodyTests.swift | 4 +- .../Subgraph/SubgraphLifecycleTests.swift | 22 +-- .../Shared/TestSupport/TestTrace.swift | 2 +- .../TestSupport/TestTraceRecorder.swift | 184 +++++++++--------- .../Tracing/TracingComparisonTests.swift | 4 +- .../Tracing/TracingCustomEventTests.swift | 6 +- .../TracingGraphInvalidationTests.swift | 20 +- .../TracingIndirectNodeLifecycleTests.swift | 12 +- .../Tracing/TracingNamedEventTests.swift | 18 +- .../Tracing/TracingNodeLifecycleTests.swift | 118 +++++------ .../TracingSubgraphLifecycleTests.swift | 12 +- .../Shared/Tracing/TracingTraceTests.swift | 2 +- .../Shared/Tracing/TracingUpdateTests.swift | 7 +- 13 files changed, 206 insertions(+), 205 deletions(-) diff --git a/Tests/ComputeTests/Shared/Attribute/AttributeBodyTests.swift b/Tests/ComputeTests/Shared/Attribute/AttributeBodyTests.swift index 60f843c5..22d89508 100644 --- a/Tests/ComputeTests/Shared/Attribute/AttributeBodyTests.swift +++ b/Tests/ComputeTests/Shared/Attribute/AttributeBodyTests.swift @@ -135,7 +135,7 @@ struct AttributeBodyTests { let subgraph = Subgraph(graph: globalSubgraph.graph) globalSubgraph.addChild(subgraph) Subgraph.current = subgraph - + let attribute = Attribute(value: 0) var weakAttributeExpired = false withUnsafeMutablePointer(to: &weakAttributeExpired) { weakAttributeExpiredPointer in @@ -148,7 +148,7 @@ struct AttributeBodyTests { return { _, _ in } } } - + #expect(weakAttributeExpiredPointer.pointee == false) subgraph.invalidate() #expect(weakAttributeExpiredPointer.pointee == true) diff --git a/Tests/ComputeTests/Shared/Subgraph/SubgraphLifecycleTests.swift b/Tests/ComputeTests/Shared/Subgraph/SubgraphLifecycleTests.swift index 42a7fd7c..3c6951dc 100644 --- a/Tests/ComputeTests/Shared/Subgraph/SubgraphLifecycleTests.swift +++ b/Tests/ComputeTests/Shared/Subgraph/SubgraphLifecycleTests.swift @@ -10,14 +10,14 @@ struct SubgraphLifecycleTests { let subgraph = Subgraph(graph: graph) #expect(subgraph.graph == graph) } - + @Test func graphCountersIncremented() { let graph = Graph() - + #expect(graph.counter(for: .subgraphs) == 0) #expect(graph.counter(for: .createdSubgraphs) == 0) - + let subgraph = Subgraph(graph: graph) withExtendedLifetime(subgraph) { #expect(graph.counter(for: .subgraphs) == 1) @@ -25,7 +25,7 @@ struct SubgraphLifecycleTests { } } } - + @Suite struct SubgraphInvalidateTests { @Test @@ -33,13 +33,13 @@ struct SubgraphLifecycleTests { await #expect(processExitsWith: .failure) { let graph = Graph() let subgraph = Subgraph(graph: graph) - + subgraph.invalidate() - - print(subgraph.graph) // will crash + + print(subgraph.graph) // will crash } } - + @Test func graphCountersDecrementedOnSubgraphInvalidate() { let graph = Graph() @@ -55,7 +55,7 @@ struct SubgraphLifecycleTests { } } } - + @Suite struct SubgraphDestroyTests { // This really just tests that Subgraph.deinit was triggered @@ -69,11 +69,11 @@ struct SubgraphLifecycleTests { } #expect(weakSubgraph == nil) } - + @Test func graphCountersDecrementedOnSubgraphDeinit() { let graph = Graph() - + #expect(graph.counter(for: .subgraphs) == 0) #expect(graph.counter(for: .createdSubgraphs) == 0) diff --git a/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift b/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift index 713de9d0..b44e1f23 100644 --- a/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift +++ b/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift @@ -139,7 +139,7 @@ public class TestTrace { } named_event: { ctx, graph, event_id, event_arg_count, event_args, data, flags in let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() let eventArgs = event_args.map { pointer in - Array(capacity: event_arg_count) { span in + [UInt32](capacity: event_arg_count) { span in for i in 0.. Bool { history.entries.append(.namedEventEnabled(.init(eventID: eventID))) return true } - + public override func setDeadline(deadline: UInt) { history.entries.append(.setDeadline(.init(deadline: deadline))) } - + public override func passedDeadline() { history.entries.append(.passedDeadline(.init())) } - + public override func compareFailed(attribute: AnyAttribute, comparisonState: ComparisonState) { history.entries.append(.compareFailed(.init(attribute: attribute, comparisonState: comparisonState))) } @@ -188,15 +188,15 @@ public class TestTraceRecorder: TestTrace { extension TestTraceRecorder { public struct History { var entries: [Entry] - + init() { self.entries = [] } - + public enum Entry { case beginTrace(BeginTraceEntry) case endTrace(EndTraceEntry) - + case beginSubgraphUpdate(BeginSubgraphUpdateEntry) case endSubgraphUpdate(EndSubgraphUpdateEntry) case beginNodeUpdate(BeginNodeUpdateEntry) @@ -205,133 +205,133 @@ extension TestTraceRecorder { case endValueUpdate(EndValueUpdateEntry) case beginGraphUpdate(BeginGraphUpdateEntry) case endGraphUpdate(EndGraphUpdateEntry) - + case beginGraphInvalidation(BeginGraphInvalidationEntry) case endGraphInvalidation(EndGraphInvalidationEntry) - + case beginModifyNode(BeginModifyNodeEntry) case endModifyNode(EndModifyNodeEntry) - + case beginEvent(BeginEventEntry) case endEvent(EndEventEntry) - + case graphCreated(GraphCreatedEntry) case graphDestroy(GraphDestroyEntry) case graphNeedsUpdate(GraphNeedsUpdateEntry) - + case subgraphCreated(SubgraphCreatedEntry) case subgraphDestroy(SubgraphDestroyEntry) case subgraphAddChild(SubgraphAddChildEntry) case subgraphRemoveChild(SubgraphRemoveChildEntry) - + case nodeAdded(NodeAddedEntry) case nodeAddEdge(NodeAddEdgeEntry) case nodeRemoveEdge(NodeRemoveEdgeEntry) case nodeSetEdgePending(NodeSetEdgePendingEntry) - + case nodeSetDirty(NodeSetDirtyEntry) case nodeSetPending(NodeSetPendingEntry) case nodeSetValue(NodeSetValueEntry) case nodeMarkValue(NodeMarkValueEntry) - + case indirectNodeAdded(IndirectNodeAddedEntry) case indirectNodeSetSource(IndirectNodeSetSourceEntry) case indirectNodeSetDependency(IndirectNodeSetDependencyEntry) - + case profileMark(ProfileMarkEntry) case customEvent(CustomEventEntry) case namedEvent(NamedEventEntry) case namedEventEnabled(NamedEventEnabledEntry) - + case setDeadline(SetDeadlineEntry) case passedDeadline(PassedDeadlineEntry) - + case compareFailed(CompareFailedEntry) } - + public struct BeginTraceEntry { public var graph: Graph } - + public struct EndTraceEntry { public var graph: Graph } - + public struct BeginSubgraphUpdateEntry { public var subgraph: Subgraph public var flags: Subgraph.Flags } - + public struct EndSubgraphUpdateEntry { public var subgraph: Subgraph } - + public struct BeginNodeUpdateEntry { public var attribute: AnyAttribute } - + public struct EndNodeUpdateEntry { public var attribute: AnyAttribute public var changed: Bool } - + public struct BeginValueUpdateEntry { public var attribute: AnyAttribute } - + public struct EndValueUpdateEntry { public var attribute: AnyAttribute public var changed: Bool } - + public struct BeginGraphUpdateEntry { public var graph: Graph } - + public struct EndGraphUpdateEntry { public var graph: Graph } - + public struct BeginGraphInvalidationEntry { public var graph: Graph public var attribute: AnyAttribute } - + public struct EndGraphInvalidationEntry { public var graph: Graph public var attribute: AnyAttribute } - + public struct BeginModifyNodeEntry { public var attribute: AnyAttribute } - + public struct EndModifyNodeEntry { public var attribute: AnyAttribute } - + public struct BeginEventEntry { public var attribute: AnyAttribute public var eventName: String } - + public struct EndEventEntry { public var attribute: AnyAttribute public var eventName: String } - + public struct GraphCreatedEntry { public var graph: Graph } - + public struct GraphDestroyEntry { public var graph: Graph } - + public struct GraphNeedsUpdateEntry { public var graph: Graph } - + public struct SubgraphCreatedEntry { public var subgraph: Subgraph } @@ -344,77 +344,77 @@ extension TestTraceRecorder { public var subgraph: Subgraph public var childSubgraph: Subgraph } - + public struct SubgraphRemoveChildEntry { public var subgraph: Subgraph public var childSubgraph: Subgraph } - + public struct NodeAddedEntry { public var attribute: AnyAttribute } - + public struct NodeAddEdgeEntry { public var attribute: AnyAttribute public var input: AnyAttribute public var options: InputOptions } - + public struct NodeRemoveEdgeEntry { public var attribute: AnyAttribute public var input: AnyAttribute } - + public struct NodeSetEdgePendingEntry { public var attribute: AnyAttribute public var input: AnyAttribute public var pending: Bool } - + public struct NodeSetDirtyEntry { public var attribute: AnyAttribute public var dirty: Bool } - + public struct NodeSetPendingEntry { public var attribute: AnyAttribute public var pending: Bool } - + public struct NodeSetValueEntry { public var attribute: AnyAttribute public var value: UnsafeRawPointer } - + public struct NodeMarkValueEntry { public var attribute: AnyAttribute } - + public struct IndirectNodeAddedEntry { public var attribute: AnyAttribute } - + public struct IndirectNodeSetSourceEntry { public var attribute: AnyAttribute public var source: AnyAttribute } - + public struct IndirectNodeSetDependencyEntry { public var attribute: AnyAttribute public var dependency: AnyAttribute } - + public struct ProfileMarkEntry { public var eventName: String } - + public struct CustomEventEntry { public var graph: Graph public var eventName: String public var value: UnsafeRawPointer public var type: Any.Type } - + public struct NamedEventEntry { public var graph: Graph public var eventID: Graph.NamedTraceEventID @@ -422,18 +422,18 @@ extension TestTraceRecorder { public var data: Data? public var flags: Graph.NamedTraceEventFlags } - + public struct NamedEventEnabledEntry { public var eventID: Graph.NamedTraceEventID } - + public struct SetDeadlineEntry { public var deadline: UInt } - + public struct PassedDeadlineEntry { } - + public struct CompareFailedEntry { public var attribute: AnyAttribute public var comparisonState: ComparisonState diff --git a/Tests/ComputeTests/Shared/Tracing/TracingComparisonTests.swift b/Tests/ComputeTests/Shared/Tracing/TracingComparisonTests.swift index a6ebc688..348e6f28 100644 --- a/Tests/ComputeTests/Shared/Tracing/TracingComparisonTests.swift +++ b/Tests/ComputeTests/Shared/Tracing/TracingComparisonTests.swift @@ -20,12 +20,12 @@ struct TracingComparisonTests { let graph = Graph() let recorder = TestTraceRecorder() recorder.install(graph: graph) - + let subgraph = Subgraph(graph: graph) let attribute = subgraph.apply { Attribute(CounterRule()) } - + let firstValue = attribute.value #expect(firstValue == CounterRule.Value(text: "value-1")) diff --git a/Tests/ComputeTests/Shared/Tracing/TracingCustomEventTests.swift b/Tests/ComputeTests/Shared/Tracing/TracingCustomEventTests.swift index 18b23dcb..bbfdd4f6 100644 --- a/Tests/ComputeTests/Shared/Tracing/TracingCustomEventTests.swift +++ b/Tests/ComputeTests/Shared/Tracing/TracingCustomEventTests.swift @@ -22,7 +22,7 @@ struct TracingCustomEventTests { recorder.install(graph: graph) try #require(recorder.history.customEventEntries.count == 0) - + "custom_event".withCString { eventName in graph.addTraceEvent(eventName, value: 42) } @@ -36,7 +36,7 @@ struct TracingCustomEventTests { #expect(recorder.capturedValue == 42) #expect(recorder.capturedType == Int.self) } - + @Test func traceCustomEventCalledWithContext() throws { let graph = Graph() @@ -44,7 +44,7 @@ struct TracingCustomEventTests { recorder.install(graph: graph) try #require(recorder.history.customEventEntries.count == 0) - + let value: Int = 42 try withUnsafePointer(to: value) { context in "custom_event".withCString { eventName in diff --git a/Tests/ComputeTests/Shared/Tracing/TracingGraphInvalidationTests.swift b/Tests/ComputeTests/Shared/Tracing/TracingGraphInvalidationTests.swift index a270e628..4abff56f 100644 --- a/Tests/ComputeTests/Shared/Tracing/TracingGraphInvalidationTests.swift +++ b/Tests/ComputeTests/Shared/Tracing/TracingGraphInvalidationTests.swift @@ -6,14 +6,14 @@ struct TracingGraphInvalidationTests { @Attribute var input: Int var value: Int { return input + 1 } } - + @Suite struct BeginGraphInvalidationTests { // In order for `beginGraphInvalidation()` to be called: // - The graph mush have an invalidation callback // - The node's output edges must be non-empty // - The node's inputs must traverse contexts - + @Test func traceBeginGraphInvalidationCalled() throws { let globalGraph = Graph() @@ -32,7 +32,7 @@ struct TracingGraphInvalidationTests { let attribute = Attribute(TestRule(input: input)) return Attribute(TestRule(input: attribute)) } - + outputGraph.onInvalidation { _ in } // Establish node edges @@ -47,7 +47,7 @@ struct TracingGraphInvalidationTests { #expect(beginGraphInvalidationEntries[0].graph == outputGraph) #expect(beginGraphInvalidationEntries[0].attribute == input.identifier) } - + @Test func traceBeginGraphInvalidationNotCalledWhenNoInvalidationCallback() throws { let globalGraph = Graph() @@ -66,9 +66,9 @@ struct TracingGraphInvalidationTests { let attribute = Attribute(TestRule(input: input)) return Attribute(TestRule(input: attribute)) } - + // No invalidation callback - + // establish node edges let _ = output.value @@ -97,9 +97,9 @@ struct TracingGraphInvalidationTests { Attribute(TestRule(input: input)) // No output edge } - + outputGraph.onInvalidation { _ in } - + // Establish node edges let _ = attribute.value @@ -125,7 +125,7 @@ struct TracingGraphInvalidationTests { let output = Attribute(TestRule(input: attribute)) return (input, output) } - + // Establish node edges let _ = output.value @@ -157,7 +157,7 @@ struct TracingGraphInvalidationTests { let attribute = Attribute(TestRule(input: input)) return Attribute(TestRule(input: attribute)) } - + outputGraph.onInvalidation { _ in } // Establish node edges diff --git a/Tests/ComputeTests/Shared/Tracing/TracingIndirectNodeLifecycleTests.swift b/Tests/ComputeTests/Shared/Tracing/TracingIndirectNodeLifecycleTests.swift index 033c98a3..bf3f6ff6 100644 --- a/Tests/ComputeTests/Shared/Tracing/TracingIndirectNodeLifecycleTests.swift +++ b/Tests/ComputeTests/Shared/Tracing/TracingIndirectNodeLifecycleTests.swift @@ -9,7 +9,7 @@ struct TracingIndirectNodeLifecycleTests { let graph = Graph() let recorder = TestTraceRecorder() recorder.install(graph: graph) - + let subgraph = Subgraph(graph: graph) let source = subgraph.apply { Attribute(value: 1) @@ -34,7 +34,7 @@ struct TracingIndirectNodeLifecycleTests { let graph = Graph() let recorder = TestTraceRecorder() recorder.install(graph: graph) - + let subgraph = Subgraph(graph: graph) let indirect = subgraph.apply { let source1 = Attribute(value: 1) @@ -42,7 +42,7 @@ struct TracingIndirectNodeLifecycleTests { } try #require(recorder.history.indirectNodeSetSourceEntries.count == 0) - + let source2 = subgraph.apply { Attribute(value: 2) } @@ -60,7 +60,7 @@ struct TracingIndirectNodeLifecycleTests { let graph = Graph() let recorder = TestTraceRecorder() recorder.install(graph: graph) - + let subgraph = Subgraph(graph: graph) let (indirect, source1) = subgraph.apply { let source1 = Attribute(value: 1) @@ -71,7 +71,7 @@ struct TracingIndirectNodeLifecycleTests { let source2 = subgraph.apply { Attribute(value: 2) } - + indirect.source = source2 try #require(recorder.history.indirectNodeSetSourceEntries.count == 1) @@ -91,7 +91,7 @@ struct TracingIndirectNodeLifecycleTests { let graph = Graph() let recorder = TestTraceRecorder() recorder.install(graph: graph) - + let subgraph = Subgraph(graph: graph) let indirect = subgraph.apply { let source = Attribute(value: 1) diff --git a/Tests/ComputeTests/Shared/Tracing/TracingNamedEventTests.swift b/Tests/ComputeTests/Shared/Tracing/TracingNamedEventTests.swift index 54a3ff5a..402c2cd4 100644 --- a/Tests/ComputeTests/Shared/Tracing/TracingNamedEventTests.swift +++ b/Tests/ComputeTests/Shared/Tracing/TracingNamedEventTests.swift @@ -8,7 +8,7 @@ struct TracingNamedEventTests { @Test func traceNamedEventCalled() throws { let eventID = Graph.registerNamedTraceEvent(name: "test_event", subsystem: "test_subsystem") - + let graph = Graph() let recorder = TestTraceRecorder() recorder.install(graph: graph) @@ -29,7 +29,7 @@ struct TracingNamedEventTests { @Test func traceNamedEventCalledWithData() throws { let eventID = Graph.registerNamedTraceEvent(name: "test_event_with_data", subsystem: "test_subsystem") - + let graph = Graph() let recorder = TestTraceRecorder() recorder.install(graph: graph) @@ -45,7 +45,7 @@ struct TracingNamedEventTests { #expect(namedEventEntries[0].data == data) #expect(namedEventEntries[0].flags == []) } - + @Test func traceNamedEventCalledWithEventArgs() throws { let eventID = Graph.registerNamedTraceEvent(name: "test_event_with_args", subsystem: "test_subsystem") @@ -64,7 +64,7 @@ struct TracingNamedEventTests { #expect(namedEventEntries[0].data == nil) #expect(namedEventEntries[0].flags == []) } - + @Test func traceNamedEventCalledWithFlags() throws { let eventID = Graph.registerNamedTraceEvent(name: "test_event_with_flags", subsystem: "test_subsystem") @@ -83,7 +83,7 @@ struct TracingNamedEventTests { #expect(namedEventEntries[0].data == nil) #expect(namedEventEntries[0].flags == .init(rawValue: 7)) } - + @Test func traceNamedEventCalledWithRecordBacktraceFlag() throws { let eventID = Graph.registerNamedTraceEvent(name: "test_event_with_flags", subsystem: "test_subsystem") @@ -102,7 +102,7 @@ struct TracingNamedEventTests { #expect(namedEventEntries[0].data == nil) #expect(namedEventEntries[0].flags == [.recordBacktrace, .init(rawValue: 7)]) } - + // `named_event_enabled` only affects built-in trace types such as the trace recorder. @Test func traceNamedEventCalledWhenNotEnabled() throws { @@ -112,9 +112,9 @@ struct TracingNamedEventTests { return false } } - + let eventID = Graph.registerNamedTraceEvent(name: "test_event_disabled", subsystem: "test_subsystem") - + let graph = Graph() let recorder = DisabledEvents() recorder.install(graph: graph) @@ -138,7 +138,7 @@ struct TracingNamedEventTests { @Test func traceNamedEventEnabledCalled() throws { let eventID = Graph.registerNamedTraceEvent(name: "test_event_enabled", subsystem: "test_subsystem") - + let graph = Graph() let recorder = TestTraceRecorder() recorder.install(graph: graph) diff --git a/Tests/ComputeTests/Shared/Tracing/TracingNodeLifecycleTests.swift b/Tests/ComputeTests/Shared/Tracing/TracingNodeLifecycleTests.swift index f259217d..bee55fb1 100644 --- a/Tests/ComputeTests/Shared/Tracing/TracingNodeLifecycleTests.swift +++ b/Tests/ComputeTests/Shared/Tracing/TracingNodeLifecycleTests.swift @@ -9,7 +9,7 @@ struct TracingNodeLifecycleTests { let graph = Graph() let recorder = TestTraceRecorder() recorder.install(graph: graph) - + let subgraph = Subgraph(graph: graph) try #require(recorder.history.nodeAddedEntries.count == 0) @@ -36,7 +36,7 @@ struct TracingNodeLifecycleTests { let graph = Graph() let recorder = TestTraceRecorder() recorder.install(graph: graph) - + let subgraph = Subgraph(graph: graph) let (attribute, input) = subgraph.apply { let attribute = Attribute(value: 0) @@ -60,14 +60,14 @@ struct TracingNodeLifecycleTests { let graph = Graph() let recorder = TestTraceRecorder() recorder.install(graph: graph) - + let subgraph = Subgraph(graph: graph) let (attribute, input) = subgraph.apply { let attribute = Attribute(value: 0) let input = Attribute(value: 1) return (attribute, input) } - + try #require(recorder.history.nodeAddEdgeEntries.count == 0) let options: InputOptions = [.unprefetched, .alwaysEnabled] @@ -85,14 +85,14 @@ struct TracingNodeLifecycleTests { let graph = Graph() let recorder = TestTraceRecorder() recorder.install(graph: graph) - + let subgraph = Subgraph(graph: graph) let (attribute, input) = subgraph.apply { let input = Attribute(value: 1) let attribute = Attribute(TestRule(input: OptionalAttribute(input))) return (attribute, input) } - + try #require(recorder.history.nodeAddEdgeEntries.count == 0) let _ = attribute.value @@ -104,27 +104,27 @@ struct TracingNodeLifecycleTests { #expect(nodeAddEdgeEntries[0].options == []) } } - + @Suite struct NodeRemoveEdgeTests { struct TestRule: Rule { @OptionalAttribute var input: Int? var value: Int { return (input ?? 0) + 1 } } - + @Test func traceNodeRemoveEdgeCalledOnUpdateWhenAddedExplicitly() throws { let graph = Graph() let recorder = TestTraceRecorder() recorder.install(graph: graph) - + let subgraph = Subgraph(graph: graph) let (attribute, input) = subgraph.apply { let input = Attribute(value: 1) let attribute = Attribute(TestRule(input: OptionalAttribute(input))) return (attribute, input) } - + attribute.addInput(input, options: [], token: 0) try #require(recorder.history.nodeAddEdgeEntries.count == 1) try #require(recorder.history.nodeRemoveEdgeEntries.count == 0) @@ -139,20 +139,20 @@ struct TracingNodeLifecycleTests { #expect(nodeRemoveEdgeEntries[0].attribute == attribute.identifier) #expect(nodeRemoveEdgeEntries[0].input == input.identifier) } - + @Test func traceNodeRemoveEdgeNOtCalledOnUpdateWhenAddedExplicitlyWithAlwaysEnabled() throws { let graph = Graph() let recorder = TestTraceRecorder() recorder.install(graph: graph) - + let subgraph = Subgraph(graph: graph) let (attribute, input) = subgraph.apply { let input = Attribute(value: 1) let attribute = Attribute(TestRule(input: OptionalAttribute(input))) return (attribute, input) } - + attribute.addInput(input, options: [.alwaysEnabled], token: 0) try #require(recorder.history.nodeAddEdgeEntries.count == 1) try #require(recorder.history.nodeRemoveEdgeEntries.count == 0) @@ -171,14 +171,14 @@ struct TracingNodeLifecycleTests { let graph = Graph() let recorder = TestTraceRecorder() recorder.install(graph: graph) - + let subgraph = Subgraph(graph: graph) let (attribute, input) = subgraph.apply { let input = Attribute(value: 1) let attribute = Attribute(TestRule(input: OptionalAttribute(input))) return (attribute, input) } - + let _ = attribute.value try #require(recorder.history.nodeAddEdgeEntries.count == 1) try #require(recorder.history.nodeRemoveEdgeEntries.count == 0) @@ -193,7 +193,7 @@ struct TracingNodeLifecycleTests { #expect(nodeRemoveEdgeEntries[0].attribute == attribute.identifier) #expect(nodeRemoveEdgeEntries[0].input == input.identifier) } - + @Test func traceNodeRemoveEdgeNotCalledOnSameSubgraphInvalidate() throws { let graph = Graph() @@ -206,7 +206,7 @@ struct TracingNodeLifecycleTests { let attribute = Attribute(TestRule(input: OptionalAttribute(input))) return (attribute, input) } - + let _ = attribute.value try #require(recorder.history.nodeAddEdgeEntries.count == 1) try #require(recorder.history.nodeRemoveEdgeEntries.count == 0) @@ -216,7 +216,7 @@ struct TracingNodeLifecycleTests { let nodeRemoveEdgeEntries = recorder.history.nodeRemoveEdgeEntries try #require(nodeRemoveEdgeEntries.count == 0) } - + @Test func traceNodeRemoveEdgeCalledOnDifferentSubgraphInvalidate() throws { let graph = Graph() @@ -232,7 +232,7 @@ struct TracingNodeLifecycleTests { let attribute = outputSubgraph.apply { Attribute(TestRule(input: OptionalAttribute(input))) } - + let _ = attribute.value try #require(recorder.history.nodeAddEdgeEntries.count == 1) try #require(recorder.history.nodeRemoveEdgeEntries.count == 0) @@ -262,7 +262,7 @@ struct TracingNodeLifecycleTests { let attribute = outputSubgraph.apply { Attribute(value: 0) } - + attribute.addInput(input1, options: [], token: 0) attribute.addInput(input2, options: [], token: 1) try #require(recorder.history.nodeAddEdgeEntries.count == 2) @@ -278,7 +278,7 @@ struct TracingNodeLifecycleTests { #expect(nodeRemoveEdgeEntries[1].input == input1.identifier) } } - + @Suite struct NodeSetEdgePendingTests { struct TestRule: Rule { @@ -371,26 +371,26 @@ struct TracingNodeLifecycleTests { #expect(nodeSetEdgePendingEntries[3].pending == false) } } - + @Suite struct NodeSetDirtyTests { struct TestRule: Rule { var value: Int { 1 } } - + @Test func traceNodeSetDirtyCalledOnInvalidateInitializedAttribute() throws { let graph = Graph() let recorder = TestTraceRecorder() recorder.install(graph: graph) - + let subgraph = Subgraph(graph: graph) let attribute = subgraph.apply { Attribute(TestRule(), initialValue: 0) } try #require(recorder.history.nodeSetDirtyEntries.count == 0) - + attribute.invalidateValue() let nodeSetDirtyEntries = recorder.history.nodeSetDirtyEntries @@ -398,39 +398,39 @@ struct TracingNodeLifecycleTests { #expect(nodeSetDirtyEntries[0].attribute == attribute.identifier) #expect(nodeSetDirtyEntries[0].dirty == true) } - + @Test func traceNodeSetDirtyNotCalledOnInvalidateUninitializedAttribute() throws { let graph = Graph() let recorder = TestTraceRecorder() recorder.install(graph: graph) - + let subgraph = Subgraph(graph: graph) let attribute = subgraph.apply { Attribute(TestRule()) } try #require(recorder.history.nodeSetDirtyEntries.count == 0) - + attribute.invalidateValue() let nodeSetDirtyEntries = recorder.history.nodeSetDirtyEntries try #require(nodeSetDirtyEntries.count == 0) } - + @Test func traceNodeSetDirtyNotCalledOnInvalidateExternalAttribute() throws { let graph = Graph() let recorder = TestTraceRecorder() recorder.install(graph: graph) - + let subgraph = Subgraph(graph: graph) let attribute = subgraph.apply { Attribute(value: 1) } try #require(recorder.history.nodeSetDirtyEntries.count == 0) - + attribute.invalidateValue() let nodeSetDirtyEntries = recorder.history.nodeSetDirtyEntries @@ -449,7 +449,7 @@ struct TracingNodeLifecycleTests { let graph = Graph() let recorder = TestTraceRecorder() recorder.install(graph: graph) - + let subgraph = Subgraph(graph: graph) let attribute = subgraph.apply { Attribute(TestRule(), initialValue: 0) @@ -464,13 +464,13 @@ struct TracingNodeLifecycleTests { #expect(nodeSetPendingEntries[0].attribute == attribute.identifier) #expect(nodeSetPendingEntries[0].pending == true) } - + @Test func traceNodeSetPendingNotCalledOnInvalidateUninitializedAttribute() throws { let graph = Graph() let recorder = TestTraceRecorder() recorder.install(graph: graph) - + let subgraph = Subgraph(graph: graph) let attribute = subgraph.apply { Attribute(TestRule()) @@ -483,13 +483,13 @@ struct TracingNodeLifecycleTests { let nodeSetPendingEntries = recorder.history.nodeSetPendingEntries try #require(nodeSetPendingEntries.count == 0) } - + @Test func traceNodeSetPendingNotCalledOnInvalidateExternalAttribute() throws { let graph = Graph() let recorder = TestTraceRecorder() recorder.install(graph: graph) - + let subgraph = Subgraph(graph: graph) let attribute = subgraph.apply { Attribute(value: 1) @@ -509,7 +509,7 @@ struct TracingNodeLifecycleTests { struct TestRule: Rule { var value: Int { 1 } } - + @Test func traceNodeSetValueCalledOnCreateAttributeWithValue() throws { class NodeSetValueTrace: TestTraceRecorder { @@ -524,38 +524,38 @@ struct TracingNodeLifecycleTests { let graph = Graph() let recorder = NodeSetValueTrace() recorder.install(graph: graph) - + try #require(recorder.history.nodeSetValueEntries.count == 0) - + let subgraph = Subgraph(graph: graph) let attribute = subgraph.apply { Attribute(value: 42) } - + let nodeSetValueEntries = recorder.history.nodeSetValueEntries try #require(nodeSetValueEntries.count == 1) #expect(nodeSetValueEntries[0].attribute == attribute.identifier) // nodeSetValueEntries[0].value is not valid by this point #expect(recorder.capturedValue == 42) } - + @Test func traceNodeSetValueNotCalledOnCreateAttributeWithType() throws { let graph = Graph() let recorder = TestTraceRecorder() recorder.install(graph: graph) - + try #require(recorder.history.nodeSetValueEntries.count == 0) - + let subgraph = Subgraph(graph: graph) let _ = subgraph.apply { Attribute(type: Int.self) } - + let nodeSetValueEntries = recorder.history.nodeSetValueEntries try #require(nodeSetValueEntries.count == 0) } - + @Test func traceNodeSetValueCalledOnCreateAttributeWithRuleAndInitialValue() throws { class NodeSetValueTrace: TestTraceRecorder { @@ -570,38 +570,38 @@ struct TracingNodeLifecycleTests { let graph = Graph() let recorder = NodeSetValueTrace() recorder.install(graph: graph) - + try #require(recorder.history.nodeSetValueEntries.count == 0) - + let subgraph = Subgraph(graph: graph) let attribute = subgraph.apply { Attribute(TestRule(), initialValue: 42) } - + let nodeSetValueEntries = recorder.history.nodeSetValueEntries try #require(nodeSetValueEntries.count == 1) #expect(nodeSetValueEntries[0].attribute == attribute.identifier) // nodeSetValueEntries[0].value is not valid by this point #expect(recorder.capturedValue == 42) } - + @Test func traceNodeSetValueCalledOnCreateUnitializedAttributeWithRule() throws { let graph = Graph() let recorder = TestTraceRecorder() recorder.install(graph: graph) - + try #require(recorder.history.nodeSetValueEntries.count == 0) - + let subgraph = Subgraph(graph: graph) let _ = subgraph.apply { Attribute(TestRule()) } - + let nodeSetValueEntries = recorder.history.nodeSetValueEntries try #require(nodeSetValueEntries.count == 0) } - + @Test func traceNodeSetValueCalledOnSetValue() throws { class NodeSetValueTrace: TestTraceRecorder { @@ -616,12 +616,12 @@ struct TracingNodeLifecycleTests { let graph = Graph() let recorder = NodeSetValueTrace() recorder.install(graph: graph) - + let subgraph = Subgraph(graph: graph) let attribute = subgraph.apply { Attribute(value: 0) } - + try #require(recorder.history.nodeSetValueEntries.count == 1) attribute.value = 42 @@ -640,12 +640,12 @@ struct TracingNodeLifecycleTests { let graph = Graph() let recorder = TestTraceRecorder() recorder.install(graph: graph) - + let subgraph = Subgraph(graph: graph) let attribute = subgraph.apply { Attribute(value: 0) } - + try #require(recorder.history.nodeMarkValueEntries.count == 0) attribute.invalidateValue() @@ -670,7 +670,7 @@ struct TracingNodeLifecycleTests { let graph = Graph() let recorder = TestTraceRecorder() recorder.install(graph: graph) - + let subgraph = Subgraph(graph: graph) let attribute = subgraph.apply { Attribute(TestRule(flag: 1)) @@ -702,7 +702,7 @@ struct TracingNodeLifecycleTests { let graph = Graph() let recorder = TestTraceRecorder() recorder.install(graph: graph) - + let subgraph = Subgraph(graph: graph) let attribute = subgraph.apply { Attribute(TestRule(flag: 1)) diff --git a/Tests/ComputeTests/Shared/Tracing/TracingSubgraphLifecycleTests.swift b/Tests/ComputeTests/Shared/Tracing/TracingSubgraphLifecycleTests.swift index 880610f1..e85b082c 100644 --- a/Tests/ComputeTests/Shared/Tracing/TracingSubgraphLifecycleTests.swift +++ b/Tests/ComputeTests/Shared/Tracing/TracingSubgraphLifecycleTests.swift @@ -19,7 +19,7 @@ struct TracingSubgraphLifecycleTests { #expect(subgraphCreatedEntries[0].subgraph == subgraph) } } - + @Suite struct SubgraphInvalidateTests { @Test @@ -39,7 +39,7 @@ struct TracingSubgraphLifecycleTests { #expect(subgraphDestroyEntries[0].subgraph == subgraph) } } - + @Suite struct SubgraphDestroyTests { @Test @@ -47,7 +47,7 @@ struct TracingSubgraphLifecycleTests { let graph = Graph() let recorder = TestTraceRecorder() recorder.install(graph: graph) - + #expect(recorder.history.subgraphDestroyEntries.count == 0) autoreleasepool { @@ -55,10 +55,10 @@ struct TracingSubgraphLifecycleTests { } let subgraphDestroyEntries = recorder.history.subgraphDestroyEntries - #expect(subgraphDestroyEntries.count == 0) // Not called when Subgraph deinit is called + #expect(subgraphDestroyEntries.count == 0) // Not called when Subgraph deinit is called } } - + @Suite struct SubgraphAddChildTests { @Test @@ -89,7 +89,7 @@ struct TracingSubgraphLifecycleTests { #expect(subgraphAddChildEntries[0].childSubgraph == childSubgraph) } } - + @Suite struct SubgraphRemoveChildTests { @Test diff --git a/Tests/ComputeTests/Shared/Tracing/TracingTraceTests.swift b/Tests/ComputeTests/Shared/Tracing/TracingTraceTests.swift index 0e1d56ab..280b1c6a 100644 --- a/Tests/ComputeTests/Shared/Tracing/TracingTraceTests.swift +++ b/Tests/ComputeTests/Shared/Tracing/TracingTraceTests.swift @@ -6,7 +6,7 @@ struct TracingTraceTests { struct BeginTraceTests { @Test func traceBeginTraceCalled() throws { - let graph = Graph() + let graph = Graph() let recorder = TestTraceRecorder() recorder.install(graph: graph) diff --git a/Tests/ComputeTests/Shared/Tracing/TracingUpdateTests.swift b/Tests/ComputeTests/Shared/Tracing/TracingUpdateTests.swift index bd98a017..a2e81b0e 100644 --- a/Tests/ComputeTests/Shared/Tracing/TracingUpdateTests.swift +++ b/Tests/ComputeTests/Shared/Tracing/TracingUpdateTests.swift @@ -198,7 +198,8 @@ struct TracingUpdateTests { } try #require(endNodeUpdateEntries.count == 2) #expect(endNodeUpdateEntries[1].attribute == attribute.identifier) - #expect(endNodeUpdateEntries[1].changed == false) // The update was cancelled and `.abortIfCancelled` was specified + // The update was cancelled and `.abortIfCancelled` was specified + #expect(endNodeUpdateEntries[1].changed == false) } } @@ -305,7 +306,7 @@ struct TracingUpdateTests { try #require(beginGraphUpdateEntries.count == 1) #expect(beginGraphUpdateEntries[0].graph == graph) } - + @Test func traceBeginGraphUpdateNotCalledWhenGraphHasNoUpdateHandler() throws { let graph = Graph() @@ -352,7 +353,7 @@ struct TracingUpdateTests { try #require(endGraphUpdateEntries.count == 1) #expect(endGraphUpdateEntries[0].graph == graph) } - + @Test func traceEndGraphUpdateNotCalledWhenGraphHasNoUpdateHandler() throws { let graph = Graph() From be70180778421aa4bff15899541f0f16d2d9fc2f Mon Sep 17 00:00:00 2001 From: James Moschou Date: Fri, 7 Aug 2026 10:27:42 +0200 Subject: [PATCH 40/42] Delete stray line of code --- Sources/ComputeCxx/Graph/Graph.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 1a224a83..16462a61 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -2086,7 +2086,6 @@ void Graph::encode_node(Encoder &encoder, const Node &node, bool encode_value) c encoder.encode_field_varint(1, node.type_id()); if (encode_value) { auto type = attribute_type(node.type_id()); - void *value = node.get_value(); if (node.is_value_initialized()) { void *value = node.get_value(); #if TARGET_OS_MAC From 6822ab916a352e95e61cd5363b61ac7afc99daa4 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Fri, 7 Aug 2026 10:47:31 +0200 Subject: [PATCH 41/42] Fix casting from Data to CFData on Linux --- Sources/Compute/Graph/Graph.swift | 2 +- .../FoundationExtensions/Data+CFData.swift | 18 ++++++++++++++++++ .../FoundationExtensions/String+CFString.swift | 1 - 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 Sources/Compute/Utility/FoundationExtensions/Data+CFData.swift diff --git a/Sources/Compute/Graph/Graph.swift b/Sources/Compute/Graph/Graph.swift index ffe10f6b..3641270f 100644 --- a/Sources/Compute/Graph/Graph.swift +++ b/Sources/Compute/Graph/Graph.swift @@ -179,7 +179,7 @@ extension Graph { eventID: eventID, eventArgCount: eventArgs.count, eventArgs: eventArgs, - data: data as CFData?, + data: data?.cfData, flags: flags ) } diff --git a/Sources/Compute/Utility/FoundationExtensions/Data+CFData.swift b/Sources/Compute/Utility/FoundationExtensions/Data+CFData.swift new file mode 100644 index 00000000..7572d53a --- /dev/null +++ b/Sources/Compute/Utility/FoundationExtensions/Data+CFData.swift @@ -0,0 +1,18 @@ +import Foundation + +extension Data { + @usableFromInline + var cfData: CFData { + #if canImport(Darwin) + self as CFData + #else + withUnsafeBytes { raw -> CFData in + CFDataCreate( + kCFAllocatorDefault, + raw.baseAddress?.assumingMemoryBound(to: UInt8.self), + raw.count + )! + } + #endif + } +} diff --git a/Sources/Compute/Utility/FoundationExtensions/String+CFString.swift b/Sources/Compute/Utility/FoundationExtensions/String+CFString.swift index a7efc33b..ac865dc0 100644 --- a/Sources/Compute/Utility/FoundationExtensions/String+CFString.swift +++ b/Sources/Compute/Utility/FoundationExtensions/String+CFString.swift @@ -29,5 +29,4 @@ extension String { } return cfString } - } From 770cee0b38377cb390e208864907346ef702ef72 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Fri, 7 Aug 2026 10:57:07 +0200 Subject: [PATCH 42/42] Fix casting from CFData to Data on Linux in tests --- .../ComputeTests/Shared/TestSupport/TestTrace.swift | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift b/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift index b44e1f23..cbc1854d 100644 --- a/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift +++ b/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift @@ -149,7 +149,7 @@ public class TestTrace { graph: graph, eventID: event_id, eventArgs: eventArgs ?? [], - data: data as Data?, + data: data.map { Data($0) }, flags: flags ) } named_event_enabled: { ctx, event_id in @@ -232,3 +232,13 @@ public class TestTrace { public func compareFailed(attribute: AnyAttribute, comparisonState: ComparisonState) {} } + +extension Data { + init(_ cfData: CFData) { + #if canImport(Darwin) + self = cfData as Data + #else + self.init(bytes: CFDataGetBytePtr(cfData), count: CFDataGetLength(cfData)) + #endif + } +}