chromium/services/tracing/public/cpp/stack_sampling/loader_lock_sampling_thread_win.cc

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "services/tracing/public/cpp/stack_sampling/loader_lock_sampling_thread_win.h"

#include "base/check.h"
#include "base/no_destructor.h"
#include "base/task/sequenced_task_runner.h"
#include "base/time/time.h"
#include "base/trace_event/common/trace_event_common.h"
#include "base/trace_event/trace_event.h"
#include "services/tracing/public/cpp/stack_sampling/loader_lock_sampler_win.h"

namespace tracing {

namespace {

// This is a prime number close to the stack sampling interval defined in
// tracing_sampler_profiler.cc, so that loader lock sampling happens on a
// similar frequency to stack sampling but not at identical times to reduce the
// change of the measurements interfering.
constexpr int kSamplingIntervalMsec = 53;

LoaderLockSampler* g_loader_lock_sampler = nullptr;

ProbingLoaderLockSampler* DefaultLoaderLockSampler() {
  static base::NoDestructor<ProbingLoaderLockSampler> default_sampler;
  return default_sampler.get();
}

}  // namespace

const char LoaderLockSamplingThread::kLoaderLockHeldEventName[] =
    "LoaderLockHeld (sampled)";

LoaderLockSamplingThread::LoaderLockSamplingThread()
    : base::Thread("LoaderLockSampler") {
  // The loader lock sampler may already have been set if
  // SetLoaderLockSamplerForTesting was called.
  if (!g_loader_lock_sampler)
    g_loader_lock_sampler = DefaultLoaderLockSampler();
  Start();
  DCHECK(task_runner());  // This should exist after Start is called.
  tracker_ = base::SequenceBound<LoaderLockTracker>(task_runner());
}

LoaderLockSamplingThread::~LoaderLockSamplingThread() {
  Stop();
}

// static
void LoaderLockSamplingThread::SetLoaderLockSamplerForTesting(
    LoaderLockSampler* sampler) {
  g_loader_lock_sampler = sampler ? sampler : DefaultLoaderLockSampler();
}

void LoaderLockSamplingThread::StartSampling() {
  tracker_.AsyncCall(&LoaderLockTracker::StartSampling);
}

void LoaderLockSamplingThread::StopSampling() {
  tracker_.AsyncCall(&LoaderLockTracker::StopSampling);
}

void LoaderLockSamplingThread::LoaderLockTracker::StartSampling() {
  sample_timer_.Start(FROM_HERE, base::Milliseconds(kSamplingIntervalMsec),
                      this, &LoaderLockTracker::SampleLoaderLock);
}

void LoaderLockSamplingThread::LoaderLockTracker::StopSampling() {
  sample_timer_.Stop();
}

void LoaderLockSamplingThread::LoaderLockTracker::SampleLoaderLock() {
  DCHECK(g_loader_lock_sampler);
  bool loader_lock_now_held = g_loader_lock_sampler->IsLoaderLockHeld();

  if (loader_lock_now_held && !loader_lock_is_held_) {
    TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"),
                                      kLoaderLockHeldEventName,
                                      TRACE_ID_LOCAL(this));
  } else if (!loader_lock_now_held && loader_lock_is_held_) {
    TRACE_EVENT_NESTABLE_ASYNC_END0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"),
                                    kLoaderLockHeldEventName,
                                    TRACE_ID_LOCAL(this));
  }
  loader_lock_is_held_ = loader_lock_now_held;
}

}  // namespace tracing