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": [ 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 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..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 @@ -2,32 +2,39 @@ #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); + +typedef AG_OPTIONS(uint32_t, AGNamedTraceEventFlags) { + AGNamedTraceEventFlagsRecordBacktrace = 1ul << 31, +} AG_SWIFT_NAME(Graph.NamedTraceEventFlags); + AG_ASSUME_NONNULL_BEGIN +AG_IMPLICIT_BRIDGING_ENABLED 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, - CFArrayRef _Nullable subsystems) - AG_SWIFT_NAME(AGGraphRef.startTracing(_:options:subsystems:)); +void AGGraphStartTracing2(AGGraphRef _Nullable graph, AGGraphTraceFlags trace_flags, CFArrayRef _Nullable subsystems) + AG_SWIFT_NAME(AGGraphRef.startTracing(_:flags:subsystems:)); AG_EXPORT AG_REFINED_FOR_SWIFT @@ -35,29 +42,30 @@ 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 -uint64_t 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, uint64_t 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 @@ -69,34 +77,36 @@ 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, uint32_t 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 -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) - 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 +AG_IMPLICIT_BRIDGING_DISABLED AG_ASSUME_NONNULL_END 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/Headers/AGTraceType.h b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/maccatalyst-arm64_arm64e_x86_64/AttributeGraph.framework/Headers/AGTraceType.h index d22355b7..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 @@ -2,6 +2,7 @@ #include #include +#include #include AG_ASSUME_NONNULL_BEGIN @@ -14,18 +15,18 @@ 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); 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, 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_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); - 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); @@ -65,13 +66,15 @@ typedef struct AG_SWIFT_NAME(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, uint32_t eventID, uint32_t eventArgCount, - const void *eventArgs, CFDataRef data, uint32_t arg6); - bool (*_Nullable named_event_enabled)(void *_Nullable context); + 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); + 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/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/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..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 @@ -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 { @@ -579,8 +579,27 @@ 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) + @_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, + 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 f235eb72..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 @@ -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 { @@ -579,8 +579,27 @@ 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) + @_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, + 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 ae0b814d..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 @@ -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 { @@ -579,8 +579,27 @@ 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) + @_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, + 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/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..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 @@ -2,32 +2,39 @@ #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); + +typedef AG_OPTIONS(uint32_t, AGNamedTraceEventFlags) { + AGNamedTraceEventFlagsRecordBacktrace = 1ul << 31, +} AG_SWIFT_NAME(Graph.NamedTraceEventFlags); + AG_ASSUME_NONNULL_BEGIN +AG_IMPLICIT_BRIDGING_ENABLED 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, - CFArrayRef _Nullable subsystems) - AG_SWIFT_NAME(AGGraphRef.startTracing(_:options:subsystems:)); +void AGGraphStartTracing2(AGGraphRef _Nullable graph, AGGraphTraceFlags trace_flags, CFArrayRef _Nullable subsystems) + AG_SWIFT_NAME(AGGraphRef.startTracing(_:flags:subsystems:)); AG_EXPORT AG_REFINED_FOR_SWIFT @@ -35,29 +42,30 @@ 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 -uint64_t 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, uint64_t 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 @@ -69,34 +77,36 @@ 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, uint32_t 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 -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) - 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 +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/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/Headers/AGTraceType.h b/CompatibilityTesting/Frameworks/AttributeGraph.xcframework/macos-arm64_arm64e_x86_64/AttributeGraph.framework/Versions/A/Headers/AGTraceType.h index d22355b7..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 @@ -2,6 +2,7 @@ #include #include +#include #include AG_ASSUME_NONNULL_BEGIN @@ -14,18 +15,18 @@ 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); 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, 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_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); - 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); @@ -65,13 +66,15 @@ typedef struct AG_SWIFT_NAME(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, uint32_t eventID, uint32_t eventArgCount, - const void *eventArgs, CFDataRef data, uint32_t arg6); - bool (*_Nullable named_event_enabled)(void *_Nullable context); + 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); + 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/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/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..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 @@ -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 { @@ -579,8 +579,27 @@ 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) + @_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, + 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 655340ae..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 @@ -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 { @@ -579,8 +579,27 @@ 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) + @_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, + 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 7d8f6905..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 @@ -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 { @@ -579,8 +579,27 @@ 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) + @_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, + eventArgs: eventArgs, + data: data as CFData?, + flags: flags + ) + } } extension AttributeGraph.Graph { public func print(includeValues: Swift.Bool) 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/Compute/Graph/Graph.swift b/Sources/Compute/Graph/Graph.swift index d8a3594f..3641270f 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( @@ -47,7 +48,6 @@ extension Graph { func IAGGraphSetOutputValue(_ value: UnsafeRawPointer, of type: Metadata) extension Graph { - @inline(__always) @inlinable public static func setOutputValue(_ value: UnsafePointer) { @@ -57,16 +57,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 +86,6 @@ func IAGGraphWithMainThreadHandler( ) extension Graph { - public func onUpdate(_ handler: @escaping () -> Void) { IAGGraphSetUpdateCallback(unsafeBitCast(self, to: UnsafeRawPointer.self), callback: handler) } @@ -128,11 +124,9 @@ extension Graph { mainThreadHandler: mainThreadHandler ) } - } extension Graph { - public static func startProfiling(_ graph: Graph?) { fatalError("not implemented") } @@ -149,23 +143,49 @@ extension Graph { public static func resetProfile() { fatalError("not implemented") } - } extension Graph { + 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, value: T) { - fatalError("not implemented") + public func addTraceEvent(_ event: UnsafePointer, context: UnsafePointer) { + self.addTraceEvent(name: event, value: context, type: Metadata(Value.self)) } - public func addTraceEvent(_ event: UnsafePointer, context: UnsafePointer) { - fatalError("not implemented") + @_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, + eventArgs: [UInt32] = [], + data: Data? = nil, + flags: Graph.NamedTraceEventFlags = [] + ) { + addNamedTraceEvent( + eventID: eventID, + eventArgCount: eventArgs.count, + eventArgs: eventArgs, + data: data?.cfData, + flags: flags + ) + } } extension Graph { - public func print(includeValues: Bool) { fatalError("not implemented") } @@ -185,5 +205,4 @@ extension Graph { public static func stackDescription(maxFrames: Int) -> String { fatalError("not implemented") } - } 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 } - } 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 688d59e1..16462a61 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" @@ -48,18 +49,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 +72,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 +98,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 @@ -659,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) { @@ -1281,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; } @@ -1874,26 +1875,22 @@ 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); static platform_once_t cleanup; - platform_once(&cleanup, []() { - atexit([]() { - Graph::trace_assertion_failure(true, "process exiting"); - }); - }); + platform_once(&cleanup, []() { atexit([]() { Graph::trace_assertion_failure(true, "process exiting"); }); }); } void Graph::stop_tracing() { @@ -1965,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()); @@ -1997,7 +1996,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) { @@ -2008,10 +2007,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(); } @@ -2081,6 +2080,115 @@ 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()); + 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 5f2cc064..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 @@ -27,6 +28,7 @@ IAG_ASSUME_NONNULL_BEGIN namespace IAG { +class Encoder; class Trace; class Graph { @@ -210,7 +212,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; } @@ -390,7 +392,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(); @@ -398,9 +400,9 @@ 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(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(); @@ -412,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); } }; @@ -421,6 +423,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/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) { diff --git a/Sources/ComputeCxx/Graph/IAGGraph.cpp b/Sources/ComputeCxx/Graph/IAGGraph.cpp index c2f130a1..9794f99d 100644 --- a/Sources/ComputeCxx/Graph/IAGGraph.cpp +++ b/Sources/ComputeCxx/Graph/IAGGraph.cpp @@ -901,9 +901,11 @@ 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 +930,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) { @@ -965,29 +967,29 @@ CFStringRef IAGGraphCopyTracePath(IAGGraphRef graph) { return graph_context->graph().copy_trace_path(); } -uint64_t 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, uint64_t 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, uint32_t 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,7 +1027,27 @@ static IAG::vector, 0, uint32_t> *names; } // namespace NamedEvents -const char *IAGGraphGetTraceEventName(uint32_t event_id) { +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; platform_lock_lock(&NamedEvents::lock); @@ -1046,7 +1059,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,24 +1071,13 @@ const char *IAGGraphGetTraceEventSubsystem(uint32_t event_id) { return event_subsystem; } -uint32_t 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 13ebe072..eede9e97 100644 --- a/Sources/ComputeCxx/Graph/TraceRecorder.cpp +++ b/Sources/ComputeCxx/Graph/TraceRecorder.cpp @@ -1,26 +1,369 @@ #include "TraceRecorder.h" +#include #include +#include +#include +#include +#if TARGET_OS_MAC && TARGET_OS_OSX +#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, IAGGraphTraceOptions trace_options, 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 && TARGET_OS_OSX + 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,180 +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 +void Graph::TraceRecorder::begin_update(const Subgraph &subgraph, IAGAttributeFlags subgraph_flags) { + 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, subgraph_flags); + 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; + } -void Graph::TraceRecorder::remove_edge(data::ptr node, uint32_t input_index) { - // TODO: not implemented + 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::set_edge_pending(data::ptr node, AttributeID input, bool pending) { - // TODO: not implemented +void Graph::TraceRecorder::remove_edge(data::ptr node, uint32_t input_index) { + 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) { + 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 -} - -void Graph::TraceRecorder::named_event(const Graph::Context &context, uint32_t 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) { - // TODO: not implemented - return false; + 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, + size_t event_arg_count, const uint32_t *event_args, CFDataRef data, + IAGNamedTraceEventFlags flags) { + 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 (flags & IAGNamedTraceEventFlagsRecordBacktrace) { + field_backtrace(_encoder); + flags &= ~IAGNamedTraceEventFlagsRecordBacktrace; + } + 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); + uint64_t length = CFDataGetLength(data); + field_data(_encoder, ptr, length); + } + encode_event_end(); +} + +bool Graph::TraceRecorder::named_event_enabled(IAGNamedTraceEventID event_id) { + 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 a6219368..1e997cec 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, IAGGraphTraceOptions trace_options, 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; @@ -33,7 +142,7 @@ class Graph::TraceRecorder : public Trace { 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, @@ -67,7 +176,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,9 +196,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, - CFDataRef data, uint32_t arg6) override; - bool named_event_enabled(uint32_t event_id) 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/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/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 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 diff --git a/Sources/ComputeCxx/Trace/ExternalTrace.cpp b/Sources/ComputeCxx/Trace/ExternalTrace.cpp index 167a856a..4922a726 100644 --- a/Sources/ComputeCxx/Trace/ExternalTrace.cpp +++ b/Sources/ComputeCxx/Trace/ExternalTrace.cpp @@ -23,17 +23,19 @@ 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); +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, subgraph_flags); + } } } 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); + } } } @@ -47,7 +49,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); } } @@ -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); + } + } } } @@ -189,14 +204,16 @@ 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)); } } } -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()) { + auto input = node->input_edges()[input_index].attribute; callback(_context, IAGAttribute(IAG::AttributeID(node)), IAGAttribute(input), pending); } } @@ -216,7 +233,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 +251,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 } } @@ -257,29 +275,32 @@ 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)); + } } } -void ExternalTrace::named_event(const IAG::Graph::Context &context, uint32_t 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, + size_t event_arg_count, const uint32_t *event_args, CFDataRef data, + IAGNamedTraceEventFlags flags) { 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, flags); + } } } -bool ExternalTrace::named_event_enabled(uint32_t event_id) { +bool ExternalTrace::named_event_enabled(IAGNamedTraceEventID event_id) { if (_trace->version < IAGTraceTypeVersionNamed) { return false; } if (auto callback = _trace->named_event_enabled) { - return callback(_context); + return callback(_context, event_id); } return _trace->named_event != nullptr; } @@ -289,7 +310,7 @@ void ExternalTrace::set_deadline(uint64_t deadline) { return; } if (auto callback = _trace->set_deadline) { - callback(_context); + callback(_context, deadline); } } @@ -302,8 +323,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 5d9c5ae9..438e950c 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; @@ -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, @@ -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; @@ -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, - const void *event_args, CFDataRef data, uint32_t arg6) override; - bool named_event_enabled(uint32_t event_id) 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; void passed_deadline() override; diff --git a/Sources/ComputeCxx/Trace/Trace.h b/Sources/ComputeCxx/Trace/Trace.h index 4525d0ef..faf47dc4 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) {}; @@ -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, @@ -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) {}; @@ -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, - const void *event_args, CFDataRef data, uint32_t arg6) {}; - virtual bool named_event_enabled(uint32_t event_id) { return false; }; + 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, 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..2514493e 100644 --- a/Sources/ComputeCxx/include/ComputeCxx/IAGGraphTracing.h +++ b/Sources/ComputeCxx/include/ComputeCxx/IAGGraphTracing.h @@ -2,32 +2,39 @@ #include #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; +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 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, - CFArrayRef _Nullable subsystems) - IAG_SWIFT_NAME(IAGGraphRef.startTracing(_:options:subsystems:)); +void IAGGraphStartTracing2(IAGGraphRef _Nullable graph, IAGGraphTraceFlags trace_flags, CFArrayRef _Nullable subsystems) + IAG_SWIFT_NAME(IAGGraphRef.startTracing(_:flags:subsystems:)); IAG_EXPORT IAG_REFINED_FOR_SWIFT @@ -35,29 +42,30 @@ 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 -uint64_t 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, uint64_t 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 @@ -69,34 +77,36 @@ 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, uint32_t 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 -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) - 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 +IAG_IMPLICIT_BRIDGING_DISABLED IAG_ASSUME_NONNULL_END 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 diff --git a/Sources/ComputeCxx/include/ComputeCxx/IAGTraceType.h b/Sources/ComputeCxx/include/ComputeCxx/IAGTraceType.h index c36b7851..1835d1ba 100644 --- a/Sources/ComputeCxx/include/ComputeCxx/IAGTraceType.h +++ b/Sources/ComputeCxx/include/ComputeCxx/IAGTraceType.h @@ -2,6 +2,7 @@ #include #include +#include #include IAG_ASSUME_NONNULL_BEGIN @@ -14,18 +15,18 @@ 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); 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, 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_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); - 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); @@ -65,13 +66,15 @@ typedef struct IAG_SWIFT_NAME(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, uint32_t eventID, uint32_t eventArgCount, - const void *eventArgs, CFDataRef data, uint32_t arg6); - bool (*_Nullable named_event_enabled)(void *_Nullable context); + 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); + 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/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()); 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/Graph/GraphDeadlineTests.swift b/Tests/ComputeTests/Shared/Graph/GraphDeadlineTests.swift new file mode 100644 index 00000000..7e9e2db5 --- /dev/null +++ b/Tests/ComputeTests/Shared/Graph/GraphDeadlineTests.swift @@ -0,0 +1,27 @@ +import Testing +import _ComputeTestSupport + +@Suite +struct GraphDeadlineTests { + @Test + func withDeadline() { + let graph = Graph() + + #expect(graph.deadline == UInt64.max) + graph.withDeadline(0) { + #expect(graph.deadline == 0) + } + #expect(graph.deadline == UInt64.max) + } + + @Test + func withDeadlineNonzero() { + let graph = Graph() + + #expect(graph.deadline == UInt64.max) + graph.withDeadline(100) { + #expect(graph.deadline == 100) + } + #expect(graph.deadline == UInt64.max) + } +} diff --git a/Tests/ComputeTests/Shared/Graph/GraphTracingTests.swift b/Tests/ComputeTests/Shared/Graph/GraphTracingTests.swift index b3e370a2..f30306d7 100644 --- a/Tests/ComputeTests/Shared/Graph/GraphTracingTests.swift +++ b/Tests/ComputeTests/Shared/Graph/GraphTracingTests.swift @@ -2,13 +2,103 @@ import Testing @Suite struct GraphTracingTests { + @Test + func initializeTraceType() { + let _ = 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 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 + 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 +142,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)) @@ -86,7 +176,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 diff --git a/Tests/ComputeTests/Shared/Subgraph/SubgraphLifecycleTests.swift b/Tests/ComputeTests/Shared/Subgraph/SubgraphLifecycleTests.swift new file mode 100644 index 00000000..3c6951dc --- /dev/null +++ b/Tests/ComputeTests/Shared/Subgraph/SubgraphLifecycleTests.swift @@ -0,0 +1,92 @@ +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) + } + } + } + + @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) + } + } + } + + @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) + } + } +} diff --git a/Tests/ComputeTests/Shared/Subgraph/SubgraphTests.swift b/Tests/ComputeTests/Shared/Subgraph/SubgraphTests.swift index 0d34af4c..72871fc9 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 { @@ -27,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 @@ -179,54 +150,6 @@ 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 subgraph = Subgraph(graph: graph) - - #expect(reporter.createdSubgraphCount == 1) - #expect(reporter.invalidateSubgraphCount == 0) - - subgraph.invalidate() - - #expect(reporter.createdSubgraphCount == 1) - #expect(reporter.invalidateSubgraphCount == 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 new file mode 100644 index 00000000..cbc1854d --- /dev/null +++ b/Tests/ComputeTests/Shared/TestSupport/TestTrace.swift @@ -0,0 +1,244 @@ +import Foundation + +public class TestTrace { + private var trace: UnsafeMutablePointer + private var handle: (Graph, UniqueID)? + + public func install(graph: Graph) { + guard handle == nil else { + return + } + let traceID = graph.addTrace(trace, context: Unmanaged.passRetained(self as AnyObject).toOpaque()) + self.handle = (graph, traceID) + } + + public func uninstall() { + guard let (graph, traceID) = handle else { + return + } + graph.removeTrace(traceID: traceID) + handle = nil + } + + 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.beginTrace(graph: graph) + } end_trace: { ctx, graph in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.endTrace(graph: graph) + + // TODO: Check this calls deinit + Unmanaged.fromOpaque(ctx!).release() + } begin_subgraph_update: { ctx, subgraph, subgraph_flags in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.beginSubgraphUpdate(subgraph: subgraph, flags: subgraph_flags) + } end_subgraph_update: { ctx, subgraph in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.endSubgraphUpdate(subgraph: subgraph) + } begin_node_update: { ctx, attribute in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.beginNodeUpdate(attribute: attribute) + } end_node_update: { ctx, attribute, changed in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + // 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.beginValueUpdate(attribute: attribute) + } end_value_update: { ctx, attribute, changed in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + // 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.beginGraphUpdate(graph: graph) + } end_graph_update: { ctx, graph in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.endGraphUpdate(graph: graph) + } begin_graph_invalidation: { ctx, graph, attribute in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.beginGraphInvalidation(graph: graph, attribute: attribute) + } end_graph_invalidation: { ctx, graph, attribute in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.endGraphInvalidation(graph: graph, attribute: attribute) + } begin_modify_node: { ctx, attribute in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.beginModifyNode(attribute: attribute) + } end_modify_node: { ctx, attribute in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.endModifyNode(attribute: attribute) + } begin_event: { ctx, attribute, event_name in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.beginEvent(attribute: attribute, eventName: String(cString: event_name)) + } end_event: { ctx, attribute, event_name in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.endEvent(attribute: attribute, eventName: String(cString: event_name)) + } graph_created: { ctx, graph in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.graphCreated(graph: graph) + } graph_destroy: { ctx, graph in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.graphDestroy(graph: graph) + } graph_needs_update: { ctx, graph in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.graphNeedsUpdate(graph: graph) + } subgraph_created: { ctx, subgraph in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.subgraphCreated(subgraph: subgraph) + } subgraph_destroy: { ctx, subgraph in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.subgraphDestroy(subgraph: subgraph) + } subgraph_add_child: { ctx, subgraph, child_subgraph in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.subgraphAddChild(subgraph: subgraph, childSubgraph: child_subgraph) + } subgraph_remove_child: { ctx, subgraph, child_subgraph in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.subgraphRemoveChild(subgraph: subgraph, childSubgraph: child_subgraph) + } node_added: { ctx, attribute in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.nodeAdded(attribute: attribute) + } node_add_edge: { ctx, attribute, input, options in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + 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, input: input) + } node_set_edge_pending: { ctx, attribute, input, pending in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + 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) + } node_set_pending: { ctx, attribute, pending in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.nodeSetPending(attribute: attribute, pending: pending) + } node_set_value: { ctx, attribute, value in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.nodeSetValue(attribute: attribute, value: value) + } node_mark_value: { ctx, attribute in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.nodeMarkValue(attribute: attribute) + } indirect_node_added: { ctx, attribute in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.indirectNodeAdded(attribute: attribute) + } indirect_node_set_source: { ctx, attribute, source in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.indirectNodeSetSource(attribute: attribute, source: source) + } indirect_node_set_dependency: { ctx, attribute, dependency in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.indirectNodeSetDependency(attribute: attribute, dependency: dependency) + } profile_mark: { ctx, event_name in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.profileMark(eventName: String(cString: event_name)) + } custom_event: { ctx, graph, event_name, value, type in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + 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() + let eventArgs = event_args.map { pointer in + [UInt32](capacity: event_arg_count) { span in + for i in 0...fromOpaque(ctx!).takeUnretainedValue() + return context.namedEventEnabled(eventID: event_id) + } set_deadline: { ctx, deadline in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.setDeadline(deadline: UInt(deadline)) + } passed_deadline: { ctx in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.passedDeadline() + } compare_failed: { ctx, attribute, comparison_state in + let context = Unmanaged.fromOpaque(ctx!).takeUnretainedValue() + context.compareFailed(attribute: attribute, comparisonState: comparison_state) + } + ) + } + + deinit { + self.trace.deallocate() + } + + public func beginTrace(graph: Graph) {} + public func endTrace(graph: Graph) {} + + 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) {} + 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, 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) {} + 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, + eventArgs: [UInt32], + data: Data?, + flags: Graph.NamedTraceEventFlags + ) {} + public func namedEventEnabled(eventID: Graph.NamedTraceEventID) -> Bool { true } + + public func setDeadline(deadline: UInt) {} + public func passedDeadline() {} + + 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 + } +} diff --git a/Tests/ComputeTests/Shared/TestSupport/TestTraceRecorder.swift b/Tests/ComputeTests/Shared/TestSupport/TestTraceRecorder.swift new file mode 100644 index 00000000..7bd271e8 --- /dev/null +++ b/Tests/ComputeTests/Shared/TestSupport/TestTraceRecorder.swift @@ -0,0 +1,854 @@ +import Foundation + +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, flags: Subgraph.Flags) { + history.entries.append(.beginSubgraphUpdate(.init(subgraph: subgraph, flags: flags))) + } + + 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, input: AnyAttribute, options: InputOptions) { + history.entries.append(.nodeAddEdge(.init(attribute: attribute, input: input, options: options))) + } + + public override func nodeRemoveEdge(attribute: AnyAttribute, input: AnyAttribute) { + history.entries.append(.nodeRemoveEdge(.init(attribute: attribute, input: input))) + } + + 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) { + 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, + 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) -> 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))) + } +} + +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 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 + } + + 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 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 + public var eventArgs: [UInt32] + 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 + } + } +} + +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 wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var subgraphDestroyEntries: [TestTraceRecorder.History.SubgraphDestroyEntry] { + entries.compactMap { entry in + if case .subgraphDestroy(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var subgraphAddChildEntries: [TestTraceRecorder.History.SubgraphAddChildEntry] { + entries.compactMap { entry in + if case .subgraphAddChild(let wrappedEntry) = entry { + wrappedEntry + } else { + nil + } + } + } + + public var subgraphRemoveChildEntries: [TestTraceRecorder.History.SubgraphRemoveChildEntry] { + entries.compactMap { entry in + 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 + } + } + } +} diff --git a/Tests/ComputeTests/Shared/Tracing/TracingComparisonTests.swift b/Tests/ComputeTests/Shared/Tracing/TracingComparisonTests.swift new file mode 100644 index 00000000..348e6f28 --- /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) + } + } +} diff --git a/Tests/ComputeTests/Shared/Tracing/TracingCustomEventTests.swift b/Tests/ComputeTests/Shared/Tracing/TracingCustomEventTests.swift new file mode 100644 index 00000000..bbfdd4f6 --- /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)) + } + } + } +} 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) + } + } +} diff --git a/Tests/ComputeTests/Shared/Tracing/TracingGraphInvalidationTests.swift b/Tests/ComputeTests/Shared/Tracing/TracingGraphInvalidationTests.swift new file mode 100644 index 00000000..4abff56f --- /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) + } + } +} 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) + } + } +} diff --git a/Tests/ComputeTests/Shared/Tracing/TracingIndirectNodeLifecycleTests.swift b/Tests/ComputeTests/Shared/Tracing/TracingIndirectNodeLifecycleTests.swift new file mode 100644 index 00000000..bf3f6ff6 --- /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) + } + } +} diff --git a/Tests/ComputeTests/Shared/Tracing/TracingNamedEventTests.swift b/Tests/ComputeTests/Shared/Tracing/TracingNamedEventTests.swift new file mode 100644 index 00000000..402c2cd4 --- /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) + } + } +} diff --git a/Tests/ComputeTests/Shared/Tracing/TracingNodeLifecycleTests.swift b/Tests/ComputeTests/Shared/Tracing/TracingNodeLifecycleTests.swift new file mode 100644 index 00000000..bee55fb1 --- /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) + } + } +} diff --git a/Tests/ComputeTests/Shared/Tracing/TracingSubgraphLifecycleTests.swift b/Tests/ComputeTests/Shared/Tracing/TracingSubgraphLifecycleTests.swift new file mode 100644 index 00000000..e85b082c --- /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) + } + } +} diff --git a/Tests/ComputeTests/Shared/Tracing/TracingTraceTests.swift b/Tests/ComputeTests/Shared/Tracing/TracingTraceTests.swift new file mode 100644 index 00000000..280b1c6a --- /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) + } + } +} diff --git a/Tests/ComputeTests/Shared/Tracing/TracingUpdateTests.swift b/Tests/ComputeTests/Shared/Tracing/TracingUpdateTests.swift new file mode 100644 index 00000000..a2e81b0e --- /dev/null +++ b/Tests/ComputeTests/Shared/Tracing/TracingUpdateTests.swift @@ -0,0 +1,377 @@ +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) + // The update was cancelled and `.abortIfCancelled` was specified + #expect(endNodeUpdateEntries[1].changed == false) + } + } + + @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) + } + } +}