| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef UkmTimeAggregator_h |
| #define UkmTimeAggregator_h |
| |
| #include "platform/PlatformExport.h" |
| #include "platform/wtf/Time.h" |
| #include "platform/wtf/Vector.h" |
| #include "platform/wtf/text/WTFString.h" |
| |
| namespace ukm { |
| class UkmRecorder; |
| } |
| |
| namespace blink { |
| class CustomCountHistogram; |
| |
| // This class is a helper class for aggregating and recording time based UKM |
| // metrics. It takes the following constructor parameters: |
| // - event_name: The name of the event(s) that will be generated by this |
| // aggregator. |
| // - source_id: UKM Source ID associated with the events. |
| // - recorder: UkmRecorder which will handle the events |
| // - metric_names: A Vector of strings that represent the base names for the |
| // metrics associated with the event. Note that the order of |
| // this array is important, since the scoped timer is created |
| // with an index into this array. |
| // - event_frequency: The interval over which metrics are aggregated. Within |
| // one interval there will be one event generated. |
| // |
| // After the aggregator is created, one can create ScopedUkmTimer object which |
| // will measure the time from creation until the object destruction. When |
| // destroyed, it will record a sample into the aggregator. This does not |
| // necessarily generate a UKM event. Instead, the sample is aggregated with |
| // other samples in the interval specified by event frequency. An event is |
| // actually generated in one of two situations: |
| // |
| // - If a sample is added that lies in the next event frequency interval (this |
| // will generate an event for the previous interval) |
| // - If the aggregator is destroyed (this will generate an event for any |
| // remaining samples in the aggregator) |
| // |
| // Note that no event is generated if there were no samples in an interval. |
| // |
| // Note that one can also give an optional CustomCountHistogram pointer when |
| // creating the scoped timer. This is provided as a convenience. Upon |
| // destruction the timer will record a sample into the histogram counter. |
| // |
| // Sample usage: |
| // // Note that values of this enum correspond to the indices of the metric |
| // // names array given to UkmTimeAggregator at construction. |
| // enum class MetricNames { kMetric1, kMetric2, kMetric3 }; |
| // std::unique_ptr<UkmTimeAggregator> aggregator( |
| // new UkmTimeAggregator("my_event", |
| // GetSourceId(), |
| // GetUkmRecorder(), |
| // {"metric1", "metric2", "metric3"}, |
| // TimeDelta::FromSeconds(1))); |
| // |
| // ... |
| // { |
| // auto timer = |
| // aggregator->GetScopedTimer(static_cast<size_t>(MetricNames::kMetric2)); |
| // ... |
| // } |
| // // At this point an sample for "metric2" is recorded, which may generate |
| // // an event for "my_event" if there were previous samples. |
| // ... |
| // // Destroying an aggregator will generate an event as well if there were |
| // // samples. |
| // aggregator.reset(); |
| // |
| // In the example above, the event name is "my_event". It will measure six |
| // metrics: |
| // "metric1.Average", "metric1.WorstCase", |
| // "metric2.Average", "metric2.WorstCase", |
| // "metric3.Average", "metric3.WorstCase" |
| // |
| // Note that these have to be specified in the appropriate ukm.xml file to be |
| // captured. |
| // |
| // If the source_id/event_name/recorder change then a new UkmTimeAggregator has |
| // to be created. |
| class PLATFORM_EXPORT UkmTimeAggregator { |
| public: |
| // This class will start a timer upon creation, which will end when the |
| // object is destroyed. Upon destruction it will record a sample into the |
| // aggregator that created the scoped timer. It will also record an event |
| // into an optional histogram counter. |
| class PLATFORM_EXPORT ScopedUkmTimer { |
| public: |
| ScopedUkmTimer(ScopedUkmTimer&&); |
| ~ScopedUkmTimer(); |
| |
| private: |
| friend class UkmTimeAggregator; |
| |
| ScopedUkmTimer(UkmTimeAggregator*, |
| size_t metric_index, |
| CustomCountHistogram* histogram_counter); |
| |
| UkmTimeAggregator* aggregator_; |
| const size_t metric_index_; |
| CustomCountHistogram* const histogram_counter_; |
| const TimeTicks start_time_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ScopedUkmTimer); |
| }; |
| |
| UkmTimeAggregator(String event_name, |
| int64_t source_id, |
| ukm::UkmRecorder*, |
| const Vector<String>& metric_names, |
| TimeDelta event_frequency); |
| ~UkmTimeAggregator(); |
| |
| // Create a scoped timer with the index of the metric and an optional |
| // histogram counter. Note the index has to correspond to the right index in |
| // metric_names. For example, if metric_names is {"a", "b", "c"} then index |
| // 1 refers to metric "a", which will end up generating "a.Average" and |
| // "a.WorstCase" metrics. |
| ScopedUkmTimer GetScopedTimer( |
| size_t metric_index, |
| CustomCountHistogram* histogram_counter = nullptr); |
| |
| private: |
| struct MetricRecord { |
| String worst_case_metric_name; |
| String average_metric_name; |
| TimeDelta total_duration; |
| TimeDelta worst_case_duration; |
| size_t sample_count = 0u; |
| |
| void reset() { |
| total_duration = TimeDelta(); |
| worst_case_duration = TimeDelta(); |
| sample_count = 0u; |
| } |
| }; |
| |
| void RecordSample(size_t metric_index, |
| TimeTicks start, |
| TimeTicks end, |
| CustomCountHistogram* histogram_counter); |
| void FlushIfNeeded(TimeTicks current_time); |
| void Flush(TimeTicks current_time); |
| |
| const String event_name_; |
| const int64_t source_id_; |
| ukm::UkmRecorder* const recorder_; |
| const TimeDelta event_frequency_; |
| TimeTicks last_flushed_time_; |
| Vector<MetricRecord> metric_records_; |
| bool has_data_ = false; |
| |
| DISALLOW_COPY_AND_ASSIGN(UkmTimeAggregator); |
| }; |
| |
| } // namespace blink |
| |
| #endif // UkmTimeAggregator_h |