DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Header

Mercurial (cdf352f02ac4)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "ObservedDocShell.h"

#include <utility>

#include "AbstractTimelineMarker.h"
#include "LayerTimelineMarker.h"
#include "MainThreadUtils.h"
#include "mozilla/AutoRestore.h"

namespace mozilla {

ObservedDocShell::ObservedDocShell(nsIDocShell* aDocShell)
    : MarkersStorage("ObservedDocShellMutex"),
      mDocShell(aDocShell),
      mPopping(false) {
  MOZ_ASSERT(NS_IsMainThread());
}

void ObservedDocShell::AddMarker(UniquePtr<AbstractTimelineMarker>&& aMarker) {
  // Only allow main thread markers to go into this list. No need to lock
  // here since `mTimelineMarkers` will only be accessed or modified on the
  // main thread only.
  MOZ_ASSERT(NS_IsMainThread());
  // Don't accept any markers generated by the process of popping
  // markers.
  if (!mPopping) {
    mTimelineMarkers.AppendElement(std::move(aMarker));
  }
}

void ObservedDocShell::AddOTMTMarker(
    UniquePtr<AbstractTimelineMarker>&& aMarker) {
  // Only allow off the main thread markers to go into this list. Since most
  // of our markers come from the main thread, be a little more efficient and
  // avoid dealing with multithreading scenarios until all the markers are
  // actually cleared or popped in `ClearMarkers` or `PopMarkers`.
  MOZ_ASSERT(!NS_IsMainThread());
  MutexAutoLock lock(GetLock());  // for `mOffTheMainThreadTimelineMarkers`.
  mOffTheMainThreadTimelineMarkers.AppendElement(std::move(aMarker));
}

void ObservedDocShell::ClearMarkers() {
  MOZ_ASSERT(NS_IsMainThread());
  MutexAutoLock lock(GetLock());  // for `mOffTheMainThreadTimelineMarkers`.
  mTimelineMarkers.Clear();
  mOffTheMainThreadTimelineMarkers.Clear();
}

void ObservedDocShell::PopMarkers(
    JSContext* aCx, nsTArray<dom::ProfileTimelineMarker>& aStore) {
  MOZ_ASSERT(NS_IsMainThread());
  MutexAutoLock lock(GetLock());  // for `mOffTheMainThreadTimelineMarkers`.

  MOZ_RELEASE_ASSERT(!mPopping);
  AutoRestore<bool> resetPopping(mPopping);
  mPopping = true;

  // First, move all of our markers into a single array. We'll chose
  // the `mTimelineMarkers` store because that's where we expect most of
  // our markers to be.
  mTimelineMarkers.AppendElements(std::move(mOffTheMainThreadTimelineMarkers));

  // If we see an unpaired START, we keep it around for the next call
  // to ObservedDocShell::PopMarkers. We store the kept START objects here.
  nsTArray<UniquePtr<AbstractTimelineMarker>> keptStartMarkers;

  for (uint32_t i = 0; i < mTimelineMarkers.Length(); ++i) {
    UniquePtr<AbstractTimelineMarker>& startPayload =
        mTimelineMarkers.ElementAt(i);

    // If this is a TIMESTAMP marker, there's no corresponding END,
    // as it's a single unit of time, not a duration.
    if (startPayload->GetTracingType() == MarkerTracingType::TIMESTAMP) {
      dom::ProfileTimelineMarker* marker = aStore.AppendElement();
      marker->mName = NS_ConvertUTF8toUTF16(startPayload->GetName());
      marker->mStart = startPayload->GetTime();
      marker->mEnd = startPayload->GetTime();
      marker->mStack = startPayload->GetStack();
      startPayload->AddDetails(aCx, *marker);
      continue;
    }

    // Whenever a START marker is found, look for the corresponding END
    // and build a {name,start,end} JS object.
    if (startPayload->GetTracingType() == MarkerTracingType::START) {
      bool hasSeenEnd = false;

      // "Paint" markers are different because painting is handled at root
      // docshell level. The information that a paint was done is stored at
      // sub-docshell level, but we can only be sure that a paint did actually
      // happen in if a "Layer" marker was recorded too.
      bool startIsPaintType = strcmp(startPayload->GetName(), "Paint") == 0;
      bool hasSeenLayerType = false;

      // If we are processing a "Paint" marker, we append information from
      // all the embedded "Layer" markers to this array.
      dom::Sequence<dom::ProfileTimelineLayerRect> layerRectangles;

      // DOM events can be nested, so we must take care when searching
      // for the matching end. It doesn't hurt to apply this logic to
      // all event types.
      uint32_t markerDepth = 0;

      // The assumption is that the devtools timeline flushes markers frequently
      // enough for the amount of markers to always be small enough that the
      // nested for loop isn't going to be a performance problem.
      for (uint32_t j = i + 1; j < mTimelineMarkers.Length(); ++j) {
        UniquePtr<AbstractTimelineMarker>& endPayload =
            mTimelineMarkers.ElementAt(j);
        bool endIsLayerType = strcmp(endPayload->GetName(), "Layer") == 0;

        // Look for "Layer" markers to stream out "Paint" markers.
        if (startIsPaintType && endIsLayerType) {
          AbstractTimelineMarker* raw = endPayload.get();
          LayerTimelineMarker* layerPayload =
              static_cast<LayerTimelineMarker*>(raw);
          layerPayload->AddLayerRectangles(layerRectangles);
          hasSeenLayerType = true;
        }
        if (!startPayload->Equals(*endPayload)) {
          continue;
        }
        if (endPayload->GetTracingType() == MarkerTracingType::START) {
          ++markerDepth;
          continue;
        }
        if (endPayload->GetTracingType() == MarkerTracingType::END) {
          if (markerDepth > 0) {
            --markerDepth;
            continue;
          }
          if (!startIsPaintType || (startIsPaintType && hasSeenLayerType)) {
            dom::ProfileTimelineMarker* marker = aStore.AppendElement();
            marker->mName = NS_ConvertUTF8toUTF16(startPayload->GetName());
            marker->mStart = startPayload->GetTime();
            marker->mEnd = endPayload->GetTime();
            marker->mStack = startPayload->GetStack();
            if (hasSeenLayerType) {
              marker->mRectangles.Construct(layerRectangles);
            }
            startPayload->AddDetails(aCx, *marker);
            endPayload->AddDetails(aCx, *marker);
          }
          hasSeenEnd = true;
          break;
        }
      }

      // If we did not see the corresponding END, keep the START.
      if (!hasSeenEnd) {
        keptStartMarkers.AppendElement(
            std::move(mTimelineMarkers.ElementAt(i)));
        mTimelineMarkers.RemoveElementAt(i);
        --i;
      }
    }
  }

  mTimelineMarkers.SwapElements(keptStartMarkers);
}

}  // namespace mozilla