| // 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 |