1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
11 #include "nsCycleCollector.h"
13 #include "CycleCollectorStats.h"
14 #include "MainThreadUtils.h"
15 #include "mozilla/BaseProfilerMarkersPrerequisites.h"
16 #include "mozilla/ProfilerMarkers.h"
17 #include "mozilla/Telemetry.h"
18 #include "mozilla/TimeStamp.h"
20 using namespace mozilla
;
22 mozilla::CycleCollectorStats::CycleCollectorStats() {
23 char* env
= getenv("MOZ_CCTIMER");
27 if (strcmp(env
, "none") == 0) {
29 } else if (strcmp(env
, "stdout") == 0) {
31 } else if (strcmp(env
, "stderr") == 0) {
34 mFile
= fopen(env
, "a");
36 NS_WARNING("Failed to open MOZ_CCTIMER log file.");
41 void mozilla::CycleCollectorStats::Clear() {
42 if (mFile
&& mFile
!= stdout
&& mFile
!= stderr
) {
45 *this = CycleCollectorStats();
49 static TimeDuration
TimeBetween(TimeStamp aStart
, TimeStamp aEnd
) {
50 MOZ_ASSERT(aEnd
>= aStart
);
54 static TimeDuration
TimeUntilNow(TimeStamp start
) {
56 return TimeDuration();
58 return TimeBetween(start
, TimeStamp::Now());
61 namespace geckoprofiler::markers
{
62 class CCSliceMarker
: public BaseMarkerType
<CCSliceMarker
> {
64 static constexpr const char* Name
= "CCSlice";
65 static constexpr const char* Description
=
66 "Information for an individual CC slice.";
68 using MS
= MarkerSchema
;
69 static constexpr MS::PayloadField PayloadFields
[] = {
70 {"idle", MS::InputType::Boolean
, "Idle", MS::Format::Integer
}};
72 static constexpr MS::Location Locations
[] = {MS::Location::MarkerChart
,
73 MS::Location::MarkerTable
,
74 MS::Location::TimelineMemory
};
75 static constexpr const char* AllLabels
=
76 "{marker.name} (idle={marker.data.idle})";
78 static constexpr MS::ETWMarkerGroup Group
= MS::ETWMarkerGroup::Memory
;
80 static void StreamJSONMarkerData(
81 mozilla::baseprofiler::SpliceableJSONWriter
& aWriter
,
83 StreamJSONMarkerDataImpl(aWriter
, aIsDuringIdle
);
86 } // namespace geckoprofiler::markers
88 void mozilla::CycleCollectorStats::AfterCycleCollectionSlice() {
89 // The meaning of the telemetry is specific to the main thread. No worker
90 // should be calling this method. (And workers currently do not have
91 // incremental CC, so the profiler marker is not needed either.)
92 MOZ_ASSERT(NS_IsMainThread());
94 if (mBeginSliceTime
.IsNull()) {
95 // We already called this method from EndCycleCollectionCallback for this
100 mEndSliceTime
= TimeStamp::Now();
101 TimeDuration duration
= mEndSliceTime
- mBeginSliceTime
;
104 "CCSlice", GCCC
, MarkerTiming::Interval(mBeginSliceTime
, mEndSliceTime
),
105 CCSliceMarker
, !mIdleDeadline
.IsNull() && mIdleDeadline
>= mEndSliceTime
);
107 if (duration
.ToSeconds()) {
108 TimeDuration idleDuration
;
109 if (!mIdleDeadline
.IsNull()) {
110 if (mIdleDeadline
< mEndSliceTime
) {
111 // This slice overflowed the idle period.
112 if (mIdleDeadline
> mBeginSliceTime
) {
113 idleDuration
= mIdleDeadline
- mBeginSliceTime
;
116 idleDuration
= duration
;
121 uint32_t(idleDuration
.ToSeconds() / duration
.ToSeconds() * 100);
122 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_SLICE_DURING_IDLE
,
126 TimeDuration sliceTime
= TimeBetween(mBeginSliceTime
, mEndSliceTime
);
127 mMaxSliceTime
= std::max(mMaxSliceTime
, sliceTime
);
128 mMaxSliceTimeSinceClear
= std::max(mMaxSliceTimeSinceClear
, sliceTime
);
129 mTotalSliceTime
+= sliceTime
;
130 mBeginSliceTime
= TimeStamp();
133 void mozilla::CycleCollectorStats::PrepareForCycleCollection(TimeStamp aNow
) {
135 mSuspected
= nsCycleCollector_suspectedCount();
138 void mozilla::CycleCollectorStats::AfterPrepareForCycleCollectionSlice(
139 TimeStamp aDeadline
, TimeStamp aBeginTime
, TimeStamp aMaybeAfterGCTime
) {
140 mBeginSliceTime
= aBeginTime
;
141 mIdleDeadline
= aDeadline
;
143 if (!aMaybeAfterGCTime
.IsNull()) {
144 mAnyLockedOut
= true;
145 mMaxGCDuration
= std::max(mMaxGCDuration
, aMaybeAfterGCTime
- aBeginTime
);
149 void mozilla::CycleCollectorStats::AfterSyncForgetSkippable(
150 TimeStamp beginTime
) {
151 mMaxSkippableDuration
=
152 std::max(mMaxSkippableDuration
, TimeUntilNow(beginTime
));
153 mRanSyncForgetSkippable
= true;
156 void mozilla::CycleCollectorStats::AfterForgetSkippable(
157 mozilla::TimeStamp aStartTime
, mozilla::TimeStamp aEndTime
,
158 uint32_t aRemovedPurples
, bool aInIdle
) {
159 mozilla::TimeDuration duration
= aEndTime
- aStartTime
;
160 if (!mMinForgetSkippableTime
|| mMinForgetSkippableTime
> duration
) {
161 mMinForgetSkippableTime
= duration
;
163 if (!mMaxForgetSkippableTime
|| mMaxForgetSkippableTime
< duration
) {
164 mMaxForgetSkippableTime
= duration
;
166 mTotalForgetSkippableTime
+= duration
;
167 ++mForgetSkippableBeforeCC
;
169 mRemovedPurples
+= aRemovedPurples
;
171 PROFILER_MARKER("ForgetSkippable", GCCC
,
172 MarkerTiming::IntervalUntilNowFrom(aStartTime
), CCSliceMarker
,
176 void mozilla::CycleCollectorStats::SendTelemetry(TimeDuration aCCNowDuration
,
177 TimeStamp aPrevCCEnd
) const {
178 // Many of the telemetry measures would not make sense off the main thread (on
179 // workers), and even for those that do, we don't want to mix mainthread and
180 // other threads' measures.
181 MOZ_ASSERT(NS_IsMainThread());
183 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_FINISH_IGC
, mAnyLockedOut
);
184 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_SYNC_SKIPPABLE
,
185 mRanSyncForgetSkippable
);
186 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_FULL
,
187 aCCNowDuration
.ToMilliseconds());
188 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_MAX_PAUSE
,
189 mMaxSliceTime
.ToMilliseconds());
191 if (!aPrevCCEnd
.IsNull()) {
192 TimeDuration timeBetween
= TimeBetween(aPrevCCEnd
, mBeginTime
);
193 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_TIME_BETWEEN
,
194 timeBetween
.ToSeconds());
197 Telemetry::Accumulate(Telemetry::FORGET_SKIPPABLE_MAX
,
198 mMaxForgetSkippableTime
.ToMilliseconds());