Bug 1428888 - Allow C++ to accumulate multiple samples into a categorical histogram in one call r=chutten

Telemetry::Accumulate now supports two new signatures: Accumulate(HistogramId, nsTArray stringLabels) and
Accumulate(nsTArray enumValues). In the stringLabels case, if the array contains an invalid label, then no
samples are accumulated at all. In the enumValues array case, the class template ensures that we do not have
a mismatch of labels in the array, since the enumValues class is tied to the id of the histogram we want to
accumulate to.
This commit is contained in:
Aditya Bharti 2018-01-28 16:53:40 +05:30
parent 1d7efd79f2
commit 2d08e81fe6
5 changed files with 181 additions and 0 deletions

View File

@ -1970,6 +1970,12 @@ AccumulateCategorical(HistogramID id, const nsCString& label)
TelemetryHistogram::AccumulateCategorical(id, label);
}
void
AccumulateCategorical(HistogramID id, const nsTArray<nsCString>& labels)
{
TelemetryHistogram::AccumulateCategorical(id, labels);
}
void
AccumulateTimeDelta(HistogramID aHistogram, TimeStamp start, TimeStamp end)
{

View File

@ -120,6 +120,30 @@ void AccumulateCategorical(E enumValue) {
static_cast<uint32_t>(enumValue));
};
/**
* Adds an array of samples to categorical histograms defined in TelemetryHistogramEnums.h
* This is the typesafe - and preferred - way to use the categorical histograms
* by passing values from the corresponding Telemetry::LABELS_* enums.
*
* @param enumValues - Array of labels from Telemetry::LABELS_* enums.
*/
template<class E>
void
AccumulateCategorical(const nsTArray<E>& enumValues)
{
static_assert(IsCategoricalLabelEnum<E>::value,
"Only categorical label enum types are supported.");
nsTArray<uint32_t> intSamples(enumValues.Length());
for (E aValue: enumValues){
intSamples.AppendElement(static_cast<uint32_t>(aValue));
}
HistogramID categoricalId = static_cast<HistogramID>(CategoricalLabelId<E>::value);
Accumulate(categoricalId, intSamples);
}
/**
* Adds sample to a keyed categorical histogram defined in TelemetryHistogramEnums.h
* This is the typesafe - and preferred - way to use the keyed categorical histograms
@ -148,6 +172,14 @@ void AccumulateCategoricalKeyed(const nsCString& key, E enumValue) {
*/
void AccumulateCategorical(HistogramID id, const nsCString& label);
/**
* Adds an array of samples to a categorical histogram defined in Histograms.json
*
* @param id - The histogram id
* @param labels - The array of labels to accumulate
*/
void AccumulateCategorical(HistogramID id, const nsTArray<nsCString>& labels);
/**
* Adds time delta in milliseconds to a histogram defined in TelemetryHistogramEnums.h
*

View File

@ -2050,6 +2050,39 @@ TelemetryHistogram::AccumulateCategorical(HistogramID aId,
internal_Accumulate(aId, labelId);
}
void
TelemetryHistogram::AccumulateCategorical(HistogramID aId, const nsTArray<nsCString>& aLabels)
{
if (NS_WARN_IF(!internal_IsHistogramEnumId(aId))) {
MOZ_ASSERT_UNREACHABLE("Histogram usage requires valid ids.");
return;
}
if (!internal_CanRecordBase()) {
return;
}
// We use two loops, one for getting label_ids and another one for actually accumulating
// the values. This ensures that in the case of an invalid label in the array, no values
// are accumulated. In any call to this API, either all or (in case of error) none of the
// values will be accumulated.
nsTArray<uint32_t> intSamples(aLabels.Length());
for (const nsCString& label: aLabels){
uint32_t labelId = 0;
if (NS_FAILED(gHistogramInfos[aId].label_id(label.get(), &labelId))) {
return;
}
intSamples.AppendElement(labelId);
}
StaticMutexAutoLock locker(gTelemetryHistogramMutex);
for (uint32_t sample: intSamples){
internal_Accumulate(aId, sample);
}
}
void
TelemetryHistogram::AccumulateChild(ProcessID aProcessType,
const nsTArray<HistogramAccumulation>& aAccumulations)

View File

@ -45,6 +45,7 @@ void Accumulate(const char* name, uint32_t sample);
void Accumulate(const char* name, const nsCString& key, uint32_t sample);
void AccumulateCategorical(mozilla::Telemetry::HistogramID aId, const nsCString& aLabel);
void AccumulateCategorical(mozilla::Telemetry::HistogramID aId, const nsTArray<nsCString>& aLabels);
void AccumulateChild(mozilla::Telemetry::ProcessID aProcessType,
const nsTArray<mozilla::Telemetry::HistogramAccumulation>& aAccumulations);

View File

@ -529,3 +529,112 @@ TEST_F(TelemetryTestFixture, TestKeyedKeysHistogram_MultipleSamples)
"TELEMETRY_TEST_KEYED_KEYS", cx.GetJSContext(),
scalarsSnapshot, expectedAccumulateUnknownCount);
}
TEST_F(TelemetryTestFixture, AccumulateCategoricalHistogram_MultipleStringLabels)
{
const uint32_t kExpectedValue = 2;
const nsTArray<nsCString> labels({
NS_LITERAL_CSTRING("CommonLabel"),
NS_LITERAL_CSTRING("CommonLabel")});
AutoJSContextWithGlobal cx(mCleanGlobal);
GetAndClearHistogram(cx.GetJSContext(), mTelemetry,
NS_LITERAL_CSTRING("TELEMETRY_TEST_CATEGORICAL"), false);
// Accumulate the units into a categorical histogram using a string label
Telemetry::AccumulateCategorical(Telemetry::TELEMETRY_TEST_CATEGORICAL, labels);
// Get a snapshot for all the histograms
JS::RootedValue snapshot(cx.GetJSContext());
GetSnapshots(cx.GetJSContext(), mTelemetry, "TELEMETRY_TEST_CATEGORICAL", &snapshot, false);
// Get our histogram from the snapshot
JS::RootedValue histogram(cx.GetJSContext());
GetProperty(cx.GetJSContext(), "TELEMETRY_TEST_CATEGORICAL", snapshot, &histogram);
// Get counts array from histogram. Each entry in the array maps to a label in the histogram.
JS::RootedValue counts(cx.GetJSContext());
GetProperty(cx.GetJSContext(), "counts", histogram, &counts);
// Get the value for the label we care about
JS::RootedValue value(cx.GetJSContext());
GetElement(cx.GetJSContext(),
static_cast<uint32_t>(Telemetry::LABELS_TELEMETRY_TEST_CATEGORICAL::CommonLabel),
counts, &value);
// Check that the value stored in the histogram matches with |kExpectedValue|
uint32_t uValue = 0;
JS::ToUint32(cx.GetJSContext(), value, &uValue);
ASSERT_EQ(uValue, kExpectedValue) << "The histogram is not returning expected value";
// Now we check for no accumulation when a bad label is present in the array.
//
// The 'counts' property is not initialized unless data is accumulated so keeping another test
// to check for this case alone is wasteful as we will have to accumulate some data anyway.
const nsTArray<nsCString> badLabelArray({
NS_LITERAL_CSTRING("CommonLabel"),
NS_LITERAL_CSTRING("BadLabel")});
// Try to accumulate the array into the histogram.
Telemetry::AccumulateCategorical(Telemetry::TELEMETRY_TEST_CATEGORICAL, badLabelArray);
// Get snapshot of all the histograms
GetSnapshots(cx.GetJSContext(), mTelemetry, "TELEMETRY_TEST_CATEGORICAL", &snapshot, false);
// Get our histogram from the snapshot
GetProperty(cx.GetJSContext(), "TELEMETRY_TEST_CATEGORICAL", snapshot, &histogram);
// Get counts array from histogram
GetProperty(cx.GetJSContext(), "counts", histogram, &counts);
// Get the value for the label we care about
GetElement(cx.GetJSContext(),
static_cast<uint32_t>(Telemetry::LABELS_TELEMETRY_TEST_CATEGORICAL::CommonLabel),
counts, &value);
// Check that the value stored in the histogram matches with |kExpectedValue|
uValue = 0;
JS::ToUint32(cx.GetJSContext(), value, &uValue);
ASSERT_EQ(uValue, kExpectedValue) << "The histogram accumulated data when it should not have";
}
TEST_F(TelemetryTestFixture, AccumulateCategoricalHistogram_MultipleEnumValues)
{
const uint32_t kExpectedValue = 2;
const nsTArray<Telemetry::LABELS_TELEMETRY_TEST_CATEGORICAL> enumLabels({
Telemetry::LABELS_TELEMETRY_TEST_CATEGORICAL::CommonLabel,
Telemetry::LABELS_TELEMETRY_TEST_CATEGORICAL::CommonLabel});
AutoJSContextWithGlobal cx(mCleanGlobal);
GetAndClearHistogram(cx.GetJSContext(), mTelemetry,
NS_LITERAL_CSTRING("TELEMETRY_TEST_CATEGORICAL"), false);
// Accumulate the units into a categorical histogram using the enumLabels array
Telemetry::AccumulateCategorical<Telemetry::LABELS_TELEMETRY_TEST_CATEGORICAL>(enumLabels);
// Get a snapshot for all the histograms
JS::RootedValue snapshot(cx.GetJSContext());
GetSnapshots(cx.GetJSContext(), mTelemetry, "TELEMETRY_TEST_CATEGORICAL", &snapshot, false);
// Get our histogram from the snapshot
JS::RootedValue histogram(cx.GetJSContext());
GetProperty(cx.GetJSContext(), "TELEMETRY_TEST_CATEGORICAL", snapshot, &histogram);
// Get counts array from histogram. Each entry in the array maps to a label in the histogram.
JS::RootedValue counts(cx.GetJSContext());
GetProperty(cx.GetJSContext(), "counts", histogram, &counts);
// Get the value for the label we care about
JS::RootedValue value(cx.GetJSContext());
GetElement(cx.GetJSContext(),
static_cast<uint32_t>(Telemetry::LABELS_TELEMETRY_TEST_CATEGORICAL::CommonLabel),
counts, &value);
// Check that the value stored in the histogram matches with |kExpectedValue|
uint32_t uValue = 0;
JS::ToUint32(cx.GetJSContext(), value, &uValue);
ASSERT_EQ(uValue, kExpectedValue) << "The histogram is not returning expected value";
}