No public description PiperOrigin-RevId: 994746222
diff --git a/centipede/BUILD b/centipede/BUILD index ca7783e..e546515 100644 --- a/centipede/BUILD +++ b/centipede/BUILD
@@ -1506,6 +1506,9 @@ ":rusage_profiler", ":rusage_stats", "@abseil-cpp//absl/flags:flag", + "@abseil-cpp//absl/strings", + "@abseil-cpp//absl/strings:string_view", + "@abseil-cpp//absl/synchronization", "@abseil-cpp//absl/time", "@com_google_fuzztest//common:logging", "@googletest//:gtest_main",
diff --git a/centipede/rusage_profiler.cc b/centipede/rusage_profiler.cc index 540855b..465fbfa 100644 --- a/centipede/rusage_profiler.cc +++ b/centipede/rusage_profiler.cc
@@ -160,13 +160,13 @@ // GenChartImpl() wrappers for the 2 available "snap" metrics. template <typename MetricT> - void GenChart(const MetricT RUsageTiming::*metric_field) { + void GenChart(const MetricT RUsageTiming::* metric_field) { GenChartImpl( // &RUsageProfiler::Snapshot::timing, metric_field, // timing_low_, timing_high_, /*is_delta=*/false); } template <typename MetricT> - void GenChart(const MetricT RUsageMemory::*metric_field) const { + void GenChart(const MetricT RUsageMemory::* metric_field) const { GenChartImpl( // &RUsageProfiler::Snapshot::memory, metric_field, // memory_low_, memory_high_, /*is_delta=*/false); @@ -174,13 +174,13 @@ // GenChartImpl() wrappers for the 2 available delta metrics. template <typename MetricT> - void GenDeltaChart(const MetricT RUsageTiming::*metric_field) { + void GenDeltaChart(const MetricT RUsageTiming::* metric_field) { GenChartImpl( // &RUsageProfiler::Snapshot::delta_timing, metric_field, // delta_timing_low_, delta_timing_high_, /*is_delta=*/true); } template <typename MetricT> - void GenDeltaChart(const MetricT RUsageMemory::*metric_field) const { + void GenDeltaChart(const MetricT RUsageMemory::* metric_field) const { GenChartImpl( // &RUsageProfiler::Snapshot::delta_memory, metric_field, // delta_memory_low_, delta_memory_high_, /*is_delta=*/true); @@ -192,11 +192,11 @@ // which has type `RUsageTiming`; an example of a matching `submetric_field` // for that is `&RUsageTiming::wall_time`. template <typename MetricT, typename SubmetricT> - void GenChartImpl( // - const MetricT RUsageProfiler::Snapshot::*metric_field, // - const SubmetricT MetricT::*submetric_field, // - MetricT metric_low_water, // - MetricT metric_high_water, // + void GenChartImpl( // + const MetricT RUsageProfiler::Snapshot::* metric_field, // + const SubmetricT MetricT::* submetric_field, // + MetricT metric_low_water, // + MetricT metric_high_water, // bool is_delta) const { constexpr SubmetricT kZero{}; // works for both ints and absl::Duration const SubmetricT low_water = metric_low_water.*submetric_field; @@ -211,7 +211,9 @@ const int notch_zero = notch_size == kZero ? kBarNotches : low_water >= kZero ? 0 : - std::floor(std::abs(low_water / notch_size)); + high_water <= kZero ? kBarNotches : + std::min<int>( // + kBarNotches, std::floor(std::abs(low_water / notch_size))); // clang-format on FUZZTEST_CHECK_GE(kBarNotches, notch_zero); // Print a zero mark only if a delta metric goes negative.
diff --git a/centipede/rusage_profiler.h b/centipede/rusage_profiler.h index 1fcd0c7..215d267 100644 --- a/centipede/rusage_profiler.h +++ b/centipede/rusage_profiler.h
@@ -406,6 +406,7 @@ private: friend class RUsageProfilerTest_ValidateManualSnapshots_Test; + friend class RUsageProfilerTest_ValidateReportWithAllNegativeDeltas_Test; //---------------------------------------------------------------------------- // Data
diff --git a/centipede/rusage_profiler_test.cc b/centipede/rusage_profiler_test.cc index 1ad46eb..db25d67 100644 --- a/centipede/rusage_profiler_test.cc +++ b/centipede/rusage_profiler_test.cc
@@ -21,8 +21,12 @@ #include <string> #include <string_view> +#include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/flags/flag.h" +#include "absl/strings/match.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" #include "absl/time/clock.h" #include "absl/time/time.h" #include "./centipede/rusage_stats.h" @@ -245,6 +249,122 @@ rprof.GenerateReport(&report_capture); } +// Regression test: when all delta metrics are negative or non-positive +// (`high_water <= kZero`), `GenerateReport()` must not trigger a CHECK failure +// in `GenChartImpl()` while calculating `notch_zero`. +TEST(RUsageProfilerTest, ValidateReportWithAllNegativeDeltas) { + RUsageProfiler rprof{ + RUsageScope::ThisProcess(), + RUsageProfiler::kAllMetrics, + RUsageProfiler::kRaiiOff, + {__FILE__, __LINE__}, + }; + + // Add snapshots whose delta metrics are all strictly negative. + const RUsageTiming delta_timing1{ + /*wall_time=*/-absl::Seconds(5), + /*user_time=*/-absl::Seconds(2), + /*sys_time=*/-absl::Seconds(1), + /*cpu_utilization=*/-0.4, + /*cpu_hyper_cores=*/-1.0, + /*is_delta=*/true, + }; + const RUsageMemory delta_memory1{ + /*mem_vsize=*/-100'000'000, + /*mem_vpeak=*/-100'000'000, + /*mem_rss=*/-50'000'000, + /*mem_data=*/-40'000'000, + /*mem_shared=*/-10'000'000, + /*is_delta=*/true, + }; + + const RUsageTiming delta_timing2{ + /*wall_time=*/-absl::Seconds(1), + /*user_time=*/-absl::Seconds(1), + /*sys_time=*/-absl::Milliseconds(500), + /*cpu_utilization=*/-0.1, + /*cpu_hyper_cores=*/-0.2, + /*is_delta=*/true, + }; + const RUsageMemory delta_memory2{ + /*mem_vsize=*/-40'000'000, + /*mem_vpeak=*/-40'000'000, + /*mem_rss=*/-20'000'000, + /*mem_data=*/-10'000'000, + /*mem_shared=*/-5'000'000, + /*is_delta=*/true, + }; + + const absl::Time now = absl::Now(); + { + absl::MutexLock lock{rprof.snapshots_mutex_}; + rprof.snapshots_.push_back(RUsageProfiler::Snapshot{ + /*id=*/0, + /*title=*/"Snap 0", + /*location=*/SourceLocation{__FILE__, __LINE__}, + /*time=*/now, + /*profiler_id=*/rprof.id_, + /*profiler_desc=*/"", + /*timing=*/RUsageTiming::Zero(), + /*delta_timing=*/delta_timing1, + /*memory=*/RUsageMemory::Zero(), + /*delta_memory=*/delta_memory1, + }); + rprof.snapshots_.push_back(RUsageProfiler::Snapshot{ + /*id=*/1, + /*title=*/"Snap 1", + /*location=*/SourceLocation{__FILE__, __LINE__}, + /*time=*/now + absl::Seconds(1), + /*profiler_id=*/rprof.id_, + /*profiler_desc=*/"", + /*timing=*/RUsageTiming::Zero(), + /*delta_timing=*/delta_timing2, + /*memory=*/RUsageMemory::Zero(), + /*delta_memory=*/delta_memory2, + }); + } + + // All-negative deltas place the zero mark at the right edge of the bar, + // whereas non-delta metrics have no zero mark. + class ReportCapture : public RUsageProfiler::ReportSink { + public: + ~ReportCapture() override = default; + + ReportCapture& operator<<(std::string_view fragment) override { + if (absl::StrContains(fragment, "=== Δ ")) { + in_delta_section_ = true; + } else if (absl::StrContains(fragment, "=== ")) { + in_delta_section_ = false; + } + + if (absl::StrContains(fragment, ":S.")) { + if (in_delta_section_) { + ++delta_bars_count_; + EXPECT_THAT(fragment, testing::HasSubstr("|]")) << VV(fragment); + } else { + ++non_delta_bars_count_; + EXPECT_THAT(fragment, testing::Not(testing::HasSubstr("|]"))) + << VV(fragment); + } + } + return *this; + } + + int delta_bars_count() const { return delta_bars_count_; } + int non_delta_bars_count() const { return non_delta_bars_count_; } + + private: + bool in_delta_section_ = false; + int delta_bars_count_ = 0; + int non_delta_bars_count_ = 0; + }; + + ReportCapture report_capture{}; + rprof.GenerateReport(&report_capture); + EXPECT_EQ(report_capture.delta_bars_count(), 20); + EXPECT_EQ(report_capture.non_delta_bars_count(), 20); +} + TEST(RUsageProfilerTest, DeadlockReproduction) { const auto rusage_scope = RUsageScope::ThisProcess(); for (int i = 0; i < 1000; ++i) {