Commit 34e3e7f9 authored by Michal Majewski's avatar Michal Majewski Committed by Commit Bot

Introduce gc flag for fuzzing over compaction.

Bug: v8:6972
Change-Id: If1f4ee04ae00c6ae1e037bbb1ca758e952a8f843
Reviewed-on: https://chromium-review.googlesource.com/738112Reviewed-by: 's avatarMichael Achenbach <machenbach@chromium.org>
Reviewed-by: 's avatarHannes Payer <hpayer@chromium.org>
Commit-Queue: Michał Majewski <majeski@google.com>
Cr-Commit-Position: refs/heads/master@{#49191}
parent 842db04f
......@@ -7,6 +7,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <new>
#include "src/base/bits.h"
......@@ -114,6 +115,85 @@ void RandomNumberGenerator::NextBytes(void* buffer, size_t buflen) {
}
}
static std::vector<uint64_t> ComplementSample(
const std::unordered_set<uint64_t>& set, uint64_t max) {
std::vector<uint64_t> result;
result.reserve(max - set.size());
for (uint64_t i = 0; i < max; i++) {
if (!set.count(i)) {
result.push_back(i);
}
}
return result;
}
std::vector<uint64_t> RandomNumberGenerator::NextSample(uint64_t max,
size_t n) {
CHECK_LE(n, max);
if (n == 0) {
return {};
}
// Choose to select or exclude, whatever needs fewer generator calls.
size_t smaller_part = static_cast<size_t>(
std::min(max - static_cast<uint64_t>(n), static_cast<uint64_t>(n)));
std::unordered_set<uint64_t> selected;
size_t counter = 0;
while (selected.size() != smaller_part && counter / 3 < smaller_part) {
uint64_t x = static_cast<uint64_t>(NextDouble() * max);
CHECK_LT(x, max);
selected.insert(x);
counter++;
}
if (selected.size() == smaller_part) {
if (smaller_part != n) {
return ComplementSample(selected, max);
}
return std::vector<uint64_t>(selected.begin(), selected.end());
}
// Failed to select numbers in smaller_part * 3 steps, try different approach.
return NextSampleSlow(max, n, selected);
}
std::vector<uint64_t> RandomNumberGenerator::NextSampleSlow(
uint64_t max, size_t n, const std::unordered_set<uint64_t>& excluded) {
CHECK_GE(max - excluded.size(), n);
std::vector<uint64_t> result;
result.reserve(max - excluded.size());
for (uint64_t i = 0; i < max; i++) {
if (!excluded.count(i)) {
result.push_back(i);
}
}
// Decrease result vector until it contains values to select or exclude,
// whatever needs fewer generator calls.
size_t larger_part = static_cast<size_t>(
std::max(max - static_cast<uint64_t>(n), static_cast<uint64_t>(n)));
// Excluded set may cause that initial result is already smaller than
// larget_part.
while (result.size() != larger_part && result.size() > n) {
size_t x = static_cast<size_t>(NextDouble() * result.size());
CHECK_LT(x, result.size());
std::swap(result[x], result.back());
result.pop_back();
}
if (result.size() != n) {
return ComplementSample(
std::unordered_set<uint64_t>(result.begin(), result.end()), max);
}
return result;
}
int RandomNumberGenerator::Next(int bits) {
DCHECK_LT(0, bits);
......
......@@ -5,6 +5,9 @@
#ifndef V8_BASE_UTILS_RANDOM_NUMBER_GENERATOR_H_
#define V8_BASE_UTILS_RANDOM_NUMBER_GENERATOR_H_
#include <unordered_set>
#include <vector>
#include "src/base/base-export.h"
#include "src/base/macros.h"
......@@ -85,6 +88,23 @@ class V8_BASE_EXPORT RandomNumberGenerator final {
// Fills the elements of a specified array of bytes with random numbers.
void NextBytes(void* buffer, size_t buflen);
// Returns the next pseudorandom set of n unique uint64 values smaller than
// max.
// n must be less or equal to max.
std::vector<uint64_t> NextSample(uint64_t max, size_t n) WARN_UNUSED_RESULT;
// Returns the next pseudorandom set of n unique uint64 values smaller than
// max.
// n must be less or equal to max.
// max - |excluded| must be less or equal to n.
//
// Generates list of all possible values and removes random values from it
// until size reaches n.
std::vector<uint64_t> NextSampleSlow(
uint64_t max, size_t n,
const std::unordered_set<uint64_t>& excluded =
std::unordered_set<uint64_t>{}) WARN_UNUSED_RESULT;
// Override the current ssed.
void SetSeed(int64_t seed);
......
......@@ -682,6 +682,9 @@ DEFINE_BOOL(force_marking_deque_overflows, false,
DEFINE_BOOL(stress_compaction, false,
"stress the GC compactor to flush out bugs (implies "
"--force_marking_deque_overflows)")
DEFINE_INT(stress_compaction_percentage, 0,
"Stress GC compaction by selecting X percent of pages as evacuation "
"candidates. It overrides stress_compaction.")
DEFINE_BOOL(stress_incremental_marking, false,
"force incremental marking for small heaps and run it more often")
DEFINE_INT(stress_incremental_marking_percentage, 0,
......@@ -941,6 +944,9 @@ DEFINE_INT(hash_seed, 0,
DEFINE_INT(random_seed, 0,
"Default seed for initializing random generator "
"(0, the default, means to use system random).")
DEFINE_INT(fuzzer_random_seed, 0,
"Default seed for initializing fuzzer random generator "
"(0, the default, means to use system random).")
DEFINE_BOOL(trace_rail, false, "trace RAIL mode")
DEFINE_BOOL(print_all_exceptions, false,
"print exception object and stack trace on each thrown exception")
......
......@@ -6,6 +6,7 @@
#include <unordered_map>
#include "src/base/utils/random-number-generator.h"
#include "src/cancelable-task.h"
#include "src/code-stubs.h"
#include "src/compilation-cache.h"
......@@ -864,7 +865,6 @@ void MarkCompactCollector::ComputeEvacuationHeuristics(
}
}
void MarkCompactCollector::CollectEvacuationCandidates(PagedSpace* space) {
DCHECK(space->identity() == OLD_SPACE || space->identity() == CODE_SPACE);
......@@ -911,6 +911,19 @@ void MarkCompactCollector::CollectEvacuationCandidates(PagedSpace* space) {
AddEvacuationCandidate(p);
}
}
} else if (FLAG_stress_compaction_percentage > 0) {
size_t percent =
isolate()->fuzzer_rng()->NextInt(FLAG_stress_compaction_percentage + 1);
size_t pages_to_mark_count = percent * pages.size() / 100;
if (pages_to_mark_count) {
for (uint64_t i : isolate()->fuzzer_rng()->NextSample(
pages.size(), pages_to_mark_count)) {
candidate_count++;
total_live_bytes += pages[i].first;
AddEvacuationCandidate(pages[i].second);
}
}
} else if (FLAG_stress_compaction) {
for (size_t i = 0; i < pages.size(); i++) {
Page* p = pages[i].second;
......
......@@ -2391,6 +2391,7 @@ Isolate::Isolate(bool enable_serializer)
// TODO(bmeurer) Initialized lazily because it depends on flags; can
// be fixed once the default isolate cleanup is done.
random_number_generator_(nullptr),
fuzzer_rng_(nullptr),
rail_mode_(PERFORMANCE_ANIMATION),
promise_hook_or_debug_is_active_(false),
promise_hook_(nullptr),
......@@ -2659,6 +2660,9 @@ Isolate::~Isolate() {
delete random_number_generator_;
random_number_generator_ = nullptr;
delete fuzzer_rng_;
fuzzer_rng_ = nullptr;
delete debug_;
debug_ = nullptr;
......@@ -3307,17 +3311,24 @@ CallInterfaceDescriptorData* Isolate::call_descriptor_data(int index) {
return &call_descriptor_data_[index];
}
base::RandomNumberGenerator* Isolate::random_number_generator() {
if (random_number_generator_ == nullptr) {
if (FLAG_random_seed != 0) {
random_number_generator_ =
new base::RandomNumberGenerator(FLAG_random_seed);
static base::RandomNumberGenerator* ensure_rng_exists(
base::RandomNumberGenerator** rng, int seed) {
if (*rng == nullptr) {
if (seed != 0) {
*rng = new base::RandomNumberGenerator(seed);
} else {
random_number_generator_ = new base::RandomNumberGenerator();
*rng = new base::RandomNumberGenerator();
}
}
return random_number_generator_;
return *rng;
}
base::RandomNumberGenerator* Isolate::random_number_generator() {
return ensure_rng_exists(&random_number_generator_, FLAG_random_seed);
}
base::RandomNumberGenerator* Isolate::fuzzer_rng() {
return ensure_rng_exists(&fuzzer_rng_, FLAG_fuzzer_random_seed);
}
int Isolate::GenerateIdentityHash(uint32_t mask) {
......
......@@ -1163,6 +1163,8 @@ class Isolate {
V8_EXPORT_PRIVATE base::RandomNumberGenerator* random_number_generator();
V8_EXPORT_PRIVATE base::RandomNumberGenerator* fuzzer_rng();
// Generates a random number that is non-zero when masked
// with the provided mask.
int GenerateIdentityHash(uint32_t mask);
......@@ -1517,6 +1519,7 @@ class Isolate {
CallInterfaceDescriptorData* call_descriptor_data_;
AccessCompilerData* access_compiler_data_;
base::RandomNumberGenerator* random_number_generator_;
base::RandomNumberGenerator* fuzzer_rng_;
base::AtomicValue<RAILMode> rail_mode_;
bool promise_hook_or_debug_is_active_;
PromiseHook promise_hook_;
......
......@@ -12,9 +12,38 @@ namespace base {
class RandomNumberGeneratorTest : public ::testing::TestWithParam<int> {};
static const int kMaxRuns = 12345;
static void CheckSample(std::vector<uint64_t> sample, uint64_t max,
size_t size) {
EXPECT_EQ(sample.size(), size);
// Check if values are unique.
std::sort(sample.begin(), sample.end());
EXPECT_EQ(std::adjacent_find(sample.begin(), sample.end()), sample.end());
for (uint64_t x : sample) {
EXPECT_LT(x, max);
}
}
static void CheckSlowSample(const std::vector<uint64_t>& sample, uint64_t max,
size_t size,
const std::unordered_set<uint64_t>& excluded) {
CheckSample(sample, max, size);
for (uint64_t i : sample) {
EXPECT_FALSE(excluded.count(i));
}
}
static void TestNextSample(RandomNumberGenerator& rng, uint64_t max,
size_t size, bool slow = false) {
std::vector<uint64_t> sample =
slow ? rng.NextSampleSlow(max, size) : rng.NextSample(max, size);
CheckSample(sample, max, size);
}
TEST_P(RandomNumberGeneratorTest, NextIntWithMaxValue) {
RandomNumberGenerator rng(GetParam());
......@@ -44,6 +73,174 @@ TEST_P(RandomNumberGeneratorTest, NextDoubleReturnsValueBetween0And1) {
}
}
TEST(RandomNumberGenerator, NextSampleInvalidParam) {
RandomNumberGenerator rng(123);
std::vector<uint64_t> sample;
EXPECT_DEATH(sample = rng.NextSample(10, 11), ".*Check failed: n <= max.*");
}
TEST(RandomNumberGenerator, NextSampleSlowInvalidParam1) {
RandomNumberGenerator rng(123);
std::vector<uint64_t> sample;
EXPECT_DEATH(sample = rng.NextSampleSlow(10, 11),
".*Check failed: max - excluded.size*");
}
TEST(RandomNumberGenerator, NextSampleSlowInvalidParam2) {
RandomNumberGenerator rng(123);
std::vector<uint64_t> sample;
EXPECT_DEATH(sample = rng.NextSampleSlow(5, 3, {0, 2, 3}),
".*Check failed: max - excluded.size*");
}
TEST_P(RandomNumberGeneratorTest, NextSample0) {
size_t m = 1;
RandomNumberGenerator rng(GetParam());
TestNextSample(rng, m, 0);
}
TEST_P(RandomNumberGeneratorTest, NextSampleSlow0) {
size_t m = 1;
RandomNumberGenerator rng(GetParam());
TestNextSample(rng, m, 0, true);
}
TEST_P(RandomNumberGeneratorTest, NextSample1) {
size_t m = 10;
RandomNumberGenerator rng(GetParam());
for (int k = 0; k < kMaxRuns; ++k) {
TestNextSample(rng, m, 1);
}
}
TEST_P(RandomNumberGeneratorTest, NextSampleSlow1) {
size_t m = 10;
RandomNumberGenerator rng(GetParam());
for (int k = 0; k < kMaxRuns; ++k) {
TestNextSample(rng, m, 1, true);
}
}
TEST_P(RandomNumberGeneratorTest, NextSampleMax) {
size_t m = 10;
RandomNumberGenerator rng(GetParam());
for (int k = 0; k < kMaxRuns; ++k) {
TestNextSample(rng, m, m);
}
}
TEST_P(RandomNumberGeneratorTest, NextSampleSlowMax) {
size_t m = 10;
RandomNumberGenerator rng(GetParam());
for (int k = 0; k < kMaxRuns; ++k) {
TestNextSample(rng, m, m, true);
}
}
TEST_P(RandomNumberGeneratorTest, NextSampleHalf) {
size_t n = 5;
uint64_t m = 10;
RandomNumberGenerator rng(GetParam());
for (int k = 0; k < kMaxRuns; ++k) {
TestNextSample(rng, m, n);
}
}
TEST_P(RandomNumberGeneratorTest, NextSampleSlowHalf) {
size_t n = 5;
uint64_t m = 10;
RandomNumberGenerator rng(GetParam());
for (int k = 0; k < kMaxRuns; ++k) {
TestNextSample(rng, m, n, true);
}
}
TEST_P(RandomNumberGeneratorTest, NextSampleMoreThanHalf) {
size_t n = 90;
uint64_t m = 100;
RandomNumberGenerator rng(GetParam());
for (int k = 0; k < kMaxRuns; ++k) {
TestNextSample(rng, m, n);
}
}
TEST_P(RandomNumberGeneratorTest, NextSampleSlowMoreThanHalf) {
size_t n = 90;
uint64_t m = 100;
RandomNumberGenerator rng(GetParam());
for (int k = 0; k < kMaxRuns; ++k) {
TestNextSample(rng, m, n, true);
}
}
TEST_P(RandomNumberGeneratorTest, NextSampleLessThanHalf) {
size_t n = 10;
uint64_t m = 100;
RandomNumberGenerator rng(GetParam());
for (int k = 0; k < kMaxRuns; ++k) {
TestNextSample(rng, m, n);
}
}
TEST_P(RandomNumberGeneratorTest, NextSampleSlowLessThanHalf) {
size_t n = 10;
uint64_t m = 100;
RandomNumberGenerator rng(GetParam());
for (int k = 0; k < kMaxRuns; ++k) {
TestNextSample(rng, m, n, true);
}
}
TEST_P(RandomNumberGeneratorTest, NextSampleSlowExcluded) {
size_t n = 2;
uint64_t m = 10;
std::unordered_set<uint64_t> excluded = {2, 4, 5, 9};
RandomNumberGenerator rng(GetParam());
for (int k = 0; k < kMaxRuns; ++k) {
std::vector<uint64_t> sample = rng.NextSampleSlow(m, n, excluded);
CheckSlowSample(sample, m, n, excluded);
}
}
TEST_P(RandomNumberGeneratorTest, NextSampleSlowExcludedMax1) {
size_t n = 1;
uint64_t m = 5;
std::unordered_set<uint64_t> excluded = {0, 2, 3, 4};
RandomNumberGenerator rng(GetParam());
for (int k = 0; k < kMaxRuns; ++k) {
std::vector<uint64_t> sample = rng.NextSampleSlow(m, n, excluded);
CheckSlowSample(sample, m, n, excluded);
}
}
TEST_P(RandomNumberGeneratorTest, NextSampleSlowExcludedMax2) {
size_t n = 7;
uint64_t m = 10;
std::unordered_set<uint64_t> excluded = {0, 4, 8};
RandomNumberGenerator rng(GetParam());
for (int k = 0; k < kMaxRuns; ++k) {
std::vector<uint64_t> sample = rng.NextSampleSlow(m, n, excluded);
CheckSlowSample(sample, m, n, excluded);
}
}
INSTANTIATE_TEST_CASE_P(RandomSeeds, RandomNumberGeneratorTest,
::testing::Values(INT_MIN, -1, 0, 1, 42, 100,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment