Skip to content

Commit d501a77

Browse files
xinhaoyuancopybara-github
authored andcommitted
Pass RNG to functions that require randomness for cleaner interface.
PiperOrigin-RevId: 817646190
1 parent f505c8b commit d501a77

File tree

6 files changed

+64
-48
lines changed

6 files changed

+64
-48
lines changed

centipede/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,9 @@ cc_library(
601601
":execution_metadata",
602602
":feature",
603603
":feature_set",
604+
":runner_result",
604605
":util",
606+
"@abseil-cpp//absl/random",
605607
"@abseil-cpp//absl/strings",
606608
"@com_google_fuzztest//common:defs",
607609
"@com_google_fuzztest//common:logging",

centipede/centipede.cc

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -452,8 +452,8 @@ bool Centipede::RunBatch(
452452
batch_gained_new_coverage = true;
453453
FUZZTEST_CHECK_GT(fv.size(), 0UL);
454454
if (function_filter_passed) {
455-
corpus_.Add(input_vec[i], fv, batch_result.results()[i].metadata(), fs_,
456-
coverage_frontier_);
455+
corpus_.Add(input_vec[i], fv, batch_result.results()[i].metadata(),
456+
batch_result.results()[i].stats(), fs_, coverage_frontier_);
457457
}
458458
if (corpus_file != nullptr) {
459459
FUZZTEST_CHECK_OK(corpus_file->Write(input_vec[i]));
@@ -493,8 +493,9 @@ void Centipede::LoadShard(const Environment &load_env, size_t shard_index,
493493
FUZZTEST_VLOG(10) << "Adding input " << Hash(input)
494494
<< "; new features: " << num_new_features;
495495
fs_.MergeFeatures(input_features);
496-
// TODO(kcc): cmp_args are currently not saved to disk and not reloaded.
497-
corpus_.Add(input, input_features, {}, fs_, coverage_frontier_);
496+
// TODO(xinhaoyuan): metadata and stats are currently not saved to disk
497+
// and not reloaded.
498+
corpus_.Add(input, input_features, {}, {}, fs_, coverage_frontier_);
498499
++num_added_inputs;
499500
} else {
500501
FUZZTEST_VLOG(10) << "Skipping input: " << Hash(input);
@@ -759,7 +760,8 @@ void Centipede::LoadSeedInputs(BlobFileWriter *absl_nonnull corpus_file,
759760
// coverage and passed the filters.
760761
if (corpus_.NumTotal() == 0) {
761762
for (const auto &seed_input : seed_inputs)
762-
corpus_.Add(seed_input, {}, {}, fs_, coverage_frontier_);
763+
corpus_.Add(seed_input, /*fv=*/{}, /*metadata=*/{}, /*stats=*/{}, fs_,
764+
coverage_frontier_);
763765
}
764766
}
765767

@@ -831,9 +833,9 @@ void Centipede::FuzzingLoop() {
831833
std::vector<MutationInputRef> mutation_inputs;
832834
mutation_inputs.reserve(env_.mutate_batch_size);
833835
for (size_t i = 0; i < env_.mutate_batch_size; i++) {
834-
const auto &corpus_record = env_.use_corpus_weights
835-
? corpus_.WeightedRandom(rng_())
836-
: corpus_.UniformRandom(rng_());
836+
const auto& corpus_record = env_.use_corpus_weights
837+
? corpus_.WeightedRandom(rng_)
838+
: corpus_.UniformRandom(rng_);
837839
mutation_inputs.push_back(
838840
MutationInputRef{corpus_record.data, &corpus_record.metadata});
839841
}

centipede/corpus.cc

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <utility>
2323
#include <vector>
2424

25+
#include "absl/random/random.h"
2526
#include "absl/strings/str_cat.h"
2627
#include "absl/strings/str_join.h"
2728
#include "absl/strings/substitute.h"
@@ -30,6 +31,7 @@
3031
#include "./centipede/execution_metadata.h"
3132
#include "./centipede/feature.h"
3233
#include "./centipede/feature_set.h"
34+
#include "./centipede/runner_result.h"
3335
#include "./centipede/util.h"
3436
#include "./common/defs.h"
3537
#include "./common/logging.h" // IWYU pragma: keep
@@ -113,23 +115,24 @@ size_t Corpus::Prune(const FeatureSet &fs,
113115
return subset_to_remove.size();
114116
}
115117

116-
void Corpus::Add(const ByteArray &data, const FeatureVec &fv,
117-
const ExecutionMetadata &metadata, const FeatureSet &fs,
118-
const CoverageFrontier &coverage_frontier) {
118+
void Corpus::Add(const ByteArray& data, const FeatureVec& fv,
119+
const ExecutionMetadata& metadata,
120+
const ExecutionResult::Stats& stats, const FeatureSet& fs,
121+
const CoverageFrontier& coverage_frontier) {
119122
// TODO(kcc): use coverage_frontier.
120123
FUZZTEST_CHECK(!data.empty())
121124
<< "Got request to add empty element to corpus: ignoring";
122125
FUZZTEST_CHECK_EQ(records_.size(), weighted_distribution_.size());
123-
records_.push_back({data, fv, metadata});
126+
records_.push_back({data, fv, metadata, stats});
124127
weighted_distribution_.AddWeight(ComputeWeight(fv, fs, coverage_frontier));
125128
}
126129

127-
const CorpusRecord &Corpus::WeightedRandom(size_t random) const {
128-
return records_[weighted_distribution_.RandomIndex(random)];
130+
const CorpusRecord& Corpus::WeightedRandom(Rng& rng) const {
131+
return records_[weighted_distribution_.RandomIndex(rng)];
129132
}
130133

131-
const CorpusRecord &Corpus::UniformRandom(size_t random) const {
132-
return records_[random % records_.size()];
134+
const CorpusRecord& Corpus::UniformRandom(Rng& rng) const {
135+
return records_[absl::Uniform<size_t>(rng, 0, records_.size())];
133136
}
134137

135138
void Corpus::DumpStatsToFile(const FeatureSet &fs, std::string_view filepath,
@@ -208,18 +211,21 @@ void WeightedDistribution::RecomputeInternalState() {
208211
}
209212

210213
__attribute__((noinline)) // to see it in profile.
211-
size_t
212-
WeightedDistribution::RandomIndex(size_t random) const {
214+
size_t WeightedDistribution::RandomIndex(Rng& rng) const {
213215
FUZZTEST_CHECK(!weights_.empty());
214216
FUZZTEST_CHECK(cumulative_weights_valid_);
215-
uint64_t sum_of_all_weights = cumulative_weights_.back();
216-
if (sum_of_all_weights == 0)
217-
return random % size(); // can't do much else here.
218-
random = random % sum_of_all_weights;
219-
auto it = std::upper_bound(cumulative_weights_.begin(),
220-
cumulative_weights_.end(), random);
217+
const uint64_t sum_of_all_weights = cumulative_weights_.back();
218+
if (sum_of_all_weights == 0) {
219+
// Can't do much else here.
220+
return absl::Uniform<size_t>(rng, 0, size());
221+
}
222+
auto it =
223+
std::upper_bound(cumulative_weights_.begin(), cumulative_weights_.end(),
224+
absl::Uniform<uint64_t>(rng, 0, sum_of_all_weights));
221225
FUZZTEST_CHECK(it != cumulative_weights_.end());
222-
return it - cumulative_weights_.begin();
226+
const size_t index = it - cumulative_weights_.begin();
227+
FUZZTEST_CHECK(weights_[index] != 0);
228+
return index;
223229
}
224230

225231
uint64_t WeightedDistribution::PopBack() {

centipede/corpus.h

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "./centipede/execution_metadata.h"
2828
#include "./centipede/feature.h"
2929
#include "./centipede/feature_set.h"
30+
#include "./centipede/runner_result.h"
3031
#include "./centipede/util.h"
3132
#include "./common/defs.h"
3233

@@ -48,7 +49,7 @@ class WeightedDistribution {
4849
// For proper randomness, `random` should come from a 64-bit RNG.
4950
// RandomIndex() must not be called after ChangeWeight() without first
5051
// calling RecomputeInternalState().
51-
size_t RandomIndex(size_t random) const;
52+
size_t RandomIndex(Rng& rng) const;
5253
// Returns the number of weights.
5354
size_t size() const { return weights_.size(); }
5455
// Removes all weights.
@@ -87,6 +88,7 @@ struct CorpusRecord {
8788
ByteArray data;
8889
FeatureVec features;
8990
ExecutionMetadata metadata;
91+
ExecutionResult::Stats stats;
9092
};
9193

9294
// Maintains the corpus of inputs.
@@ -105,9 +107,10 @@ class Corpus {
105107
// Adds a corpus element, consisting of 'data' (the input bytes, non-empty),
106108
// 'fv' (the features associated with this input), and execution `metadata`.
107109
// `fs` is used to compute weights of `fv`.
108-
void Add(const ByteArray &data, const FeatureVec &fv,
109-
const ExecutionMetadata &metadata, const FeatureSet &fs,
110-
const CoverageFrontier &coverage_frontier);
110+
void Add(const ByteArray& data, const FeatureVec& fv,
111+
const ExecutionMetadata& metadata,
112+
const ExecutionResult::Stats& stats, const FeatureSet& fs,
113+
const CoverageFrontier& coverage_frontier);
111114
// Removes elements that contain only frequent features, according to 'fs'.
112115
// Also, randomly removes elements to reduce the size to <= `max_corpus_size`.
113116
// `max_corpus_size` should be positive.
@@ -128,9 +131,9 @@ class Corpus {
128131
std::pair<size_t, size_t> MaxAndAvgSize() const;
129132
// Returns a random active corpus record using weighted distribution.
130133
// See WeightedDistribution.
131-
const CorpusRecord &WeightedRandom(size_t random) const;
134+
const CorpusRecord& WeightedRandom(Rng& rng) const;
132135
// Returns a random active corpus record using uniform distribution.
133-
const CorpusRecord &UniformRandom(size_t random) const;
136+
const CorpusRecord& UniformRandom(Rng& rng) const;
134137
// Returns the element with index 'idx', where `idx` < NumActive().
135138
const ByteArray &Get(size_t idx) const { return records_[idx].data; }
136139
// Returns the execution metadata for the element `idx`, `idx` < NumActive().

centipede/corpus_test.cc

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ TEST(Corpus, GetCmpData) {
4545
ByteArray cmp_data{2, 0, 1, 2, 3};
4646
FeatureVec features1 = {10, 20, 30};
4747
fs.MergeFeatures(features1);
48-
corpus.Add({1}, features1, /*metadata=*/{cmp_data}, fs, coverage_frontier);
48+
corpus.Add({1}, features1, /*metadata=*/{cmp_data}, /*stats=*/{}, fs,
49+
coverage_frontier);
4950
EXPECT_EQ(corpus.NumActive(), 1);
5051
EXPECT_EQ(corpus.GetMetadata(0).cmp_data, cmp_data);
5152
}
@@ -61,9 +62,9 @@ TEST(Corpus, PrintStats) {
6162
FeatureVec features1 = {10, 20, 30};
6263
FeatureVec features2 = {20, 40};
6364
fs.MergeFeatures(features1);
64-
corpus.Add({1, 2, 3}, features1, {}, fs, coverage_frontier);
65+
corpus.Add({1, 2, 3}, features1, {}, /*stats=*/{}, fs, coverage_frontier);
6566
fs.MergeFeatures(features2);
66-
corpus.Add({4, 5}, features2, {}, fs, coverage_frontier);
67+
corpus.Add({4, 5}, features2, {}, /*stats=*/{}, fs, coverage_frontier);
6768
const std::string stats_filepath = test_tmpdir / "corpus.txt";
6869
corpus.DumpStatsToFile(fs, stats_filepath, "Test corpus");
6970
std::string stats_file_contents;
@@ -93,7 +94,8 @@ TEST(Corpus, Prune) {
9394

9495
auto Add = [&](const CorpusRecord& record) {
9596
fs.MergeFeatures(record.features);
96-
corpus.Add(record.data, record.features, {}, fs, coverage_frontier);
97+
corpus.Add(record.data, record.features, {}, /*stats=*/{}, fs,
98+
coverage_frontier);
9799
};
98100

99101
auto VerifyActiveInputs = [&](std::vector<ByteArray> expected_inputs) {
@@ -152,7 +154,8 @@ TEST(Corpus, PruneRegressionTest1) {
152154

153155
auto Add = [&](const CorpusRecord& record) {
154156
fs.MergeFeatures(record.features);
155-
corpus.Add(record.data, record.features, {}, fs, coverage_frontier);
157+
corpus.Add(record.data, record.features, {}, /*stats=*/{}, fs,
158+
coverage_frontier);
156159
};
157160

158161
Add({{1}, {10, 20}});
@@ -172,20 +175,19 @@ TEST(WeightedDistribution, WeightedDistribution) {
172175
}
173176
};
174177

178+
Rng rng(12345);
175179
auto compute_freq = [&]() {
176180
freq.clear();
177181
freq.resize(wd.size());
178-
// We use numbers in [0, kNumIter) instead of random numbers
179-
// for simplicity.
180182
for (int i = 0; i < kNumIter; i++) {
181-
freq[wd.RandomIndex(i)]++;
183+
freq[wd.RandomIndex(rng)]++;
182184
}
183185
};
184186

185187
set_weights({1, 1});
186188
compute_freq();
187-
EXPECT_EQ(freq[0], kNumIter / 2);
188-
EXPECT_EQ(freq[1], kNumIter / 2);
189+
EXPECT_NEAR(freq[0], kNumIter / 2, 100);
190+
EXPECT_NEAR(freq[1], kNumIter / 2, 100);
189191

190192
set_weights({1, 2});
191193
compute_freq();
@@ -325,7 +327,7 @@ TEST(CoverageFrontier, Compute) {
325327

326328
auto Add = [&](feature_t feature) {
327329
fs.MergeFeatures({feature});
328-
corpus.Add({42}, {feature}, {}, fs, frontier);
330+
corpus.Add({42}, {feature}, {}, /*stats=*/{}, fs, frontier);
329331
};
330332

331333
// Add PC-based features.

centipede/util.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,13 @@ template <typename T>
121121
void RemoveSubset(const std::vector<size_t> &subset_indices,
122122
std::vector<T> &set) {
123123
size_t pos_to_write = 0;
124-
for (size_t i = 0, n = set.size(); i < n; i++) {
125-
// If subset_indices.size() is k, this loop's complexity is O(n*log(k)).
126-
// We can do it in O(n+k) with a bit more code, but this loop is not
127-
// expected to be hot. Besides, k would typically be small.
128-
if (!std::binary_search(subset_indices.begin(), subset_indices.end(), i))
129-
std::swap(set[pos_to_write++], set[i]);
124+
for (size_t i = 0, n = set.size(), j = 0, m = subset_indices.size(); i < n;
125+
i++) {
126+
if (j < m && i == subset_indices[j]) {
127+
++j;
128+
continue;
129+
}
130+
std::swap(set[pos_to_write++], set[i]);
130131
}
131132
set.resize(pos_to_write);
132133
}

0 commit comments

Comments
 (0)