blob: 10c5cd5f5561077f112fe416bc68e7d5083b2df2 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/controller/highest_pmf_reporter.h"
#include <limits>
#include "base/metrics/histogram_functions.h"
#include "base/task_runner.h"
#include "base/time/default_tick_clock.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/loader/document_loader.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
namespace blink {
const char* HighestPmfReporter::highest_pmf_metric_names[] = {
"Memory.Experimental.Renderer.HighestPrivateMemoryFootprint.0to2min",
"Memory.Experimental.Renderer.HighestPrivateMemoryFootprint.2to4min",
"Memory.Experimental.Renderer.HighestPrivateMemoryFootprint.4to8min",
"Memory.Experimental.Renderer.HighestPrivateMemoryFootprint.8to16min"};
const char* HighestPmfReporter::peak_resident_bytes_metric_names[] = {
"Memory.Experimental.Renderer.PeakResidentSet."
"AtHighestPrivateMemoryFootprint.0to2min",
"Memory.Experimental.Renderer.PeakResidentSet."
"AtHighestPrivateMemoryFootprint.2to4min",
"Memory.Experimental.Renderer.PeakResidentSet."
"AtHighestPrivateMemoryFootprint.4to8min",
"Memory.Experimental.Renderer.PeakResidentSet."
"AtHighestPrivateMemoryFootprint.8to16min"};
const char* HighestPmfReporter::webpage_counts_metric_names[] = {
"Memory.Experimental.Renderer.WebpageCount.AtHighestPrivateMemoryFootprint."
"0to2min",
"Memory.Experimental.Renderer.WebpageCount.AtHighestPrivateMemoryFootprint."
"2to4min",
"Memory.Experimental.Renderer.WebpageCount.AtHighestPrivateMemoryFootprint."
"4to8min",
"Memory.Experimental.Renderer.WebpageCount.AtHighestPrivateMemoryFootprint."
"8to16min"};
constexpr base::TimeDelta HighestPmfReporter::time_to_report[] = {
base::TimeDelta::FromMinutes(2), base::TimeDelta::FromMinutes(4),
base::TimeDelta::FromMinutes(8), base::TimeDelta::FromMinutes(16)};
HighestPmfReporter& HighestPmfReporter::Instance() {
DEFINE_STATIC_LOCAL(HighestPmfReporter, reporter, ());
return reporter;
}
HighestPmfReporter::HighestPmfReporter()
: task_runner_(Thread::MainThread()->GetTaskRunner()),
clock_(base::DefaultTickClock::GetInstance()) {
MemoryUsageMonitor::Instance().AddObserver(this);
}
HighestPmfReporter::HighestPmfReporter(
scoped_refptr<base::SingleThreadTaskRunner> task_runner_for_testing,
const base::TickClock* clock_for_testing)
: task_runner_(task_runner_for_testing), clock_(clock_for_testing) {
MemoryUsageMonitor::Instance().AddObserver(this);
}
bool HighestPmfReporter::FirstNavigationStarted() {
if (first_navigation_detected_)
return false;
for (Page* page : Page::OrdinaryPages()) {
Frame* frame = page->MainFrame();
if (!frame)
continue;
auto* local_frame = DynamicTo<LocalFrame>(frame);
if (!local_frame)
continue;
DocumentLoader* loader = local_frame->Loader().GetDocumentLoader();
if (!loader)
continue;
if (!loader->GetTiming().NavigationStart().is_null()) {
first_navigation_detected_ = true;
return true;
}
}
return false;
}
void HighestPmfReporter::OnMemoryPing(MemoryUsage usage) {
DCHECK(IsMainThread());
if (FirstNavigationStarted()) {
task_runner_->PostDelayedTask(
FROM_HERE,
WTF::Bind(&HighestPmfReporter::OnReportMetrics, WTF::Unretained(this)),
time_to_report[0]);
}
if (current_highest_pmf_ > usage.private_footprint_bytes)
return;
current_highest_pmf_ = usage.private_footprint_bytes;
peak_resident_bytes_at_current_highest_pmf_ = usage.peak_resident_bytes;
webpage_counts_at_current_highest_pmf_ = Page::OrdinaryPages().size();
// TODO(tasak): Report the highest memory footprint throughout renderer's
// lifetime.
}
void HighestPmfReporter::OnReportMetrics() {
DCHECK(IsMainThread());
ReportMetrics();
// The following code is not accurate, because OnReportMetrics will be late
// when renderer is slow (e.g. caused by near-OOM or heavy tasks is running
// or ...). However such signal getting late by minutes is unlikely, so it's
// ok to say "this is good enough".
current_highest_pmf_ = 0.0;
peak_resident_bytes_at_current_highest_pmf_ = 0.0;
webpage_counts_at_current_highest_pmf_ = 0;
report_count_++;
if (report_count_ >= base::size(time_to_report))
return;
base::TimeDelta delay =
time_to_report[report_count_] - time_to_report[report_count_ - 1];
task_runner_->PostDelayedTask(
FROM_HERE,
WTF::Bind(&HighestPmfReporter::OnReportMetrics, WTF::Unretained(this)),
delay);
}
void HighestPmfReporter::ReportMetrics() {
base::UmaHistogramMemoryMB(highest_pmf_metric_names[report_count_],
base::saturated_cast<base::Histogram::Sample>(
current_highest_pmf_ / 1024 / 1024));
base::UmaHistogramMemoryMB(
peak_resident_bytes_metric_names[report_count_],
base::saturated_cast<base::Histogram::Sample>(
peak_resident_bytes_at_current_highest_pmf_ / 1024 / 1024));
base::UmaHistogramCounts100(webpage_counts_metric_names[report_count_],
webpage_counts_at_current_highest_pmf_);
}
} // namespace blink