Skip to content

Commit

Permalink
No public description
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 718872113
  • Loading branch information
xinhaoyuan authored and copybara-github committed Jan 24, 2025
1 parent eddb5c2 commit ac46805
Show file tree
Hide file tree
Showing 25 changed files with 1,065 additions and 523 deletions.
2 changes: 2 additions & 0 deletions centipede/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -824,6 +824,7 @@ cc_library(
":util",
":workdir",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/cleanup",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/log",
"@com_google_absl//absl/log:check",
Expand All @@ -833,6 +834,7 @@ cc_library(
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/time",
"@com_google_absl//absl/types:span",
"@com_google_fuzztest//common:bazel",
"@com_google_fuzztest//common:blob_file",
"@com_google_fuzztest//common:defs",
"@com_google_fuzztest//common:hash",
Expand Down
4 changes: 3 additions & 1 deletion centipede/centipede.cc
Original file line number Diff line number Diff line change
Expand Up @@ -811,7 +811,9 @@ void Centipede::ReportCrash(std::string_view binary,
const std::vector<ByteArray> &input_vec,
const BatchResult &batch_result) {
CHECK_EQ(input_vec.size(), batch_result.results().size());
if (ShouldStop()) return;
// Skip reporting only if RequestEarlyStop is called with a failure exit code.
// Still report if time runs out.
if (ShouldStop() && ExitCode() != 0) return;

if (++num_crashes_ > env_.max_num_crash_reports) return;

Expand Down
5 changes: 5 additions & 0 deletions centipede/centipede_callbacks.cc
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,11 @@ bool CentipedeCallbacks::GetSeedsViaExternalBinary(
.temp_file_path = temp_input_file_path_}};
const int retval = cmd.Execute();

if (env_.print_runner_log) {
LOG(INFO) << "Getting seeds via external binary returns " << retval;
PrintExecutionLog();
}

std::vector<std::string> seed_input_filenames;
for (const auto &dir_ent : std::filesystem::directory_iterator(output_dir)) {
seed_input_filenames.push_back(dir_ent.path().filename());
Expand Down
316 changes: 161 additions & 155 deletions centipede/centipede_interface.cc

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions centipede/environment.cc
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,8 @@ void Environment::UpdateWithTargetConfig(
// Update `timeout_per_input` and consequently `timeout_per_batch`.
const size_t time_limit_per_input_sec =
convert_to_seconds(config.time_limit_per_input, "Time limit per input");
CHECK(timeout_per_input == Default().timeout_per_input ||
CHECK(timeout_per_input == 0 ||
timeout_per_input == Default().timeout_per_input ||
timeout_per_input == time_limit_per_input_sec)
<< "Value for --timeout_per_input is inconsistent with the value for "
"time_limit_per_input in the target binary:"
Expand Down Expand Up @@ -304,7 +305,7 @@ void Environment::UpdateWithTargetConfig(
<< VV(stack_limit_kb) << VV(config.stack_limit);
stack_limit_kb = bytes_to_kb(config.stack_limit);

if (config.only_replay_corpus) {
if (config.only_replay) {
load_shards_only = true;
populate_binary_info = false;
}
Expand Down
3 changes: 3 additions & 0 deletions centipede/environment.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ struct Environment {
bool first_corpus_dir_output_only = false;
// If set, load/merge shards without fuzzing new inputs.
bool load_shards_only = false;
// If set, operate on the corpus database for a single test specified by
// FuzzTest instead of all the tests.
bool fuzztest_single_test_mode = false;

// Command line-related fields -----------------------------------------------

Expand Down
2 changes: 1 addition & 1 deletion centipede/environment_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ TEST(Environment, DiesOnInconsistentStackLimitKbAndTargetConfigStackLimit) {

TEST(Environment, UpdatesReplayOnlyConfiguration) {
Environment env;
fuzztest::internal::Configuration config{.only_replay_corpus = true};
fuzztest::internal::Configuration config{.only_replay = true};
env.UpdateWithTargetConfig(config);
EXPECT_TRUE(env.load_shards_only);
EXPECT_FALSE(env.populate_binary_info);
Expand Down
12 changes: 12 additions & 0 deletions common/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ exports_files(

### Libraries

cc_library(
name = "bazel",
srcs = ["bazel.cc"],
hdrs = ["bazel.h"],
deps = [
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/status",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/time",
],
)

cc_library(
name = "blob_file",
srcs = ["blob_file.cc"],
Expand Down
14 changes: 14 additions & 0 deletions common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@ endif()

### Libraries

fuzztest_cc_library(
NAME
bazel
HDRS
"bazel.h"
SRCS
"bazel.cc"
DEPS
absl::check
absl::strings
absl::status
absl::time
)

fuzztest_cc_library(
NAME
blob_file
Expand Down
120 changes: 120 additions & 0 deletions common/bazel.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// Copyright 2024 The Centipede Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "./common/bazel.h"

#include <algorithm>
#include <cstdlib>
#include <string>

#include "absl/log/check.h"
#include "absl/status/status.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_cat.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"

namespace centipede {

namespace {

absl::Duration GetBazelTestTimeout() {
const char *test_timeout_env = std::getenv("TEST_TIMEOUT");
if (test_timeout_env == nullptr) return absl::InfiniteDuration();
int timeout_s = 0;
CHECK(absl::SimpleAtoi(test_timeout_env, &timeout_s))
<< "Failed to parse TEST_TIMEOUT: \"" << test_timeout_env << "\"";
return absl::Seconds(timeout_s);
}

} // namespace

const TestShard &GetBazelTestShard() {
static TestShard cached_test_shard = [] {
TestShard test_shard;
if (const char *test_total_shards_env = std::getenv("TEST_TOTAL_SHARDS");
test_total_shards_env != nullptr) {
CHECK(absl::SimpleAtoi(test_total_shards_env, &test_shard.total_shards))
<< "Failed to parse TEST_TOTAL_SHARDS as an integer: \""
<< test_total_shards_env << "\"";
CHECK_GT(test_shard.total_shards, 0)
<< "TEST_TOTAL_SHARDS must be greater than 0.";
}
if (const char *test_shard_index_env = std::getenv("TEST_SHARD_INDEX");
test_shard_index_env != nullptr) {
CHECK(absl::SimpleAtoi(test_shard_index_env, &test_shard.index))
<< "Failed to parse TEST_SHARD_INDEX as an integer: \""
<< test_shard_index_env << "\"";
CHECK(0 <= test_shard.index && test_shard.index < test_shard.total_shards)
<< "TEST_SHARD_INDEX must be in the range [0, "
<< test_shard.total_shards << ").";
}
return test_shard;
}();
return cached_test_shard;
}

absl::Status CheckBazelHasEnoughTimeToRunTest(absl::Time target_start_time,
absl::Duration test_time_limit,
int executed_tests_in_shard,
int fuzz_test_count) {
static const absl::Duration bazel_test_timeout = GetBazelTestTimeout();
const int shard_count = GetBazelTestShard().total_shards;
constexpr float kTimeoutSafetyFactor = 1.2;
const auto required_test_time = kTimeoutSafetyFactor * test_time_limit;
const auto remaining_duration =
bazel_test_timeout - (absl::Now() - target_start_time);
if (required_test_time <= remaining_duration) return absl::OkStatus();
std::string error =
"Cannot fuzz a fuzz test within the given timeout. Please ";
if (executed_tests_in_shard == 0) {
// Increasing number of shards won't help.
const absl::Duration suggested_timeout =
required_test_time * ((fuzz_test_count - 1) / shard_count + 1);
absl::StrAppend(&error, "set the `timeout` to ", suggested_timeout,
" or reduce the fuzzing time, ");
} else {
constexpr int kMaxShardCount = 50;
const int suggested_shard_count = std::min(
(fuzz_test_count - 1) / executed_tests_in_shard + 1, kMaxShardCount);
const int suggested_tests_per_shard =
(fuzz_test_count - 1) / suggested_shard_count + 1;
if (suggested_tests_per_shard > executed_tests_in_shard) {
// We wouldn't be able to execute the suggested number of tests without
// timeout. This case can only happen if we would in fact need more than
// `kMaxShardCount` shards, indicating that there are simply too many fuzz
// tests in a binary.
CHECK_EQ(suggested_shard_count, kMaxShardCount);
absl::StrAppend(&error,
"split the fuzz tests into several test binaries where "
"each binary has at most ",
executed_tests_in_shard * kMaxShardCount, "tests ",
"with `shard_count` = ", kMaxShardCount, ", ");
} else {
// In this case, `suggested_shard_count` must be greater than
// `shard_count`, otherwise we would have already executed all the tests
// without a timeout.
CHECK_GT(suggested_shard_count, shard_count);
absl::StrAppend(&error, "increase the `shard_count` to ",
suggested_shard_count, ", ");
}
}
absl::StrAppend(&error, "to avoid this issue. ");
absl::StrAppend(&error,
"(https://bazel.build/reference/be/"
"common-definitions#common-attributes-tests)");
return absl::ResourceExhaustedError(error);
}

} // namespace centipede
44 changes: 44 additions & 0 deletions common/bazel.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright 2024 The Centipede Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef FUZZTEST_COMMON_BAZEL_H_
#define FUZZTEST_COMMON_BAZEL_H_

#include "absl/status/status.h"
#include "absl/time/time.h"

namespace centipede {

struct TestShard {
int index = 0;
int total_shards = 1;
};

// Get Bazel sharding information based on //
// https://bazel.build/reference/test-encyclopedia#initial-conditions
const TestShard& GetBazelTestShard();

// Checks and returns Ok if there is enough time left to run the current test
// for `test_time_limit` given `target_start_time` and timeout and sharding
// information from Bazel, or returns an error status otherwise. It uses
// `executed_tests_in_shard` and `fuzz_test_count` to include suggestions into
// the error status message.
absl::Status CheckBazelHasEnoughTimeToRunTest(absl::Time target_start_time,
absl::Duration test_time_limit,
int executed_tests_in_shard,
int fuzz_test_count);

} // namespace centipede

#endif // FUZZTEST_COMMON_BAZEL_H_
2 changes: 2 additions & 0 deletions e2e_tests/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ cc_binary(
# Must be run with `--config=fuzztest-experimental --config=asan`.
cc_test(
name = "corpus_database_test",
size = "large",
srcs = ["corpus_database_test.cc"],
data = [
"@com_google_fuzztest//centipede:centipede_uninstrumented",
Expand All @@ -125,6 +126,7 @@ cc_test(
"@com_google_absl//absl/time",
"@com_google_fuzztest//centipede:weak_sancov_stubs",
"@com_google_fuzztest//fuzztest:io",
"@com_google_fuzztest//fuzztest:logging",
"@com_google_fuzztest//fuzztest:subprocess",
"@com_google_googletest//:gtest_main",
],
Expand Down
Loading

0 comments on commit ac46805

Please sign in to comment.