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/. */
7 #include "mozilla/layout/LayoutTelemetryTools.h"
9 #include "MainThreadUtils.h"
10 #include "mozilla/Atomics.h"
11 #include "mozilla/PodOperations.h"
12 #include "mozilla/Telemetry.h"
14 using namespace mozilla
;
15 using namespace mozilla::layout_telemetry
;
17 // Returns the key name expected by telemetry. Keep to date with
18 // toolkits/components/telemetry/Histograms.json.
19 static nsLiteralCString
SubsystemTelemetryKey(LayoutSubsystem aSubsystem
) {
22 MOZ_CRASH("Unexpected LayoutSubsystem value");
23 case LayoutSubsystem::Restyle
:
25 case LayoutSubsystem::Reflow
:
26 return "ReflowOther"_ns
;
27 case LayoutSubsystem::ReflowFlex
:
28 return "ReflowFlex"_ns
;
29 case LayoutSubsystem::ReflowGrid
:
30 return "ReflowGrid"_ns
;
31 case LayoutSubsystem::ReflowTable
:
32 return "ReflowTable"_ns
;
33 case LayoutSubsystem::ReflowText
:
34 return "ReflowText"_ns
;
38 static AutoRecord
* sCurrentRecord
;
40 static FlushKind
ToKind(FlushType aFlushType
) {
43 MOZ_CRASH("Expected FlushType::Style or FlushType::Layout");
44 case FlushType::Style
:
45 return FlushKind::Style
;
46 case FlushType::Layout
:
47 return FlushKind::Layout
;
52 namespace layout_telemetry
{
55 PodZero(&mReqsPerFlush
);
56 PodZero(&mFlushesPerTick
);
57 PodZero(&mLayoutSubsystemDurationMs
);
60 void Data::IncReqsPerFlush(FlushType aFlushType
) {
61 mReqsPerFlush
[ToKind(aFlushType
)]++;
64 void Data::IncFlushesPerTick(FlushType aFlushType
) {
65 mFlushesPerTick
[ToKind(aFlushType
)]++;
68 void Data::PingReqsPerFlushTelemetry(FlushType aFlushType
) {
69 auto flushKind
= ToKind(aFlushType
);
70 if (flushKind
== FlushKind::Layout
) {
71 auto styleFlushReqs
= mReqsPerFlush
[FlushKind::Style
].value();
72 auto layoutFlushReqs
= mReqsPerFlush
[FlushKind::Layout
].value();
73 Telemetry::Accumulate(Telemetry::PRESSHELL_REQS_PER_LAYOUT_FLUSH
,
74 "Style"_ns
, styleFlushReqs
);
75 Telemetry::Accumulate(Telemetry::PRESSHELL_REQS_PER_LAYOUT_FLUSH
,
76 "Layout"_ns
, layoutFlushReqs
);
77 mReqsPerFlush
[FlushKind::Style
] = SaturateUint8(0);
78 mReqsPerFlush
[FlushKind::Layout
] = SaturateUint8(0);
80 auto styleFlushReqs
= mReqsPerFlush
[FlushKind::Style
].value();
81 Telemetry::Accumulate(Telemetry::PRESSHELL_REQS_PER_STYLE_FLUSH
,
83 mReqsPerFlush
[FlushKind::Style
] = SaturateUint8(0);
87 void Data::PingFlushPerTickTelemetry(FlushType aFlushType
) {
88 auto flushKind
= ToKind(aFlushType
);
89 auto styleFlushes
= mFlushesPerTick
[FlushKind::Style
].value();
90 if (styleFlushes
> 0) {
91 Telemetry::Accumulate(Telemetry::PRESSHELL_FLUSHES_PER_TICK
, "Style"_ns
,
93 mFlushesPerTick
[FlushKind::Style
] = SaturateUint8(0);
96 auto layoutFlushes
= mFlushesPerTick
[FlushKind::Layout
].value();
97 if (flushKind
== FlushKind::Layout
&& layoutFlushes
> 0) {
98 Telemetry::Accumulate(Telemetry::PRESSHELL_FLUSHES_PER_TICK
, "Layout"_ns
,
100 mFlushesPerTick
[FlushKind::Layout
] = SaturateUint8(0);
104 void Data::PingTotalMsPerTickTelemetry(FlushType aFlushType
) {
105 auto flushKind
= ToKind(aFlushType
);
106 auto range
= (flushKind
== FlushKind::Style
)
107 ? MakeEnumeratedRange(LayoutSubsystem::Restyle
,
108 LayoutSubsystem::Reflow
)
109 : MakeEnumeratedRange(LayoutSubsystem::Reflow
,
110 LayoutSubsystem::Count
);
112 for (auto subsystem
: range
) {
113 auto key
= SubsystemTelemetryKey(subsystem
);
114 double& duration
= mLayoutSubsystemDurationMs
[subsystem
];
115 if (duration
> 0.0) {
116 Telemetry::Accumulate(Telemetry::PRESSHELL_LAYOUT_TOTAL_MS_PER_TICK
, key
,
117 static_cast<uint32_t>(duration
));
123 void Data::PingPerTickTelemetry(FlushType aFlushType
) {
124 PingFlushPerTickTelemetry(aFlushType
);
125 PingTotalMsPerTickTelemetry(aFlushType
);
128 AutoRecord::AutoRecord(LayoutSubsystem aSubsystem
)
129 : AutoRecord(nullptr, aSubsystem
) {}
131 AutoRecord::AutoRecord(Data
* aLayoutTelemetry
, LayoutSubsystem aSubsystem
)
132 : mParentRecord(sCurrentRecord
),
133 mLayoutTelemetry(aLayoutTelemetry
),
134 mSubsystem(aSubsystem
),
135 mStartTime(TimeStamp::Now()),
137 MOZ_ASSERT(NS_IsMainThread());
139 // If we're re-entering the same subsystem, don't update the current record.
141 if (mParentRecord
->mSubsystem
== mSubsystem
) {
145 mLayoutTelemetry
= mParentRecord
->mLayoutTelemetry
;
146 MOZ_ASSERT(mLayoutTelemetry
);
148 // If we're entering a new subsystem, record the amount of time spent in the
149 // parent record before setting the new current record.
150 mParentRecord
->mDurationMs
+=
151 (mStartTime
- mParentRecord
->mStartTime
).ToMilliseconds();
154 sCurrentRecord
= this;
157 AutoRecord::~AutoRecord() {
158 if (sCurrentRecord
!= this) {
159 // If this record is not head of the list, do nothing.
163 TimeStamp now
= TimeStamp::Now();
164 mDurationMs
+= (now
- mStartTime
).ToMilliseconds();
165 mLayoutTelemetry
->mLayoutSubsystemDurationMs
[mSubsystem
] += mDurationMs
;
168 // Restart the parent recording from this point
169 mParentRecord
->mStartTime
= now
;
172 // Unlink this record from the current record list
173 sCurrentRecord
= mParentRecord
;
176 } // namespace layout_telemetry
177 } // namespace mozilla