mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-07 18:04:46 +00:00
Bug 1675277 - Support FOG Labeled Metrics in C++ and JS r=janerik
Differential Revision: https://phabricator.services.mozilla.com/D103233
This commit is contained in:
parent
7296c82cbf
commit
926bf6dca9
@ -1472,6 +1472,10 @@ DOMInterfaces = {
|
||||
'nativeType': 'mozilla::glean::GleanPings',
|
||||
'headerFile': 'mozilla/glean/bindings/GleanPings.h',
|
||||
},
|
||||
'GleanLabeled': {
|
||||
'nativeType': 'mozilla::glean::GleanLabeled',
|
||||
'headerFile': 'mozilla/glean/bindings/Labeled.h',
|
||||
},
|
||||
|
||||
# WebRTC
|
||||
|
||||
|
@ -26,3 +26,20 @@ interface GleanImpl {
|
||||
*/
|
||||
getter GleanCategory (DOMString identifier);
|
||||
};
|
||||
|
||||
[ChromeOnly, Exposed=Window]
|
||||
interface GleanLabeled {
|
||||
/**
|
||||
* Get a specific metric for a given label.
|
||||
*
|
||||
* If a set of acceptable labels were specified in the `metrics.yaml` file,
|
||||
* and the given label is not in the set, it will be recorded under the
|
||||
* special `OTHER_LABEL` label.
|
||||
*
|
||||
* If a set of acceptable labels was not specified in the `metrics.yaml` file,
|
||||
* only the first 16 unique labels will be used.
|
||||
* After that, any additional labels will be recorded under the special
|
||||
* `OTHER_LABEL` label.
|
||||
*/
|
||||
getter nsISupports (DOMString identifier);
|
||||
};
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "mozilla/glean/bindings/Counter.h"
|
||||
#include "mozilla/glean/bindings/Datetime.h"
|
||||
#include "mozilla/glean/bindings/Event.h"
|
||||
#include "mozilla/glean/bindings/Labeled.h"
|
||||
#include "mozilla/glean/bindings/MemoryDistribution.h"
|
||||
#include "mozilla/glean/bindings/String.h"
|
||||
#include "mozilla/glean/bindings/StringList.h"
|
||||
|
64
toolkit/components/glean/bindings/private/Labeled.cpp
Normal file
64
toolkit/components/glean/bindings/private/Labeled.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/glean/bindings/Labeled.h"
|
||||
|
||||
#include "mozilla/dom/GleanBinding.h"
|
||||
#include "mozilla/glean/fog_ffi_generated.h"
|
||||
#include "mozilla/glean/bindings/GleanJSMetricsLookup.h"
|
||||
#include "mozilla/glean/bindings/MetricTypes.h"
|
||||
#include "nsString.h"
|
||||
|
||||
namespace mozilla::glean {
|
||||
|
||||
namespace impl {
|
||||
template <>
|
||||
BooleanMetric Labeled<BooleanMetric>::Get(const nsACString& aLabel) const {
|
||||
auto submetricId = fog_labeled_boolean_get(mId, &aLabel);
|
||||
return BooleanMetric(submetricId);
|
||||
}
|
||||
|
||||
template <>
|
||||
CounterMetric Labeled<CounterMetric>::Get(const nsACString& aLabel) const {
|
||||
auto submetricId = fog_labeled_counter_get(mId, &aLabel);
|
||||
return CounterMetric(submetricId);
|
||||
}
|
||||
|
||||
template <>
|
||||
StringMetric Labeled<StringMetric>::Get(const nsACString& aLabel) const {
|
||||
auto submetricId = fog_labeled_string_get(mId, &aLabel);
|
||||
return StringMetric(submetricId);
|
||||
}
|
||||
} // namespace impl
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(GleanLabeled)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(GleanLabeled)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(GleanLabeled)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GleanLabeled)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
JSObject* GleanLabeled::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) {
|
||||
return dom::GleanLabeled_Binding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
already_AddRefed<nsISupports> GleanLabeled::NamedGetter(const nsAString& aName,
|
||||
bool& aFound) {
|
||||
auto label = NS_ConvertUTF16toUTF8(aName);
|
||||
aFound = true;
|
||||
return NewSubMetricFromIds(mTypeId, mId, label);
|
||||
}
|
||||
|
||||
bool GleanLabeled::NameIsEnumerable(const nsAString& aName) { return false; }
|
||||
|
||||
void GleanLabeled::GetSupportedNames(nsTArray<nsString>& aNames) {
|
||||
// We really don't know, so don't do anything.
|
||||
}
|
||||
|
||||
} // namespace mozilla::glean
|
75
toolkit/components/glean/bindings/private/Labeled.h
Normal file
75
toolkit/components/glean/bindings/private/Labeled.h
Normal file
@ -0,0 +1,75 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_glean_Labeled_h
|
||||
#define mozilla_glean_Labeled_h
|
||||
|
||||
#include "nsIGleanMetrics.h"
|
||||
#include "nsISupports.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/glean/fog_ffi_generated.h"
|
||||
|
||||
namespace mozilla::glean {
|
||||
|
||||
namespace impl {
|
||||
|
||||
template <typename T>
|
||||
class Labeled {
|
||||
public:
|
||||
constexpr explicit Labeled<T>(uint32_t id) : mId(id) {}
|
||||
|
||||
/**
|
||||
* Gets a specific metric for a given label.
|
||||
*
|
||||
* If a set of acceptable labels were specified in the `metrics.yaml` file,
|
||||
* and the given label is not in the set, it will be recorded under the
|
||||
* special `OTHER_LABEL` label.
|
||||
*
|
||||
* If a set of acceptable labels was not specified in the `metrics.yaml` file,
|
||||
* only the first 16 unique labels will be used.
|
||||
* After that, any additional labels will be recorded under the special
|
||||
* `OTHER_LABEL` label.
|
||||
*
|
||||
* @param aLabel - a snake_case string under 30 characters in length,
|
||||
* otherwise the metric will be recorded under the special
|
||||
* `OTHER_LABEL` label and an error will be recorded.
|
||||
*/
|
||||
T Get(const nsACString& aLabel) const;
|
||||
|
||||
private:
|
||||
const uint32_t mId;
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
|
||||
class GleanLabeled final : public nsISupports, public nsWrapperCache {
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(GleanLabeled)
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
nsISupports* GetParentObject() { return nullptr; }
|
||||
|
||||
explicit GleanLabeled(uint32_t aId, uint32_t aTypeId)
|
||||
: mId(aId), mTypeId(aTypeId){};
|
||||
|
||||
already_AddRefed<nsISupports> NamedGetter(const nsAString& aName,
|
||||
bool& aFound);
|
||||
bool NameIsEnumerable(const nsAString& aName);
|
||||
void GetSupportedNames(nsTArray<nsString>& aNames);
|
||||
|
||||
private:
|
||||
virtual ~GleanLabeled() = default;
|
||||
|
||||
const uint32_t mId;
|
||||
const uint32_t mTypeId;
|
||||
};
|
||||
|
||||
} // namespace mozilla::glean
|
||||
|
||||
#endif /* mozilla_glean_Labeled_h */
|
@ -48,6 +48,9 @@ def type_name(obj):
|
||||
Returns the C++ type to use for a given metric object.
|
||||
"""
|
||||
|
||||
if getattr(obj, "labeled", False):
|
||||
class_name = util.Camelize(obj.type[8:]) # strips "labeled_" off the front.
|
||||
return "Labeled<impl::{}Metric>".format(class_name)
|
||||
generate_enums = getattr(obj, "_generate_enums", []) # Extra Keys? Reasons?
|
||||
if len(generate_enums):
|
||||
for name, suffix in generate_enums:
|
||||
|
@ -71,12 +71,26 @@ def metric_identifier(category, metric_name):
|
||||
return f"{category}.{util.camelize(metric_name)}"
|
||||
|
||||
|
||||
def type_name(type):
|
||||
def type_name(obj):
|
||||
"""
|
||||
Returns the C++ type to use for a given metric object.
|
||||
"""
|
||||
|
||||
return "Glean" + util.Camelize(type)
|
||||
if getattr(obj, "labeled", False):
|
||||
return "GleanLabeled"
|
||||
return "Glean" + util.Camelize(obj.type)
|
||||
|
||||
|
||||
def subtype_name(obj):
|
||||
"""
|
||||
Returns the subtype name for labeled metrics.
|
||||
(e.g. 'boolean' for 'labeled_boolean').
|
||||
Returns "" for non-labeled metrics.
|
||||
"""
|
||||
if getattr(obj, "labeled", False):
|
||||
type = obj.type[8:] # strips "labeled_" off the front
|
||||
return "Glean" + util.Camelize(type)
|
||||
return ""
|
||||
|
||||
|
||||
def output_js(objs, output_fd, options={}):
|
||||
@ -118,7 +132,6 @@ def write_metrics(objs, output_fd, template_filename):
|
||||
|
||||
template = util.get_jinja2_template(
|
||||
template_filename,
|
||||
filters=(("type_name", type_name),),
|
||||
)
|
||||
|
||||
assert (
|
||||
@ -142,11 +155,12 @@ def write_metrics(objs, output_fd, template_filename):
|
||||
|
||||
for metric in objs.values():
|
||||
identifier = metric_identifier(category_name, metric.name)
|
||||
if metric.type in metric_type_ids:
|
||||
type_id = metric_type_ids[metric.type]
|
||||
metric_type_tuple = (type_name(metric), subtype_name(metric))
|
||||
if metric_type_tuple in metric_type_ids:
|
||||
type_id, _ = metric_type_ids[metric_type_tuple]
|
||||
else:
|
||||
type_id = len(metric_type_ids) + 1
|
||||
metric_type_ids[metric.type] = type_id
|
||||
metric_type_ids[metric_type_tuple] = (type_id, metric.type)
|
||||
|
||||
idx = metric_string_table.stringIndex(identifier)
|
||||
metric_id = get_metric_id(metric)
|
||||
|
@ -14,6 +14,7 @@ Jinja2 template is not. Please file bugs! #}
|
||||
#include "mozilla/PerfectHash.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/glean/bindings/MetricTypes.h"
|
||||
#include "mozilla/glean/fog_ffi_generated.h"
|
||||
|
||||
#define GLEAN_INDEX_BITS ({{index_bits}})
|
||||
#define GLEAN_ID_BITS ({{id_bits}})
|
||||
@ -39,10 +40,10 @@ static already_AddRefed<nsISupports> NewMetricFromId(uint32_t id) {
|
||||
uint32_t metricId = GLEAN_METRIC_ID(id);
|
||||
|
||||
switch (typeId) {
|
||||
{% for type, type_id in metric_type_ids.items() %}
|
||||
case {{ type_id }}: /* {{ type|Camelize }} */
|
||||
{% for (type_name, subtype_name), (type_id, original_type) in metric_type_ids.items() %}
|
||||
case {{ type_id }}: /* {{ original_type }} */
|
||||
{
|
||||
return MakeAndAddRef<{{type | type_name}}>(metricId);
|
||||
return MakeAndAddRef<{{type_name}}>(metricId{% if subtype_name|length > 0 %}, {{ type_id }}{% endif %});
|
||||
}
|
||||
{% endfor %}
|
||||
default:
|
||||
@ -51,6 +52,22 @@ static already_AddRefed<nsISupports> NewMetricFromId(uint32_t id) {
|
||||
}
|
||||
}
|
||||
|
||||
static already_AddRefed<nsISupports> NewSubMetricFromIds(uint32_t aParentTypeId, uint32_t aParentMetricId, const nsACString& aLabel) {
|
||||
switch (aParentTypeId) {
|
||||
{% for (type_name, subtype_name), (type_id, original_type) in metric_type_ids.items() %}
|
||||
{% if subtype_name|length > 0 %}
|
||||
case {{ type_id }}: { /* {{ original_type }} */
|
||||
return MakeAndAddRef<{{subtype_name}}>(impl::fog_{{original_type}}_get(aParentMetricId, &aLabel));
|
||||
}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
default: {
|
||||
MOZ_ASSERT_UNREACHABLE("Invalid type ID for submetric.");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Maybe<uint32_t> category_result_check(const nsACString& aKey, category_entry_t entry);
|
||||
static Maybe<uint32_t> metric_result_check(const nsACString& aKey, metric_entry_t entry);
|
||||
|
||||
|
@ -214,3 +214,50 @@ TEST(FOG, TestCppTimingDistWorks)
|
||||
}
|
||||
ASSERT_EQ(sampleCount, (uint64_t)2);
|
||||
}
|
||||
|
||||
TEST(FOG, TestLabeledBooleanWorks)
|
||||
{
|
||||
ASSERT_EQ(mozilla::Nothing(),
|
||||
test_only::mabels_like_balloons.Get("hot_air"_ns).TestGetValue());
|
||||
test_only::mabels_like_balloons.Get("hot_air"_ns).Set(true);
|
||||
test_only::mabels_like_balloons.Get("helium"_ns).Set(false);
|
||||
ASSERT_EQ(
|
||||
true,
|
||||
test_only::mabels_like_balloons.Get("hot_air"_ns).TestGetValue().ref());
|
||||
ASSERT_EQ(
|
||||
false,
|
||||
test_only::mabels_like_balloons.Get("helium"_ns).TestGetValue().ref());
|
||||
}
|
||||
|
||||
TEST(FOG, TestLabeledCounterWorks)
|
||||
{
|
||||
ASSERT_EQ(mozilla::Nothing(),
|
||||
test_only::mabels_kitchen_counters.Get("marble"_ns).TestGetValue());
|
||||
test_only::mabels_kitchen_counters.Get("marble"_ns).Add(1);
|
||||
test_only::mabels_kitchen_counters.Get("laminate"_ns).Add(2);
|
||||
ASSERT_EQ(
|
||||
1,
|
||||
test_only::mabels_kitchen_counters.Get("marble"_ns).TestGetValue().ref());
|
||||
ASSERT_EQ(2, test_only::mabels_kitchen_counters.Get("laminate"_ns)
|
||||
.TestGetValue()
|
||||
.ref());
|
||||
}
|
||||
|
||||
TEST(FOG, TestLabeledStringWorks)
|
||||
{
|
||||
ASSERT_EQ(mozilla::Nothing(),
|
||||
test_only::mabels_balloon_strings.Get("twine"_ns).TestGetValue());
|
||||
test_only::mabels_balloon_strings.Get("twine"_ns).Set("seems acceptable"_ns);
|
||||
test_only::mabels_balloon_strings.Get("parachute_cord"_ns)
|
||||
.Set("preferred"_ns);
|
||||
ASSERT_STREQ("seems acceptable",
|
||||
test_only::mabels_balloon_strings.Get("twine"_ns)
|
||||
.TestGetValue()
|
||||
.ref()
|
||||
.get());
|
||||
ASSERT_STREQ("preferred",
|
||||
test_only::mabels_balloon_strings.Get("parachute_cord"_ns)
|
||||
.TestGetValue()
|
||||
.ref()
|
||||
.get());
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ if CONFIG["MOZ_GLEAN"]:
|
||||
"bindings/private/Datetime.h",
|
||||
"bindings/private/DistributionData.h",
|
||||
"bindings/private/Event.h",
|
||||
"bindings/private/Labeled.h",
|
||||
"bindings/private/MemoryDistribution.h",
|
||||
"bindings/private/Ping.h",
|
||||
"bindings/private/String.h",
|
||||
@ -64,6 +65,7 @@ if CONFIG["MOZ_GLEAN"]:
|
||||
"bindings/private/Counter.cpp",
|
||||
"bindings/private/Datetime.cpp",
|
||||
"bindings/private/Event.cpp",
|
||||
"bindings/private/Labeled.cpp",
|
||||
"bindings/private/MemoryDistribution.cpp",
|
||||
"bindings/private/Ping.cpp",
|
||||
"bindings/private/String.cpp",
|
||||
|
@ -219,3 +219,90 @@ add_task(async function test_fog_timing_distribution_works() {
|
||||
"Only two buckets with samples"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function test_fog_labeled_boolean_works() {
|
||||
Assert.equal(
|
||||
undefined,
|
||||
Glean.testOnly.mabelsLikeBalloons.at_parties.testGetValue(),
|
||||
"New labels with no values should return undefined"
|
||||
);
|
||||
Glean.testOnly.mabelsLikeBalloons.at_parties.set(true);
|
||||
Glean.testOnly.mabelsLikeBalloons.at_funerals.set(false);
|
||||
Assert.equal(
|
||||
true,
|
||||
Glean.testOnly.mabelsLikeBalloons.at_parties.testGetValue()
|
||||
);
|
||||
Assert.equal(
|
||||
false,
|
||||
Glean.testOnly.mabelsLikeBalloons.at_funerals.testGetValue()
|
||||
);
|
||||
// What about invalid/__other__?
|
||||
Assert.equal(
|
||||
undefined,
|
||||
Glean.testOnly.mabelsLikeBalloons.__other__.testGetValue()
|
||||
);
|
||||
Glean.testOnly.mabelsLikeBalloons.InvalidLabel.set(true);
|
||||
Assert.equal(
|
||||
true,
|
||||
Glean.testOnly.mabelsLikeBalloons.__other__.testGetValue()
|
||||
);
|
||||
// TODO: Test that we have the right number and type of errors (bug 1683171)
|
||||
});
|
||||
|
||||
add_task(async function test_fog_labeled_counter_works() {
|
||||
Assert.equal(
|
||||
undefined,
|
||||
Glean.testOnly.mabelsKitchenCounters.near_the_sink.testGetValue(),
|
||||
"New labels with no values should return undefined"
|
||||
);
|
||||
Glean.testOnly.mabelsKitchenCounters.near_the_sink.add(1);
|
||||
Glean.testOnly.mabelsKitchenCounters.with_junk_on_them.add(2);
|
||||
Assert.equal(
|
||||
1,
|
||||
Glean.testOnly.mabelsKitchenCounters.near_the_sink.testGetValue()
|
||||
);
|
||||
Assert.equal(
|
||||
2,
|
||||
Glean.testOnly.mabelsKitchenCounters.with_junk_on_them.testGetValue()
|
||||
);
|
||||
// What about invalid/__other__?
|
||||
Assert.equal(
|
||||
undefined,
|
||||
Glean.testOnly.mabelsKitchenCounters.__other__.testGetValue()
|
||||
);
|
||||
Glean.testOnly.mabelsKitchenCounters.InvalidLabel.add(1);
|
||||
Assert.equal(
|
||||
1,
|
||||
Glean.testOnly.mabelsKitchenCounters.__other__.testGetValue()
|
||||
);
|
||||
// TODO: Test that we have the right number and type of errors (bug 1683171)
|
||||
});
|
||||
|
||||
add_task(async function test_fog_labeled_string_works() {
|
||||
Assert.equal(
|
||||
undefined,
|
||||
Glean.testOnly.mabelsBalloonStrings.colour_of_99.testGetValue(),
|
||||
"New labels with no values should return undefined"
|
||||
);
|
||||
Glean.testOnly.mabelsBalloonStrings.colour_of_99.set("crimson");
|
||||
Glean.testOnly.mabelsBalloonStrings.string_lengths.set("various");
|
||||
Assert.equal(
|
||||
"crimson",
|
||||
Glean.testOnly.mabelsBalloonStrings.colour_of_99.testGetValue()
|
||||
);
|
||||
Assert.equal(
|
||||
"various",
|
||||
Glean.testOnly.mabelsBalloonStrings.string_lengths.testGetValue()
|
||||
);
|
||||
// What about invalid/__other__?
|
||||
Assert.equal(
|
||||
undefined,
|
||||
Glean.testOnly.mabelsBalloonStrings.__other__.testGetValue()
|
||||
);
|
||||
Glean.testOnly.mabelsBalloonStrings.InvalidLabel.set("valid");
|
||||
Assert.equal(
|
||||
"valid",
|
||||
Glean.testOnly.mabelsBalloonStrings.__other__.testGetValue()
|
||||
);
|
||||
// TODO: Test that we have the right number and type of errors (bug 1683171)
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user