blob: 7b888895910b39173136e0b957a5decc735f96d5 [file] [log] [blame]
// Copyright 2022 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 <cstdint>
#include <cstdlib>
#include <filesystem> // NOLINT
#include <string>
#include "absl/base/nullability.h"
#include "absl/flags/flag.h"
#include "absl/status/status.h"
#include "absl/strings/str_format.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "./centipede/config_init.h"
#include "./centipede/rusage_profiler.h"
#include "./common/blob_file.h"
#include "./common/defs.h"
#include "./common/logging.h"
#include "./common/remote_file.h"
ABSL_FLAG(std::string, in, "", "Input path");
ABSL_FLAG(std::string, out, "", "Output path");
ABSL_FLAG(std::string, out_format, "riegeli", "--out format (legacy|riegeli)");
namespace fuzztest::internal {
namespace {
// TODO(ussuri): Pare down excessive rusage profiling after breaking in.
class StatsLogger {
public:
StatsLogger(absl::Duration log_every, RUsageProfiler& rprof)
: log_every_(log_every),
next_log_at_(start_ + log_every),
rprof_(rprof) {}
void UpdateStats(ByteSpan blob) {
++num_blobs_;
num_bytes_ += blob.size();
}
void Log() {
RPROF_THIS_FUNCTION_BY_EXISTING_RPROF(rprof_);
const auto secs = absl::ToDoubleSeconds(absl::Now() - start_);
const std::string stats = absl::StrFormat(
"blobs: %9lld | blobs/s: %5.0f | bytes: %12lld | bytes/s: %8.0f",
num_blobs_, num_blobs_ / secs, num_bytes_, num_bytes_ / secs);
if (FUZZTEST_VLOG_IS_ON(3)) {
const RUsageProfiler::Snapshot& snapshot = RPROF_SNAPSHOT(stats);
FUZZTEST_LOG(INFO) << stats << " | " << snapshot.memory.ShortStr();
} else {
FUZZTEST_LOG(INFO) << stats;
}
}
void MaybeLogIfTime() {
const auto now = absl::Now();
if (now >= next_log_at_) {
Log();
next_log_at_ += log_every_;
if (next_log_at_ < now) next_log_at_ = now + log_every_;
}
}
private:
int64_t num_blobs_ = 0;
int64_t num_bytes_ = 0;
const absl::Time start_ = absl::Now();
const absl::Duration log_every_;
absl::Time next_log_at_;
RUsageProfiler& rprof_;
};
void Convert( //
const std::string& in, //
const std::string& out, const std::string& out_format) {
RPROF_THIS_FUNCTION_WITH_REPORT(/*enable=*/FUZZTEST_VLOG_IS_ON(1));
FUZZTEST_LOG(INFO) << "Converting:\n"
<< VV(in) << "\n"
<< VV(out) << VV(out_format);
const bool out_is_riegeli = out_format == "riegeli";
// Verify and prepare source and destination.
FUZZTEST_CHECK(RemotePathExists(in)) << VV(in);
FUZZTEST_CHECK_OK(
RemoteMkdir(std::filesystem::path{out}.parent_path().c_str()));
// Open blob file reader and writer.
RPROF_START_TIMELAPSE( //
absl::Seconds(20), /*also_log=*/FUZZTEST_VLOG_IS_ON(3), "Opening --in");
const auto in_reader = DefaultBlobFileReaderFactory();
FUZZTEST_CHECK_OK(in_reader->Open(in)) << VV(in);
RPROF_STOP_TIMELAPSE();
RPROF_SNAPSHOT_AND_LOG("Opened --in; opening --out");
const auto out_writer = DefaultBlobFileWriterFactory(out_is_riegeli);
FUZZTEST_CHECK_OK(out_writer->Open(out, "w")) << VV(out);
RPROF_SNAPSHOT_AND_LOG("Opened --out");
// Read and write blobs one-by-one.
ByteSpan blob;
absl::Status read_status = absl::OkStatus();
StatsLogger stats_logger{
absl::Seconds(FUZZTEST_VLOG_IS_ON(1) ? 20 : 60),
FUNCTION_LEVEL_RPROF_NAME,
};
while ((read_status = in_reader->Read(blob)).ok()) {
FUZZTEST_CHECK_OK(out_writer->Write(blob));
stats_logger.UpdateStats(blob);
stats_logger.MaybeLogIfTime();
}
stats_logger.Log();
FUZZTEST_CHECK(read_status.ok() || absl::IsOutOfRange(read_status))
<< VV(read_status);
FUZZTEST_CHECK_OK(out_writer->Close()) << VV(out);
}
} // namespace
} // namespace fuzztest::internal
int main(int argc, char** absl_nonnull argv) {
(void)fuzztest::internal::InitRuntime(argc, argv);
const std::string in = absl::GetFlag(FLAGS_in);
FUZZTEST_QCHECK(!in.empty());
const std::string out = absl::GetFlag(FLAGS_out);
FUZZTEST_QCHECK(!out.empty());
const std::string out_format = absl::GetFlag(FLAGS_out_format);
FUZZTEST_QCHECK(out_format == "legacy" || out_format == "riegeli")
<< VV(out_format);
fuzztest::internal::Convert(in, out, out_format);
return EXIT_SUCCESS;
}