Coverage Report

Created: 2018-09-25 14:53

/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