Add histogram macros for microsecond timings

As with the similar macro in Chrome, this one will drop the metric on
clients that do not support high performance counters (HPC). This will
be handled in the implementation of a new virtual function
HistogramCustomCountsHPC.

Bug: dawn:1087
Change-Id: I1ece0226b14f185c8fe2ec6a242f4f483ba4ad6c
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/141227
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Antonio Maiorano <amaiorano@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/include/dawn/platform/DawnPlatform.h b/include/dawn/platform/DawnPlatform.h
index 4d215bc..92aa987 100644
--- a/include/dawn/platform/DawnPlatform.h
+++ b/include/dawn/platform/DawnPlatform.h
@@ -100,6 +100,14 @@
                                        int max,
                                        int bucketCount);
 
+    // Invoked to add a UMA histogram count-based sample that requires high-performance
+    // counter (HPC) support.
+    virtual void HistogramCustomCountsHPC(const char* name,
+                                          int sample,
+                                          int min,
+                                          int max,
+                                          int bucketCount);
+
     // Invoked to add a UMA histogram enumeration sample
     virtual void HistogramEnumeration(const char* name, int sample, int boundaryValue);
 
diff --git a/src/dawn/platform/DawnPlatform.cpp b/src/dawn/platform/DawnPlatform.cpp
index 1c6ce03..7343ed2 100644
--- a/src/dawn/platform/DawnPlatform.cpp
+++ b/src/dawn/platform/DawnPlatform.cpp
@@ -59,6 +59,12 @@
                                      int max,
                                      int bucketCount) {}
 
+void Platform::HistogramCustomCountsHPC(const char* name,
+                                        int sample,
+                                        int min,
+                                        int max,
+                                        int bucketCount) {}
+
 void Platform::HistogramEnumeration(const char* name, int sample, int boundaryValue) {}
 
 void Platform::HistogramSparse(const char* name, int sample) {}
diff --git a/src/dawn/platform/metrics/HistogramMacros.h b/src/dawn/platform/metrics/HistogramMacros.h
index 642a317..2982cf0 100644
--- a/src/dawn/platform/metrics/HistogramMacros.h
+++ b/src/dawn/platform/metrics/HistogramMacros.h
@@ -44,6 +44,11 @@
 #define DAWN_HISTOGRAM_CUSTOM_TIMES(platform, name, sample_ms, min, max, bucket_count) \
     DAWN_HISTOGRAM_CUSTOM_COUNTS(platform, name, sample_ms, min, max, bucket_count)
 
+// Same as DAWN_HISTOGRAM_CUSTOM_TIMES but reports |sample| in microseconds,
+// dropping the report if this client doesn't have a high-resolution clock.
+#define DAWN_HISTOGRAM_CUSTOM_MICROSECOND_TIMES(platform, name, sample_us, min, max, bucket_count) \
+    DAWN_HISTOGRAM_CUSTOM_COUNTS_HPC(platform, name, sample_us, min, max, bucket_count)
+
 //------------------------------------------------------------------------------
 // Count histograms. These are used for collecting numeric data. Note that we
 // have macros for more specialized use cases below (memory, time, percentages).
@@ -80,6 +85,12 @@
 #define DAWN_HISTOGRAM_CUSTOM_COUNTS(platformObj, name, sample, min, max, bucket_count) \
     platformObj->HistogramCustomCounts(name, sample, min, max, bucket_count)
 
+// Same as DAWN_HISTOGRAM_CUSTOM_COUNTS, but the stat will be dropped if the
+// client does not support high-performance counters (HPC). Ueful for logging
+// microsecond-resolution timings.
+#define DAWN_HISTOGRAM_CUSTOM_COUNTS_HPC(platformObj, name, sample, min, max, bucket_count) \
+    platformObj->HistogramCustomCountsHPC(name, sample, min, max, bucket_count)
+
 // Used for capturing basic percentages. This will be 100 buckets of size 1.
 #define DAWN_HISTOGRAM_PERCENTAGE(platform, name, percent_as_int) \
     DAWN_HISTOGRAM_ENUMERATION(platform, name, percent_as_int, 101)
@@ -121,24 +132,37 @@
 #define DAWN_HISTOGRAM_SPARSE(platformObj, name, sparse_sample) \
     platformObj->HistogramSparse(name, sparse_sample)
 
