From 22c9ba0949551271e1325b7a9eebcd5de8c442d4 Mon Sep 17 00:00:00 2001 From: Mert Buran Date: Thu, 10 Jun 2021 10:20:05 +0200 Subject: [PATCH] RUMM-1276 PR comments addressed --- .../RUM/RUMVitals/VitalCPUReader.swift | 13 +++++-- .../RUM/RUMVitals/VitalCPUReaderTest.swift | 36 ++++++++++++------- 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/Sources/Datadog/RUM/RUMVitals/VitalCPUReader.swift b/Sources/Datadog/RUM/RUMVitals/VitalCPUReader.swift index 73448c8106..78113912dc 100644 --- a/Sources/Datadog/RUM/RUMVitals/VitalCPUReader.swift +++ b/Sources/Datadog/RUM/RUMVitals/VitalCPUReader.swift @@ -7,7 +7,7 @@ import Foundation import UIKit.UIApplication -/// A class reading the CPU ticks (_1 second = 600 ticks_) since the start of the process. +/// A class reading the CPU ticks of the processor. internal class VitalCPUReader: VitalReader { /// host_cpu_load_info_count is 4 (tested in iOS 14.4) private static let host_cpu_load_info_count = MemoryLayout.stride / MemoryLayout.stride @@ -28,6 +28,9 @@ internal class VitalCPUReader: VitalReader { return nil } + // TODO: RUMM-1276 appWillResignActive&appDidBecomeActive are called in main thread + // IF readVitalData() is called from non-main threads, they must be synchronized + @objc private func appWillResignActive() { utilizedTicksWhenResigningActive = readUtilizedTicks() @@ -60,7 +63,13 @@ internal class VitalCPUReader: VitalReader { } } if result != KERN_SUCCESS { - // TODO: RUMM-1276 use sdkLogger to log errors? + // in case of error, refer to `kern_return.h` (Objc) + // as its Swift interface doesn't have integer values + // + // NOTE: RUMM-1276 consider using sdkLogger.errorOnce(...) to avoid flooding + InternalMonitoringFeature.instance?.monitor.sdkLogger.error( + "CPU Vital cannot be read! Error code: \(result)" + ) return nil } diff --git a/Tests/DatadogTests/Datadog/RUM/RUMVitals/VitalCPUReaderTest.swift b/Tests/DatadogTests/Datadog/RUM/RUMVitals/VitalCPUReaderTest.swift index 9bac4d0a28..81bb30cc88 100644 --- a/Tests/DatadogTests/Datadog/RUM/RUMVitals/VitalCPUReaderTest.swift +++ b/Tests/DatadogTests/Datadog/RUM/RUMVitals/VitalCPUReaderTest.swift @@ -12,12 +12,9 @@ class VitalCPUReaderTest: XCTestCase { let testNotificationCenter = NotificationCenter() lazy var cpuReader = VitalCPUReader(notificationCenter: testNotificationCenter) - func test_whenCPUUnderHeavyLoad_itMeasuresHigherCPUTicks() throws { + func testWhenCPUUnderHeavyLoadItMeasuresHigherCPUTicks() throws { let highLoadAverage = try averageCPUTicks { - for _ in 0...500_000 { - let random = Double.random(in: Double.leastNonzeroMagnitude...Double.greatestFiniteMagnitude) - _ = tan(random).squareRoot() - } + heavyLoad() } let lowLoadAverage = try averageCPUTicks { @@ -27,14 +24,7 @@ class VitalCPUReaderTest: XCTestCase { XCTAssertGreaterThan(highLoadAverage, lowLoadAverage) } - func test_whenInactiveAppState_itIggnoresCPUTicks() throws { - let heavyLoad = { - for _ in 0...500_000 { - let random = Double.random(in: Double.leastNonzeroMagnitude...Double.greatestFiniteMagnitude) - _ = tan(random).squareRoot() - } - } - + func testWhenInactiveAppStateItIgnoresCPUTicks() throws { let baseline = try XCTUnwrap(cpuReader.readVitalData()) testNotificationCenter.post(name: UIApplication.willResignActiveNotification, object: nil) heavyLoad() @@ -52,7 +42,9 @@ class VitalCPUReaderTest: XCTestCase { private func averageCPUTicks(with block: () -> Void) throws -> Double { let startDate = Date() let startUtilization = try XCTUnwrap(cpuReader.readVitalData()) + block() + let endUtilization = try XCTUnwrap(cpuReader.readVitalData()) let duration = Date().timeIntervalSince(startDate) @@ -62,3 +54,21 @@ class VitalCPUReaderTest: XCTestCase { return utilization } } + +fileprivate func heavyLoad() { + // cpuTicksResolution is measured by trial&error. + // most of the time `readVitalData()` returns incremented data after 0.01sec. + // however, sometimes it returns the same value for 1.0sec. + // looking at the source code, iOS should update cpu ticks at + // every thread scheduling and/or system->user/user->system mode changes in CPU. + // but empirically, it gets stuck for 1.0sec randomly. + let worstCaseCPUTicksResolution: TimeInterval = 1.0 + let startDate = Date() + + while Date().timeIntervalSince(startDate) <= worstCaseCPUTicksResolution { + for _ in 0...100_000 { + let random = Double.random(in: Double.leastNonzeroMagnitude...Double.greatestFiniteMagnitude) + _ = tan(random).squareRoot() + } + } +}