import Foundation /// Memory footprint probe for the long-chapter optimization work /// (Doc/LONG_CHAPTER_MEMORY_OPTIMIZATION_PLAN.md, P0-2). Enabled with the /// `--demo-memory-probe` launch argument; logs phys_footprint at chapter /// load, every 20 page turns, settings invalidation, and rotation. enum RDEPUBMemoryProbe { static let isEnabled = ProcessInfo.processInfo.arguments.contains("--demo-memory-probe") private static let pageTurnLogStride = 20 /// Main-thread only (page turns are delivered on main). private static var pageTurnCount = 0 static func logPageTurn() { guard isEnabled else { return } pageTurnCount += 1 guard pageTurnCount % pageTurnLogStride == 0 else { return } log("pageTurn count=\(pageTurnCount)") } static func log(_ event: String) { guard isEnabled else { return } print(String(format: "[EPUB][MemoryProbe] %@ footprint=%.1fMB", event, footprintMB)) } /// Current phys_footprint in MB. Cheap enough (one task_info call) to /// surface in the demo state snapshot for automated memory assertions. static var footprintMB: Double { Double(currentFootprint()) / 1_048_576 } /// phys_footprint matches the value Xcode's memory gauge and Jetsam use. private static func currentFootprint() -> UInt64 { var info = task_vm_info_data_t() var count = mach_msg_type_number_t( MemoryLayout.size / MemoryLayout.size ) let result = withUnsafeMutablePointer(to: &info) { pointer in pointer.withMemoryRebound(to: integer_t.self, capacity: Int(count)) { task_info(mach_task_self_, task_flavor_t(TASK_VM_INFO), $0, &count) } } guard result == KERN_SUCCESS else { return 0 } return info.phys_footprint } }