Thanks to visit codestin.com
Credit goes to chromium.googlesource.com

blob: 6927a397085f3a341760af80c8f0a2eee9b68753 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/synchronization/lock_metrics_recorder.h"
#include "base/auto_reset.h"
#include "base/check.h"
#include "base/containers/ring_buffer.h"
#include "base/metrics/histogram_macros.h"
#include "base/no_destructor.h"
#include "base/strings/strcat.h"
#include "base/synchronization/lock.h"
#include "base/threading/platform_thread.h"
#include "base/threading/platform_thread_ref.h"
#include "base/time/time.h"
namespace base {
namespace {
// The histogram bounds (1us to 1s) are chosen to select for meaningful lock
// contention. A lower bound of 1us (the approximate maximum overhead of a
// no-op syscall) helps filter out noise from uncontended lock acquisitions
// including cases where the detection of contention is best-effort, such as
// in Lock. An upper bound of 1s is used because waits longer than this
// are exceptionally rare, and bucketing them together is sufficient for
// analysis.
void ReportBaseLockHistogram(const TimeDelta& sample) {
static const NoDestructor<const std::string> base_lock_hist_name(
StrCat({"Scheduling.ContendedLockAcquisitionTime.BaseLock.",
PlatformThread::GetName()}));
UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(*base_lock_hist_name, sample,
Microseconds(1), Seconds(1), 100);
}
void ReportPartitionAllocLockHistogram(const TimeDelta& sample) {
static const NoDestructor<const std::string> pa_lock_hist_name(
StrCat({"Scheduling.ContendedLockAcquisitionTime.PartitionAllocLock.",
PlatformThread::GetName()}));
UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(*pa_lock_hist_name, sample,
Microseconds(1), Seconds(1), 100);
}
} // namespace
void LockMetricsRecorder::RecordLockAcquisitionTime(TimeDelta sample,
LockType type) {
buffer_[static_cast<size_t>(type)].SaveToBuffer(sample);
}
void LockMetricsRecorder::ForEachSample(LockType type,
FunctionRef<void(const TimeDelta&)> f) {
CHECK(!iterating_in_progress_);
CHECK_LE(type, LockType::kMax);
// Set the `reporting_in_progress_` flag to true to prevent re-entrancy due to
// any lock contention during the recording of the histogram. This keeps the
// recording and reporting logic simple at the cost of a tiny blind-spot in
// our metrics.
AutoReset<bool> mark_iterating_in_progress(&iterating_in_progress_, true);
auto& buffer = buffer_[static_cast<size_t>(type)];
for (auto it = buffer.Begin(); it; ++it) {
f(**it);
}
buffer.Clear();
}
void LockMetricsRecorder::ReportLockAcquisitionTimes() {
// Only report samples from target thread.
if (!IsCurrentThreadTarget()) {
return;
}
ForEachSample(LockType::kBaseLock, ReportBaseLockHistogram);
ForEachSample(LockType::kPartitionAllocLock,
ReportPartitionAllocLockHistogram);
}
// static
void LockMetricsRecorder::SetTargetCurrentThread() {
PlatformThreadRef current_thread = PlatformThread::CurrentRef();
PlatformThreadRef prev_target_thread =
target_thread_.exchange(current_thread, std::memory_order_relaxed);
CHECK(prev_target_thread.is_null() || prev_target_thread == current_thread);
}
// static
LockMetricsRecorder* LockMetricsRecorder::Get() {
static LockMetricsRecorder g_lock_metrics_recorder;
return &g_lock_metrics_recorder;
}
// static
LockMetricsRecorder::ScopedLockAcquisitionTimer
LockMetricsRecorder::ScopedLockAcquisitionTimer::CreateForTest(
LockMetricsRecorder* recorder) {
return LockMetricsRecorder::ScopedLockAcquisitionTimer(recorder);
}
} // namespace base