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) {