+namespace dawn::detail {
+enum class ScopedHistogramTiming { kMicrosecondTimes, kMediumTimes, kLongTimes };
+}
+
 // Scoped class which logs its time on this earth as a UMA statistic. This is
 // recommended for when you want a histogram which measures the time it takes
 // for a method to execute. This measures up to 10 seconds.
 #define SCOPED_DAWN_HISTOGRAM_TIMER(platform, name) \
-    SCOPED_DAWN_HISTOGRAM_TIMER_EXPANDER(platform, name, false, __COUNTER__)
+    SCOPED_DAWN_HISTOGRAM_TIMER_EXPANDER(           \
+        platform, name, dawn::detail::ScopedHistogramTiming::kMediumTimes, __COUNTER__)
 
 // Similar scoped histogram timer, but this uses DAWN_HISTOGRAM_LONG_TIMES_100,
 // which measures up to an hour, and uses 100 buckets. This is more expensive
 // to store, so only use if this often takes >10 seconds.
 #define SCOPED_DAWN_HISTOGRAM_LONG_TIMER(platform, name) \
-    SCOPED_DAWN_HISTOGRAM_TIMER_EXPANDER(platform, name, true, __COUNTER__)
+    SCOPED_DAWN_HISTOGRAM_TIMER_EXPANDER(                \
+        platform, name, dawn::detail::ScopedHistogramTiming::kLongTimes, __COUNTER__)
+
+// Similar scoped histogram timer, but this uses
+// DAWN_HISTOGRAM_CUSTOM_MICROSECOND_TIMES, measuring from 1 microseconds to 1 second,
+// with 50 buckets.
+#define SCOPED_DAWN_HISTOGRAM_TIMER_MICROS(platform, name) \
+    SCOPED_DAWN_HISTOGRAM_TIMER_EXPANDER(                  \
+        platform, name, dawn::detail::ScopedHistogramTiming::kMicrosecondTimes, __COUNTER__)
 
 // This nested macro is necessary to expand __COUNTER__ to an actual value.
-#define SCOPED_DAWN_HISTOGRAM_TIMER_EXPANDER(platform, name, is_long, key) \
-    SCOPED_DAWN_HISTOGRAM_TIMER_UNIQUE(platform, name, is_long, key)
+#define SCOPED_DAWN_HISTOGRAM_TIMER_EXPANDER(platform, name, timing, key) \
+    SCOPED_DAWN_HISTOGRAM_TIMER_UNIQUE(platform, name, timing, key)
 
 // This is a helper macro used by other macros and shouldn't be used directly.
-#define SCOPED_DAWN_HISTOGRAM_TIMER_UNIQUE(platform, name, is_long, key)                    \
+#define SCOPED_DAWN_HISTOGRAM_TIMER_UNIQUE(platform, name, timing, key)                     \
     using PlatformType##key = std::decay_t<std::remove_pointer_t<decltype(platform)>>;      \
     class [[nodiscard]] ScopedHistogramTimer##key {                                         \
       public:                                                                               \
@@ -149,11 +173,20 @@
             if (constructed_ == 0)                                                          \
                 return;                                                                     \
             double elapsed = this->platform_->MonotonicallyIncreasingTime() - constructed_; \
-            int elapsedMS = static_cast<int>(elapsed * 1000.0);                             \
-            if (is_long) {                                                                  \
-                DAWN_HISTOGRAM_LONG_TIMES_100(platform_, name, elapsedMS);                  \
-            } else {                                                                        \
-                DAWN_HISTOGRAM_TIMES(platform_, name, elapsedMS);                           \
+            switch (timing) {                                                               \
+                case dawn::detail::ScopedHistogramTiming::kMicrosecondTimes: {              \
+                    int elapsedUS = static_cast<int>(elapsed * 1'000'000.0);                \
+                    DAWN_HISTOGRAM_CUSTOM_MICROSECOND_TIMES(platform_, name, elapsedUS, 1,  \
+                                                            1'000'000, 50);                 \
+                } break;                                                                    \
+                case dawn::detail::ScopedHistogramTiming::kMediumTimes: {                   \
+                    int elapsedMS = static_cast<int>(elapsed * 1'000.0);                    \
+                    DAWN_HISTOGRAM_TIMES(platform_, name, elapsedMS);                       \
+                } break;                                                                    \
+                case dawn::detail::ScopedHistogramTiming::kLongTimes: {                     \
+                    int elapsedMS = static_cast<int>(elapsed * 1'000.0);                    \
+                    DAWN_HISTOGRAM_LONG_TIMES_100(platform_, name, elapsedMS);              \
+                }                                                                           \
             }                                                                               \
         }                                                                                   \
                                                                                             \
