blob: ebff9244fe8aacc8b6d46aad8208576d305362e5 [file] [log] [blame]
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/sys/fuzzing/libfuzzer/stats.h"
#include <gtest/gtest.h>
#include "src/lib/files/file.h"
#include "src/lib/fxl/strings/split_string.h"
namespace fuzzing {
using fuchsia::fuzzer::ProcessStats;
TEST(LibFuzzerStatsTest, ParseTestFuzzerStats) {
std::string contents;
ASSERT_TRUE(files::ReadFileToString("/pkg/data/test_fuzzer.stderr", &contents));
auto lines = SplitString(contents, "\n", fxl::kTrimWhitespace, fxl::kSplitWantAll);
Status status;
size_t num_inits = 0;
size_t num_news = 0;
size_t num_reduces = 0;
for (auto line : lines) {
UpdateReason reason;
if (!ParseLibFuzzerStats(line, &reason, &status)) {
continue;
}
// Check lines with unique reasons. Count lines with repeated reasons.
// This log represents an almost immediate crash, and has no non-zero exec/s.
switch (reason) {
case UpdateReason::INIT:
++num_inits;
EXPECT_EQ(status.runs(), 2U);
EXPECT_EQ(status.covered_pcs(), 2U);
EXPECT_EQ(status.covered_features(), 2U);
EXPECT_EQ(status.corpus_num_inputs(), 1U);
EXPECT_EQ(status.corpus_total_size(), 1U);
EXPECT_FALSE(status.has_elapsed());
EXPECT_EQ(status.process_stats()[0].koid, ZX_KOID_INVALID);
EXPECT_EQ(status.process_stats()[0].mem_private_bytes, 35U * kOneMb);
break;
case UpdateReason::NEW:
++num_news;
EXPECT_FALSE(status.has_elapsed());
break;
case UpdateReason::REDUCE:
++num_reduces;
EXPECT_FALSE(status.has_elapsed());
break;
default:
break;
}
}
EXPECT_EQ(num_inits, 1U);
EXPECT_EQ(num_news, 5U);
EXPECT_EQ(num_reduces, 2U);
}
TEST(LibFuzzerStatsTest, ParseCertFuzzerStats) {
std::string contents;
ASSERT_TRUE(files::ReadFileToString("/pkg/data/cert_fuzzer.stderr", &contents));
auto lines = SplitString(contents, "\n", fxl::kTrimWhitespace, fxl::kSplitWantAll);
Status status;
size_t num_inits = 0;
size_t num_news = 0;
size_t num_reduces = 0;
zx_duration_t previous_elapsed = 0;
for (auto line : lines) {
UpdateReason reason;
if (!ParseLibFuzzerStats(line, &reason, &status)) {
continue;
}
// Check lines with unique reasons. Count lines with repeated reasons.
switch (reason) {
case UpdateReason::INIT:
++num_inits;
EXPECT_EQ(status.runs(), 1306U);
EXPECT_EQ(status.covered_pcs(), 2560U);
EXPECT_EQ(status.covered_features(), 9711U);
EXPECT_EQ(status.corpus_num_inputs(), 418U);
EXPECT_EQ(status.corpus_total_size(), 490U * kOneKb);
EXPECT_FALSE(status.has_elapsed());
EXPECT_EQ(status.process_stats()[0].koid, ZX_KOID_INVALID);
EXPECT_EQ(status.process_stats()[0].mem_private_bytes, 83U * kOneMb);
break;
case UpdateReason::NEW:
++num_news;
break;
case UpdateReason::PULSE:
EXPECT_EQ(status.runs(), 8192U);
EXPECT_EQ(status.covered_pcs(), 2564U);
EXPECT_EQ(status.covered_features(), 9760U);
EXPECT_EQ(status.corpus_num_inputs(), 443U);
EXPECT_EQ(status.corpus_total_size(), 533U * kOneKb);
EXPECT_GE(status.elapsed(), 3000732600LL); // (8192 runs * 1e9 s/ns) / (2730 exec/s)
EXPECT_EQ(status.process_stats()[0].koid, ZX_KOID_INVALID);
EXPECT_EQ(status.process_stats()[0].mem_private_bytes, 187U * kOneMb);
break;
case UpdateReason::REDUCE:
++num_reduces;
break;
case UpdateReason::DONE:
EXPECT_EQ(status.runs(), 10000U);
EXPECT_EQ(status.covered_pcs(), 2564U);
EXPECT_EQ(status.covered_features(), 9761U);
EXPECT_EQ(status.corpus_num_inputs(), 444U);
EXPECT_EQ(status.corpus_total_size(), 533 * kOneKb);
EXPECT_GE(status.elapsed(), 3000300030LL); // (10000 runs * 1e9 ns/s) / (3333 exec/ns)
EXPECT_EQ(status.process_stats()[0].koid, ZX_KOID_INVALID);
EXPECT_EQ(status.process_stats()[0].mem_private_bytes, 212U * kOneMb);
break;
default:
ADD_FAILURE() << "Unexpected update reason: " << reason;
break;
}
if (status.has_elapsed()) {
// This log includes non-zero exec/s. `elapsed` should monotonically increase.
EXPECT_GE(status.elapsed(), previous_elapsed);
previous_elapsed = status.elapsed();
}
}
EXPECT_EQ(num_inits, 1U);
EXPECT_EQ(num_news, 26U);
EXPECT_EQ(num_reduces, 12U);
EXPECT_NE(previous_elapsed, 0U);
}
TEST(LibFuzzerStatsTest, Format) {
Status status;
status.set_runs(123); // 41 * 3
status.set_covered_pcs(456);
status.set_covered_features(789);
status.set_corpus_num_inputs(1111);
status.set_corpus_total_size(222 * kOneKb);
status.set_elapsed(41 * kOneSecond);
auto line = FormatLibFuzzerStats(UpdateReason::INIT, status);
EXPECT_EQ(line, "#123\tINITED cov: 456 ft: 789 corp: 1111/222Kb exec/s: 3 rss: 0Mb");
status.set_runs(168); // 42 * 4
status.set_elapsed(42 * kOneSecond);
auto* process_stats = status.mutable_process_stats();
process_stats->resize(10);
line = FormatLibFuzzerStats(UpdateReason::NEW, status);
EXPECT_EQ(line, "#168\tNEW cov: 456 ft: 789 corp: 1111/222Kb exec/s: 4 rss: 0Mb");
status.set_runs(215); // 43 * 5
status.set_elapsed(43 * kOneSecond);
for (size_t i = 0; i < 10; ++i) {
auto& stats = (*process_stats)[i];
stats.mem_mapped_bytes = (i + 10) * kOneMb;
stats.mem_private_bytes = i * kOneMb;
stats.mem_shared_bytes = 10 * kOneMb;
stats.mem_scaled_shared_bytes = kOneMb;
}
line = FormatLibFuzzerStats(UpdateReason::PULSE, status);
EXPECT_EQ(line, "#215\tpulse cov: 456 ft: 789 corp: 1111/222Kb exec/s: 5 rss: 55Mb");
status.set_runs(264); // 44 * 6
status.set_covered_pcs(500);
status.set_covered_features(1000);
status.set_corpus_num_inputs(2048);
status.set_corpus_total_size(1 * kOneMb);
status.set_elapsed(44 * kOneSecond);
line = FormatLibFuzzerStats(UpdateReason::REDUCE, status);
EXPECT_EQ(line, "#264\tREDUCE cov: 500 ft: 1000 corp: 2048/1Mb exec/s: 6 rss: 55Mb");
status.set_runs(315); // 45 * 5
status.set_elapsed(45 * kOneSecond);
(*process_stats)[0].mem_private_bytes += 20 * kOneMb;
line = FormatLibFuzzerStats(UpdateReason::DONE, status);
EXPECT_EQ(line, "#315\tDONE cov: 500 ft: 1000 corp: 2048/1Mb exec/s: 7 rss: 75Mb");
}
TEST(LibFuzzerStatsTest, RoundTrip) {
Status in_status;
in_status.set_runs(11111);
in_status.set_covered_pcs(22);
in_status.set_covered_features(333);
in_status.set_corpus_num_inputs(44);
in_status.set_corpus_total_size(555 * kOneKb);
in_status.set_elapsed(168348484848); // (11111 runs * 1e9 ns/s) / (66 exec/s)
std::vector<ProcessStats> process_stats(1);
process_stats[0].mem_mapped_bytes = 77 * kOneMb;
in_status.set_process_stats(std::move(process_stats));
UpdateReason reasons[] = {
UpdateReason::INIT, UpdateReason::NEW, UpdateReason::PULSE,
UpdateReason::REDUCE, UpdateReason::DONE,
};
for (auto in_reason : reasons) {
auto line = FormatLibFuzzerStats(in_reason, in_status);
Status out_status;
UpdateReason out_reason;
EXPECT_TRUE(ParseLibFuzzerStats(line, &out_reason, &out_status));
EXPECT_EQ(in_reason, out_reason);
EXPECT_EQ(in_status.runs(), out_status.runs());
EXPECT_EQ(in_status.covered_pcs(), out_status.covered_pcs());
EXPECT_EQ(in_status.covered_features(), out_status.covered_features());
EXPECT_EQ(in_status.corpus_num_inputs(), out_status.corpus_num_inputs());
EXPECT_EQ(in_status.corpus_total_size(), out_status.corpus_total_size());
EXPECT_EQ(in_status.elapsed(), out_status.elapsed());
EXPECT_EQ(in_status.process_stats()[0].mem_private_bytes,
out_status.process_stats()[0].mem_private_bytes);
}
}
} // namespace fuzzing