Skip to content

Commit 24720ef

Browse files
Merge pull request #2234 from DataDog/simaoseica/RUM-8628/calculate-hang-rate-and-hitch-rate
RUM-8628: Calculate Hang rate and Hitch rate
2 parents b387c1c + 84b59a1 commit 24720ef

File tree

7 files changed

+238
-7
lines changed

7 files changed

+238
-7
lines changed

DatadogCore/Tests/Datadog/Mocks/RUM/RUMFeatureMocks.swift

+4
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,7 @@ extension RUMScopeDependencies {
775775
sessionSampler: Sampler = .mockKeepAll(),
776776
trackBackgroundEvents: Bool = .mockAny(),
777777
trackFrustrations: Bool = true,
778+
hasAppHangsEnabled: Bool = true,
778779
firstPartyHosts: FirstPartyHosts = .init([:]),
779780
eventBuilder: RUMEventBuilder = RUMEventBuilder(eventsMapper: .mockNoOp()),
780781
rumUUIDGenerator: RUMUUIDGenerator = DefaultRUMUUIDGenerator(),
@@ -805,6 +806,7 @@ extension RUMScopeDependencies {
805806
sessionSampler: sessionSampler,
806807
trackBackgroundEvents: trackBackgroundEvents,
807808
trackFrustrations: trackFrustrations,
809+
hasAppHangsEnabled: hasAppHangsEnabled,
808810
firstPartyHosts: firstPartyHosts,
809811
eventBuilder: eventBuilder,
810812
rumUUIDGenerator: rumUUIDGenerator,
@@ -831,6 +833,7 @@ extension RUMScopeDependencies {
831833
sessionSampler: Sampler? = nil,
832834
trackBackgroundEvents: Bool? = nil,
833835
trackFrustrations: Bool? = nil,
836+
hasAppHangsEnabled: Bool? = nil,
834837
firstPartyHosts: FirstPartyHosts? = nil,
835838
eventBuilder: RUMEventBuilder? = nil,
836839
rumUUIDGenerator: RUMUUIDGenerator? = nil,
@@ -855,6 +858,7 @@ extension RUMScopeDependencies {
855858
sessionSampler: sessionSampler ?? self.sessionSampler,
856859
trackBackgroundEvents: trackBackgroundEvents ?? self.trackBackgroundEvents,
857860
trackFrustrations: trackFrustrations ?? self.trackFrustrations,
861+
hasAppHangsEnabled: hasAppHangsEnabled ?? self.hasAppHangsEnabled,
858862
firstPartyHosts: firstPartyHosts ?? self.firstPartyHosts,
859863
eventBuilder: eventBuilder ?? self.eventBuilder,
860864
rumUUIDGenerator: rumUUIDGenerator ?? self.rumUUIDGenerator,

DatadogRUM/Sources/Feature/RUMFeature.swift

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ internal final class RUMFeature: DatadogRemoteFeature {
7474
sessionSampler: Sampler(samplingRate: configuration.debugSDK ? 100 : configuration.sessionSampleRate),
7575
trackBackgroundEvents: configuration.trackBackgroundEvents,
7676
trackFrustrations: configuration.trackFrustrations,
77+
hasAppHangsEnabled: configuration.appHangThreshold != nil,
7778
firstPartyHosts: {
7879
switch configuration.urlSessionTracking?.firstPartyHostsTracing {
7980
case let .trace(hosts, _, _):

DatadogRUM/Sources/RUMMonitor/Scopes/RUMScopeDependencies.swift

+3
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ internal struct RUMScopeDependencies {
3333
let sessionSampler: Sampler
3434
let trackBackgroundEvents: Bool
3535
let trackFrustrations: Bool
36+
let hasAppHangsEnabled: Bool
3637
let firstPartyHosts: FirstPartyHosts?
3738
let eventBuilder: RUMEventBuilder
3839
let rumUUIDGenerator: RUMUUIDGenerator
@@ -71,6 +72,7 @@ internal struct RUMScopeDependencies {
7172
sessionSampler: Sampler,
7273
trackBackgroundEvents: Bool,
7374
trackFrustrations: Bool,
75+
hasAppHangsEnabled: Bool,
7476
firstPartyHosts: FirstPartyHosts?,
7577
eventBuilder: RUMEventBuilder,
7678
rumUUIDGenerator: RUMUUIDGenerator,
@@ -94,6 +96,7 @@ internal struct RUMScopeDependencies {
9496
self.sessionSampler = sessionSampler
9597
self.trackBackgroundEvents = trackBackgroundEvents
9698
self.trackFrustrations = trackFrustrations
99+
self.hasAppHangsEnabled = hasAppHangsEnabled
97100
self.firstPartyHosts = firstPartyHosts
98101
self.eventBuilder = eventBuilder
99102
self.rumUUIDGenerator = rumUUIDGenerator

DatadogRUM/Sources/RUMMonitor/Scopes/RUMViewScope.swift

+18-2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ internal class RUMViewScope: RUMScope, RUMContextProvider {
1111
struct Constants {
1212
static let frozenFrameThresholdInNs = (0.7).toInt64Nanoseconds // 700ms
1313
static let slowRenderingThresholdFPS = 55.0
14+
static let minimumTimeSpentForRates = 1.0 // 1s
1415
/// The pre-warming detection attribute key
1516
static let activePrewarm = "active_pre_warm"
1617
}
@@ -116,6 +117,8 @@ internal class RUMViewScope: RUMScope, RUMContextProvider {
116117
private let viewEndedMetric: ViewEndedMetricController
117118
/// Tracks "View Hitches" for this view.
118119
private let viewHitchesMetric: (ViewHitchesMetric & RenderLoopReader)?
120+
/// Tracks "View Hangs" for this view.
121+
private var totalAppHangDuration: Double = 0.0
119122

120123
init(
121124
isInitialView: Bool,
@@ -546,6 +549,18 @@ internal class RUMViewScope: RUMScope, RUMContextProvider {
546549
let isSlowRendered = refreshRateInfo?.meanValue.map { $0 < Constants.slowRenderingThresholdFPS }
547550
let networkSettledTime = networkSettledMetric.value(with: context.applicationStateHistory)
548551
var interactionToNextViewTime = interactionToNextViewMetric?.value(for: viewUUID) ?? .failure(.disabled)
552+
var slowFramesRate: Double?
553+
var freezeRate: Double?
554+
if let command = command as? RUMStopViewCommand,
555+
command.identity == identity,
556+
timeSpent >= Constants.minimumTimeSpentForRates {
557+
if let totalHitchesDuration = viewHitchesMetric?.hitchesDataModel.hitchesDuration {
558+
slowFramesRate = totalHitchesDuration / timeSpent * Double(1.toMilliseconds) // milliseconds/second
559+
}
560+
if dependencies.hasAppHangsEnabled {
561+
freezeRate = totalAppHangDuration / timeSpent * 1.hours // seconds/hour
562+
}
563+
}
549564
// Only overwrite with a custom value if INV was disabled
550565
if interactionToNextViewTime == .failure(.disabled),
551566
let customInvValue = internalAttributes[CrossPlatformAttributes.customINVValue] as? (any BinaryInteger),
@@ -636,7 +651,7 @@ internal class RUMViewScope: RUMScope, RUMContextProvider {
636651
firstInputTime: nil,
637652
flutterBuildTime: viewPerformanceMetrics[.flutterBuildTime]?.asFlutterBuildTime(),
638653
flutterRasterTime: viewPerformanceMetrics[.flutterRasterTime]?.asFlutterRasterTime(),
639-
freezeRate: nil,
654+
freezeRate: freezeRate,
640655
frozenFrame: .init(count: frozenFramesCount),
641656
frustration: .init(count: frustrationCount),
642657
id: viewUUID.toRUMDataFormat,
@@ -664,7 +679,7 @@ internal class RUMViewScope: RUMScope, RUMContextProvider {
664679
refreshRateMin: refreshRateInfo?.minValue,
665680
resource: .init(count: resourcesCount.toInt64),
666681
slowFrames: viewHitchesMetric?.hitchesDataModel.hitches.map { .init(duration: $0.duration, start: $0.start) },
667-
slowFramesRate: nil,
682+
slowFramesRate: slowFramesRate,
668683
timeSpent: timeSpent.toInt64Nanoseconds,
669684
url: viewPath
670685
)
@@ -703,6 +718,7 @@ internal class RUMViewScope: RUMScope, RUMContextProvider {
703718

704719
private func sendErrorEvent(on command: RUMErrorCommand, context: DatadogContext, writer: Writer) {
705720
errorsCount += 1
721+
totalAppHangDuration += (command as? RUMAddCurrentViewAppHangCommand)?.hangDuration ?? 0
706722

707723
var commandAttributes = command.globalAttributes.merging(command.attributes) { $1 }
708724
let errorFingerprint: String? = commandAttributes.removeValue(forKey: RUM.Attributes.errorFingerprint)?.dd.decode()

DatadogRUM/Sources/RUMVitals/RenderLoop/ViewHitchesReader.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ internal typealias Hitch = (start: Int64, duration: Int64)
1515
/**
1616
- parameters:
1717
- hitches: Array of view hitches (slow frames)
18-
- hitchesDuration: Cumulative duration in ms of the view hitches
18+
- hitchesDuration: Cumulative duration in seconds of the view hitches
1919
*/
2020
internal typealias HitchesDataModel = (hitches: [Hitch], hitchesDuration: Double)
2121

@@ -42,8 +42,8 @@ internal final class ViewHitchesReader: ViewHitchesMetric {
4242
private var startTimestamp: Double = 0
4343
private var nextFrameTimestamp: Double?
4444

45-
private var hangThreshold: TimeInterval
46-
private var acceptableLatency: TimeInterval
45+
private let hangThreshold: TimeInterval
46+
private let acceptableLatency: TimeInterval
4747

4848
/// Amount of time when the frames are rendered too late.
4949
private var _hitchesDuration: Double = 0.0

DatadogRUM/Tests/Mocks/RUMFeatureMocks.swift

+5-1
Original file line numberDiff line numberDiff line change
@@ -795,14 +795,15 @@ extension RUMScopeDependencies {
795795
sessionSampler: Sampler = .mockKeepAll(),
796796
trackBackgroundEvents: Bool = .mockAny(),
797797
trackFrustrations: Bool = true,
798+
hasAppHangsEnabled: Bool = true,
798799
firstPartyHosts: FirstPartyHosts = .init([:]),
799800
eventBuilder: RUMEventBuilder = RUMEventBuilder(eventsMapper: .mockNoOp()),
800801
rumUUIDGenerator: RUMUUIDGenerator = DefaultRUMUUIDGenerator(),
801802
backtraceReporter: BacktraceReporting = BacktraceReporterMock(backtrace: nil),
802803
ciTest: RUMCITest? = nil,
803804
syntheticsTest: RUMSyntheticsTest? = nil,
804805
renderLoopObserver: RenderLoopObserver? = nil,
805-
viewHitchesMetricFactory: @escaping () -> RenderLoopReader & ViewHitchesMetric = { ViewHitchesMock.mockAny() },
806+
viewHitchesMetricFactory: @escaping () -> (RenderLoopReader & ViewHitchesMetric)? = { ViewHitchesMock.mockAny() },
806807
vitalsReaders: VitalsReaders? = nil,
807808
onSessionStart: @escaping RUM.SessionListener = mockNoOpSessionListener(),
808809
viewCache: ViewCache = ViewCache(dateProvider: SystemDateProvider()),
@@ -821,6 +822,7 @@ extension RUMScopeDependencies {
821822
sessionSampler: sessionSampler,
822823
trackBackgroundEvents: trackBackgroundEvents,
823824
trackFrustrations: trackFrustrations,
825+
hasAppHangsEnabled: hasAppHangsEnabled,
824826
firstPartyHosts: firstPartyHosts,
825827
eventBuilder: eventBuilder,
826828
rumUUIDGenerator: rumUUIDGenerator,
@@ -847,6 +849,7 @@ extension RUMScopeDependencies {
847849
sessionSampler: Sampler? = nil,
848850
trackBackgroundEvents: Bool? = nil,
849851
trackFrustrations: Bool? = nil,
852+
hasAppHangsEnabled: Bool? = nil,
850853
firstPartyHosts: FirstPartyHosts? = nil,
851854
eventBuilder: RUMEventBuilder? = nil,
852855
rumUUIDGenerator: RUMUUIDGenerator? = nil,
@@ -871,6 +874,7 @@ extension RUMScopeDependencies {
871874
sessionSampler: sessionSampler ?? self.sessionSampler,
872875
trackBackgroundEvents: trackBackgroundEvents ?? self.trackBackgroundEvents,
873876
trackFrustrations: trackFrustrations ?? self.trackFrustrations,
877+
hasAppHangsEnabled: hasAppHangsEnabled ?? self.hasAppHangsEnabled,
874878
firstPartyHosts: firstPartyHosts ?? self.firstPartyHosts,
875879
eventBuilder: eventBuilder ?? self.eventBuilder,
876880
rumUUIDGenerator: rumUUIDGenerator ?? self.rumUUIDGenerator,

0 commit comments

Comments
 (0)