/src/mozilla-central/docshell/base/timeline/ObservedDocShell.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | ObservedDocShell::ObservedDocShell(nsIDocShell* aDocShell) |
18 | | : MarkersStorage("ObservedDocShellMutex") |
19 | | , mDocShell(aDocShell) |
20 | | , mPopping(false) |
21 | 0 | { |
22 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
23 | 0 | } |
24 | | |
25 | | void |
26 | | ObservedDocShell::AddMarker(UniquePtr<AbstractTimelineMarker>&& aMarker) |
27 | 0 | { |
28 | 0 | // Only allow main thread markers to go into this list. No need to lock |
29 | 0 | // here since `mTimelineMarkers` will only be accessed or modified on the |
30 | 0 | // main thread only. |
31 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
32 | 0 | // Don't accept any markers generated by the process of popping |
33 | 0 | // markers. |
34 | 0 | if (!mPopping) { |
35 | 0 | mTimelineMarkers.AppendElement(std::move(aMarker)); |
36 | 0 | } |
37 | 0 | } |
38 | | |
39 | | void |
40 | | ObservedDocShell::AddOTMTMarker(UniquePtr<AbstractTimelineMarker>&& aMarker) |
41 | 0 | { |
42 | 0 | // Only allow off the main thread markers to go into this list. Since most |
43 | 0 | // of our markers come from the main thread, be a little more efficient and |
44 | 0 | // avoid dealing with multithreading scenarios until all the markers are |
45 | 0 | // 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(std::move(aMarker)); |
49 | 0 | } |
50 | | |
51 | | void |
52 | | ObservedDocShell::ClearMarkers() |
53 | 0 | { |
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 | | ObservedDocShell::PopMarkers(JSContext* aCx, |
62 | | nsTArray<dom::ProfileTimelineMarker>& aStore) |
63 | 0 | { |
64 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
65 | 0 | MutexAutoLock lock(GetLock()); // for `mOffTheMainThreadTimelineMarkers`. |
66 | 0 |
|
67 | 0 | MOZ_RELEASE_ASSERT(!mPopping); |
68 | 0 | AutoRestore<bool> resetPopping(mPopping); |
69 | 0 | mPopping = true; |
70 | 0 |
|
71 | 0 | // First, move all of our markers into a single array. We'll chose |
72 | 0 | // the `mTimelineMarkers` store because that's where we expect most of |
73 | 0 | // our markers to be. |
74 | 0 | mTimelineMarkers.AppendElements(std::move(mOffTheMainThreadTimelineMarkers)); |
75 | 0 |
|
76 | 0 | // If we see an unpaired START, we keep it around for the next call |
77 | 0 | // to ObservedDocShell::PopMarkers. We store the kept START objects here. |
78 | 0 | nsTArray<UniquePtr<AbstractTimelineMarker>> keptStartMarkers; |
79 | 0 |
|
80 | 0 | for (uint32_t i = 0; i < mTimelineMarkers.Length(); ++i) { |
81 | 0 | UniquePtr<AbstractTimelineMarker>& startPayload = mTimelineMarkers.ElementAt(i); |
82 | 0 |
|
83 | 0 | // If this is a TIMESTAMP marker, there's no corresponding END, |
84 | 0 | // 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 | 0 | } |
94 | 0 | |
95 | 0 | // Whenever a START marker is found, look for the corresponding END |
96 | 0 | // and build a {name,start,end} JS object. |
97 | 0 | if (startPayload->GetTracingType() == MarkerTracingType::START) { |
98 | 0 | bool hasSeenEnd = false; |
99 | 0 |
|
100 | 0 | // "Paint" markers are different because painting is handled at root |
101 | 0 | // docshell level. The information that a paint was done is stored at |
102 | 0 | // sub-docshell level, but we can only be sure that a paint did actually |
103 | 0 | // happen in if a "Layer" marker was recorded too. |
104 | 0 | bool startIsPaintType = strcmp(startPayload->GetName(), "Paint") == 0; |
105 | 0 | bool hasSeenLayerType = false; |
106 | 0 |
|
107 | 0 | // If we are processing a "Paint" marker, we append information from |
108 | 0 | // all the embedded "Layer" markers to this array. |
109 | 0 | dom::Sequence<dom::ProfileTimelineLayerRect> layerRectangles; |
110 | 0 |
|
111 | 0 | // DOM events can be nested, so we must take care when searching |
112 | 0 | // for the matching end. It doesn't hurt to apply this logic to |
113 | 0 | // all event types. |
114 | 0 | uint32_t markerDepth = 0; |
115 | 0 |
|
116 | 0 | // The assumption is that the devtools timeline flushes markers frequently |
117 | 0 | // enough for the amount of markers to always be small enough that the |
118 | 0 | // 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 | 0 |
|
123 | 0 | // 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 | 0 | } |
130 | 0 | if (!startPayload->Equals(*endPayload)) { |
131 | 0 | continue; |
132 | 0 | } |
133 | 0 | if (endPayload->GetTracingType() == MarkerTracingType::START) { |
134 | 0 | ++markerDepth; |
135 | 0 | continue; |
136 | 0 | } |
137 | 0 | if (endPayload->GetTracingType() == MarkerTracingType::END) { |
138 | 0 | if (markerDepth > 0) { |
139 | 0 | --markerDepth; |
140 | 0 | continue; |
141 | 0 | } |
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 | 0 | } |
151 | 0 | startPayload->AddDetails(aCx, *marker); |
152 | 0 | endPayload->AddDetails(aCx, *marker); |
153 | 0 | } |
154 | 0 | hasSeenEnd = true; |
155 | 0 | break; |
156 | 0 | } |
157 | 0 | } |
158 | 0 |
|
159 | 0 | // If we did not see the corresponding END, keep the START. |
160 | 0 | if (!hasSeenEnd) { |
161 | 0 | keptStartMarkers.AppendElement(std::move(mTimelineMarkers.ElementAt(i))); |
162 | 0 | mTimelineMarkers.RemoveElementAt(i); |
163 | 0 | --i; |
164 | 0 | } |
165 | 0 | } |
166 | 0 | } |
167 | 0 |
|
168 | 0 | mTimelineMarkers.SwapElements(keptStartMarkers); |
169 | 0 | } |
170 | | |
171 | | } // namespace mozilla |