blob: 9b67b5536d96687489f460c847158df5c7ddb1e7 [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 "chrome/browser/metrics/perf/heap_collector.h"
#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include "base/allocator/allocator_extension.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/macros.h"
#include "base/sampling_heap_profiler/poisson_allocation_sampler.h"
#include "base/sampling_heap_profiler/sampling_heap_profiler.h"
#include "base/strings/string_number_conversions.h"
#include "base/task/post_task.h"
#include "base/test/bind_test_util.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/metrics/perf/windowed_incognito_observer.h"
#include "components/services/heap_profiling/public/cpp/settings.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/metrics_proto/sampled_profile.pb.h"
namespace metrics {
namespace {
// Returns a sample PerfDataProto for a heap profile.
PerfDataProto GetSampleHeapPerfDataProto() {
PerfDataProto proto;
// Add file attributes.
PerfDataProto_PerfFileAttr* file_attr = proto.add_file_attrs();
PerfDataProto_PerfEventAttr* event_attr = file_attr->mutable_attr();
event_attr->set_type(1); // PERF_TYPE_SOFTWARE
event_attr->set_size(96); // PERF_ATTR_SIZE_VER3
event_attr->set_config(9); // PERF_COUNT_SW_DUMMY
event_attr->set_sample_id_all(true);
event_attr->set_sample_period(111222);
event_attr->set_sample_type(1 /*PERF_SAMPLE_IP*/ | 2 /*PERF_SAMPLE_TID*/ |
32 /*PERF_SAMPLE_CALLCHAIN*/ |
64 /*PERF_SAMPLE_ID*/ |
256 /*PERF_SAMPLE_PERIOD*/);
event_attr->set_mmap(true);
file_attr->add_ids(0);
PerfDataProto_PerfEventType* event_type = proto.add_event_types();
event_type->set_id(9); // PERF_COUNT_SW_DUMMY
file_attr = proto.add_file_attrs();
event_attr = file_attr->mutable_attr();
event_attr->set_type(1); // PERF_TYPE_SOFTWARE
event_attr->set_size(96); // PERF_ATTR_SIZE_VER3
event_attr->set_config(9); // PERF_COUNT_SW_DUMMY
event_attr->set_sample_id_all(true);
event_attr->set_sample_period(111222);
event_attr->set_sample_type(1 /*PERF_SAMPLE_IP*/ | 2 /*PERF_SAMPLE_TID*/ |
32 /*PERF_SAMPLE_CALLCHAIN*/ |
64 /*PERF_SAMPLE_ID*/ |
256 /*PERF_SAMPLE_PERIOD*/);
file_attr->add_ids(1);
event_type = proto.add_event_types();
event_type->set_id(9); // PERF_COUNT_SW_DUMMY
// Add MMAP event.
PerfDataProto_PerfEvent* event = proto.add_events();
PerfDataProto_EventHeader* header = event->mutable_header();
header->set_type(1); // PERF_RECORD_MMAP
header->set_misc(0);
header->set_size(0);
PerfDataProto_MMapEvent* mmap = event->mutable_mmap_event();
mmap->set_pid(3456);
mmap->set_tid(3456);
mmap->set_start(0x617aa770f000);
mmap->set_len(0x617ab0689000 - 0x617aa770f000);
mmap->set_pgoff(16);
PerfDataProto_SampleInfo* sample_info = mmap->mutable_sample_info();
sample_info->set_pid(3456);
sample_info->set_tid(3456);
sample_info->set_id(0);
// Add Sample events.
event = proto.add_events();
header = event->mutable_header();
header->set_type(9); // PERF_RECORD_SAMPLE
header->set_misc(2); // PERF_RECORD_MISC_USER
header->set_size(0);
double scale = 1 / (1 - exp(-(1024.00 / 2.00) / 111222.00));
PerfDataProto_SampleEvent* sample = event->mutable_sample_event();
sample->set_ip(0x617aae951c31);
sample->set_pid(3456);
sample->set_tid(3456);
sample->set_id(0);
sample->set_period(2 * scale);
sample->add_callchain(static_cast<uint64_t>(-512)); // PERF_CONTEXT_USER
sample->add_callchain(0x617aae951c31);
sample->add_callchain(0x617aae95062e);
event = proto.add_events();
header = event->mutable_header();
header->set_type(9); // PERF_RECORD_SAMPLE
header->set_misc(2); // PERF_RECORD_MISC_USER
header->set_size(0);
sample = event->mutable_sample_event();
sample->set_ip(0x617aae951c31);
sample->set_pid(3456);
sample->set_tid(3456);
sample->set_id(1);
sample->set_period(1024 * scale);
sample->add_callchain(static_cast<uint64_t>(-512)); // PERF_CONTEXT_USER
sample->add_callchain(0x617aae951c31);
sample->add_callchain(0x617aae95062e);
return proto;
}
// Allows testing of HeapCollector behavior when an incognito window is opened.
class TestIncognitoObserver : public WindowedIncognitoObserver {
public:
// Factory function to create a TestIncognitoObserver object contained in a
// std::unique_ptr<WindowedIncognitoObserver> object. |incognito_launched|
// simulates the presence of an open incognito window, or the lack thereof.
// Used for passing observers to ParseOutputProtoIfValid().
static std::unique_ptr<WindowedIncognitoObserver> CreateWithIncognitoLaunched(
bool incognito_launched) {
return base::WrapUnique(new TestIncognitoObserver(incognito_launched));
}
bool IncognitoLaunched() const override { return incognito_launched_; }
private:
explicit TestIncognitoObserver(bool incognito_launched)
: WindowedIncognitoObserver(nullptr, 0),
incognito_launched_(incognito_launched) {}
bool incognito_launched_;
DISALLOW_COPY_AND_ASSIGN(TestIncognitoObserver);
};
// Allows access to some private methods for testing.
class TestHeapCollector : public HeapCollector {
public:
TestHeapCollector() : HeapCollector(HeapCollectionMode::kTcmalloc) {}
explicit TestHeapCollector(HeapCollectionMode mode) : HeapCollector(mode) {}
using HeapCollector::AddCachedDataDelta;
using HeapCollector::collection_params;
using HeapCollector::DumpProfileToTempFile;
using HeapCollector::Init;
using HeapCollector::IsEnabled;
using HeapCollector::IsRunning;
using HeapCollector::MakeQuipperCommand;
using HeapCollector::Mode;
using HeapCollector::ParseAndSaveProfile;
using HeapCollector::RecordUserLogin;
using HeapCollector::set_profile_done_callback;
DISALLOW_COPY_AND_ASSIGN(TestHeapCollector);
};
const base::TimeDelta kPeriodicCollectionInterval =
base::TimeDelta::FromHours(1);
#if !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
size_t HeapSamplingPeriod(const TestHeapCollector& collector) {
CHECK_EQ(collector.Mode(), HeapCollectionMode::kTcmalloc)
<< "Reading heap sampling period works only with tcmalloc sampling";
size_t sampling_period;
CHECK(base::allocator::GetNumericProperty("tcmalloc.sampling_period_bytes",
&sampling_period))
<< "Failed to read heap sampling period";
return sampling_period;
}
#endif
} // namespace
class HeapCollectorTest : public testing::Test {
public:
HeapCollectorTest()
: task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
void SaveProfile(std::unique_ptr<SampledProfile> sampled_profile) {
cached_profile_data_.resize(cached_profile_data_.size() + 1);
cached_profile_data_.back().Swap(sampled_profile.get());
}
void MakeHeapCollector(HeapCollectionMode mode) {
heap_collector_ = std::make_unique<TestHeapCollector>(mode);
// Set the periodic collection delay to a well known quantity, so we can
// fast forward the time.
heap_collector_->collection_params().periodic_interval =
kPeriodicCollectionInterval;
heap_collector_->set_profile_done_callback(base::BindRepeating(
&HeapCollectorTest::SaveProfile, base::Unretained(this)));
heap_collector_->Init();
// HeapCollector requires the user to be logged in.
heap_collector_->RecordUserLogin(base::TimeTicks::Now());
}
void TearDown() override {
heap_collector_.reset();
cached_profile_data_.clear();
}
protected:
content::BrowserTaskEnvironment task_environment_;
std::vector<SampledProfile> cached_profile_data_;
std::unique_ptr<TestHeapCollector> heap_collector_;
DISALLOW_COPY_AND_ASSIGN(HeapCollectorTest);
};
TEST_F(HeapCollectorTest, CheckTestIncognitoObserver) {
EXPECT_FALSE(TestIncognitoObserver::CreateWithIncognitoLaunched(false)
->IncognitoLaunched());
EXPECT_TRUE(TestIncognitoObserver::CreateWithIncognitoLaunched(true)
->IncognitoLaunched());
}
#if !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
TEST_F(HeapCollectorTest, CheckSetup_Tcmalloc) {
MakeHeapCollector(HeapCollectionMode::kTcmalloc);
// No profiles are cached on start.
EXPECT_TRUE(cached_profile_data_.empty());
// Heap sampling is enabled.
size_t sampling_period = HeapSamplingPeriod(*heap_collector_);
EXPECT_GT(sampling_period, 0u);
}
TEST_F(HeapCollectorTest, NoCollectionWhenProfileCacheFull_Tcmalloc) {
MakeHeapCollector(HeapCollectionMode::kTcmalloc);
// Timer is active after login and a periodic collection is scheduled.
EXPECT_TRUE(heap_collector_->IsRunning());
// Pretend the cache is full.
heap_collector_->AddCachedDataDelta(4 * 1024 * 1024);
// Advance the clock by a periodic collection interval. We shouldn't find a
// profile because the cache is full.
task_environment_.FastForwardBy(kPeriodicCollectionInterval);
EXPECT_TRUE(cached_profile_data_.empty());
}
TEST_F(HeapCollectorTest, DumpProfileToTempFile_NoIncognito_Tcmalloc) {
MakeHeapCollector(HeapCollectionMode::kTcmalloc);
auto incognito_observer =
TestIncognitoObserver::CreateWithIncognitoLaunched(false);
base::Optional<base::FilePath> got_path =
heap_collector_->DumpProfileToTempFile(std::move(incognito_observer));
// Check that we got a path.
ASSERT_TRUE(got_path);
// Check that the file is readable and not empty.
base::File temp(got_path.value(),
base::File::FLAG_OPEN | base::File::FLAG_READ);
ASSERT_TRUE(temp.IsValid());
EXPECT_GT(temp.GetLength(), 0);
temp.Close();
// We must be able to remove the temp file.
ASSERT_TRUE(base::DeleteFile(got_path.value(), false));
}
TEST_F(HeapCollectorTest, DumpProfileToTempFile_IncognitoOpened_Tcmalloc) {
MakeHeapCollector(HeapCollectionMode::kTcmalloc);
auto incognito_observer =
TestIncognitoObserver::CreateWithIncognitoLaunched(true);
base::Optional<base::FilePath> got_path =
heap_collector_->DumpProfileToTempFile(std::move(incognito_observer));
// Check that we got a path.
ASSERT_FALSE(got_path);
}
TEST_F(HeapCollectorTest, ParseAndSaveProfile_Tcmalloc) {
MakeHeapCollector(HeapCollectionMode::kTcmalloc);
// Write a sample perf data proto to a temp file.
const base::FilePath kTempProfile(
FILE_PATH_LITERAL("/tmp/ParseAndSaveProfile.test"));
PerfDataProto heap_proto = GetSampleHeapPerfDataProto();
std::string serialized_proto = heap_proto.SerializeAsString();
base::File temp(kTempProfile, base::File::FLAG_CREATE_ALWAYS |
base::File::FLAG_READ |
base::File::FLAG_WRITE);
EXPECT_TRUE(temp.created());
EXPECT_TRUE(temp.IsValid());
int res = temp.WriteAtCurrentPos(serialized_proto.c_str(),
serialized_proto.length());
EXPECT_EQ(res, static_cast<int>(serialized_proto.length()));
temp.Close();
// Create a command line that copies the input file to the output.
base::CommandLine::StringVector argv;
argv.push_back("cat");
argv.push_back(kTempProfile.value());
auto cat = std::make_unique<base::CommandLine>(argv);
// Run the command.
auto sampled_profile = std::make_unique<SampledProfile>();
sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
heap_collector_->ParseAndSaveProfile(std::move(cat), kTempProfile,
std::move(sampled_profile));
task_environment_.RunUntilIdle();
// Check that the profile was cached.
ASSERT_EQ(1U, cached_profile_data_.size());
const SampledProfile& profile = cached_profile_data_[0];
EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, profile.trigger_event());
EXPECT_TRUE(profile.has_ms_after_boot());
EXPECT_TRUE(profile.has_ms_after_login());
ASSERT_TRUE(profile.has_perf_data());
EXPECT_EQ(serialized_proto, profile.perf_data().SerializeAsString());
// Check that the temp profile file is removed after pending tasks complete.
temp =
base::File(kTempProfile, base::File::FLAG_OPEN | base::File::FLAG_READ);
ASSERT_FALSE(temp.IsValid());
}
#endif
TEST_F(HeapCollectorTest, CheckSetup_ShimLayer) {
MakeHeapCollector(HeapCollectionMode::kShimLayer);
// No profiles are cached on start.
EXPECT_TRUE(cached_profile_data_.empty());
// Heap sampling is enabled.
EXPECT_TRUE(heap_collector_->IsEnabled());
}
TEST_F(HeapCollectorTest, NoCollectionWhenProfileCacheFull_ShimLayer) {
MakeHeapCollector(HeapCollectionMode::kShimLayer);
// Timer is active after login and a periodic collection is scheduled.
EXPECT_TRUE(heap_collector_->IsRunning());
// Pretend the cache is full.
heap_collector_->AddCachedDataDelta(4 * 1024 * 1024);
// Advance the clock by a periodic collection interval. We shouldn't find a
// profile because the cache is full.
task_environment_.FastForwardBy(kPeriodicCollectionInterval);
EXPECT_TRUE(cached_profile_data_.empty());
}
TEST_F(HeapCollectorTest, DumpProfileToTempFile_NoIncognito_ShimLayer) {
MakeHeapCollector(HeapCollectionMode::kShimLayer);
auto incognito_observer =
TestIncognitoObserver::CreateWithIncognitoLaunched(false);
base::Optional<base::FilePath> got_path =
heap_collector_->DumpProfileToTempFile(std::move(incognito_observer));
// Check that we got a path.
ASSERT_TRUE(got_path);
// Check that the file is readable and not empty.
base::File temp(got_path.value(),
base::File::FLAG_OPEN | base::File::FLAG_READ);
ASSERT_TRUE(temp.IsValid());
EXPECT_GT(temp.GetLength(), 0);
temp.Close();
// We must be able to remove the temp file.
ASSERT_TRUE(base::DeleteFile(got_path.value(), false));
}
TEST_F(HeapCollectorTest, DumpProfileToTempFile_IncognitoOpened_ShimLayer) {
MakeHeapCollector(HeapCollectionMode::kShimLayer);
auto incognito_observer =
TestIncognitoObserver::CreateWithIncognitoLaunched(true);
base::Optional<base::FilePath> got_path =
heap_collector_->DumpProfileToTempFile(std::move(incognito_observer));
// Check that we got a path.
ASSERT_FALSE(got_path);
}
TEST_F(HeapCollectorTest, MakeQuipperCommand) {
const base::FilePath kTempProfile(
FILE_PATH_LITERAL("/tmp/MakeQuipperCommand.test"));
std::unique_ptr<base::CommandLine> got =
TestHeapCollector::MakeQuipperCommand(kTempProfile);
ASSERT_TRUE(got);
// Check that we got the correct two switch names.
ASSERT_EQ(got->GetSwitches().size(), 2u);
EXPECT_TRUE(got->HasSwitch("input_heap_profile"));
EXPECT_TRUE(got->HasSwitch("pid"));
// Check that we got the correct program name and switch values.
EXPECT_EQ(got->GetProgram().value(), "/usr/bin/quipper");
EXPECT_EQ(got->GetSwitchValuePath("input_heap_profile"), kTempProfile);
EXPECT_EQ(got->GetSwitchValueASCII("pid"),
base::NumberToString(base::GetCurrentProcId()));
}
TEST_F(HeapCollectorTest, ParseAndSaveProfile_ShimLayer) {
MakeHeapCollector(HeapCollectionMode::kShimLayer);
// Write a sample perf data proto to a temp file.
const base::FilePath kTempProfile(
FILE_PATH_LITERAL("/tmp/ParseAndSaveProfile.test"));
PerfDataProto heap_proto = GetSampleHeapPerfDataProto();
std::string serialized_proto = heap_proto.SerializeAsString();
base::File temp(kTempProfile, base::File::FLAG_CREATE_ALWAYS |
base::File::FLAG_READ |
base::File::FLAG_WRITE);
EXPECT_TRUE(temp.created());
EXPECT_TRUE(temp.IsValid());
int res = temp.WriteAtCurrentPos(serialized_proto.c_str(),
serialized_proto.length());
EXPECT_EQ(res, static_cast<int>(serialized_proto.length()));
temp.Close();
// Create a command line that copies the input file to the output.
base::CommandLine::StringVector argv;
argv.push_back("cat");
argv.push_back(kTempProfile.value());
auto cat = std::make_unique<base::CommandLine>(argv);
// Run the command.
auto sampled_profile = std::make_unique<SampledProfile>();
sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
heap_collector_->ParseAndSaveProfile(std::move(cat), kTempProfile,
std::move(sampled_profile));
task_environment_.RunUntilIdle();
// Check that the profile was cached.
ASSERT_EQ(1U, cached_profile_data_.size());
const SampledProfile& profile = cached_profile_data_[0];
EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, profile.trigger_event());
EXPECT_TRUE(profile.has_ms_after_boot());
EXPECT_TRUE(profile.has_ms_after_login());
ASSERT_TRUE(profile.has_perf_data());
EXPECT_EQ(serialized_proto, profile.perf_data().SerializeAsString());
// Check that the temp profile file is removed after pending tasks complete.
temp =
base::File(kTempProfile, base::File::FLAG_OPEN | base::File::FLAG_READ);
ASSERT_FALSE(temp.IsValid());
}
class HeapCollectorCollectionParamsTest : public testing::Test {
public:
HeapCollectorCollectionParamsTest() = default;
protected:
content::BrowserTaskEnvironment task_environment_;
DISALLOW_COPY_AND_ASSIGN(HeapCollectorCollectionParamsTest);
};
#if !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
TEST_F(HeapCollectorCollectionParamsTest, ParametersOverride_Tcmalloc) {
std::map<std::string, std::string> params;
params.insert(std::make_pair("SamplingIntervalBytes", "800000"));
params.insert(std::make_pair("PeriodicCollectionIntervalMs", "3600000"));
params.insert(std::make_pair("ResumeFromSuspend::SamplingFactor", "1"));
params.insert(std::make_pair("ResumeFromSuspend::MaxDelaySec", "10"));
params.insert(std::make_pair("RestoreSession::SamplingFactor", "2"));
params.insert(std::make_pair("RestoreSession::MaxDelaySec", "20"));
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeatureWithParameters(
heap_profiling::kOOPHeapProfilingFeature, params);
auto heap_collector =
std::make_unique<TestHeapCollector>(HeapCollectionMode::kTcmalloc);
const auto& parsed_params = heap_collector->collection_params();
// Not initialized yet:
size_t sampling_period = HeapSamplingPeriod(*heap_collector);
EXPECT_NE(800000u, sampling_period);
EXPECT_NE(base::TimeDelta::FromHours(1), parsed_params.periodic_interval);
EXPECT_NE(1, parsed_params.resume_from_suspend.sampling_factor);
EXPECT_NE(base::TimeDelta::FromSeconds(10),
parsed_params.resume_from_suspend.max_collection_delay);
EXPECT_NE(2, parsed_params.restore_session.sampling_factor);
EXPECT_NE(base::TimeDelta::FromSeconds(20),
parsed_params.restore_session.max_collection_delay);
heap_collector->Init();
sampling_period = HeapSamplingPeriod(*heap_collector);
EXPECT_EQ(800000u, sampling_period);
EXPECT_EQ(base::TimeDelta::FromHours(1), parsed_params.periodic_interval);
EXPECT_EQ(1, parsed_params.resume_from_suspend.sampling_factor);
EXPECT_EQ(base::TimeDelta::FromSeconds(10),
parsed_params.resume_from_suspend.max_collection_delay);
EXPECT_EQ(2, parsed_params.restore_session.sampling_factor);
EXPECT_EQ(base::TimeDelta::FromSeconds(20),
parsed_params.restore_session.max_collection_delay);
}
#endif
TEST_F(HeapCollectorCollectionParamsTest, ParametersOverride_ShimLayer) {
std::map<std::string, std::string> params;
params.insert(std::make_pair("SamplingIntervalBytes", "800000"));
params.insert(std::make_pair("PeriodicCollectionIntervalMs", "3600000"));
params.insert(std::make_pair("ResumeFromSuspend::SamplingFactor", "1"));
params.insert(std::make_pair("ResumeFromSuspend::MaxDelaySec", "10"));
params.insert(std::make_pair("RestoreSession::SamplingFactor", "2"));
params.insert(std::make_pair("RestoreSession::MaxDelaySec", "20"));
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeatureWithParameters(
heap_profiling::kOOPHeapProfilingFeature, params);
auto heap_collector =
std::make_unique<TestHeapCollector>(HeapCollectionMode::kShimLayer);
const auto& parsed_params = heap_collector->collection_params();
// Not initialized yet:
EXPECT_NE(base::TimeDelta::FromHours(1), parsed_params.periodic_interval);
EXPECT_NE(1, parsed_params.resume_from_suspend.sampling_factor);
EXPECT_NE(base::TimeDelta::FromSeconds(10),
parsed_params.resume_from_suspend.max_collection_delay);
EXPECT_NE(2, parsed_params.restore_session.sampling_factor);
EXPECT_NE(base::TimeDelta::FromSeconds(20),
parsed_params.restore_session.max_collection_delay);
heap_collector->Init();
EXPECT_EQ(base::TimeDelta::FromHours(1), parsed_params.periodic_interval);
EXPECT_EQ(1, parsed_params.resume_from_suspend.sampling_factor);
EXPECT_EQ(base::TimeDelta::FromSeconds(10),
parsed_params.resume_from_suspend.max_collection_delay);
EXPECT_EQ(2, parsed_params.restore_session.sampling_factor);
EXPECT_EQ(base::TimeDelta::FromSeconds(20),
parsed_params.restore_session.max_collection_delay);
}
namespace {
std::vector<base::SamplingHeapProfiler::Sample> SamplingHeapProfilerSamples() {
// Generate a sample using the SamplingHeapProfiler collector. Then, duplicate
// and customize their values.
base::SamplingHeapProfiler::Init();
auto* collector = base::SamplingHeapProfiler::Get();
collector->Start();
auto* sampler = base::PoissonAllocationSampler::Get();
sampler->SuppressRandomnessForTest(true);
sampler->SetSamplingInterval(1000000);
// Generate and remove a dummy sample, because the first sample is potentially
// ignored by the SHP profiler.
sampler->RecordAlloc(reinterpret_cast<void*>(1), 1000000,
base::PoissonAllocationSampler::AllocatorType::kMalloc,
nullptr);
sampler->RecordFree(reinterpret_cast<void*>(1));
// Generate a second sample that we are going to retrieve.
sampler->RecordAlloc(reinterpret_cast<void*>(2), 1000000,
base::PoissonAllocationSampler::AllocatorType::kMalloc,
nullptr);
auto samples = collector->GetSamples(0);
EXPECT_EQ(1lu, samples.size());
sampler->RecordFree(reinterpret_cast<void*>(2));
collector->Stop();
// Customize the sample.
auto& sample1 = samples[0];
sample1.size = 100;
sample1.total = 1000;
sample1.stack = {reinterpret_cast<void*>(0x10000),
reinterpret_cast<void*>(0x10100),
reinterpret_cast<void*>(0x10200)};
samples.emplace_back(samples.back());
EXPECT_EQ(2lu, samples.size());
auto& sample2 = samples[1];
sample2.size = 200;
sample2.total = 2000;
sample2.stack = {
reinterpret_cast<void*>(0x20000), reinterpret_cast<void*>(0x20100),
reinterpret_cast<void*>(0x20200), reinterpret_cast<void*>(0x20300)};
samples.emplace_back(samples.back());
EXPECT_EQ(3lu, samples.size());
auto& sample3 = samples[2];
sample3.size = 300;
sample3.total = 3000;
sample3.stack = {reinterpret_cast<void*>(0x30000),
reinterpret_cast<void*>(0x30100),
reinterpret_cast<void*>(0x30200)};
return samples;
}
std::string GetProcMaps() {
return R"text(304acba71000-304acba72000 ---p 00000000 00:00 0
304acba72000-304acd86a000 rw-p 00000000 00:00 0
304acd86a000-304acd86b000 ---p 00000000 00:00 0
304acd86b000-304acd88a000 rw-p 00000000 00:00 0
304acd88a000-304acd88b000 ---p 00000000 00:00 0
304acd88b000-304acd8aa000 rw-p 00000000 00:00 0
5ffa92db8000-5ffa93d15000 r--p 00000000 b3:03 71780 /opt/google/chrome/chrome
5ffa93d15000-5ffa93d16000 r--p 00f5d000 b3:03 71780 /opt/google/chrome/chrome
5ffa93d16000-5ffa93d17000 r--p 00f5e000 b3:03 71780 /opt/google/chrome/chrome
5ffa93d17000-5ffa9d176000 r-xp 00f5f000 b3:03 71780 /opt/google/chrome/chrome
5ffa9d176000-5ffa9d1ca000 rw-p 0a3be000 b3:03 71780 /opt/google/chrome/chrome
5ffa9d1ca000-5ffa9d8ff000 r--p 0a412000 b3:03 71780 /opt/google/chrome/chrome
5ffa9d8ff000-5ffa9db6c000 rw-p 00000000 00:00 0
7f9c9e11c000-7f9c9e11d000 ---p 00000000 00:00 0
7f9c9e11d000-7f9c9e91d000 rw-p 00000000 00:00 0 [stack:1843]
7f9c9e91d000-7f9c9e91e000 ---p 00000000 00:00 0
7f9c9e91e000-7f9c9f11e000 rw-p 00000000 00:00 0
7f9ca0d47000-7f9ca0d4c000 r-xp 00000000 b3:03 46090 /lib64/libnss_dns-2.27.so
7f9ca0d4c000-7f9ca0f4b000 ---p 00005000 b3:03 46090 /lib64/libnss_dns-2.27.so
7f9ca0f4b000-7f9ca0f4c000 r--p 00004000 b3:03 46090 /lib64/libnss_dns-2.27.so
7f9ca0f4c000-7f9ca0f4d000 rw-p 00005000 b3:03 46090 /lib64/libnss_dns-2.27.so
7f9ca0f4d000-7f9ca114d000 rw-s 00000000 00:13 16382 /dev/shm/.com.google.Chrome.nohIdv (deleted)
7f9ca26a0000-7f9ca26a1000 ---p 00000000 00:00 0
7f9ca26a1000-7f9ca2ea1000 rw-p 00000000 00:00 0 [stack:1796]
7f9ca2ea1000-7f9ca2f31000 r-xp 00000000 b3:03 46120 /lib64/libpcre.so.1.2.9
7f9ca2f31000-7f9ca2f32000 r--p 0008f000 b3:03 46120 /lib64/libpcre.so.1.2.9
7f9ca2f32000-7f9ca2f33000 rw-p 00090000 b3:03 46120 /lib64/libpcre.so.1.2.9
7f9ca2f33000-7f9ca302d000 r-xp 00000000 b3:03 40207 /usr/lib64/libglib-2.0.so.0.5200.3
7f9ca302e000-7f9ca302f000 r--p 000fa000 b3:03 40207 /usr/lib64/libglib-2.0.so.0.5200.3
7f9ca4687000-7f9ca4887000 rw-s 00000000 00:13 12699 /dev/shm/.com.google.Chrome.KWz8dN (deleted)
7f9ca4887000-7f9ca4888000 ---p 00000000 00:00 0
7f9ca4888000-7f9ca5088000 rw-p 00000000 00:00 0 [stack:1423]
7f9ca52c8000-7f9ca52c9000 ---p 00000000 00:00 0
7f9ca52c9000-7f9ca5ac9000 rw-p 00000000 00:00 0 [stack:1411]
7f9ca5aec000-7f9ca5b66000 r--p 00000000 b3:03 39083 /usr/share/fonts/roboto/Roboto-Light.ttf
7f9ca5b66000-7f9ca5d66000 rw-s 00000000 00:13 10081 /dev/shm/.com.google.Chrome.MLuvgs (deleted)
)text";
}
} // namespace
TEST(HeapCollectorShimLayerTest, WriteHeapProfileToFile_InvalidProcMaps) {
base::FilePath temp_path;
ASSERT_TRUE(base::CreateTemporaryFile(&temp_path));
base::File temp(temp_path, base::File::FLAG_CREATE_ALWAYS |
base::File::FLAG_READ |
base::File::FLAG_WRITE);
ASSERT_TRUE(temp.created());
ASSERT_TRUE(temp.IsValid());
auto samples = SamplingHeapProfilerSamples();
std::string proc_maps = "Bogus proc maps\n";
EXPECT_FALSE(internal::WriteHeapProfileToFile(&temp, samples, proc_maps));
temp.Close();
base::DeleteFile(temp_path, false);
}
TEST(HeapCollectorShimLayerTest, WriteHeapProfileToFile) {
base::FilePath temp_path;
ASSERT_TRUE(base::CreateTemporaryFile(&temp_path));
base::File temp(temp_path, base::File::FLAG_CREATE_ALWAYS |
base::File::FLAG_READ |
base::File::FLAG_WRITE);
ASSERT_TRUE(temp.created());
ASSERT_TRUE(temp.IsValid());
auto samples = SamplingHeapProfilerSamples();
auto proc_maps = GetProcMaps();
EXPECT_TRUE(internal::WriteHeapProfileToFile(&temp, samples, proc_maps));
temp.Close();
std::string got;
EXPECT_TRUE(base::ReadFileToString(temp_path, &got));
std::string want = R"text(heap profile: 3: 6000 [3: 6000] @ heap_v2/1
1: 1000 [1: 1000] @ 0x10000 0x10100 0x10200
1: 2000 [1: 2000] @ 0x20000 0x20100 0x20200 0x20300
1: 3000 [1: 3000] @ 0x30000 0x30100 0x30200
MAPPED_LIBRARIES:
304acba71000-304acba72000 ---p 00000000 00:00 0
304acba72000-304acd86a000 rw-p 00000000 00:00 0
304acd86a000-304acd86b000 ---p 00000000 00:00 0
304acd86b000-304acd88a000 rw-p 00000000 00:00 0
304acd88a000-304acd88b000 ---p 00000000 00:00 0
304acd88b000-304acd8aa000 rw-p 00000000 00:00 0
5ffa92db8000-5ffa93d15000 r--p 00000000 00:00 0 /opt/google/chrome/chrome
5ffa93d15000-5ffa93d16000 r--p 00f5d000 00:00 0 /opt/google/chrome/chrome
5ffa93d16000-5ffa93d17000 r--p 00f5e000 00:00 0 /opt/google/chrome/chrome
5ffa93d17000-5ffa9d176000 r-xp 00f5f000 00:00 0 /opt/google/chrome/chrome
5ffa9d176000-5ffa9d1ca000 rw-p 0a3be000 00:00 0 /opt/google/chrome/chrome
5ffa9d1ca000-5ffa9d8ff000 r--p 0a412000 00:00 0 /opt/google/chrome/chrome
5ffa9d8ff000-5ffa9db6c000 rw-p 00000000 00:00 0
7f9c9e11c000-7f9c9e11d000 ---p 00000000 00:00 0
7f9c9e11d000-7f9c9e91d000 rw-p 00000000 00:00 0 [stack:1843]
7f9c9e91d000-7f9c9e91e000 ---p 00000000 00:00 0
7f9c9e91e000-7f9c9f11e000 rw-p 00000000 00:00 0
7f9ca0d47000-7f9ca0d4c000 r-xp 00000000 00:00 0 /lib64/libnss_dns-2.27.so
7f9ca0d4c000-7f9ca0f4b000 ---p 00005000 00:00 0 /lib64/libnss_dns-2.27.so
7f9ca0f4b000-7f9ca0f4c000 r--p 00004000 00:00 0 /lib64/libnss_dns-2.27.so
7f9ca0f4c000-7f9ca0f4d000 rw-p 00005000 00:00 0 /lib64/libnss_dns-2.27.so
7f9ca0f4d000-7f9ca114d000 rw-- 00000000 00:00 0 /dev/shm/.com.google.Chrome.nohIdv (deleted)
7f9ca26a0000-7f9ca26a1000 ---p 00000000 00:00 0
7f9ca26a1000-7f9ca2ea1000 rw-p 00000000 00:00 0 [stack:1796]
7f9ca2ea1000-7f9ca2f31000 r-xp 00000000 00:00 0 /lib64/libpcre.so.1.2.9
7f9ca2f31000-7f9ca2f32000 r--p 0008f000 00:00 0 /lib64/libpcre.so.1.2.9
7f9ca2f32000-7f9ca2f33000 rw-p 00090000 00:00 0 /lib64/libpcre.so.1.2.9
7f9ca2f33000-7f9ca302d000 r-xp 00000000 00:00 0 /usr/lib64/libglib-2.0.so.0.5200.3
7f9ca302e000-7f9ca302f000 r--p 000fa000 00:00 0 /usr/lib64/libglib-2.0.so.0.5200.3
7f9ca4687000-7f9ca4887000 rw-- 00000000 00:00 0 /dev/shm/.com.google.Chrome.KWz8dN (deleted)
7f9ca4887000-7f9ca4888000 ---p 00000000 00:00 0
7f9ca4888000-7f9ca5088000 rw-p 00000000 00:00 0 [stack:1423]
7f9ca52c8000-7f9ca52c9000 ---p 00000000 00:00 0
7f9ca52c9000-7f9ca5ac9000 rw-p 00000000 00:00 0 [stack:1411]
7f9ca5aec000-7f9ca5b66000 r--p 00000000 00:00 0 /usr/share/fonts/roboto/Roboto-Light.ttf
7f9ca5b66000-7f9ca5d66000 rw-- 00000000 00:00 0 /dev/shm/.com.google.Chrome.MLuvgs (deleted)
)text";
EXPECT_EQ(want, got);
base::DeleteFile(temp_path, false);
}
} // namespace metrics