Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/tools/profiler/core/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 11 kB image not shown  

Quelle  ProfilerThreadRegistrationData.cpp   Sprache: C

 
/* -*- 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 "mozilla/ProfilerThreadRegistrationData.h"

#include "mozilla/CycleCollectedJSContext.h"
#include "mozilla/FOGIPC.h"
#include "mozilla/ProfilerMarkers.h"
#include "js/AllocationRecording.h"
#include "js/ProfilingStack.h"

#if defined(XP_WIN)
#  include <windows.h>
#elif defined(XP_DARWIN)
#  include <pthread.h>
#endif

#ifdef NIGHTLY_BUILD
namespace geckoprofiler::markers {

using namespace mozilla;

struct ThreadCpuUseMarker {
  static constexpr Span<const char> MarkerTypeName() {
    return MakeStringSpan("ThreadCpuUse");
  }
  static void StreamJSONMarkerData(baseprofiler::SpliceableJSONWriter& aWriter,
                                   ProfilerThreadId aThreadId,
                                   int64_t aCpuTimeMs, int64_t aWakeUps,
                                   const ProfilerString8View& aThreadName) {
    aWriter.IntProperty("threadId"static_cast<int64_t>(aThreadId.ToNumber()));
    aWriter.IntProperty("time", aCpuTimeMs);
    aWriter.IntProperty("wakeups", aWakeUps);
    aWriter.StringProperty("label", aThreadName);
  }
  static MarkerSchema MarkerTypeDisplay() {
    using MS = MarkerSchema;
    MS schema{MS::Location::MarkerChart, MS::Location::MarkerTable};
    schema.AddKeyLabelFormat("time""CPU Time", MS::Format::Milliseconds);
    schema.AddKeyLabelFormat("wakeups""Wake ups", MS::Format::Integer);
    schema.SetTooltipLabel("{marker.name} - {marker.data.label}");
    schema.SetTableLabel(
        "{marker.name} - {marker.data.label}: {marker.data.time} of CPU time, "
        "{marker.data.wakeups} wake ups");
    return schema;
  }
};

}  // namespace geckoprofiler::markers
#endif

namespace mozilla::profiler {

ThreadRegistrationData::ThreadRegistrationData(const char* aName,
                                               const void* aStackTop)
    : mInfo(aName),
      mPlatformData(mInfo.ThreadId()),
      mStackTop(
#if defined(XP_WIN)
          // We don't have to guess on Windows.
          reinterpret_cast<const void*>(
              reinterpret_cast<PNT_TIB>(NtCurrentTeb())->StackBase)
#elif defined(XP_DARWIN)
          // We don't have to guess on Mac/Darwin.
          reinterpret_cast<const void*>(
              pthread_get_stackaddr_np(pthread_self()))
#else
          // Otherwise use the given guess.
          aStackTop
#endif
      ) {
}

// This is a simplified version of profiler_add_marker that can be easily passed
// into the JS engine.
static void profiler_add_js_marker(const char* aMarkerName,
                                   const char* aMarkerText) {
  PROFILER_MARKER_TEXT(
      mozilla::ProfilerString8View::WrapNullTerminatedString(aMarkerName), JS,
      {}, mozilla::ProfilerString8View::WrapNullTerminatedString(aMarkerText));
}

static void profiler_add_js_allocation_marker(JS::RecordAllocationInfo&& info) {
  if (!profiler_thread_is_being_profiled_for_markers()) {
    return;
  }

  struct JsAllocationMarker {
    static constexpr mozilla::Span<const char> MarkerTypeName() {
      return mozilla::MakeStringSpan("JS allocation");
    }
    static void StreamJSONMarkerData(
        mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
        const mozilla::ProfilerString16View& aTypeName,
        const mozilla::ProfilerString8View& aClassName,
        const mozilla::ProfilerString16View& aDescriptiveTypeName,
        const mozilla::ProfilerString8View& aCoarseType, uint64_t aSize,
        bool aInNursery) {
      if (aClassName.Length() != 0) {
        aWriter.StringProperty("className", aClassName);
      }
      if (aTypeName.Length() != 0) {
        aWriter.StringProperty("typeName", NS_ConvertUTF16toUTF8(aTypeName));
      }
      if (aDescriptiveTypeName.Length() != 0) {
        aWriter.StringProperty("descriptiveTypeName",
                               NS_ConvertUTF16toUTF8(aDescriptiveTypeName));
      }
      aWriter.StringProperty("coarseType", aCoarseType);
      aWriter.IntProperty("size", aSize);
      aWriter.BoolProperty("inNursery", aInNursery);
    }
    static mozilla::MarkerSchema MarkerTypeDisplay() {
      return mozilla::MarkerSchema::SpecialFrontendLocation{};
    }
  };

  profiler_add_marker(
      "JS allocation", geckoprofiler::category::JS,
      mozilla::MarkerStack::Capture(), JsAllocationMarker{},
      mozilla::ProfilerString16View::WrapNullTerminatedString(info.typeName),
      mozilla::ProfilerString8View::WrapNullTerminatedString(info.className),
      mozilla::ProfilerString16View::WrapNullTerminatedString(
          info.descriptiveTypeName),
      mozilla::ProfilerString8View::WrapNullTerminatedString(info.coarseType),
      info.size, info.inNursery);
}

JSContext* ThreadRegistrationUnlockedReaderAndAtomicRWOnThread::GetJSContext()
    const {
  if (!mCCJSContext) {
    return nullptr;
  }
  return mCCJSContext->Context();
}

void ThreadRegistrationLockedRWFromAnyThread::SetProfilingFeaturesAndData(
    ThreadProfilingFeatures aProfilingFeatures,
    ProfiledThreadData* aProfiledThreadData, const PSAutoLock&) {
  MOZ_ASSERT(mProfilingFeatures == ThreadProfilingFeatures::NotProfiled);
  mProfilingFeatures = aProfilingFeatures;

  MOZ_ASSERT(!mProfiledThreadData);
  MOZ_ASSERT(aProfiledThreadData);
  mProfiledThreadData = aProfiledThreadData;

  if (mCCJSContext) {
    // The thread is now being profiled, and we already have a JSContext,
    // allocate a JsFramesBuffer to allow profiler-unlocked on-thread sampling.
    MOZ_ASSERT(!mJsFrameBuffer);
    mJsFrameBuffer = new JsFrame[MAX_JS_FRAMES];
  }

  // Check invariants.
  MOZ_ASSERT((mProfilingFeatures != ThreadProfilingFeatures::NotProfiled) ==
             !!mProfiledThreadData);
  MOZ_ASSERT((mCCJSContext &&
              (mProfilingFeatures != ThreadProfilingFeatures::NotProfiled)) ==
             !!mJsFrameBuffer);
}

void ThreadRegistrationLockedRWFromAnyThread::ClearProfilingFeaturesAndData(
    const PSAutoLock&) {
  mProfilingFeatures = ThreadProfilingFeatures::NotProfiled;
  mProfiledThreadData = nullptr;

  if (mJsFrameBuffer) {
    delete[] mJsFrameBuffer;
    mJsFrameBuffer = nullptr;
  }

  // Check invariants.
  MOZ_ASSERT((mProfilingFeatures != ThreadProfilingFeatures::NotProfiled) ==
             !!mProfiledThreadData);
  MOZ_ASSERT((mCCJSContext &&
              (mProfilingFeatures != ThreadProfilingFeatures::NotProfiled)) ==
             !!mJsFrameBuffer);
}

void ThreadRegistrationLockedRWOnThread::SetCycleCollectedJSContext(
    CycleCollectedJSContext* aCx) {
  MOZ_ASSERT(aCx && !mCCJSContext);
  MOZ_ASSERT(aCx->Context());

  mCCJSContext = aCx;

  if (mProfiledThreadData) {
    MOZ_ASSERT((mProfilingFeatures != ThreadProfilingFeatures::NotProfiled) ==
               !!mProfiledThreadData);
    // We now have a JSContext, and the thread is already being profiled,
    // allocate a JsFramesBuffer to allow profiler-unlocked on-thread sampling.
    MOZ_ASSERT(!mJsFrameBuffer);
    mJsFrameBuffer = new JsFrame[MAX_JS_FRAMES];
  }

  // We give the JS engine a non-owning reference to the ProfilingStack. It's
  // important that the JS engine doesn't touch this once the thread dies.
  js::SetContextProfilingStack(aCx->Context(), &ProfilingStackRef());

  // Check invariants.
  MOZ_ASSERT((mCCJSContext &&
              (mProfilingFeatures != ThreadProfilingFeatures::NotProfiled)) ==
             !!mJsFrameBuffer);
}

void ThreadRegistrationLockedRWOnThread::ClearCycleCollectedJSContext() {
  mCCJSContext = nullptr;

  if (mJsFrameBuffer) {
    delete[] mJsFrameBuffer;
    mJsFrameBuffer = nullptr;
  }

  // Check invariants.
  MOZ_ASSERT((mCCJSContext &&
              (mProfilingFeatures != ThreadProfilingFeatures::NotProfiled)) ==
             !!mJsFrameBuffer);
}

void ThreadRegistrationLockedRWOnThread::PollJSSampling() {
  // We can't start/stop profiling until we have the thread's JSContext.
  if (mCCJSContext) {
    // It is possible for mJSSampling to go through the following sequences.
    //
    // - INACTIVE, ACTIVE_REQUESTED, INACTIVE_REQUESTED, INACTIVE
    //
    // - ACTIVE, INACTIVE_REQUESTED, ACTIVE_REQUESTED, ACTIVE
    //
    // Therefore, the if and else branches here aren't always interleaved.
    // This is ok because the JS engine can handle that.
    //
    JSContext* cx = mCCJSContext->Context();
    if (mJSSampling == ACTIVE_REQUESTED) {
      mJSSampling = ACTIVE;
      js::EnableContextProfilingStack(cx, true);

      if (JSAllocationsEnabled()) {
        // TODO - This probability should not be hardcoded. See Bug 1547284.
        JS::EnableRecordingAllocations(cx, profiler_add_js_allocation_marker,
                                       0.01);
      }
      js::RegisterContextProfilingEventMarker(cx, profiler_add_js_marker);

    } else if (mJSSampling == INACTIVE_REQUESTED) {
      mJSSampling = INACTIVE;
      js::EnableContextProfilingStack(cx, false);

      if (JSAllocationsEnabled()) {
        JS::DisableRecordingAllocations(cx);
      }
    }
  }
}

#ifdef NIGHTLY_BUILD
void ThreadRegistrationUnlockedConstReaderAndAtomicRW::RecordWakeCount() const {
  baseprofiler::detail::BaseProfilerAutoLock lock(mRecordWakeCountMutex);

  uint64_t newWakeCount = mWakeCount - mAlreadyRecordedWakeCount;
  if (newWakeCount == 0 && mSleep != AWAKE) {
    // If no new wake-up was counted, and the thread is not marked awake,
    // we can be pretty sure there is no CPU activity to record.
    // Threads that are never annotated as asleep/awake (typically rust threads)
    // start as awake.
    return;
  }

  uint64_t cpuTimeNs;
  if (!GetCpuTimeSinceThreadStartInNs(&cpuTimeNs, PlatformDataCRef())) {
    cpuTimeNs = 0;
  }

  constexpr uint64_t NS_PER_MS = 1'000'000;
  uint64_t cpuTimeMs = cpuTimeNs / NS_PER_MS;

  uint64_t newCpuTimeMs = MOZ_LIKELY(cpuTimeMs > mAlreadyRecordedCpuTimeInMs)
                              ? cpuTimeMs - mAlreadyRecordedCpuTimeInMs
                              : 0;

  if (!newWakeCount && !newCpuTimeMs) {
    // Nothing to report, avoid computing the Glean friendly thread name.
    return;
  }

  nsAutoCString threadName(mInfo.Name());
  // Trim the trailing number of threads that are part of a thread pool.
  for (size_t length = threadName.Length(); length > 0; --length) {
    const char c = threadName.CharAt(length - 1);
    if ((c < '0' || c > '9') && c != '#' && c != ' ') {
      if (length != threadName.Length()) {
        threadName.SetLength(length);
      }
      break;
    }
  }

  mozilla::glean::RecordThreadCpuUse(threadName, newCpuTimeMs, newWakeCount);

  // The thread id is provided as part of the payload because this call is
  // inside a ThreadRegistration data function, which could be invoked with
  // the ThreadRegistry locked. We cannot call any function/option that could
  // attempt to lock the ThreadRegistry again, like MarkerThreadId.
  PROFILER_MARKER("Thread CPU use", OTHER, {}, ThreadCpuUseMarker,
                  mInfo.ThreadId(), newCpuTimeMs, newWakeCount, threadName);
  mAlreadyRecordedCpuTimeInMs = cpuTimeMs;
  mAlreadyRecordedWakeCount += newWakeCount;
}
#endif

}  // namespace mozilla::profiler

91%


¤ Dauer der Verarbeitung: 0.5 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.