Line data Source code
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/. */
6 :
7 : #include "ObservedDocShell.h"
8 :
9 : #include "AbstractTimelineMarker.h"
10 : #include "LayerTimelineMarker.h"
11 : #include "MainThreadUtils.h"
12 : #include "mozilla/Move.h"
13 : #include "mozilla/AutoRestore.h"
14 :
15 : namespace mozilla {
16 :
17 0 : ObservedDocShell::ObservedDocShell(nsIDocShell* aDocShell)
18 : : MarkersStorage("ObservedDocShellMutex")
19 : , mDocShell(aDocShell)
20 0 : , mPopping(false)
21 : {
22 0 : MOZ_ASSERT(NS_IsMainThread());
23 0 : }
24 :
25 : void
26 0 : ObservedDocShell::AddMarker(UniquePtr<AbstractTimelineMarker>&& aMarker)
27 : {
28 : // Only allow main thread markers to go into this list. No need to lock
29 : // here since `mTimelineMarkers` will only be accessed or modified on the
30 : // main thread only.
31 0 : MOZ_ASSERT(NS_IsMainThread());
32 : // Don't accept any markers generated by the process of popping
33 : // markers.
34 0 : if (!mPopping) {
35 0 : mTimelineMarkers.AppendElement(Move(aMarker));
36 : }
37 0 : }
38 :
39 : void
40 0 : ObservedDocShell::AddOTMTMarker(UniquePtr<AbstractTimelineMarker>&& aMarker)
41 : {
42 : // Only allow off the main thread markers to go into this list. Since most
43 : // of our markers come from the main thread, be a little more efficient and
44 : // avoid dealing with multithreading scenarios until all the markers are
45 : // actually cleared or popped in `ClearMarkers` or `PopMarkers`.
46 0 : MOZ_ASSERT(!NS_IsMainThread());
47 0 : MutexAutoLock lock(GetLock()); // for `mOffTheMainThreadTimelineMarkers`.
48 0 : mOffTheMainThreadTimelineMarkers.AppendElement(Move(aMarker));
49 0 : }
50 :
51 : void
52 0 : ObservedDocShell::ClearMarkers()
53 : {
54 0 : MOZ_ASSERT(NS_IsMainThread());
55 0 : MutexAutoLock lock(GetLock()); // for `mOffTheMainThreadTimelineMarkers`.
56 0 : mTimelineMarkers.Clear();
57 0 : mOffTheMainThreadTimelineMarkers.Clear();
58 0 : }
59 :
60 : void
61 0 : ObservedDocShell::PopMarkers(JSContext* aCx,
62 : nsTArray<dom::ProfileTimelineMarker>& aStore)
63 : {
64 0 : MOZ_ASSERT(NS_IsMainThread());
65 0 : MutexAutoLock lock(GetLock()); // for `mOffTheMainThreadTimelineMarkers`.
66 :
67 0 : MOZ_RELEASE_ASSERT(!mPopping);
68 0 : AutoRestore<bool> resetPopping(mPopping);
69 0 : mPopping = true;
70 :
71 : // First, move all of our markers into a single array. We'll chose
72 : // the `mTimelineMarkers` store because that's where we expect most of
73 : // our markers to be.
74 0 : mTimelineMarkers.AppendElements(Move(mOffTheMainThreadTimelineMarkers));
75 :
76 : // If we see an unpaired START, we keep it around for the next call
77 : // to ObservedDocShell::PopMarkers. We store the kept START objects here.
78 0 : nsTArray<UniquePtr<AbstractTimelineMarker>> keptStartMarkers;
79 :
80 0 : for (uint32_t i = 0; i < mTimelineMarkers.Length(); ++i) {
81 0 : UniquePtr<AbstractTimelineMarker>& startPayload = mTimelineMarkers.ElementAt(i);
82 :
83 : // If this is a TIMESTAMP marker, there's no corresponding END,
84 : // as it's a single unit of time, not a duration.
85 0 : if (startPayload->GetTracingType() == MarkerTracingType::TIMESTAMP) {
86 0 : dom::ProfileTimelineMarker* marker = aStore.AppendElement();
87 0 : marker->mName = NS_ConvertUTF8toUTF16(startPayload->GetName());
88 0 : marker->mStart = startPayload->GetTime();
89 0 : marker->mEnd = startPayload->GetTime();
90 0 : marker->mStack = startPayload->GetStack();
91 0 : startPayload->AddDetails(aCx, *marker);
92 0 : continue;
93 : }
94 :
95 : // Whenever a START marker is found, look for the corresponding END
96 : // and build a {name,start,end} JS object.
97 0 : if (startPayload->GetTracingType() == MarkerTracingType::START) {
98 0 : bool hasSeenEnd = false;
99 :
100 : // "Paint" markers are different because painting is handled at root
101 : // docshell level. The information that a paint was done is stored at
102 : // sub-docshell level, but we can only be sure that a paint did actually
103 : // happen in if a "Layer" marker was recorded too.
104 0 : bool startIsPaintType = strcmp(startPayload->GetName(), "Paint") == 0;
105 0 : bool hasSeenLayerType = false;
106 :
107 : // If we are processing a "Paint" marker, we append information from
108 : // all the embedded "Layer" markers to this array.
109 0 : dom::Sequence<dom::ProfileTimelineLayerRect> layerRectangles;
110 :
111 : // DOM events can be nested, so we must take care when searching
112 : // for the matching end. It doesn't hurt to apply this logic to
113 : // all event types.
114 0 : uint32_t markerDepth = 0;
115 :
116 : // The assumption is that the devtools timeline flushes markers frequently
117 : // enough for the amount of markers to always be small enough that the
118 : // nested for loop isn't going to be a performance problem.
119 0 : for (uint32_t j = i + 1; j < mTimelineMarkers.Length(); ++j) {
120 0 : UniquePtr<AbstractTimelineMarker>& endPayload = mTimelineMarkers.ElementAt(j);
121 0 : bool endIsLayerType = strcmp(endPayload->GetName(), "Layer") == 0;
122 :
123 : // Look for "Layer" markers to stream out "Paint" markers.
124 0 : if (startIsPaintType && endIsLayerType) {
125 0 : AbstractTimelineMarker* raw = endPayload.get();
126 0 : LayerTimelineMarker* layerPayload = static_cast<LayerTimelineMarker*>(raw);
127 0 : layerPayload->AddLayerRectangles(layerRectangles);
128 0 : hasSeenLayerType = true;
129 : }
130 0 : if (!startPayload->Equals(*endPayload)) {
131 0 : continue;
132 : }
133 0 : if (endPayload->GetTracingType() == MarkerTracingType::START) {
134 0 : ++markerDepth;
135 0 : continue;
136 : }
137 0 : if (endPayload->GetTracingType() == MarkerTracingType::END) {
138 0 : if (markerDepth > 0) {
139 0 : --markerDepth;
140 0 : continue;
141 : }
142 0 : if (!startIsPaintType || (startIsPaintType && hasSeenLayerType)) {
143 0 : dom::ProfileTimelineMarker* marker = aStore.AppendElement();
144 0 : marker->mName = NS_ConvertUTF8toUTF16(startPayload->GetName());
145 0 : marker->mStart = startPayload->GetTime();
146 0 : marker->mEnd = endPayload->GetTime();
147 0 : marker->mStack = startPayload->GetStack();
148 0 : if (hasSeenLayerType) {
149 0 : marker->mRectangles.Construct(layerRectangles);
150 : }
151 0 : startPayload->AddDetails(aCx, *marker);
152 0 : endPayload->AddDetails(aCx, *marker);
153 : }
154 0 : hasSeenEnd = true;
155 0 : break;
156 : }
157 : }
158 :
159 : // If we did not see the corresponding END, keep the START.
160 0 : if (!hasSeenEnd) {
161 0 : keptStartMarkers.AppendElement(Move(mTimelineMarkers.ElementAt(i)));
162 0 : mTimelineMarkers.RemoveElementAt(i);
163 0 : --i;
164 : }
165 : }
166 : }
167 :
168 0 : mTimelineMarkers.SwapElements(keptStartMarkers);
169 0 : }
170 :
171 : } // namespace mozilla
|