diff --git a/Sources/OpenSwiftUICore/Data/Environment/EnvironmentalView.swift b/Sources/OpenSwiftUICore/Data/Environment/EnvironmentalView.swift index 25cb4bc08..ea9f67411 100644 --- a/Sources/OpenSwiftUICore/Data/Environment/EnvironmentalView.swift +++ b/Sources/OpenSwiftUICore/Data/Environment/EnvironmentalView.swift @@ -3,7 +3,7 @@ // OpenSwiftUICore // // Audited for iOS 18.0 -// Status: Blocked by Tracing +// Status: Complete import OpenGraphShims @@ -55,8 +55,9 @@ struct EnvironmentalViewChild: StatefulRule, AsyncAttribute, CustomStringConv guard shouldReset else { return } tracker.reset() tracker.initializeValues(from: env.plist) - // TODO: Tracing - value = view.body(environment: env) + value = traceBody(V.self) { + view.body(environment: env) + } } var description: String { diff --git a/Sources/OpenSwiftUICore/Data/Update.swift b/Sources/OpenSwiftUICore/Data/Update.swift index 6b343a969..880b1dfb8 100644 --- a/Sources/OpenSwiftUICore/Data/Update.swift +++ b/Sources/OpenSwiftUICore/Data/Update.swift @@ -3,7 +3,7 @@ // OpenSwiftUI // // Audited for iOS 18.0 -// Status: Blocked by Signpost +// Status: Complete // ID: EA173074DA35FA471DC70643259B7E74 (SwiftUI) // ID: 61534957AEEC2EDC447ABDC13B4D426F (SwiftUICore) @@ -68,14 +68,34 @@ package enum Update { lock() depth += 1 if depth == 1 { - // TODO: Signpost.renderUpdate.trace + trackHost + #if canImport(Darwin) + Signpost.viewHost.traceEvent( + type: .begin, + object: trackHost, + "", // TODO: For os_log use + [ + 0, + UInt(bitPattern: Unmanaged.passUnretained(trackHost).toOpaque()), + ] + ) + #endif } } package static func end() { if depth == 1 { dispatchActions() - // TODO: Signpost.renderUpdate.trace + trackHost + #if canImport(Darwin) + Signpost.viewHost.traceEvent( + type: .end, + object: trackHost, + "", // TODO: For os_log use + [ + 0, + UInt(bitPattern: Unmanaged.passUnretained(trackHost).toOpaque()), + ] + ) + #endif } depth -= 1 unlock() diff --git a/Sources/OpenSwiftUICore/Log/Signpost.swift b/Sources/OpenSwiftUICore/Log/Signpost.swift index 4be2f4d50..5deb06f37 100644 --- a/Sources/OpenSwiftUICore/Log/Signpost.swift +++ b/Sources/OpenSwiftUICore/Log/Signpost.swift @@ -1,32 +1,32 @@ // // Signpost.swift -// OpenSwiftUI +// OpenSwiftUICore // // Audited for iOS 18.0 -// Status: WIP -// ID: 34756F646CF7AC3DBE2A8E0B344C962F (RELEASE_2021) -// ID: 59349949219F590F26B6A55CEC9D59A2 (RELEASE_2024) +// Status: Complete +// ID: 34756F646CF7AC3DBE2A8E0B344C962F (SwiftUI) +// ID: 59349949219F590F26B6A55CEC9D59A2 (SwiftUICore) import OpenSwiftUI_SPI import OpenGraphShims -#if canImport(Darwin) +#if canImport(os) package import os.signpost #endif extension Signpost { - package static let render = Signpost.kdebug(0, "Render") - package static let postUpdateActions = Signpost.kdebug(2, "PostUpdateActions") - package static let renderUpdate = Signpost.kdebug(3, "RenderUpdate") - package static let renderFlattened = Signpost.kdebug(4, "RenderFlattened") - package static let bodyInvoke = Signpost.kdebug(5, "BodyInvoke") - package static let linkCreate = Signpost.os_log(6, "LinkCreate") - package static let linkUpdate = Signpost.os_log(7, "LinkUpdate") - package static let linkDestroy = Signpost.os_log(8, "LinkDestroy") - package static let viewHost = Signpost.kdebug(9, "ViewHost") - package static let platformView = Signpost.os_log(10, "ViewMapping") - package static let platformUpdate = Signpost.os_log(11, "PlatformViewUpdate") - package static let animationState = Signpost.os_log(12, "AnimationState") - package static let eventHandling = Signpost.os_log(13, "EventHandling") + package static let render = Signpost.kdebug(0, "Render").published + package static let postUpdateActions = Signpost.kdebug(2, "PostUpdateActions").published + package static let renderUpdate = Signpost.kdebug(3, "RenderUpdate").published + package static let renderFlattened = Signpost.kdebug(4, "RenderFlattened").published + package static let bodyInvoke = Signpost.kdebug(5, "BodyInvoke").published + package static let linkCreate = Signpost.os_log(6, "LinkCreate").published + package static let linkUpdate = Signpost.os_log(7, "LinkUpdate").published + package static let linkDestroy = Signpost.os_log(8, "LinkDestroy").published + package static let viewHost = Signpost.kdebug(9, "ViewHost").published + package static let platformView = Signpost.os_log(10, "ViewMapping").published + package static let platformUpdate = Signpost.os_log(11, "PlatformViewUpdate").published + package static let animationState = Signpost.os_log(12, "AnimationState").published + package static let eventHandling = Signpost.os_log(13, "EventHandling").published } #if canImport(Darwin) @@ -42,13 +42,21 @@ package struct Signpost { package static let moduleName: String = Tracing.libraryName(defining: Signpost.self) @inlinable - package static func os_log(_: UInt8, _ name: StaticString) -> Signpost { + package static func os_log(_ code: UInt8, _ name: StaticString) -> Signpost { + #if OPENSWIFTUI_SIGNPOST_KDEBUG + Signpost(style: .kdebug(code), stability: .debug) + #else Signpost(style: .os_log(name), stability: .debug) + #endif } @inlinable - package static func kdebug(_ code: UInt8, _: StaticString?) -> Signpost { + package static func kdebug(_ code: UInt8, _ name: StaticString?) -> Signpost { + #if OPENSWIFTUI_SIGNPOST_OS_LOG + Signpost(style: .os_log(name), stability: .debug) + #else Signpost(style: .kdebug(code), stability: .debug) + #endif } package static func kdebug(_ code: UInt8) -> Signpost { @@ -95,25 +103,24 @@ package struct Signpost { } package var isEnabled: Bool { - guard Stability.valid.contains(where: { $0 == stability }) else { + guard Stability.valid.contains(stability) else { return false } #if canImport(Darwin) switch style { - case let .kdebug(code): - return kdebug_is_enabled(UInt32(OSSignpostType.event.rawValue & 0xfc) | (UInt32(code) << 2) & 0x3fc | 0x14110000) - case .os_log: - guard kdebug_is_enabled(UInt32(OSSignpostType.event.rawValue & 0xfc) | 0x14110000) else { - return false + case let .kdebug(code): + return kdebug_is_enabled(MISC_INSTRUMENTS_DGB_EVENT_CODE(code: code)) + case .os_log: + guard kdebug_is_enabled(MISC_INSTRUMENTS_DGB_EVENT_CODE()) else { + return false + } + return _signpostLog.signpostsEnabled } - return _signpostLog.signpostsEnabled - } #else - return true + return false #endif } - // TODO @_transparent package func traceInterval( object: AnyObject?, @@ -126,18 +133,18 @@ package struct Signpost { #if canImport(Darwin) let id = OSSignpostID.makeExclusiveID(object) switch style { - case let .kdebug(code): - kdebug_trace(UInt32(code) << 2 | (UInt32(OSSignpostType.begin.rawValue & 0xfc) | 0x14110000), id.rawValue, 0, 0, 0) - defer { kdebug_trace(UInt32(code) << 2 | (UInt32(OSSignpostType.end.rawValue & 0xfc) | 0x14110000), id.rawValue, 0, 0, 0) } - return closure() - case let .os_log(name): - if let message { - os_signpost(.begin, log: _signpostLog, name: name, signpostID: id, message, []) - } else { - os_signpost(.begin, log: _signpostLog, name: name, signpostID: id) - } - defer { os_signpost(.end, log: _signpostLog, name: name, signpostID: id) } - return closure() + case let .kdebug(code): + kdebug_trace(MISC_INSTRUMENTS_DGB_CODE(type: .begin, code: code), id.rawValue, 0, 0, 0) + defer { kdebug_trace(MISC_INSTRUMENTS_DGB_CODE(type: .end, code: code), id.rawValue, 0, 0, 0) } + return closure() + case let .os_log(name): + if let message { + os_signpost(.begin, log: _signpostLog, name: name, signpostID: id, message, []) + } else { + os_signpost(.begin, log: _signpostLog, name: name, signpostID: id) + } + defer { os_signpost(.end, log: _signpostLog, name: name, signpostID: id) } + return closure() } #else return closure() @@ -146,7 +153,7 @@ package struct Signpost { @_transparent package func traceInterval( - object: AnyObject? = nil, + object: AnyObject?, _ message: StaticString, _ args: @autoclosure () -> [any CVarArg], closure: () -> T @@ -156,15 +163,16 @@ package struct Signpost { } #if canImport(Darwin) let id = OSSignpostID.makeExclusiveID(object) + let args = args() switch style { - case let .kdebug(code): - // FIXME: _primitive - print(code) - return closure() - case let .os_log(name): - os_signpost(.begin, log: _signpostLog, name: name, signpostID: id, message, args()) - defer { os_signpost(.end, log: _signpostLog, name: name, signpostID: id) } - return closure() + case let .kdebug(code): + _primitive(.begin, log: _signpostLog, signpostID: id, message, args) + defer { kdebug_trace(MISC_INSTRUMENTS_DGB_CODE(type: .end, code: code), id.rawValue, 0, 0, 0) } + return closure() + case let .os_log(name): + os_signpost(.begin, log: _signpostLog, name: name, signpostID: id, message, args) + defer { os_signpost(.end, log: _signpostLog, name: name, signpostID: id) } + return closure() } #else return closure() @@ -184,19 +192,26 @@ package struct Signpost { } let id = OSSignpostID.makeExclusiveID(object) let args = args() - switch style { - case let .kdebug(code): - // FIXME: _primitive - print(code) - return - case let .os_log(name): - os_signpost(type, log: _signpostLog, name: name, signpostID: id, message, args) + case let .kdebug(code): + _primitive(type, log: _signpostLog, signpostID: id, message, args) + case let .os_log(name): + os_signpost(type, log: _signpostLog, name: name, signpostID: id, message, args) } } #endif + #if canImport(Darwin) + + @inline(__always) + private var styleCode: UInt8 { + switch style { + case let .kdebug(code): code + case .os_log: 0 + } + } + private func _primitive( _ type: OSSignpostType, log: OSLog, @@ -204,14 +219,28 @@ package struct Signpost { _ message: StaticString?, _ arguments: [any CVarArg]? ) { - // TODO + let code = MISC_INSTRUMENTS_DGB_CODE(type: type, code: styleCode) + var id = signpostID + var iterator = (arguments ?? []).makeIterator() + repeat { + let arg0 = iterator.next() + let arg1 = iterator.next() + let arg2 = iterator.next() + withKDebugValues(code, [arg0, arg1, arg2]) { args in + kdebug_trace(code, id.rawValue, args[0], args[1], args[2]) + } + guard arg2 != nil else { + break + } + id = OSSignpostID.continuation + } while true } #endif } -#if canImport(Darwin) +#if canImport(os) extension OSSignpostID { - private static let continuation = OSSignpostID(0x0ea89ce2) + fileprivate static let continuation = OSSignpostID(0x0ea89ce2) @inline(__always) static func makeExclusiveID(_ object: AnyObject?) -> OSSignpostID { @@ -224,6 +253,106 @@ extension OSSignpostID { } #endif +#if canImport(Darwin) + +// MARK: - kdebug + private func withKDebugValues(_ code: UInt32, _ args: [(any CVarArg)?], closure: (([UInt64]) -> Void)) { - // TODO + let values = args.map { $0?.kdebugValue(code) } + closure(values.map { $0?.arg ?? 0 }) + values.forEach { $0?.destructor?() } +} + +private protocol KDebuggableCVarArg: CVarArg { + var kdebugableCVarArgValue: String { get } } + +extension String: KDebuggableCVarArg { + var kdebugableCVarArgValue: String { self } +} + +extension CVarArg { + fileprivate func kdebugValue(_ code: UInt32) -> (arg: UInt64, destructor: (() -> Void)?) { + if let kdebuggableCVarArg = self as? KDebuggableCVarArg { + let description = kdebuggableCVarArg.kdebugableCVarArgValue + if description == Signpost.moduleName { + return (0, nil) + } else { + return description.withCString { pointer in + let stringID = kdebug_trace_string(code & KDBG_CSC_MASK, 0, pointer) + return (stringID, { + kdebug_trace_string((code << 0x8) & KDBG_CSC_MASK, stringID, nil) + }) + } + } + } else { + let encoding = _cVarArgEncoding + if encoding.count == 1 { + return (UInt64(bitPattern: Int64(encoding[0])), nil) + } else { + let description = String(describing: self) + if description == Signpost.moduleName { + return (0, nil) + } else { + return description.withCString { pointer in + let stringID = kdebug_trace_string(code & KDBG_CSC_MASK, 0, pointer) + return (stringID, { + kdebug_trace_string((code << 0x8) & KDBG_CSC_MASK, stringID, nil) + }) + } + } + } + } + } +} + +// MARK: - kdebug macro helper + +@_transparent +func KDBG_EVENTID(_ class: UInt32, _ subclass: UInt32, _ code: UInt32) -> UInt32 { + ((`class` & UInt32(KDBG_CLASS_MAX)) << KDBG_CLASS_OFFSET) | + ((subclass & UInt32(KDBG_SUBCLASS_MAX)) << KDBG_SUBCLASS_OFFSET) | + ((code & UInt32(KDBG_CODE_MAX)) << KDBG_CODE_OFFSET) +} + +@_transparent +func KDBG_DEBUGID(_ class: UInt32, _ subclass: UInt32, _ code: UInt32, _ function: UInt32) -> UInt32 { + KDBG_EVENTID(`class`, subclass, code) | (function & UInt32(KDBG_FUNC_MASK)) +} + +@_transparent +func KDBG_EXTRACT_CLASS(_ debugid: UInt32) -> UInt32 { + (debugid & KDBG_CLASS_MASK) >> KDBG_CLASS_OFFSET +} + +@_transparent +func KDBG_EXTRACT_SUBCLASS(_ debugid: UInt32) -> UInt32 { + (debugid & UInt32(bitPattern: KDBG_SUBCLASS_MASK)) >> KDBG_SUBCLASS_OFFSET +} + +@_transparent +func KDBG_EXTRACT_CODE(_ debugid: UInt32) -> UInt32 { + (debugid & UInt32(bitPattern: KDBG_CODE_MASK)) >> KDBG_CODE_OFFSET +} + +@_transparent +func KDBG_CLASS_ENCODE(_ class: UInt32, _ subclass: UInt32) -> UInt32 { + KDBG_EVENTID(`class`, subclass, 0) +} + +@_transparent +func KDBG_CLASS_DECODE(_ debugid: UInt32) -> UInt32 { + debugid & KDBG_CSC_MASK +} + +@_transparent +func MISC_INSTRUMENTS_DGB_EVENT_CODE(code: UInt8 = 0) -> UInt32 { + KDBG_EVENTID(UInt32(bitPattern: DBG_MISC), UInt32(bitPattern: DBG_MISC_INSTRUMENTS), UInt32(OSSignpostType.event.rawValue >> 2 | code)) +} + +@_transparent +func MISC_INSTRUMENTS_DGB_CODE(type: OSSignpostType, code: UInt8 = 0) -> UInt32 { + KDBG_DEBUGID(UInt32(bitPattern: DBG_MISC), UInt32(bitPattern: DBG_MISC_INSTRUMENTS), UInt32(type.rawValue >> 2 | code), UInt32(type.rawValue)) +} + +#endif diff --git a/Sources/OpenSwiftUICore/Util/Tracing.swift b/Sources/OpenSwiftUICore/Util/Tracing.swift index 78ec068b5..2c730df71 100644 --- a/Sources/OpenSwiftUICore/Util/Tracing.swift +++ b/Sources/OpenSwiftUICore/Util/Tracing.swift @@ -4,8 +4,8 @@ // // Audited for iOS 18.0 // Status: WIP -// ID: D59B7A281FFF29619A43A3D8F551CCE1 (RELEASE_2021) -// ID: 56D4CED87D5B226E2B40FB60C47D6F49 (RELEASE_2024) +// ID: D59B7A281FFF29619A43A3D8F551CCE1 (SwiftUI) +// ID: 56D4CED87D5B226E2B40FB60C47D6F49 (SwiftUICore) #if canImport(Darwin) import Darwin @@ -23,7 +23,7 @@ import OpenSwiftUI_SPI import os.log #endif -enum Tracing { +package struct Tracing { private static var moduleLookupCache = ThreadSpecific<[UnsafeRawPointer: String]>([:]) static func libraryName(defining type: Any.Type) -> String { @@ -57,24 +57,41 @@ enum Tracing { @_transparent package func traceBody(_ v: any Any.Type, body: () -> Body) -> Body { - #if canImport(Darwin) - // Signpost.bodyInvoke.traceInterval - guard kdebug_is_enabled(UInt32(OSSignpostType.event.rawValue) & 0xF8 | 0x1411_0014) else { - return body() - } - // TODO: Metadata(type).description, Tracing.libraryName(defining: v) - return body() - #else - body() - #endif + Signpost.bodyInvoke.traceInterval( + object: nil, + "", // TODO: For os_log use + [ + Metadata(v).description, + Tracing.libraryName(defining: v) + ], + closure: body + ) } @_transparent package func traceRuleBody(_ v: any Any.Type, body: () -> Body) -> Body { - #if canImport(Darwin) - // TODO: - return body() - #else - body() - #endif + defer { + #if canImport(Darwin) + let current = AnyAttribute.current! + Signpost.bodyInvoke.traceEvent( + type: .end, + object: nil, + "", // TODO: For os_log use + [ + current.rawValue, + 1, + current.graph.counter(for: ._4) // FIXME: UInt + ] + ) + #endif + } + return Signpost.bodyInvoke.traceInterval( + object: nil, + "", // TODO: For os_log use + [ + Metadata(v).description, + Tracing.libraryName(defining: v) + ], + closure: body + ) } diff --git a/Sources/OpenSwiftUI_SPI/Shims/kdebug_Private.h b/Sources/OpenSwiftUI_SPI/Shims/kdebug_Private.h index 110760e2e..95ca04811 100644 --- a/Sources/OpenSwiftUI_SPI/Shims/kdebug_Private.h +++ b/Sources/OpenSwiftUI_SPI/Shims/kdebug_Private.h @@ -46,6 +46,33 @@ #if OPENSWIFTUI_TARGET_OS_OSX #include +#else + +// iOS SDK does not have kdebug.h header, so we define the necessary constants here. + +#define KDBG_CLASS_MASK (0xff000000) +#define KDBG_CLASS_OFFSET (24) +#define KDBG_CLASS_MAX (0xff) +#define KDBG_SUBCLASS_MASK (0x00ff0000) +#define KDBG_SUBCLASS_OFFSET (16) +#define KDBG_SUBCLASS_MAX (0xff) +#define KDBG_CSC_MASK (0xffff0000) +#define KDBG_CSC_OFFSET (KDBG_SUBCLASS_OFFSET) +#define KDBG_CSC_MAX (0xffff) +#define KDBG_CODE_MASK (0x0000fffc) +#define KDBG_CODE_OFFSET (2) +#define KDBG_CODE_MAX (0x3fff) +#define KDBG_EVENTID_MASK (0xfffffffc) +#define KDBG_FUNC_MASK (0x00000003) + +#pragma mark - Class and subclass definitions + +#define DBG_MISC 20 + +/* The Kernel Debug Sub Classes for DBG_MISC */ + +#define DBG_MISC_INSTRUMENTS 0x11 + #endif #include @@ -141,7 +168,7 @@ __OSX_AVAILABLE(10.10) __IOS_AVAILABLE(8.2); * `str` is an invalid address or NULL when `str_id` is 0. */ OPENSWIFTUI_EXPORT uint64_t kdebug_trace_string(uint32_t debugid, uint64_t str_id, - const char *str) + const char * _Nullable str) __OSX_AVAILABLE(10.11) __IOS_AVAILABLE(9.0); /*