blob: 74c5265e2b44381373c9712ee50e241f59c02141 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/gwp_asan/client/gwp_asan.h"
#include <map>
#include <optional>
#include <set>
#include <string>
#include <string_view>
#include <utility>
#include "base/metrics/field_trial_params.h"
#include "base/test/gtest_util.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace gwp_asan {
namespace internal {
std::optional<AllocatorSettings> GetAllocatorSettingsImpl(
const base::Feature& feature,
bool boost_sampling,
std::string_view process_type);
std::optional<AllocatorSettings> GetAllocatorSettings(
const base::Feature& feature,
bool boost_sampling,
std::string_view process_type);
namespace {
constexpr size_t kLoopIterations = 100;
BASE_FEATURE(kTestFeature1,
"GwpAsanTestFeature1",
base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kTestFeature2,
"GwpAsanTestFeature2",
base::FEATURE_ENABLED_BY_DEFAULT);
BASE_FEATURE(kTestFeature3,
"GwpAsanTestFeature3",
base::FEATURE_ENABLED_BY_DEFAULT);
inline constexpr std::string_view kDummyProcessType =
"assuredly not a real process type";
// Tries to enable hooking with the given process sampling parameters
// kLoopIterations times and return the number of times hooking was enabled.
size_t processSamplingTest(const char* process_sampling,
const char* process_sampling_boost) {
std::map<std::string, std::string> parameters;
parameters["ProcessSamplingProbability"] = process_sampling;
if (process_sampling_boost)
parameters["ProcessSamplingBoost2"] = process_sampling_boost;
base::test::ScopedFeatureList scoped_feature;
scoped_feature.InitAndEnableFeatureWithParameters(kTestFeature1, parameters);
size_t enabled = 0;
for (size_t i = 0; i < kLoopIterations; i++) {
if (GetAllocatorSettings(kTestFeature1, process_sampling_boost != nullptr,
kDummyProcessType)) {
enabled++;
}
}
return enabled;
}
// Enables hooking kLoopIterations times with the given allocation sampling
// parameters and returns the allocation sampling frequencies hooking was
// enabled with.
std::set<size_t> allocationSamplingTest(
const char* allocation_sampling_multiplier,
const char* allocation_sampling_range) {
std::map<std::string, std::string> parameters;
parameters["ProcessSamplingProbability"] = "1.0";
parameters["AllocationSamplingMultiplier"] = allocation_sampling_multiplier;
parameters["AllocationSamplingRange"] = allocation_sampling_range;
base::test::ScopedFeatureList scoped_feature;
scoped_feature.InitAndEnableFeatureWithParameters(kTestFeature2, parameters);
std::set<size_t> frequencies;
for (size_t i = 0; i < kLoopIterations; i++) {
if (auto settings =
GetAllocatorSettings(kTestFeature2, false, kDummyProcessType)) {
frequencies.insert(settings->sampling_frequency);
}
}
return frequencies;
}
} // namespace
TEST(GwpAsanTest, ProcessSamplingWorks) {
EXPECT_EQ(processSamplingTest("1.0", nullptr), kLoopIterations);
EXPECT_EQ(processSamplingTest("1.0", "100000"), kLoopIterations);
EXPECT_EQ(processSamplingTest("0.01", "100"), kLoopIterations);
EXPECT_EQ(processSamplingTest("0.0", nullptr), 0U);
EXPECT_EQ(processSamplingTest("0.0", "100000"), 0U);
size_t num_enabled = processSamplingTest("0.5", nullptr);
EXPECT_GT(num_enabled, 0U);
EXPECT_LT(num_enabled, kLoopIterations);
num_enabled = processSamplingTest("0.01", "50");
EXPECT_GT(num_enabled, 0U);
EXPECT_LT(num_enabled, kLoopIterations);
}
TEST(GwpAsanTest, AllocationSamplingWorks) {
std::set<size_t> frequencies = allocationSamplingTest("1000", "1");
EXPECT_THAT(frequencies, testing::ElementsAre(1000));
frequencies = allocationSamplingTest("1000", "64");
EXPECT_GT(frequencies.size(), 1U);
for (const size_t freq : frequencies) {
EXPECT_GE(freq, 1000U);
EXPECT_LE(freq, 64000U);
}
}
TEST(GwpAsanTest, GetDefaultAllocatorSettings) {
std::map<std::string, std::string> empty_parameters;
base::test::ScopedFeatureList scoped_feature;
scoped_feature.InitAndEnableFeatureWithParameters(kTestFeature3,
empty_parameters);
const auto settings =
GetAllocatorSettingsImpl(kTestFeature3, false, kDummyProcessType);
EXPECT_TRUE(settings.has_value());
}
TEST(GwpAsanTest, GetOutOfRangeAllocatorSettings) {
std::map<std::string, std::string> bad_parameters;
// Exceeds `MaxMetadata`, forcing failure.
bad_parameters["MaxAllocations"] = "9999";
base::test::ScopedFeatureList scoped_feature;
scoped_feature.InitAndEnableFeatureWithParameters(kTestFeature3,
bad_parameters);
const auto settings =
GetAllocatorSettingsImpl(kTestFeature3, false, kDummyProcessType);
EXPECT_FALSE(settings.has_value());
}
TEST(GwpAsanTest, GetProcessSpecificAllocatorSettings) {
std::map<std::string, std::string> process_specific_parameters;
// Set to weird and distinctive values.
process_specific_parameters["BrowserMaxAllocations"] = "21";
process_specific_parameters["RendererMaxAllocations"] = "22";
base::test::ScopedFeatureList scoped_feature;
scoped_feature.InitAndEnableFeatureWithParameters(
kTestFeature3, process_specific_parameters);
// The empty `process_type` string denotes the browser process.
const auto browser_settings =
GetAllocatorSettingsImpl(kTestFeature3, false, "");
EXPECT_TRUE(browser_settings.has_value());
EXPECT_EQ(browser_settings->max_allocated_pages, 21ul);
const auto renderer_settings =
GetAllocatorSettingsImpl(kTestFeature3, false, "renderer");
EXPECT_TRUE(renderer_settings.has_value());
EXPECT_EQ(renderer_settings->max_allocated_pages, 22ul);
}
TEST(GwpAsanTest, GetProcessSpecificAllocationSamplingMultiplier) {
std::map<std::string, std::string> parameters;
// This parameter is never overridden, hence common to all processes.
parameters["AllocationSamplingRange"] = "1";
// Since the range is set to 1, the multiplier fully determines
// the sampling frequency:
// 2 * (1 ** RandDouble()) == 2
parameters["AllocationSamplingMultiplier"] = "2";
// Set browser-specific sampling frequency:
// 2000 * (1 ** RandDouble()) == 2000
parameters["BrowserAllocationSamplingMultiplier"] = "2000";
// Set renderer-specific sampling frequency:
// 300 * (1 ** RandDouble()) == 300
parameters["RendererAllocationSamplingMultiplier"] = "300";
base::test::ScopedFeatureList scoped_feature;
scoped_feature.InitAndEnableFeatureWithParameters(kTestFeature3, parameters);
const auto generic_settings = GetAllocatorSettingsImpl(
kTestFeature3, false, "invalid process type to force generic params");
EXPECT_TRUE(generic_settings.has_value());
EXPECT_EQ(generic_settings->sampling_frequency, 2ul);
const auto renderer_settings =
GetAllocatorSettingsImpl(kTestFeature3, false, "renderer");
EXPECT_TRUE(renderer_settings.has_value());
EXPECT_EQ(renderer_settings->sampling_frequency, 300ul);
// The empty `process_type` string denotes the browser process.
const auto browser_settings =
GetAllocatorSettingsImpl(kTestFeature3, false, "");
EXPECT_TRUE(browser_settings.has_value());
EXPECT_EQ(browser_settings->sampling_frequency, 2000ul);
}
TEST(GwpAsanTest, GetProcessSpecificAllocationSamplingRange) {
std::map<std::string, std::string> parameters;
// This parameter is never overridden, hence common to all processes.
parameters["AllocationSamplingMultiplier"] = "1";
// The default sampling frequency, then, is
// 1 * (1048576 ** RandDouble()) >= 1
// ...and will _most likely_ not equal 1.
parameters["AllocationSamplingRange"] = "1048576";
// Set browser-specific sampling frequency:
// 1 * (1 ** RandDouble()) == 1
parameters["BrowserAllocationSamplingRange"] = "1";
base::test::ScopedFeatureList scoped_feature;
scoped_feature.InitAndEnableFeatureWithParameters(kTestFeature3, parameters);
const auto generic_settings = GetAllocatorSettingsImpl(
kTestFeature3, false, "invalid process type to force generic params");
EXPECT_TRUE(generic_settings.has_value());
EXPECT_GE(generic_settings->sampling_frequency, 1ul);
EXPECT_LE(generic_settings->sampling_frequency, 1ul << 20);
// The empty `process_type` string denotes the browser process.
const auto browser_settings =
GetAllocatorSettingsImpl(kTestFeature3, false, "");
EXPECT_TRUE(browser_settings.has_value());
EXPECT_EQ(browser_settings->sampling_frequency, 1ul);
}
} // namespace internal
} // namespace gwp_asan