blob: 520ec90a9a5ff244858a85e78dcff3c5a01d2fc6 [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/web_test/browser/leak_detector.h"
#include <utility>
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/json/json_writer.h"
#include "base/values.h"
#include "content/public/browser/render_process_host.h"
namespace content {
// The initial states of the DOM objects at about:blank. The four nodes are a
// Document, a HTML, a HEAD and a BODY.
//
// TODO(hajimehoshi): Now these are hard-corded. If we add a target to count
// objects like RefCounted whose initial state is diffcult to estimate, we stop
// using hard-coded values. Instead, we need to load about:blank ahead of the
// web tests actually and initialize LeakDetector by the got values.
const int kInitialNumberOfLiveAudioNodes = 0;
const int kInitialNumberOfLiveDocuments = 1;
const int kInitialNumberOfLiveNodes = 4;
const int kInitialNumberOfLiveLayoutObjects = 3;
const int kInitialNumberOfLiveResources = 0;
const int kInitialNumberOfLiveFrames = 1;
const int kInitialNumberOfWorkerGlobalScopes = 0;
const int kInitialNumberOfLiveResourceFetchers = 1;
// Each Document has a ScriptRunner and a ScriptedAnimationController,
// which are ContextLifecycleStateObservers.
const int kInitialNumberOfLiveContextLifecycleStateObservers = 2;
// This includes not only about:blank's context but also ScriptRegexp (e.g.
// created by isValidEmailAddress in EmailInputType.cpp). The leak detector
// always creates the latter to stabilize the number of V8PerContextData
// objects.
const int kInitialNumberOfV8PerContextData = 2;
LeakDetector::LeakDetector() {
previous_result_ = blink::mojom::LeakDetectionResult::New();
previous_result_->number_of_live_audio_nodes = kInitialNumberOfLiveAudioNodes;
previous_result_->number_of_live_documents = kInitialNumberOfLiveDocuments;
previous_result_->number_of_live_nodes = kInitialNumberOfLiveNodes;
previous_result_->number_of_live_layout_objects =
kInitialNumberOfLiveLayoutObjects;
previous_result_->number_of_live_resources = kInitialNumberOfLiveResources;
previous_result_->number_of_live_context_lifecycle_state_observers =
kInitialNumberOfLiveContextLifecycleStateObservers;
previous_result_->number_of_live_frames = kInitialNumberOfLiveFrames;
previous_result_->number_of_live_v8_per_context_data =
kInitialNumberOfV8PerContextData;
previous_result_->number_of_worker_global_scopes =
kInitialNumberOfWorkerGlobalScopes;
previous_result_->number_of_live_resource_fetchers =
kInitialNumberOfLiveResourceFetchers;
}
LeakDetector::~LeakDetector() {}
void LeakDetector::TryLeakDetection(RenderProcessHost* process,
ReportCallback callback) {
callback_ = std::move(callback);
if (!leak_detector_) {
process->BindReceiver(leak_detector_.BindNewPipeAndPassReceiver());
leak_detector_.set_disconnect_handler(base::BindOnce(
&LeakDetector::OnLeakDetectorIsGone, base::Unretained(this)));
}
leak_detector_->PerformLeakDetection(base::BindOnce(
&LeakDetector::OnLeakDetectionComplete, weak_factory_.GetWeakPtr()));
}
void LeakDetector::OnLeakDetectionComplete(
blink::mojom::LeakDetectionResultPtr result) {
LeakDetectionReport report;
report.leaked = false;
base::Value::Dict detail;
if (previous_result_ && !result.is_null()) {
if (previous_result_->number_of_live_audio_nodes <
result->number_of_live_audio_nodes) {
base::Value::List list;
list.Append(
static_cast<int>(previous_result_->number_of_live_audio_nodes));
list.Append(static_cast<int>(result->number_of_live_audio_nodes));
detail.Set("numberOfLiveAudioNodes", std::move(list));
}
if (previous_result_->number_of_live_documents <
result->number_of_live_documents) {
base::Value::List list;
list.Append(static_cast<int>(previous_result_->number_of_live_documents));
list.Append(static_cast<int>(result->number_of_live_documents));
detail.Set("numberOfLiveDocuments", std::move(list));
}
if (previous_result_->number_of_live_nodes < result->number_of_live_nodes) {
base::Value::List list;
list.Append(static_cast<int>(previous_result_->number_of_live_nodes));
list.Append(static_cast<int>(result->number_of_live_nodes));
detail.Set("numberOfLiveNodes", std::move(list));
}
if (previous_result_->number_of_live_layout_objects <
result->number_of_live_layout_objects) {
base::Value::List list;
list.Append(
static_cast<int>(previous_result_->number_of_live_layout_objects));
list.Append(static_cast<int>(result->number_of_live_layout_objects));
detail.Set("numberOfLiveLayoutObjects", std::move(list));
}
// Resources associated with User Agent CSS should be excluded from leak
// detection as they are persisted through page navigation.
if (previous_result_->number_of_live_resources <
(result->number_of_live_resources -
result->number_of_live_ua_css_resources)) {
base::Value::List list;
list.Append(static_cast<int>(previous_result_->number_of_live_resources));
list.Append(static_cast<int>(result->number_of_live_resources));
detail.Set("numberOfLiveResources", std::move(list));
}
if (previous_result_->number_of_live_context_lifecycle_state_observers <
result->number_of_live_context_lifecycle_state_observers) {
base::Value::List list;
list.Append(static_cast<int>(
previous_result_->number_of_live_context_lifecycle_state_observers));
list.Append(static_cast<int>(
result->number_of_live_context_lifecycle_state_observers));
detail.Set("numberOfLiveContextLifecycleStateObservers", std::move(list));
}
if (previous_result_->number_of_live_frames <
result->number_of_live_frames) {
base::Value::List list;
list.Append(static_cast<int>(previous_result_->number_of_live_frames));
list.Append(static_cast<int>(result->number_of_live_frames));
detail.Set("numberOfLiveFrames", std::move(list));
}
if (previous_result_->number_of_live_v8_per_context_data <
result->number_of_live_v8_per_context_data) {
base::Value::List list;
list.Append(static_cast<int>(
previous_result_->number_of_live_v8_per_context_data));
list.Append(static_cast<int>(result->number_of_live_v8_per_context_data));
detail.Set("numberOfLiveV8PerContextData", std::move(list));
}
if (previous_result_->number_of_worker_global_scopes <
result->number_of_worker_global_scopes) {
base::Value::List list;
list.Append(
static_cast<int>(previous_result_->number_of_worker_global_scopes));
list.Append(static_cast<int>(result->number_of_worker_global_scopes));
detail.Set("numberOfWorkerGlobalScopes", std::move(list));
}
if (previous_result_->number_of_live_resource_fetchers <
result->number_of_live_resource_fetchers) {
base::Value::List list;
list.Append(
static_cast<int>(previous_result_->number_of_live_resource_fetchers));
list.Append(static_cast<int>(result->number_of_live_resource_fetchers));
detail.Set("numberOfLiveResourceFetchers", std::move(list));
}
}
if (!detail.empty()) {
std::string detail_str;
base::JSONWriter::Write(detail, &detail_str);
report.detail = detail_str;
report.leaked = true;
}
if (!result.is_null()) {
previous_result_ = std::move(result);
}
std::move(callback_).Run(report);
}
void LeakDetector::OnLeakDetectorIsGone() {
leak_detector_.reset();
if (!callback_)
return;
LeakDetectionReport report;
report.leaked = false;
std::move(callback_).Run(report);
}
} // namespace content