diff --git a/src/dawn/tests/end2end/HistogramTests.cpp b/src/dawn/tests/end2end/HistogramTests.cpp
index 8da9dc4..b71f5f6 100644
--- a/src/dawn/tests/end2end/HistogramTests.cpp
+++ b/src/dawn/tests/end2end/HistogramTests.cpp
@@ -36,6 +36,11 @@
                 (override));
 
     MOCK_METHOD(void,
+                HistogramCustomCountsHPC,
+                (const char* name, int sample, int min, int max, int bucketCount),
+                (override));
+
+    MOCK_METHOD(void,
                 HistogramEnumeration,
                 (const char* name, int sample, int boundaryValue),
                 (override));
@@ -68,12 +73,16 @@
     EXPECT_CALL(*mMockPlatform, HistogramCustomCounts("long_times", 3, _, _, _));
     EXPECT_CALL(*mMockPlatform, HistogramCustomCounts("long_times_100", 4, _, _, _));
     EXPECT_CALL(*mMockPlatform, HistogramCustomCounts("custom_times", 5, 0, 10, 42));
+    EXPECT_CALL(*mMockPlatform,
+                HistogramCustomCountsHPC("custom_microsecond_times", 6, 0, 100, 420));
 
     DAWN_HISTOGRAM_TIMES(mMockPlatform, "times", 1);
     DAWN_HISTOGRAM_MEDIUM_TIMES(mMockPlatform, "medium_times", 2);
     DAWN_HISTOGRAM_LONG_TIMES(mMockPlatform, "long_times", 3);
     DAWN_HISTOGRAM_LONG_TIMES_100(mMockPlatform, "long_times_100", 4);
     DAWN_HISTOGRAM_CUSTOM_TIMES(mMockPlatform, "custom_times", 5, 0, 10, 42);
+    DAWN_HISTOGRAM_CUSTOM_MICROSECOND_TIMES(mMockPlatform, "custom_microsecond_times", 6, 0, 100,
+                                            420);
 }
 
 TEST_P(HistogramTests, Percentage) {
@@ -129,10 +138,11 @@
 
 TEST_P(HistogramTests, ScopedTimer) {
     InSequence seq;
-    EXPECT_CALL(*mMockPlatform, HistogramCustomCounts("timer0", 2500, _, _, _));
-    EXPECT_CALL(*mMockPlatform, HistogramCustomCounts("timer1", 15500, _, _, _));
-    EXPECT_CALL(*mMockPlatform, HistogramCustomCounts("timer4", 2500, _, _, _));
-    EXPECT_CALL(*mMockPlatform, HistogramCustomCounts("timer3", 6000, _, _, _));
+    EXPECT_CALL(*mMockPlatform, HistogramCustomCounts("timer0", 2'500, _, _, _));
+    EXPECT_CALL(*mMockPlatform, HistogramCustomCounts("timer1", 15'500, _, _, _));
+    EXPECT_CALL(*mMockPlatform, HistogramCustomCounts("timer4", 2'500, _, _, _));
+    EXPECT_CALL(*mMockPlatform, HistogramCustomCounts("timer3", 6'000, _, _, _));
+    EXPECT_CALL(*mMockPlatform, HistogramCustomCountsHPC("timer5", 1'500'000, _, _, _));
 
     {
         mMockPlatform->SetTime(1.0);
@@ -151,6 +161,11 @@
         SCOPED_DAWN_HISTOGRAM_LONG_TIMER(mMockPlatform, "timer4");
         mMockPlatform->SetTime(7.0);
     }
+    {
+        mMockPlatform->SetTime(1.0);
+        SCOPED_DAWN_HISTOGRAM_TIMER_MICROS(mMockPlatform, "timer5");
+        mMockPlatform->SetTime(2.5);
+    }
 }
 
 DAWN_INSTANTIATE_TEST(HistogramTests,