Custom `centipede` for targets that read inputs from file & write execution results to file
This change implements a custom `centipede` binary to exercise target binaries that read a batch of inputs from a file and store their execution results one by one into an output file, as opposed to the regular `centipede`'s data exchange protocol via shared memory and pipes.
PiperOrigin-RevId: 547673267
diff --git a/centipede/batch_fuzz_example/BUILD b/centipede/batch_fuzz_example/BUILD
index 774a6e9..b5f77b1 100644
--- a/centipede/batch_fuzz_example/BUILD
+++ b/centipede/batch_fuzz_example/BUILD
@@ -27,3 +27,26 @@
"@com_google_fuzztest//centipede:centipede_runner_no_main", # build-cleaner:keep
],
)
+
+cc_binary(
+ name = "customized_centipede",
+ srcs = ["customized_centipede.cc"],
+ deps = [
+ "@com_google_absl//absl/base:log_severity",
+ "@com_google_absl//absl/flags:parse",
+ "@com_google_absl//absl/log",
+ "@com_google_absl//absl/log:check",
+ "@com_google_absl//absl/log:globals",
+ "@com_google_absl//absl/log:initialize",
+ "@com_google_absl//absl/strings",
+ "@com_google_fuzztest//centipede:centipede_callbacks",
+ "@com_google_fuzztest//centipede:centipede_interface",
+ "@com_google_fuzztest//centipede:command",
+ "@com_google_fuzztest//centipede:config_file",
+ "@com_google_fuzztest//centipede:defs",
+ "@com_google_fuzztest//centipede:environment",
+ "@com_google_fuzztest//centipede:execution_result",
+ "@com_google_fuzztest//centipede:shared_memory_blob_sequence",
+ "@com_google_fuzztest//centipede:util",
+ ],
+)
diff --git a/centipede/batch_fuzz_example/customized_centipede.cc b/centipede/batch_fuzz_example/customized_centipede.cc
new file mode 100644
index 0000000..a686396
--- /dev/null
+++ b/centipede/batch_fuzz_example/customized_centipede.cc
@@ -0,0 +1,141 @@
+// Copyright 2023 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 <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#include "absl/base/log_severity.h"
+#include "absl/flags/parse.h"
+#include "absl/log/check.h"
+#include "absl/log/globals.h"
+#include "absl/log/initialize.h"
+#include "absl/log/log.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/string_view.h"
+#include "./centipede/centipede_callbacks.h"
+#include "./centipede/centipede_interface.h"
+#include "./centipede/command.h"
+#include "./centipede/config_file.h"
+#include "./centipede/defs.h"
+#include "./centipede/environment.h"
+#include "./centipede/execution_result.h"
+#include "./centipede/shared_memory_blob_sequence.h"
+#include "./centipede/util.h"
+
+namespace centipede {
+namespace {
+
+bool UpdateBatchResult(absl::string_view output_file,
+ BatchResult& batch_result) {
+ ByteArray content;
+ ReadFromLocalFile(output_file, content);
+ if (content.empty()) {
+ LOG(WARNING) << "Skip updating batch result with an emtpy output file: "
+ << output_file;
+ return true;
+ }
+
+ BlobSequence blob_seq(content.data(), content.size());
+ if (batch_result.Read(blob_seq)) return true;
+
+ LOG(ERROR) << "Failed to read blob sequence from file: " << output_file;
+ return false;
+}
+
+// This class implements the `Execute()` method of the `CentipedeCallbacks`
+// class. It saves a collection of inputs into files and passes them to a target
+// binary. The binary should exercise them in a batch and store the execution
+// result of each input into an output file. Those execution results will be
+// loaded from the output file and packed as the given `batch_result`.
+class CustomizedCallbacks : public CentipedeCallbacks {
+ public:
+ explicit CustomizedCallbacks(const Environment& env)
+ : CentipedeCallbacks(env) {}
+
+ bool Execute(std::string_view binary, const std::vector<ByteArray>& inputs,
+ BatchResult& batch_result) override {
+ const std::string temp_dir = TemporaryLocalDirPath();
+ CHECK(!temp_dir.empty());
+ std::filesystem::create_directory(temp_dir);
+
+ std::string input_file_list;
+ int index = 0;
+ for (const auto& input : inputs) {
+ const std::string temp_file_path = std::filesystem::path(temp_dir).append(
+ absl::StrCat("input-", index++));
+ WriteToLocalFile(temp_file_path, input);
+ absl::StrAppend(&input_file_list, temp_file_path);
+ absl::StrAppend(&input_file_list, "\n");
+ }
+ const std::string input_list_filepath =
+ std::filesystem::path(temp_dir).append("input_file_list");
+ WriteToLocalFile(input_list_filepath, input_file_list);
+
+ const std::string tmp_output_filepath =
+ std::filesystem::path(temp_dir).append("output_execution_results");
+ const std::string tmp_log_filepath =
+ std::filesystem::path(temp_dir).append("tmp_log");
+
+ // Execute.
+ Command cmd{env_.binary,
+ {input_list_filepath, tmp_output_filepath},
+ // TODO: pass additional runner flags, such as use_cmp_features,
+ // based on `env`. Will require a small refactoring.
+ /*env=*/{},
+ tmp_log_filepath,
+ tmp_log_filepath};
+ const int retval = cmd.Execute();
+
+ std::string tmp_log;
+ ReadFromLocalFile(tmp_log_filepath, tmp_log);
+ LOG(INFO) << tmp_log;
+
+ batch_result.ClearAndResize(inputs.size());
+ CHECK(UpdateBatchResult(tmp_output_filepath, batch_result));
+ return retval == 0;
+ }
+};
+} // namespace
+} // namespace centipede
+
+int main(int argc, char** argv) {
+ // TODO: Refactoring the following code into a utility function that takes
+ // callbacks_factory as an argument.
+ const centipede::config::MainRuntimeInit runtime_init =
+ [](int argc, char** argv) -> std::vector<std::string> {
+ // NB: The invocation order is important here.
+ // By default, log everything to stderr. Explicit --stderrthreshold=N on the
+ // command line takes precedence.
+ absl::SetStderrThreshold(absl::LogSeverityAtLeast::kInfo);
+ // Perform the initial command line parsing.
+ std::vector<std::string> leftover_argv =
+ centipede::config::CastArgv(absl::ParseCommandLine(argc, argv));
+ // Initialize the logging subsystem.
+ absl::InitializeLog();
+ return leftover_argv;
+ };
+
+ // Resolve any possible config-related flags in the command line and reparse
+ // it if any augmentations had to be made.
+ const auto leftover_argv =
+ centipede::config::InitCentipede(argc, argv, runtime_init);
+
+ // Reads flags; must happen after ParseCommandLine().
+ centipede::Environment env{leftover_argv};
+ centipede::DefaultCallbacksFactory<centipede::CustomizedCallbacks>
+ callbacks_factory;
+ return CentipedeMain(env, callbacks_factory);
+}