blob: 202a52d8614cc7d7533a0b820fede2a193119567 [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/core/execution_context/agent_metrics_collector.h"
#include "base/metrics/histogram_macros.h"
#include "base/time/default_tick_clock.h"
#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/execution_context/window_agent.h"
#include "third_party/blink/renderer/platform/instrumentation/histogram.h"
#include "third_party/blink/renderer/platform/web_test_support.h"
#include "third_party/blink/renderer/platform/wtf/hash_set.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
using WTF::HashSet;
using WTF::String;
using WTF::Vector;
namespace blink {
namespace {
const char kAgentsPerRendererByTimeHistogram[] =
"PerformanceManager.AgentsPerRendererByTime";
base::TimeDelta kReportingInterval = base::TimeDelta::FromMinutes(5);
} // namespace
AgentMetricsCollector::AgentMetricsCollector()
: reporting_timer_(std::make_unique<TaskRunnerTimer<AgentMetricsCollector>>(
// Some tests might not have a MainThreadScheduler.
scheduler::WebThreadScheduler::MainThreadScheduler()
? scheduler::WebThreadScheduler::MainThreadScheduler()
->DefaultTaskRunner()
: nullptr,
this,
&AgentMetricsCollector::ReportingTimerFired)),
clock_(base::DefaultTickClock::GetInstance()) {
// From now until we call CreatedNewAgent will be reported as having 0
// agents.
time_last_reported_ = clock_->NowTicks();
}
AgentMetricsCollector::~AgentMetricsCollector() {
// Note: This won't be called during a fast-shutdown (i.e. tab closed). We
// manually call it from Page::WillBeDestroyed().
ReportMetrics();
}
void AgentMetricsCollector::DidAttachDocument(const Document& doc) {
ReportMetrics();
AgentToDocumentsMap::AddResult result =
agent_to_documents_map_.insert(doc.GetAgent(), nullptr);
if (result.is_new_entry)
result.stored_value->value = MakeGarbageCollected<DocumentSet>();
result.stored_value->value->insert(&doc);
ReportToBrowser();
}
void AgentMetricsCollector::DidDetachDocument(const Document& doc) {
ReportMetrics();
auto agent_itr = agent_to_documents_map_.find(doc.GetAgent());
DCHECK(agent_itr != agent_to_documents_map_.end());
DocumentSet& documents = *agent_itr->value.Get();
auto document_itr = documents.find(&doc);
DCHECK(document_itr != documents.end());
documents.erase(document_itr);
if (documents.IsEmpty())
agent_to_documents_map_.erase(agent_itr);
ReportToBrowser();
}
void AgentMetricsCollector::ReportMetrics() {
DCHECK(!time_last_reported_.is_null());
// Don't run the timer in tests. Doing so causes tests that RunUntilIdle to
// never exit.
if (!reporting_timer_->IsActive() && !WebTestSupport::IsRunningWebTest()) {
reporting_timer_->StartRepeating(kReportingInterval, FROM_HERE);
}
// This computation and reporting is based on the one in
// chrome/browser/performance_manager/observers/isolation_context_metrics.cc.
base::TimeTicks now = clock_->NowTicks();
base::TimeDelta elapsed = now - time_last_reported_;
time_last_reported_ = now;
// Account for edge cases like hibernate/sleep. See
// GetSecondsSinceLastReportAndUpdate in isolation_context_metrics.cc
if (elapsed >= 2 * kReportingInterval)
elapsed = base::TimeDelta();
int to_add = static_cast<int>(std::round(elapsed.InSecondsF()));
// Time can be negative in tests when we replace the clock_.
if (to_add <= 0)
return;
AddTimeToTotalAgents(to_add);
}
void AgentMetricsCollector::AddTimeToTotalAgents(int time_delta_to_add) {
DEFINE_STATIC_LOCAL(LinearHistogram, agents_per_renderer_histogram,
(kAgentsPerRendererByTimeHistogram, 1, 100, 101));
agents_per_renderer_histogram.CountMany(agent_to_documents_map_.size(),
time_delta_to_add);
}
void AgentMetricsCollector::ReportToBrowser() {
Vector<String> agents;
for (const auto& kv : agent_to_documents_map_) {
const Member<DocumentSet>& doc_set = kv.value;
String tuple_origin;
DCHECK(!doc_set->IsEmpty());
const auto& doc = *doc_set->begin();
auto* security_origin = doc->GetSecurityOrigin();
if (security_origin && !security_origin->IsOpaque() &&
!security_origin->IsLocal()) {
// We shouldn't ever host multiple tuple-origins in an Agent. However,
// this does happen in tests because we have
// GetAllowUniversalAccessFromFileURLs enabled but that's ok in tests.
tuple_origin = security_origin->Protocol() + "://" +
security_origin->RegistrableDomain();
} else {
// We use an empty string to specify that there isn't any one
// tuple-origin this agent represents. This will typically be for
// file:// or opaque origins. We shouldn't ever host multiple sites
// inside an agent.
tuple_origin = "";
}
agents.push_back(tuple_origin);
}
mojom::blink::AgentMetricsDataPtr data =
mojom::blink::AgentMetricsData::New();
data->agents = agents;
GetAgentMetricsCollectorHost()->ReportRendererMetrics(std::move(data));
}
void AgentMetricsCollector::ReportingTimerFired(TimerBase*) {
ReportMetrics();
ReportToBrowser();
}
mojo::Remote<blink::mojom::blink::AgentMetricsCollectorHost>&
AgentMetricsCollector::GetAgentMetricsCollectorHost() {
if (!agent_metrics_collector_host_) {
blink::Platform::Current()->GetBrowserInterfaceBroker()->GetInterface(
agent_metrics_collector_host_.BindNewPipeAndPassReceiver());
}
return agent_metrics_collector_host_;
}
void AgentMetricsCollector::Trace(Visitor* visitor) {
visitor->Trace(agent_to_documents_map_);
}
} // namespace blink