blob: b94ad301c9025864a02864d6002f1e8c7191fc5b [file] [log] [blame]
// Copyright 2017 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 "chrome/browser/metrics/process_memory_metrics_emitter.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/trace_event_analyzer.h"
#include "base/trace_event/memory_dump_manager.h"
#include "base/trace_event/trace_config_memory_test_util.h"
#include "build/build_config.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/tracing.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/ukm/test_ukm_recorder.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/test/test_utils.h"
#include "extensions/buildflags/buildflags.h"
#include "net/dns/mock_host_resolver.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_source.h"
#include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h"
#include "url/gurl.h"
#include "url/url_constants.h"
#if BUILDFLAG(ENABLE_EXTENSIONS)
#include "extensions/browser/process_manager.h"
#include "extensions/common/extension.h"
#include "extensions/test/background_page_watcher.h"
#include "extensions/test/test_extension_dir.h"
#endif
namespace {
using base::trace_event::MemoryDumpType;
using memory_instrumentation::GlobalMemoryDump;
using memory_instrumentation::mojom::ProcessType;
#if BUILDFLAG(ENABLE_EXTENSIONS)
using extensions::BackgroundPageWatcher;
using extensions::Extension;
using extensions::ProcessManager;
using extensions::TestExtensionDir;
#endif
using UkmEntry = ukm::builders::Memory_Experimental;
void RequestGlobalDumpCallback(base::Closure quit_closure,
bool success,
uint64_t) {
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, quit_closure);
ASSERT_TRUE(success);
}
void OnStartTracingDoneCallback(
base::trace_event::MemoryDumpLevelOfDetail explicit_dump_type,
base::Closure quit_closure) {
memory_instrumentation::MemoryInstrumentation::GetInstance()
->RequestGlobalDumpAndAppendToTrace(
MemoryDumpType::EXPLICITLY_TRIGGERED, explicit_dump_type,
Bind(&RequestGlobalDumpCallback, quit_closure));
}
class ProcessMemoryMetricsEmitterFake : public ProcessMemoryMetricsEmitter {
public:
explicit ProcessMemoryMetricsEmitterFake(base::RunLoop* run_loop,
ukm::TestUkmRecorder* recorder)
: run_loop_(run_loop), recorder_(recorder) {}
private:
~ProcessMemoryMetricsEmitterFake() override {}
void ReceivedMemoryDump(bool success,
std::unique_ptr<GlobalMemoryDump> ptr) override {
EXPECT_TRUE(success);
ProcessMemoryMetricsEmitter::ReceivedMemoryDump(success, std::move(ptr));
finished_memory_dump_ = true;
QuitIfFinished();
}
void ReceivedProcessInfos(
std::vector<resource_coordinator::mojom::ProcessInfoPtr> process_infos)
override {
ProcessMemoryMetricsEmitter::ReceivedProcessInfos(std::move(process_infos));
finished_process_info_ = true;
QuitIfFinished();
}
void QuitIfFinished() {
if (!finished_memory_dump_ || !finished_process_info_)
return;
if (run_loop_)
run_loop_->Quit();
}
ukm::UkmRecorder* GetUkmRecorder() override { return recorder_; }
base::RunLoop* run_loop_;
bool finished_memory_dump_ = false;
bool finished_process_info_ = false;
ukm::TestUkmRecorder* recorder_;
DISALLOW_COPY_AND_ASSIGN(ProcessMemoryMetricsEmitterFake);
};
void CheckMemoryMetric(const std::string& name,
const base::HistogramTester& histogram_tester,
int count,
bool check_minimum,
int number_of_processes = 1u) {
std::unique_ptr<base::HistogramSamples> samples(
histogram_tester.GetHistogramSamplesSinceCreation(name));
ASSERT_TRUE(samples);
bool count_matches = samples->TotalCount() == count * number_of_processes;
// The exact number of renderers present at the time the metrics are emitted
// is not deterministic. Sometimes there is an extra renderer.
if (name.find("Renderer") != std::string::npos) {
count_matches = samples->TotalCount() >= (count * number_of_processes) &&
samples->TotalCount() <= (number_of_processes + 1) * count;
}
EXPECT_TRUE(count_matches);
if (check_minimum)
EXPECT_GT(samples->sum(), 0u) << name;
// As a sanity check, no memory stat should exceed 4 GB.
int64_t maximum_expected_size = 1ll << 32;
EXPECT_LT(samples->sum(), maximum_expected_size) << name;
}
void CheckAllMemoryMetrics(const base::HistogramTester& histogram_tester,
int count,
int number_of_renderer_processes = 1u,
int number_of_extenstion_processes = 0u) {
#if !defined(OS_WIN)
CheckMemoryMetric("Memory.Experimental.Browser2.Malloc", histogram_tester,
count, true);
#endif
#if !defined(OS_MACOSX)
CheckMemoryMetric("Memory.Experimental.Browser2.Resident", histogram_tester,
count, true);
#endif
CheckMemoryMetric("Memory.Experimental.Browser2.PrivateMemoryFootprint",
histogram_tester, count, true);
if (number_of_renderer_processes) {
#if !defined(OS_WIN)
CheckMemoryMetric("Memory.Experimental.Renderer2.Malloc", histogram_tester,
count, true, number_of_renderer_processes);
#endif
#if !defined(OS_MACOSX)
CheckMemoryMetric("Memory.Experimental.Renderer2.Resident",
histogram_tester, count, true,
number_of_renderer_processes);
#endif
CheckMemoryMetric("Memory.Experimental.Renderer2.BlinkGC", histogram_tester,
count, false, number_of_renderer_processes);
CheckMemoryMetric("Memory.Experimental.Renderer2.PartitionAlloc",
histogram_tester, count, false,
number_of_renderer_processes);
CheckMemoryMetric("Memory.Experimental.Renderer2.V8", histogram_tester,
count, true, number_of_renderer_processes);
CheckMemoryMetric("Memory.Experimental.Renderer2.PrivateMemoryFootprint",
histogram_tester, count, true,
number_of_renderer_processes);
}
if (number_of_extenstion_processes) {
#if !defined(OS_WIN)
CheckMemoryMetric("Memory.Experimental.Extension2.Malloc", histogram_tester,
count, true, number_of_extenstion_processes);
#endif
#if !defined(OS_MACOSX)
CheckMemoryMetric("Memory.Experimental.Extension2.Resident",
histogram_tester, count, true,
number_of_extenstion_processes);
#endif
CheckMemoryMetric("Memory.Experimental.Extension2.BlinkGC",
histogram_tester, count, false,
number_of_extenstion_processes);
CheckMemoryMetric("Memory.Experimental.Extension2.PartitionAlloc",
histogram_tester, count, false,
number_of_extenstion_processes);
CheckMemoryMetric("Memory.Experimental.Extension2.V8", histogram_tester,
count, true, number_of_extenstion_processes);
CheckMemoryMetric("Memory.Experimental.Extension2.PrivateMemoryFootprint",
histogram_tester, count, true,
number_of_extenstion_processes);
}
CheckMemoryMetric("Memory.Experimental.Total2.PrivateMemoryFootprint",
histogram_tester, count, true);
}
} // namespace
class ProcessMemoryMetricsEmitterTest
: public extensions::ExtensionBrowserTest {
public:
ProcessMemoryMetricsEmitterTest() {
scoped_feature_list_.InitAndEnableFeature(ukm::kUkmFeature);
}
~ProcessMemoryMetricsEmitterTest() override {}
void SetUpOnMainThread() override {
extensions::ExtensionBrowserTest::SetUpOnMainThread();
host_resolver()->AddRule("*", "127.0.0.1");
}
void PreRunTestOnMainThread() override {
InProcessBrowserTest::PreRunTestOnMainThread();
test_ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
}
protected:
std::unique_ptr<ukm::TestAutoSetUkmRecorder> test_ukm_recorder_;
void CheckMetricWithName(const ukm::mojom::UkmEntry* entry,
const char* name,
std::function<bool(int64_t)> check) {
const int64_t* value = test_ukm_recorder_->GetEntryMetric(entry, name);
EXPECT_TRUE(value && check(*value)) << name;
}
void CheckExactMetricWithName(const ukm::mojom::UkmEntry* entry,
const char* name,
int64_t expected_value) {
CheckMetricWithName(entry, name, [expected_value](int64_t value) -> bool {
return value == expected_value;
});
}
void CheckMemoryMetricWithName(const ukm::mojom::UkmEntry* entry,
const char* name,
bool can_be_zero) {
CheckMetricWithName(entry, name, [can_be_zero](int64_t value) -> bool {
return value >= (can_be_zero ? 0 : 1) && value <= 4000;
});
}
void CheckTimeMetricWithName(const ukm::mojom::UkmEntry* entry,
const char* name) {
CheckMetricWithName(entry, name, [](int64_t value) -> bool {
return value >= 0 && value <= 10;
});
}
void CheckAllUkmEntries(size_t entry_count = 1u) {
const auto& entries =
test_ukm_recorder_->GetEntriesByName(UkmEntry::kEntryName);
size_t browser_entry_count = 0;
size_t renderer_entry_count = 0;
size_t total_entry_count = 0;
for (const auto* entry : entries) {
if (ProcessHasTypeForEntry(entry, ProcessType::BROWSER)) {
browser_entry_count++;
CheckUkmBrowserEntry(entry);
} else if (ProcessHasTypeForEntry(entry, ProcessType::RENDERER)) {
renderer_entry_count++;
CheckUkmRendererEntry(entry);
} else if (ProcessHasTypeForEntry(entry, ProcessType::GPU)) {
CheckUkmGPUEntry(entry);
} else {
// This must be Total2.
total_entry_count++;
CheckMemoryMetricWithName(
entry, UkmEntry::kTotal2_PrivateMemoryFootprintName, false);
}
}
EXPECT_EQ(entry_count, browser_entry_count);
EXPECT_EQ(entry_count, total_entry_count);
EXPECT_GE(renderer_entry_count, entry_count);
}
void CheckUkmRendererEntry(const ukm::mojom::UkmEntry* entry) {
#if !defined(OS_WIN)
CheckMemoryMetricWithName(entry, UkmEntry::kMallocName, false);
#endif
#if !defined(OS_MACOSX)
CheckMemoryMetricWithName(entry, UkmEntry::kResidentName, false);
#endif
CheckMemoryMetricWithName(entry, UkmEntry::kPrivateMemoryFootprintName,
false);
CheckMemoryMetricWithName(entry, UkmEntry::kBlinkGCName, true);
CheckMemoryMetricWithName(entry, UkmEntry::kPartitionAllocName, true);
CheckMemoryMetricWithName(entry, UkmEntry::kV8Name, true);
CheckMemoryMetricWithName(entry, UkmEntry::kNumberOfExtensionsName, true);
CheckTimeMetricWithName(entry, UkmEntry::kUptimeName);
CheckMemoryMetricWithName(entry, UkmEntry::kNumberOfDocumentsName, true);
CheckMemoryMetricWithName(entry, UkmEntry::kNumberOfFramesName, true);
CheckMemoryMetricWithName(entry, UkmEntry::kNumberOfLayoutObjectsName,
true);
CheckMemoryMetricWithName(entry, UkmEntry::kNumberOfNodesName, true);
}
void CheckUkmBrowserEntry(const ukm::mojom::UkmEntry* entry) {
#if !defined(OS_WIN)
CheckMemoryMetricWithName(entry, UkmEntry::kMallocName, false);
#endif
#if !defined(OS_MACOSX)
CheckMemoryMetricWithName(entry, UkmEntry::kResidentName, false);
#endif
CheckMemoryMetricWithName(entry, UkmEntry::kPrivateMemoryFootprintName,
false);
CheckTimeMetricWithName(entry, UkmEntry::kUptimeName);
}
void CheckUkmGPUEntry(const ukm::mojom::UkmEntry* entry) {
CheckTimeMetricWithName(entry, UkmEntry::kUptimeName);
}
bool ProcessHasTypeForEntry(const ukm::mojom::UkmEntry* entry,
ProcessType process_type) {
const int64_t* value =
test_ukm_recorder_->GetEntryMetric(entry, UkmEntry::kProcessTypeName);
return value && *value == static_cast<int64_t>(process_type);
}
void CheckPageInfoUkmMetrics(GURL url,
bool is_visible,
size_t entry_count = 1u) {
const auto& entries =
test_ukm_recorder_->GetEntriesByName(UkmEntry::kEntryName);
size_t found_count = false;
const ukm::mojom::UkmEntry* last_entry = nullptr;
for (const auto* entry : entries) {
const ukm::UkmSource* source =
test_ukm_recorder_->GetSourceForSourceId(entry->source_id);
if (!source || source->url() != url)
continue;
if (!test_ukm_recorder_->EntryHasMetric(entry, UkmEntry::kIsVisibleName))
continue;
found_count++;
last_entry = entry;
EXPECT_TRUE(ProcessHasTypeForEntry(entry, ProcessType::RENDERER));
CheckTimeMetricWithName(entry, UkmEntry::kTimeSinceLastNavigationName);
CheckTimeMetricWithName(entry,
UkmEntry::kTimeSinceLastVisibilityChangeName);
}
CheckExactMetricWithName(last_entry, UkmEntry::kIsVisibleName, is_visible);
EXPECT_EQ(entry_count, found_count);
}
#if BUILDFLAG(ENABLE_EXTENSIONS)
// Create an barebones extension with a background page for the given name.
const Extension* CreateExtension(const std::string& name) {
auto dir = std::make_unique<TestExtensionDir>();
dir->WriteManifestWithSingleQuotes(
base::StringPrintf("{"
"'name': '%s',"
"'version': '1',"
"'manifest_version': 2,"
"'background': {'page': 'bg.html'}"
"}",
name.c_str()));
dir->WriteFile(FILE_PATH_LITERAL("bg.html"), "");
const Extension* extension = LoadExtension(dir->UnpackedPath());
EXPECT_TRUE(extension);
temp_dirs_.push_back(std::move(dir));
return extension;
}
const Extension* CreateHostedApp(const std::string& name,
const GURL& app_url) {
std::unique_ptr<TestExtensionDir> dir(new TestExtensionDir);
dir->WriteManifestWithSingleQuotes(base::StringPrintf(
"{"
"'name': '%s',"
"'version': '1',"
"'manifest_version': 2,"
"'app': {'urls': ['%s'], 'launch': {'web_url': '%s'}}"
"}",
name.c_str(), app_url.spec().c_str(), app_url.spec().c_str()));
const Extension* extension = LoadExtension(dir->UnpackedPath());
EXPECT_TRUE(extension);
temp_dirs_.push_back(std::move(dir));
return extension;
}
#endif
private:
base::test::ScopedFeatureList scoped_feature_list_;
#if BUILDFLAG(ENABLE_EXTENSIONS)
std::vector<std::unique_ptr<TestExtensionDir>> temp_dirs_;
#endif
DISALLOW_COPY_AND_ASSIGN(ProcessMemoryMetricsEmitterTest);
};
#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER)
#define MAYBE_FetchAndEmitMetrics DISABLED_FetchAndEmitMetrics
#else
// TODO(michaelpg): Remove this unconditional disabling once new UKM testing
// style CLs land: crbug.com/761524.
#define MAYBE_FetchAndEmitMetrics DISABLED_FetchAndEmitMetrics
#endif
IN_PROC_BROWSER_TEST_F(ProcessMemoryMetricsEmitterTest,
MAYBE_FetchAndEmitMetrics) {
ASSERT_TRUE(embedded_test_server()->Start());
const GURL url = embedded_test_server()->GetURL("foo.com", "/empty.html");
ui_test_utils::NavigateToURLWithDisposition(
browser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
base::HistogramTester histogram_tester;
base::RunLoop run_loop;
// Intentionally let emitter leave scope to check that it correctly keeps
// itself alive.
{
scoped_refptr<ProcessMemoryMetricsEmitterFake> emitter(
new ProcessMemoryMetricsEmitterFake(&run_loop,
test_ukm_recorder_.get()));
emitter->FetchAndEmitProcessMemoryMetrics();
}
run_loop.Run();
CheckAllMemoryMetrics(histogram_tester, 1);
CheckAllUkmEntries();
CheckPageInfoUkmMetrics(url, true);
}
#if BUILDFLAG(ENABLE_EXTENSIONS)
#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER)
#define MAYBE_FetchAndEmitMetricsWithExtensions \
DISABLED_FetchAndEmitMetricsWithExtensions
#else
// TODO(michaelpg): Remove this unconditional disabling once new UKM testing
// style CLs land: crbug.com/761524.
#define MAYBE_FetchAndEmitMetricsWithExtensions \
DISABLED_FetchAndEmitMetricsWithExtensions
#endif
IN_PROC_BROWSER_TEST_F(ProcessMemoryMetricsEmitterTest,
MAYBE_FetchAndEmitMetricsWithExtensions) {
const Extension* extension1 = CreateExtension("Extension 1");
const Extension* extension2 = CreateExtension("Extension 2");
ProcessManager* pm = ProcessManager::Get(profile());
// Verify that the extensions has loaded.
BackgroundPageWatcher(pm, extension1).WaitForOpen();
BackgroundPageWatcher(pm, extension2).WaitForOpen();
EXPECT_EQ(1u, pm->GetRenderFrameHostsForExtension(extension1->id()).size());
EXPECT_EQ(1u, pm->GetRenderFrameHostsForExtension(extension2->id()).size());
ASSERT_TRUE(embedded_test_server()->Start());
const GURL url = embedded_test_server()->GetURL("foo.com", "/empty.html");
ui_test_utils::NavigateToURLWithDisposition(
browser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
base::HistogramTester histogram_tester;
base::RunLoop run_loop;
// Intentionally let emitter leave scope to check that it correctly keeps
// itself alive.
{
scoped_refptr<ProcessMemoryMetricsEmitterFake> emitter(
new ProcessMemoryMetricsEmitterFake(&run_loop,
test_ukm_recorder_.get()));
emitter->FetchAndEmitProcessMemoryMetrics();
}
run_loop.Run();
CheckAllMemoryMetrics(histogram_tester, 1, 1, 2);
// Extension processes do not have page_info.
CheckAllUkmEntries();
CheckPageInfoUkmMetrics(url, true);
}
#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER)
#define MAYBE_FetchAndEmitMetricsWithHostedApps \
DISABLED_FetchAndEmitMetricsWithHostedApps
#else
// TODO(michaelpg): Remove this unconditional disabling once new UKM testing
// style CLs land: crbug.com/761524.
#define MAYBE_FetchAndEmitMetricsWithHostedApps \
DISABLED_FetchAndEmitMetricsWithHostedApps
#endif
IN_PROC_BROWSER_TEST_F(ProcessMemoryMetricsEmitterTest,
MAYBE_FetchAndEmitMetricsWithHostedApps) {
ASSERT_TRUE(embedded_test_server()->Start());
const GURL app_url = embedded_test_server()->GetURL("app.org", "/empty.html");
const Extension* app = CreateHostedApp("App", GURL("http://app.org"));
ui_test_utils::NavigateToURL(browser(), app_url);
// Verify that the hosted app has loaded.
ProcessManager* pm = ProcessManager::Get(profile());
EXPECT_EQ(1u, pm->GetRenderFrameHostsForExtension(app->id()).size());
const GURL url = embedded_test_server()->GetURL("foo.com", "/empty.html");
ui_test_utils::NavigateToURLWithDisposition(
browser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
base::HistogramTester histogram_tester;
base::RunLoop run_loop;
// Intentionally let emitter leave scope to check that it correctly keeps
// itself alive.
{
scoped_refptr<ProcessMemoryMetricsEmitterFake> emitter(
new ProcessMemoryMetricsEmitterFake(&run_loop,
test_ukm_recorder_.get()));
emitter->FetchAndEmitProcessMemoryMetrics();
}
run_loop.Run();
// No extensions should be observed
CheckAllMemoryMetrics(histogram_tester, 1, 1, 0);
CheckAllUkmEntries();
CheckPageInfoUkmMetrics(url, true);
}
// Breaks when attempting to add tests for new UKMs: crbug.com/761524
// Re-enable with crrev.com/c/774120.
#define MAYBE_FetchAndEmitMetricsWithExtensionsAndHostReuse \
DISABLED_FetchAndEmitMetricsWithExtensionsAndHostReuse
IN_PROC_BROWSER_TEST_F(ProcessMemoryMetricsEmitterTest,
MAYBE_FetchAndEmitMetricsWithExtensionsAndHostReuse) {
// This test does not work with --site-per-process flag since this test
// combines multiple extensions in the same process.
if (content::AreAllSitesIsolatedForTesting())
return;
// Limit the number of renderer processes to force reuse.
content::RenderProcessHost::SetMaxRendererProcessCount(1);
const Extension* extension1 = CreateExtension("Extension 1");
const Extension* extension2 = CreateExtension("Extension 2");
ProcessManager* pm = ProcessManager::Get(profile());
// Verify that the extensions has loaded.
BackgroundPageWatcher(pm, extension1).WaitForOpen();
BackgroundPageWatcher(pm, extension2).WaitForOpen();
EXPECT_EQ(1u, pm->GetRenderFrameHostsForExtension(extension1->id()).size());
EXPECT_EQ(1u, pm->GetRenderFrameHostsForExtension(extension2->id()).size());
ASSERT_TRUE(embedded_test_server()->Start());
const GURL url = embedded_test_server()->GetURL("foo.com", "/empty.html");
ui_test_utils::NavigateToURLWithDisposition(
browser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
base::HistogramTester histogram_tester;
base::RunLoop run_loop;
// Intentionally let emitter leave scope to check that it correctly keeps
// itself alive.
{
scoped_refptr<ProcessMemoryMetricsEmitterFake> emitter(
new ProcessMemoryMetricsEmitterFake(&run_loop,
test_ukm_recorder_.get()));
emitter->FetchAndEmitProcessMemoryMetrics();
}
run_loop.Run();
CheckAllMemoryMetrics(histogram_tester, 1, 1, 1);
CheckAllUkmEntries();
// When hosts share a process, no unique URL is identified, therefore no page
// info.
const auto& entries =
test_ukm_recorder_->GetEntriesByName(UkmEntry::kEntryName);
for (const auto* entry : entries) {
EXPECT_EQ(nullptr,
test_ukm_recorder_->GetSourceForSourceId(entry->source_id));
}
}
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER)
#define MAYBE_FetchDuringTrace DISABLED_FetchDuringTrace
#else
// TODO(michaelpg): Remove this unconditional disabling once new UKM testing
// style CLs land: crbug.com/761524.
#define MAYBE_FetchDuringTrace DISABLED_FetchDuringTrace
#endif
IN_PROC_BROWSER_TEST_F(ProcessMemoryMetricsEmitterTest,
MAYBE_FetchDuringTrace) {
ASSERT_TRUE(embedded_test_server()->Start());
const GURL url = embedded_test_server()->GetURL("foo.com", "/empty.html");
ui_test_utils::NavigateToURLWithDisposition(
browser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
base::HistogramTester histogram_tester;
{
base::RunLoop run_loop;
base::trace_event::TraceConfig trace_config(
base::trace_event::TraceConfigMemoryTestUtil::
GetTraceConfig_EmptyTriggers());
ASSERT_TRUE(tracing::BeginTracingWithTraceConfig(
trace_config, Bind(&OnStartTracingDoneCallback,
base::trace_event::MemoryDumpLevelOfDetail::DETAILED,
run_loop.QuitClosure())));
run_loop.Run();
}
{
base::RunLoop run_loop;
scoped_refptr<ProcessMemoryMetricsEmitterFake> emitter(
new ProcessMemoryMetricsEmitterFake(&run_loop,
test_ukm_recorder_.get()));
emitter->FetchAndEmitProcessMemoryMetrics();
run_loop.Run();
}
std::string json_events;
ASSERT_TRUE(tracing::EndTracing(&json_events));
trace_analyzer::TraceEventVector events;
std::unique_ptr<trace_analyzer::TraceAnalyzer> analyzer(
trace_analyzer::TraceAnalyzer::Create(json_events));
analyzer->FindEvents(
trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_MEMORY_DUMP),
&events);
ASSERT_GT(events.size(), 1u);
ASSERT_TRUE(trace_analyzer::CountMatches(
events, trace_analyzer::Query::EventNameIs(MemoryDumpTypeToString(
MemoryDumpType::EXPLICITLY_TRIGGERED))));
CheckAllMemoryMetrics(histogram_tester, 1);
CheckAllUkmEntries();
CheckPageInfoUkmMetrics(url, true);
}
#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER)
#define MAYBE_FetchThreeTimes DISABLED_FetchThreeTimes
#else
// TODO(michaelpg): Remove this unconditional disabling once new UKM testing
// style CLs land: crbug.com/761524.
#define MAYBE_FetchThreeTimes DISABLED_FetchThreeTimes
#endif
IN_PROC_BROWSER_TEST_F(ProcessMemoryMetricsEmitterTest, MAYBE_FetchThreeTimes) {
ASSERT_TRUE(embedded_test_server()->Start());
const GURL url = embedded_test_server()->GetURL("foo.com", "/empty.html");
ui_test_utils::NavigateToURLWithDisposition(
browser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
base::HistogramTester histogram_tester;
base::RunLoop run_loop;
int count = 3;
for (int i = 0; i < count; ++i) {
// Only the last emitter should stop the run loop.
auto emitter = base::MakeRefCounted<ProcessMemoryMetricsEmitterFake>(
(i == count - 1) ? &run_loop : nullptr, test_ukm_recorder_.get());
emitter->FetchAndEmitProcessMemoryMetrics();
}
run_loop.Run();
CheckAllMemoryMetrics(histogram_tester, count);
CheckAllUkmEntries(count);
CheckPageInfoUkmMetrics(url, true, count);
}
#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER)
#define MAYBE_ForegroundAndBackgroundPages DISABLED_ForegroundAndBackgroundPages
#else
// TODO(michaelpg): Remove this unconditional disabling once new UKM testing
// style CLs land: crbug.com/761524.
#define MAYBE_ForegroundAndBackgroundPages DISABLED_ForegroundAndBackgroundPages
#endif
IN_PROC_BROWSER_TEST_F(ProcessMemoryMetricsEmitterTest,
MAYBE_ForegroundAndBackgroundPages) {
ui_test_utils::WindowedTabAddedNotificationObserver tab_observer(
content::NotificationService::AllSources());
ASSERT_TRUE(embedded_test_server()->Start());
const GURL url1 = embedded_test_server()->GetURL("a.com", "/empty.html");
const GURL url2 = embedded_test_server()->GetURL("b.com", "/empty.html");
ui_test_utils::NavigateToURLWithDisposition(
browser(), url1, WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
tab_observer.Wait();
content::WebContents* tab1 = tab_observer.GetTab();
ui_test_utils::NavigateToURLWithDisposition(
browser(), url2, WindowOpenDisposition::NEW_BACKGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
tab_observer.Wait();
content::WebContents* tab2 = tab_observer.GetTab();
base::HistogramTester histogram_tester;
{
base::RunLoop run_loop;
scoped_refptr<ProcessMemoryMetricsEmitterFake> emitter(
new ProcessMemoryMetricsEmitterFake(&run_loop,
test_ukm_recorder_.get()));
emitter->FetchAndEmitProcessMemoryMetrics();
run_loop.Run();
}
CheckAllMemoryMetrics(histogram_tester, 1, 2);
CheckAllUkmEntries();
CheckPageInfoUkmMetrics(url1, true /* is_visible */);
CheckPageInfoUkmMetrics(url2, false /* is_visible */);
tab1->WasHidden();
tab2->WasShown();
{
base::RunLoop run_loop;
scoped_refptr<ProcessMemoryMetricsEmitterFake> emitter(
new ProcessMemoryMetricsEmitterFake(&run_loop,
test_ukm_recorder_.get()));
emitter->FetchAndEmitProcessMemoryMetrics();
run_loop.Run();
}
CheckAllMemoryMetrics(histogram_tester, 2, 2);
CheckAllUkmEntries(2);
CheckPageInfoUkmMetrics(url1, false /* is_visible */, 2);
CheckPageInfoUkmMetrics(url2, true /* is_visible */, 2);
}