blob: 1415131cdda3aa282edb6528440ecb681d95767a [file] [log] [blame]
// 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