Add the ability to set an external crash reporter for crash summaries. PiperOrigin-RevId: 777628969
diff --git a/centipede/BUILD b/centipede/BUILD index c43809e..cbc0df6 100644 --- a/centipede/BUILD +++ b/centipede/BUILD
@@ -847,7 +847,9 @@ "@com_google_fuzztest//common:remote_file", "@com_google_fuzztest//common:status_macros", "@com_google_fuzztest//fuzztest/internal:configuration", - ], + ] + select({ + "//conditions:default": [], + }), ) cc_library( @@ -1858,6 +1860,7 @@ srcs = ["crash_summary_test.cc"], deps = [ ":crash_summary", + "@abseil-cpp//absl/log:check", "@googletest//:gtest_main", ], )
diff --git a/centipede/crash_summary.cc b/centipede/crash_summary.cc index 8dbf313..6be9368 100644 --- a/centipede/crash_summary.cc +++ b/centipede/crash_summary.cc
@@ -21,12 +21,20 @@ #include "./common/defs.h" namespace fuzztest::internal { +namespace { + +ExternalCrashReporter external_crash_reporter = nullptr; + +} // namespace void CrashSummary::AddCrash(Crash crash) { crashes_.push_back(std::move(crash)); } void CrashSummary::Report(absl::FormatRawSink sink) const { + if (external_crash_reporter != nullptr) { + external_crash_reporter(*this); + } absl::Format(sink, "=== Summary of detected crashes ===\n\n"); absl::Format(sink, "Binary ID : %s\n", binary_id()); absl::Format(sink, "Fuzz test : %s\n", fuzz_test()); @@ -43,4 +51,8 @@ absl::Format(sink, "=== End of summary of detected crashes ===\n\n"); } +void SetExternalCrashReporter(ExternalCrashReporter reporter) { + external_crash_reporter = reporter; +} + } // namespace fuzztest::internal
diff --git a/centipede/crash_summary.h b/centipede/crash_summary.h index b5c631d..067ed36 100644 --- a/centipede/crash_summary.h +++ b/centipede/crash_summary.h
@@ -54,6 +54,8 @@ void AddCrash(Crash crash); // Reports a summary of the crashes to `sink`. + // If an external crash reporter has been set with `SetExternalCrashReporter`, + // calls it with the stored crashes. void Report(absl::FormatRawSink sink) const; std::string_view binary_id() const { return binary_id_; } @@ -71,6 +73,12 @@ std::vector<Crash> crashes_; }; +using ExternalCrashReporter = void (*)(const CrashSummary&); + +// Sets an external crash reporter that will be called when a `CrashSummary` is +// reported. +void SetExternalCrashReporter(ExternalCrashReporter reporter); + } // namespace fuzztest::internal #endif // FUZZTEST_CENTIPEDE_CRASH_SUMMARY_H_
diff --git a/centipede/crash_summary_test.cc b/centipede/crash_summary_test.cc index 1656a42..891bd5c 100644 --- a/centipede/crash_summary_test.cc +++ b/centipede/crash_summary_test.cc
@@ -19,14 +19,36 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/log/check.h" namespace fuzztest::internal { namespace { using ::testing::AllOf; using ::testing::HasSubstr; +using ::testing::Pointee; -TEST(CrashSummaryTest, ReportPrintsSummary) { +class CrashSummaryTest : public testing::Test { + public: + ~CrashSummaryTest() { + if (dumped_summary_ != nullptr) { + delete dumped_summary_; + dumped_summary_ = nullptr; + } + } + + protected: + static void DumpCrashSummary(const CrashSummary& summary) { + CHECK(dumped_summary_ == nullptr); + dumped_summary_ = new CrashSummary{summary}; + }; + + static CrashSummary* dumped_summary_; +}; + +CrashSummary* CrashSummaryTest::dumped_summary_ = nullptr; + +TEST_F(CrashSummaryTest, ReportPrintsSummary) { CrashSummary summary("binary_id", "fuzz_test"); summary.AddCrash({"id1", "category1", "signature1", "description1"}); summary.AddCrash({"id2", "category2", @@ -50,5 +72,16 @@ HasSubstr("Description: description2"))); } +TEST_F(CrashSummaryTest, ReportCallsExternalCrashReporter) { + CrashSummary summary("binary_id", "fuzz_test"); + summary.AddCrash({"id1", "category1", "signature1", "description1"}); + summary.AddCrash({"id2", "category2", "signature2", "description2"}); + SetExternalCrashReporter(DumpCrashSummary); + std::string output; + summary.Report(&output); + + EXPECT_THAT(dumped_summary_, Pointee(summary)); +} + } // namespace } // namespace fuzztest::internal