summaryrefslogtreecommitdiffstats
path: root/toolkit/components/glean/build_scripts/glean_parser_ext/templates
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/glean/build_scripts/glean_parser_ext/templates')
-rw-r--r--toolkit/components/glean/build_scripts/glean_parser_ext/templates/cpp.jinja2101
-rw-r--r--toolkit/components/glean/build_scripts/glean_parser_ext/templates/cpp_pings.jinja230
-rw-r--r--toolkit/components/glean/build_scripts/glean_parser_ext/templates/gifft.jinja275
-rw-r--r--toolkit/components/glean/build_scripts/glean_parser_ext/templates/gifft_events.jinja252
-rw-r--r--toolkit/components/glean/build_scripts/glean_parser_ext/templates/jog_factory.jinja2149
-rw-r--r--toolkit/components/glean/build_scripts/glean_parser_ext/templates/js.jinja2170
-rw-r--r--toolkit/components/glean/build_scripts/glean_parser_ext/templates/js_h.jinja277
-rw-r--r--toolkit/components/glean/build_scripts/glean_parser_ext/templates/js_pings.jinja267
-rw-r--r--toolkit/components/glean/build_scripts/glean_parser_ext/templates/js_pings_h.jinja235
-rw-r--r--toolkit/components/glean/build_scripts/glean_parser_ext/templates/rust.jinja2355
-rw-r--r--toolkit/components/glean/build_scripts/glean_parser_ext/templates/rust_pings.jinja277
11 files changed, 1188 insertions, 0 deletions
diff --git a/toolkit/components/glean/build_scripts/glean_parser_ext/templates/cpp.jinja2 b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/cpp.jinja2
new file mode 100644
index 0000000000..2a4e40d6ac
--- /dev/null
+++ b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/cpp.jinja2
@@ -0,0 +1,101 @@
+// -*- mode: C++ -*-
+
+// AUTOGENERATED BY glean_parser. DO NOT EDIT.
+{# The rendered source is autogenerated, but this
+Jinja2 template is not. Please file bugs! #}
+
+/* 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_Metrics_h
+#define mozilla_Metrics_h
+
+#include "mozilla/glean/bindings/MetricTypes.h"
+#include "mozilla/Maybe.h"
+#include "nsTArray.h"
+#include "nsPrintfCString.h"
+
+#include <tuple>
+
+namespace mozilla::glean {
+
+{%- macro generate_extra_keys(obj) -%}
+{% for name, suffix in obj["_generate_enums"] %}
+{# we always use the `extra` suffix, because we only expose the new event API #}
+{% set suffix = "Extra" %}
+{% if obj|attr(name)|length %}
+ {{ extra_keys_with_types(obj, name, suffix)|indent }}
+{% endif %}
+{% endfor %}
+{%- endmacro -%}
+
+{%- macro extra_keys_with_types(obj, name, suffix) -%}
+struct {{ obj.name|Camelize }}{{ suffix }} {
+ {% for item, type in obj|attr(name) %}
+ mozilla::Maybe<{{type|extra_type_name}}> {{ item|camelize }};
+ {% endfor %}
+
+ std::tuple<nsTArray<nsCString>, nsTArray<nsCString>> ToFfiExtra() const {
+ nsTArray<nsCString> extraKeys;
+ nsTArray<nsCString> extraValues;
+ {% for item, type in obj|attr(name) %}
+ if ({{item|camelize}}) {
+ extraKeys.AppendElement()->AssignASCII("{{item}}");
+ {% if type == "string" %}
+ extraValues.EmplaceBack({{item|camelize}}.value());
+ {% elif type == "boolean" %}
+ extraValues.AppendElement()->AssignASCII({{item|camelize}}.value() ? "true" : "false");
+ {% elif type == "quantity" %}
+ extraValues.EmplaceBack(nsPrintfCString("%d", {{item|camelize}}.value()));
+ {% else %}
+#error "Glean: Invalid extra key type for metric {{obj.category}}.{{obj.name}}, defined in: {{obj.defined_in['filepath']}}:{{obj.defined_in['line']}})"
+ {% endif %}
+ }
+ {% endfor %}
+ return std::make_tuple(std::move(extraKeys), std::move(extraValues));
+ }
+};
+{%- endmacro -%}
+
+{# Okay, so though `enum class` means we can't pollute the namespace with the
+ enum variants' identifiers, there's no guarantee there isn't some
+ preprocessor #define lying in wait to collide with us. Using CamelCase
+ helps, but isn't foolproof (X11/X.h has `#define Success 0`).
+ So we prefix it. I chose `e` (for `enum`) for the prefix. #}
+{%- macro generate_label_enum(obj) -%}
+enum class {{ obj.name|Camelize }}Label: uint16_t {
+ {% for label in obj.ordered_labels %}
+ e{{ label|Camelize }} = {{loop.index0}},
+ {% endfor %}
+ e__Other__,
+};
+{%- endmacro %}
+
+struct NoExtraKeys;
+enum class DynamicLabel: uint16_t { };
+
+{% for category_name, objs in all_objs.items() %}
+namespace {{ category_name|snake_case }} {
+ {% for obj in objs.values() %}
+ /**
+ * generated from {{ category_name }}.{{ obj.name }}
+ */
+ {% if obj|attr("_generate_enums") %}
+{{ generate_extra_keys(obj) }}
+ {%- endif %}
+ {% if obj.labeled and obj.labels and obj.labels|length %}
+ {{ generate_label_enum(obj)|indent }}
+ {% endif %}
+ /**
+ * {{ obj.description|wordwrap() | replace('\n', '\n * ') }}
+ */
+ constexpr impl::{{ obj|type_name }} {{obj.name|snake_case }}({{obj|metric_id}});
+
+ {% endfor %}
+}
+{% endfor %}
+
+} // namespace mozilla::glean
+
+#endif // mozilla_Metrics_h
diff --git a/toolkit/components/glean/build_scripts/glean_parser_ext/templates/cpp_pings.jinja2 b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/cpp_pings.jinja2
new file mode 100644
index 0000000000..f877cb9685
--- /dev/null
+++ b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/cpp_pings.jinja2
@@ -0,0 +1,30 @@
+// -*- mode: C++ -*-
+
+// AUTOGENERATED BY glean_parser. DO NOT EDIT.
+{# The rendered source is autogenerated, but this
+Jinja2 template is not. Please file bugs! #}
+
+/* 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_Pings_h
+#define mozilla_glean_Pings_h
+
+#include "mozilla/glean/bindings/Ping.h"
+
+namespace mozilla::glean_pings {
+
+{% for obj in all_objs['pings'].values() %}
+/*
+ * Generated from {{ obj.name }}.
+ *
+ * {{ obj.description|wordwrap() | replace('\n', '\n * ') }}
+ */
+constexpr glean::impl::Ping {{ obj.name|Camelize }}({{ obj.name|ping_id }});
+
+{% endfor %}
+
+} // namespace mozilla::glean_pings
+
+#endif // mozilla_glean_Pings_h
diff --git a/toolkit/components/glean/build_scripts/glean_parser_ext/templates/gifft.jinja2 b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/gifft.jinja2
new file mode 100644
index 0000000000..4cfb5f242d
--- /dev/null
+++ b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/gifft.jinja2
@@ -0,0 +1,75 @@
+// -*- mode: C++ -*-
+
+/* This file is auto-generated by run_glean_parser.py.
+ It is only for internal use by types in
+ toolkit/components/glean/bindings/private */
+{# The rendered source is autogenerated, but this
+Jinja2 template is not. Please file bugs! #}
+
+#include "mozilla/AppShutdown.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/glean/bindings/GleanJSMetricsLookup.h"
+#include "mozilla/glean/bindings/jog/JOG.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/Telemetry.h"
+#include "nsIThread.h"
+#include "nsThreadUtils.h"
+
+#ifndef mozilla_glean_{{ probe_type }}GifftMap_h
+#define mozilla_glean_{{ probe_type }}GifftMap_h
+
+#define DYNAMIC_METRIC_BIT ({{runtime_metric_bit}})
+#define GLEAN_METRIC_ID(id) ((id) & ((1ULL << {{id_bits}}) - 1))
+
+namespace mozilla::glean {
+
+using Telemetry::{{ probe_type }}ID;
+
+{% if probe_type == "Scalar" %}
+static inline bool IsSubmetricId(uint32_t aId) {
+ // Submetrics have the 2^{{id_bits - id_signal_bits}} bit set.
+ // (ID_BITS - ID_SIGNAL_BITS, keep it in sync with js.py).
+ return (aId & (1 << {{id_bits - id_signal_bits}})) > 0;
+}
+{% endif %}
+
+static{% if probe_type == "Event" or probe_type == "Scalar" %} inline{% endif %} Maybe<{{ probe_type }}ID> {{ probe_type }}IdForMetric(uint32_t aId) {
+ switch(aId) {
+{% for id, (mirror, metric_name) in ids_to_probes.items() %}
+ case {{ id }}: { // {{ metric_name }}
+ return Some({{ probe_type }}ID::{{ mirror }});
+ }
+{% endfor %}
+ default: {
+ if (MOZ_UNLIKELY(aId & (1 << DYNAMIC_METRIC_BIT))) {
+ // Dynamic (runtime-registered) metric. Use its static (compiletime-
+ // registered) metric's telemetry_mirror mapping.
+ // ...if applicable.
+
+ // Only JS can use dynamic (runtime-registered) metric ids.
+ MOZ_ASSERT(NS_IsMainThread());
+
+ auto metricName = JOG::GetMetricName(aId);
+ // All of these should have names, but the storage only lasts until
+ // XPCOMWillShutdown, so it might return `Nothing()`.
+ if (metricName.isSome()) {
+ auto maybeMetric = MetricByNameLookup(metricName.ref());
+ if (maybeMetric.isSome()) {
+ uint32_t staticId = GLEAN_METRIC_ID(maybeMetric.value());
+ // Let's ensure we don't infinite loop, huh.
+ MOZ_ASSERT(!(staticId & (1 << DYNAMIC_METRIC_BIT)));
+ return {{ probe_type }}IdForMetric(staticId);
+ }
+ }
+ }
+ return Nothing();
+ }
+ }
+}
+
+} // namespace mozilla::glean
+
+#undef GLEAN_METRIC_ID
+#undef DYNAMIC_METRIC_BIT
+
+#endif // mozilla_glean_{{ probe_type }}GifftMaps_h
diff --git a/toolkit/components/glean/build_scripts/glean_parser_ext/templates/gifft_events.jinja2 b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/gifft_events.jinja2
new file mode 100644
index 0000000000..f32640fc19
--- /dev/null
+++ b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/gifft_events.jinja2
@@ -0,0 +1,52 @@
+// -*- mode: C++ -*-
+
+/* This file is auto-generated by run_glean_parser.py.
+ It is only for internal use by types in
+ toolkit/components/glean/bindings/private */
+{# The rendered source is autogenerated, but this
+Jinja2 template is not. Pleas file bugs! #}
+
+#include "mozilla/glean/bindings/Event.h"
+#include "mozilla/glean/GleanMetrics.h"
+
+namespace mozilla::glean {
+
+template <>
+/*static*/ const nsCString impl::EventMetric<NoExtraKeys>::ExtraStringForKey(uint32_t aKey) {
+ MOZ_ASSERT_UNREACHABLE("What are you doing here? No extra keys!");
+ return ""_ns;
+}
+
+{% for category_name, objs in all_objs.items() %}
+{% for obj in objs.values() %}
+{% if obj|attr("_generate_enums") %}
+{# we always use the `extra` suffix, because we only expose the new event API #}
+{% set suffix = "Extra" %}
+{% for name, _ in obj["_generate_enums"] %}
+{% if obj|attr(name)|length %}
+{% set ns %}{{ category_name|snake_case }}{% endset %}
+{% set type %}{{ obj.name|Camelize }}{{ suffix }}{% endset %}
+template <>
+/*static*/ const nsCString impl::EventMetric<{{ ns }}::{{ type }}>::ExtraStringForKey(uint32_t aKey) {
+ using {{ ns }}::{{ type }};
+ switch (aKey) {
+{% if obj|attr("telemetry_mirror") %}{# Optimization: Do not generate switch if not mirrored #}
+{% for key, _ in obj|attr(name) %}
+ case {{loop.index-1}}: {
+ return "{{ key }}"_ns;
+ }
+{% endfor %}
+{% endif %}
+ default: {
+ MOZ_ASSERT_UNREACHABLE("Impossible event key reached.");
+ return ""_ns;
+ }
+ }
+}
+
+{% endif %}
+{% endfor %}
+{% endif %}
+{% endfor %}
+{% endfor %}
+}; // namespace mozilla::glean
diff --git a/toolkit/components/glean/build_scripts/glean_parser_ext/templates/jog_factory.jinja2 b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/jog_factory.jinja2
new file mode 100644
index 0000000000..4b7838a47d
--- /dev/null
+++ b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/jog_factory.jinja2
@@ -0,0 +1,149 @@
+// -*- mode: Rust -*-
+
+// AUTOGENERATED BY glean_parser. DO NOT EDIT.
+{# The rendered source is autogenerated, but this
+Jinja2 template is not. Please file bugs! #}
+
+/* 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/. */
+
+/// This file contains factory implementation information for the
+/// JOG Runtime Registration module.
+/// It is responsible for being able to build metrics and pings described at runtime.
+/// It is generated to keep it in sync with how the runtime definitions are defined.
+
+use std::borrow::Cow;
+use std::sync::atomic::{AtomicU32, Ordering};
+use crate::private::{
+ CommonMetricData,
+ Lifetime,
+ MemoryUnit,
+ TimeUnit,
+ Ping,
+ LabeledMetric,
+{% for metric_type_name in metric_types.keys() if not metric_type_name.startswith('labeled_') %}
+ {{ metric_type_name|Camelize }}Metric,
+{% endfor %}};
+use crate::private::traits::HistogramType;
+
+pub(crate) static DYNAMIC_METRIC_BIT: u32 = {{runtime_metric_bit}};
+// 2**DYNAMIC_METRIC_BIT + 1 (+1 because we reserve the 0 metric id)
+static NEXT_METRIC_ID: AtomicU32 = AtomicU32::new({{2**runtime_metric_bit + 1}});
+#[cfg(feature = "with_gecko")] // only used in submit_ping_by_id, which is gecko-only.
+pub(crate) static DYNAMIC_PING_BIT: u32 = {{runtime_ping_bit}};
+// 2**DYNAMIC_PING_BIT + 1 (+1 because we reserve the 0 ping id)
+static NEXT_PING_ID: AtomicU32 = AtomicU32::new({{2**runtime_ping_bit + 1}});
+
+pub(crate) mod __jog_metric_maps {
+ use crate::metrics::DynamicLabel;
+ use crate::private::MetricId;
+ use crate::private::{
+ Ping,
+ LabeledMetric,
+ NoExtraKeys,
+ {% for metric_type_name in metric_types.keys() %}
+ {{ metric_type_name|Camelize }}Metric,
+ {% endfor %}
+ };
+ use once_cell::sync::Lazy;
+ use std::collections::HashMap;
+ use std::sync::{Arc, RwLock};
+
+{% for metric_type_name in metric_types.keys() if metric_type_name != "event" and not metric_type_name.startswith('labeled_') %}
+ pub static {{ metric_type_name.upper() }}_MAP: Lazy<Arc<RwLock<HashMap<MetricId, {{ metric_type_name|Camelize }}Metric>>>> =
+ Lazy::new(|| Arc::new(RwLock::new(HashMap::new())));
+
+{% endfor %}
+{# Labeled metrics are special because they're LabeledMetric<Labeled{Counter|Boolean|...}Metric> #}
+{% for metric_type_name in metric_types.keys() if metric_type_name.startswith('labeled_') %}
+ pub static {{ metric_type_name.upper() }}_MAP: Lazy<Arc<RwLock<HashMap<MetricId, LabeledMetric<{{ metric_type_name|Camelize }}Metric, DynamicLabel>>>>> =
+ Lazy::new(|| Arc::new(RwLock::new(HashMap::new())));
+
+{% endfor %}
+ pub static PING_MAP: Lazy<Arc<RwLock<HashMap<u32, Ping>>>> =
+ Lazy::new(|| Arc::new(RwLock::new(HashMap::new())));
+
+{# Event metrics are special because they're EventMetric<K> #}
+ pub static EVENT_MAP: Lazy<Arc<RwLock<HashMap<MetricId, EventMetric<NoExtraKeys>>>>> =
+ Lazy::new(|| Arc::new(RwLock::new(HashMap::new())));
+}
+
+#[derive(Debug)]
+struct MetricTypeNotFoundError(String);
+impl std::error::Error for MetricTypeNotFoundError {}
+impl std::fmt::Display for MetricTypeNotFoundError {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(f, "Metric type {} not found", self.0)
+ }
+}
+
+/// Creates and registers a metric, returning its type+id.
+pub fn create_and_register_metric(
+ metric_type: &str,
+{# The rest of these are handrolled because it proved easier than maintaining a
+map of argument name to argument type. I may regret this if I need it again. #}
+{# In order of util.common_metric_args and util.extra_metric_args, because why not. #}
+ category: String,
+ name: String,
+ send_in_pings: Vec<String>,
+ lifetime: Lifetime,
+ disabled: bool,
+ time_unit: Option<TimeUnit>,
+ memory_unit: Option<MemoryUnit>,
+ allowed_extra_keys: Option<Vec<String>>,
+{# Skipping reason_codes since that's a ping thing. #}
+ range_min: Option<u64>,
+ range_max: Option<u64>,
+ bucket_count: Option<u64>,
+ histogram_type: Option<HistogramType>,
+ numerators: Option<Vec<CommonMetricData>>,
+{# And, don't forget the list of acceptable labels for a labeled metric. #}
+ labels: Option<Vec<Cow<'static, str>>>,
+) -> Result<(u32, u32), Box<dyn std::error::Error>> {
+ let metric_id = NEXT_METRIC_ID.fetch_add(1, Ordering::SeqCst);
+ let metric32 = match metric_type {
+{% for metric_type_name, metric_type in metric_types.items() %}
+ "{{ metric_type_name }}" => {
+ let metric = {{ metric_type_name|Camelize if not metric_type_name.startswith('labeled_') else "Labeled"}}Metric::{% if metric_type_name == 'event' %}with_runtime_extra_keys{% else %}new{% endif %}(metric_id.into(), CommonMetricData {
+ {% for arg_name in common_metric_data_args if arg_name in metric_type.args %}
+ {{ arg_name }},
+ {% endfor %}
+ ..Default::default()
+ }
+ {%- for arg_name in metric_type.args if arg_name not in common_metric_data_args -%}
+ , {{ arg_name }}.unwrap()
+ {%- endfor -%}
+ {%- if metric_type_name.startswith('labeled_') -%}
+ , labels
+ {%- endif -%}
+ );
+ let metric32: u32 = ({{metric_type.id}} << {{ID_BITS}}) | metric_id;
+ assert!(
+ __jog_metric_maps::{{metric_type_name.upper()}}_MAP.write()?.insert(metric_id.into(), metric).is_none(),
+ "We should never insert a runtime metric with an already-used id."
+ );
+ metric32
+ }
+{% endfor %}
+ _ => return Err(Box::new(MetricTypeNotFoundError(metric_type.to_string())))
+ };
+ Ok((metric32, metric_id))
+}
+
+/// Creates and registers a ping, returning its id.
+pub fn create_and_register_ping(
+ ping_name: String,
+ include_client_id: bool,
+ send_if_empty: bool,
+ precise_timestamps: bool,
+ reason_codes: Vec<String>,
+) -> Result<u32, Box<dyn std::error::Error>> {
+ let ping_id = NEXT_PING_ID.fetch_add(1, Ordering::SeqCst);
+ let ping = Ping::new(ping_name, include_client_id, send_if_empty, precise_timestamps, reason_codes);
+ assert!(
+ __jog_metric_maps::PING_MAP.write()?.insert(ping_id.into(), ping).is_none(),
+ "We should never insert a runtime ping with an already-used id."
+ );
+ Ok(ping_id)
+}
diff --git a/toolkit/components/glean/build_scripts/glean_parser_ext/templates/js.jinja2 b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/js.jinja2
new file mode 100644
index 0000000000..aa0d741083
--- /dev/null
+++ b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/js.jinja2
@@ -0,0 +1,170 @@
+// -*- mode: C++ -*-
+
+// AUTOGENERATED BY glean_parser. DO NOT EDIT.
+{# The rendered source is autogenerated, but this
+Jinja2 template is not. Please file bugs! #}
+
+/* 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/GleanJSMetricsLookup.h"
+
+#include "mozilla/PerfectHash.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/glean/bindings/MetricTypes.h"
+#include "mozilla/glean/fog_ffi_generated.h"
+#include "nsString.h"
+
+#define GLEAN_INDEX_BITS ({{index_bits}})
+#define GLEAN_TYPE_BITS ({{type_bits}})
+#define GLEAN_ID_BITS ({{id_bits}})
+#define GLEAN_TYPE_ID(id) ((id) >> GLEAN_ID_BITS)
+#define GLEAN_METRIC_ID(id) ((id) & ((1ULL << GLEAN_ID_BITS) - 1))
+#define GLEAN_OFFSET(entry) (entry & ((1ULL << GLEAN_INDEX_BITS) - 1))
+
+namespace mozilla::glean {
+
+// The category lookup table's entry type
+using category_entry_t = uint32_t;
+// The metric lookup table's entry type
+// This is a bitpacked type with {{index_bits}} bits available to index into
+// the string table, {{type_bits}} bits available to signify the metric type,
+// and the remaining {{id_bits}} bits devoted to {{id_signal_bits}} "signal"
+// bits to signify important characteristics (metric's a labeled metric's
+// submetric, metric's been registered at runtime) and {{id_bits - id_signal_bits}} bits
+// for built-in metric ids.
+// Gives room for {{2 ** (id_bits - id_signal_bits)}} of each combination of
+// characteristics (which hopefully will prove to be enough).
+using metric_entry_t = uint64_t;
+
+static_assert(GLEAN_INDEX_BITS + GLEAN_TYPE_BITS + GLEAN_ID_BITS == sizeof(metric_entry_t) * 8, "Index, Type, and ID bits need to fit into a metric_entry_t");
+static_assert(GLEAN_TYPE_BITS + GLEAN_ID_BITS <= sizeof(uint32_t) * 8, "Metric Types and IDs need to fit into at most 32 bits");
+static_assert({{ categories|length }} < UINT32_MAX, "Too many metric categories generated.");
+static_assert({{ metric_id_mapping|length }} < {{2 ** (id_bits - id_signal_bits)}}, "Too many metrics generated. Need room for {{id_signal_bits}} signal bits.");
+static_assert({{ metric_type_ids|length }} < {{2 ** type_bits}}, "Too many different metric types.");
+
+already_AddRefed<GleanMetric> NewMetricFromId(uint32_t id, nsISupports* aParent) {
+ uint32_t typeId = GLEAN_TYPE_ID(id);
+ uint32_t metricId = GLEAN_METRIC_ID(id);
+
+ switch (typeId) {
+ {% for (type_name, subtype_name), (type_id, original_type) in metric_type_ids.items() %}
+ case {{ type_id }}: /* {{ original_type }} */
+ {
+ return MakeAndAddRef<{{type_name}}>(metricId{% if subtype_name|length > 0 %}, {{ type_id }}{% endif %}, aParent);
+ }
+ {% endfor %}
+ default:
+ MOZ_ASSERT_UNREACHABLE("Invalid type ID reached when trying to instantiate a new metric");
+ return nullptr;
+ }
+}
+
+/**
+ * Create a submetric instance for a labeled metric of the provided type and id for the given label.
+ * Assigns or retrieves an id for the submetric from the SDK.
+ *
+ * @param aParentTypeId - The type of the parent labeled metric identified as a number generated during codegen.
+ * Only used to identify which X of LabeledX you are so that X can be created here.
+ * @param aParentMetricId - The metric id for the parent labeled metric.
+ * @param aLabel - The label for the submetric. Might not adhere to the SDK label format.
+ * @param aSubmetricId - an outparam which is assigned the submetric's SDK-generated submetric id.
+ * Used only by GIFFT.
+ */
+already_AddRefed<GleanMetric> NewSubMetricFromIds(uint32_t aParentTypeId,
+ uint32_t aParentMetricId,
+ const nsACString& aLabel,
+ uint32_t* aSubmetricId,
+ nsISupports* aParent) {
+ switch (aParentTypeId) {
+ {% for (type_name, subtype_name), (type_id, original_type) in metric_type_ids.items() %}
+ {# TODO: Remove the subtype inclusion clause when we suport the rest of labeled_* #}
+ {% if subtype_name|length > 0 and original_type in ['labeled_boolean', 'labeled_counter', 'labeled_string'] %}
+ case {{ type_id }}: { /* {{ original_type }} */
+ auto id = impl::fog_{{original_type}}_get(aParentMetricId, &aLabel);
+ *aSubmetricId = id;
+ return MakeAndAddRef<{{subtype_name}}>(id, aParent);
+ }
+ {% 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);
+
+{{ category_string_table }}
+static_assert(sizeof(gCategoryStringTable) < UINT32_MAX, "Category string table is too large.");
+
+{{ category_by_name_lookup }}
+
+{{ metric_string_table }}
+static_assert(sizeof(gMetricStringTable) < {{2 ** index_bits}}, "Metric string table is too large.");
+
+{{ metric_by_name_lookup }}
+
+/**
+ * Get a category's name from the string table.
+ */
+const char* GetCategoryName(category_entry_t entry) {
+ MOZ_ASSERT(entry < sizeof(gCategoryStringTable), "Entry identifier offset larger than string table");
+ return &gCategoryStringTable[entry];
+}
+
+/**
+ * Get a metric's identifier from the string table.
+ */
+const char* GetMetricIdentifier(metric_entry_t entry) {
+ uint32_t offset = GLEAN_OFFSET(entry);
+ MOZ_ASSERT(offset < sizeof(gMetricStringTable), "Entry identifier offset larger than string table");
+ return &gMetricStringTable[offset];
+}
+
+/**
+ * Check that the found entry is pointing to the right key
+ * and return it.
+ * Or return `Nothing()` if the entry was not found.
+ */
+static Maybe<uint32_t> category_result_check(const nsACString& aKey, category_entry_t entry) {
+ if (MOZ_UNLIKELY(entry > sizeof(gCategoryStringTable))) {
+ return Nothing();
+ }
+ if (aKey.EqualsASCII(gCategoryStringTable + entry)) {
+ return Some(entry);
+ }
+ return Nothing();
+}
+
+/**
+ * Check if the found entry index is pointing to the right key
+ * and return the corresponding metric ID.
+ * Or return `Nothing()` if the entry was not found.
+ */
+static Maybe<uint32_t> metric_result_check(const nsACString& aKey, uint64_t entry) {
+ uint32_t metricId = entry >> GLEAN_INDEX_BITS;
+ uint32_t offset = GLEAN_OFFSET(entry);
+
+ if (offset > sizeof(gMetricStringTable)) {
+ return Nothing();
+ }
+
+ if (aKey.EqualsASCII(gMetricStringTable + offset)) {
+ return Some(metricId);
+ }
+
+ return Nothing();
+}
+
+
+#undef GLEAN_INDEX_BITS
+#undef GLEAN_ID_BITS
+#undef GLEAN_TYPE_ID
+#undef GLEAN_METRIC_ID
+#undef GLEAN_OFFSET
+
+} // namespace mozilla::glean
diff --git a/toolkit/components/glean/build_scripts/glean_parser_ext/templates/js_h.jinja2 b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/js_h.jinja2
new file mode 100644
index 0000000000..1de790be10
--- /dev/null
+++ b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/js_h.jinja2
@@ -0,0 +1,77 @@
+// -*- mode: C++ -*-
+
+// AUTOGENERATED BY glean_parser. DO NOT EDIT.
+{# The rendered source is autogenerated, but this
+Jinja2 template is not. Please file bugs! #}
+
+/* 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_GleanJSMetricsLookup_h
+#define mozilla_GleanJSMetricsLookup_h
+
+#include <cstdint>
+
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/glean/bindings/GleanMetric.h"
+#include "nsStringFwd.h"
+
+class nsISupports;
+
+namespace mozilla::glean {
+
+// The category lookup table's entry type
+using category_entry_t = uint32_t;
+// The metric lookup table's entry type
+// This is a bitpacked type with {{index_bits}} bits available to index into
+// the string table, {{type_bits}} bits available to signify the metric type,
+// and the remaining {{id_bits}} bits devoted to {{id_signal_bits}} "signal"
+// bits to signify important characteristics (metric's a labeled metric's
+// submetric, metric's been registered at runtime) and {{id_bits - id_signal_bits}} bits
+// for built-in metric ids.
+// Gives room for {{2 ** (id_bits - id_signal_bits)}} of each combination of
+// characteristics (which hopefully will prove to be enough).
+using metric_entry_t = uint64_t;
+
+already_AddRefed<GleanMetric> NewMetricFromId(uint32_t id, nsISupports* aParent);
+
+/**
+ * Create a submetric instance for a labeled metric of the provided type and id for the given label.
+ * Assigns or retrieves an id for the submetric from the SDK.
+ *
+ * @param aParentTypeId - The type of the parent labeled metric identified as a number generated during codegen.
+ * Only used to identify which X of LabeledX you are so that X can be created here.
+ * @param aParentMetricId - The metric id for the parent labeled metric.
+ * @param aLabel - The label for the submetric. Might not adhere to the SDK label format.
+ * @param aSubmetricId - an outparam which is assigned the submetric's SDK-generated submetric id.
+ * Used only by GIFFT.
+ */
+already_AddRefed<GleanMetric> NewSubMetricFromIds(uint32_t aParentTypeId, uint32_t aParentMetricId, const nsACString& aLabel, uint32_t* aSubmetricId, nsISupports* aParent);
+
+/**
+ * Get a category's name from the string table.
+ */
+const char* GetCategoryName(category_entry_t entry);
+
+/**
+ * Get a metric's identifier from the string table.
+ */
+const char* GetMetricIdentifier(metric_entry_t entry);
+
+/**
+ * Get a metric's id given its name.
+ */
+Maybe<uint32_t> MetricByNameLookup(const nsACString&);
+
+/**
+ * Get a category's id given its name.
+ */
+Maybe<uint32_t> CategoryByNameLookup(const nsACString&);
+
+extern const category_entry_t sCategoryByNameLookupEntries[{{num_categories}}];
+extern const metric_entry_t sMetricByNameLookupEntries[{{num_metrics}}];
+
+} // namespace mozilla::glean
+#endif // mozilla_GleanJSMetricsLookup_h
diff --git a/toolkit/components/glean/build_scripts/glean_parser_ext/templates/js_pings.jinja2 b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/js_pings.jinja2
new file mode 100644
index 0000000000..4fa43832a6
--- /dev/null
+++ b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/js_pings.jinja2
@@ -0,0 +1,67 @@
+// -*- mode: C++ -*-
+
+// AUTOGENERATED BY glean_parser. DO NOT EDIT.
+{# The rendered source is autogenerated, but this
+Jinja2 template is not. Please file bugs! #}
+
+/* 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/GleanJSPingsLookup.h"
+
+#include "mozilla/PerfectHash.h"
+#include "nsString.h"
+
+#include "mozilla/PerfectHash.h"
+
+#define GLEAN_PING_INDEX_BITS ({{ping_index_bits}})
+#define GLEAN_PING_ID(entry) ((entry) >> GLEAN_PING_INDEX_BITS)
+#define GLEAN_PING_INDEX(entry) ((entry) & ((1UL << GLEAN_PING_INDEX_BITS) - 1))
+
+namespace mozilla::glean {
+
+// Contains the ping id and the index into the ping string table.
+using ping_entry_t = uint32_t;
+
+Maybe<uint32_t> ping_result_check(const nsACString& aKey, ping_entry_t aEntry);
+
+{{ ping_string_table }}
+
+{{ ping_by_name_lookup }}
+
+/**
+ * Get a ping's name given its entry from the PHF.
+ */
+const char* GetPingName(ping_entry_t aEntry) {
+ uint32_t idx = GLEAN_PING_INDEX(aEntry);
+ MOZ_ASSERT(idx < sizeof(gPingStringTable), "Ping index larger than string table");
+ return &gPingStringTable[idx];
+}
+
+/**
+ * Check if the found entry is pointing at the correct ping.
+ * PHF can false-positive a result when the key isn't present, so we check
+ * for a string match. If it fails, return Nothing(). If we found it,
+ * return the ping's id.
+ */
+Maybe<uint32_t> ping_result_check(const nsACString& aKey, ping_entry_t aEntry) {
+ uint32_t idx = GLEAN_PING_INDEX(aEntry);
+ uint32_t id = GLEAN_PING_ID(aEntry);
+
+ if (MOZ_UNLIKELY(idx > sizeof(gPingStringTable))) {
+ return Nothing();
+ }
+
+ if (aKey.EqualsASCII(&gPingStringTable[idx])) {
+ return Some(id);
+ }
+
+ return Nothing();
+}
+
+#undef GLEAN_PING_INDEX_BITS
+#undef GLEAN_PING_ID
+#undef GLEAN_PING_INDEX
+
+} // namespace mozilla::glean
diff --git a/toolkit/components/glean/build_scripts/glean_parser_ext/templates/js_pings_h.jinja2 b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/js_pings_h.jinja2
new file mode 100644
index 0000000000..3052b8549b
--- /dev/null
+++ b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/js_pings_h.jinja2
@@ -0,0 +1,35 @@
+// -*- mode: C++ -*-
+
+// AUTOGENERATED BY glean_parser. DO NOT EDIT.
+{# The rendered source is autogenerated, but this
+Jinja2 template is not. Please file bugs! #}
+
+/* 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_GleanJSPingsLookup_h
+#define mozilla_GleanJSPingsLookup_h
+
+#include <cstdint>
+#include "mozilla/Maybe.h"
+#include "nsStringFwd.h"
+
+namespace mozilla::glean {
+
+// Contains the ping id and the index into the ping string table.
+using ping_entry_t = uint32_t;
+
+/**
+ * Get a ping's name given its entry in the PHF.
+ */
+const char* GetPingName(ping_entry_t aEntry);
+
+/**
+ * Get a ping's id given its name.
+ */
+Maybe<uint32_t> PingByNameLookup(const nsACString&);
+
+extern const ping_entry_t sPingByNameLookupEntries[{{num_pings}}];
+} // namespace mozilla::glean
+#endif // mozilla_GleanJSPingsLookup_h
diff --git a/toolkit/components/glean/build_scripts/glean_parser_ext/templates/rust.jinja2 b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/rust.jinja2
new file mode 100644
index 0000000000..cc29805099
--- /dev/null
+++ b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/rust.jinja2
@@ -0,0 +1,355 @@
+// -*- mode: Rust -*-
+
+// AUTOGENERATED BY glean_parser. DO NOT EDIT.
+{# The rendered source is autogenerated, but this
+Jinja2 template is not. Please file bugs! #}
+
+/* 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/. */
+
+{% macro generate_extra_keys(obj) -%}
+{% for name, _ in obj["_generate_enums"] %}
+{# we always use the `extra` suffix, because we only expose the new event API #}
+{% set suffix = "Extra" %}
+{% if obj|attr(name)|length %}
+ {{ extra_keys_with_types(obj, name, suffix)|indent }}
+{% endif %}
+{% endfor %}
+{%- endmacro -%}
+
+{%- macro extra_keys_with_types(obj, name, suffix) -%}
+#[derive(Default, Debug, Clone, Hash, Eq, PartialEq)]
+pub struct {{ obj.name|Camelize }}{{ suffix }} {
+ {% for item, type in obj|attr(name) %}
+ pub {{ item|snake_case }}: Option<{{type|extra_type_name}}>,
+ {% endfor %}
+}
+
+impl ExtraKeys for {{ obj.name|Camelize }}{{ suffix }} {
+ const ALLOWED_KEYS: &'static [&'static str] = {{ obj.allowed_extra_keys|extra_keys }};
+
+ fn into_ffi_extra(self) -> ::std::collections::HashMap<String, String> {
+ let mut map = ::std::collections::HashMap::new();
+ {% for key, _ in obj|attr(name) %}
+ self.{{key|snake_case}}.and_then(|val| map.insert("{{key|snake_case}}".into(), val.to_string()));
+ {% endfor %}
+ map
+ }
+}
+{%- endmacro -%}
+
+{% macro generate_label_enum(obj) %}
+#[repr(u16)]
+pub enum {{ obj.name|Camelize }}Label {
+ {% for label in obj.ordered_labels %}
+ {# Specifically _not_ using r# as C++ doesn't have it #}
+ {{ label|Camelize }} = {{loop.index0}},
+ {% endfor %}
+ __Other__,
+}
+impl From<u16> for {{ obj.name|Camelize }}Label {
+ fn from(v: u16) -> Self {
+ match v {
+ {% for label in obj.ordered_labels %}
+ {{ loop.index0 }} => Self::{{ label|Camelize }},
+ {% endfor %}
+ _ => Self::__Other__,
+ }
+ }
+}
+impl Into<&'static str> for {{ obj.name|Camelize }}Label {
+ fn into(self) -> &'static str {
+ match self {
+ {% for label in obj.ordered_labels %}
+ Self::{{ label| Camelize }} => "{{label}}",
+ {% endfor %}
+ Self::__Other__ => "__other__",
+ }
+ }
+}
+{%- endmacro -%}
+
+pub enum DynamicLabel { }
+
+{% for category_name, objs in all_objs.items() %}
+pub mod {{ category_name|snake_case }} {
+ use crate::private::*;
+ #[allow(unused_imports)] // CommonMetricData might be unused, let's avoid warnings
+ use glean::CommonMetricData;
+ #[allow(unused_imports)] // HistogramType might be unusued, let's avoid warnings
+ use glean::HistogramType;
+ use once_cell::sync::Lazy;
+
+ {% for obj in objs.values() %}
+ {% if obj|attr("_generate_enums") %}
+{{ generate_extra_keys(obj) }}
+ {%- endif %}
+ {% if obj.labeled and obj.labels and obj.labels|length %}
+ {{ generate_label_enum(obj)|indent }}
+ {% endif %}
+ #[allow(non_upper_case_globals)]
+ /// generated from {{ category_name }}.{{ obj.name }}
+ ///
+ /// {{ obj.description|wordwrap() | replace('\n', '\n /// ') }}
+ {% if obj.type == "counter" and obj.send_in_pings|length == 1 and not obj.disabled and obj.lifetime|rust == "Lifetime::Ping" %}
+ {# Use optimized CounterMetric ctor in a common case (esp. for Use Counters) #}
+ pub static {{ obj.name|snake_case }}: Lazy<{{ obj|type_name }}> = Lazy::new(|| {
+ CounterMetric::codegen_new(
+ {{obj|metric_id}},
+ "{{obj.category}}",
+ "{{obj.name}}",
+ "{{obj.send_in_pings[0]}}"
+ )
+ });
+ {% else %}
+ pub static {{ obj.name|snake_case }}: Lazy<{{ obj|type_name }}> = Lazy::new(|| {
+ {{ obj|ctor }}({{obj|metric_id}}.into(), CommonMetricData {
+ {% for arg_name in common_metric_data_args if obj[arg_name] is defined %}
+ {{ arg_name }}: {{ obj[arg_name]|rust }},
+ {% endfor %}
+ ..Default::default()
+ }
+ {%- for arg_name in extra_args if obj[arg_name] is defined and arg_name not in common_metric_data_args and arg_name != 'allowed_extra_keys' -%}
+ , {{ obj[arg_name]|rust }}
+ {%- endfor -%}
+ {{ ", " if obj.labeled else ")\n" }}
+ {%- if obj.labeled -%}
+ {%- if obj.labels -%}
+ Some({{ obj.labels|rust }})
+ {%- else -%}
+ None
+ {%- endif -%})
+ {% endif %}
+ });
+ {% endif %}
+
+ {% endfor %}
+}
+{% endfor %}
+
+{% if metric_by_type|length > 0 %}
+#[allow(dead_code)]
+pub(crate) mod __glean_metric_maps {
+ use std::collections::HashMap;
+
+ use crate::metrics::extra_keys_len;
+ use crate::private::*;
+ use once_cell::sync::Lazy;
+
+{% for typ, metrics in metric_by_type.items() %}
+ pub static {{typ.0}}: Lazy<HashMap<MetricId, &Lazy<{{typ.1}}>>> = Lazy::new(|| {
+ let mut map = HashMap::with_capacity({{metrics|length}});
+ {% for metric in metrics %}
+ map.insert({{metric.0}}.into(), &super::{{metric.1}});
+ {% endfor %}
+ map
+ });
+
+{% endfor %}
+
+ /// Wrapper to record an event based on its metric ID.
+ ///
+ /// # Arguments
+ ///
+ /// * `metric_id` - The metric's ID to look up
+ /// * `extra` - An map of (extra key id, string) pairs.
+ /// The map will be decoded into the appropriate `ExtraKeys` type.
+ /// # Returns
+ ///
+ /// Returns `Ok(())` if the event was found and `record` was called with the given `extra`,
+ /// or an `EventRecordingError::InvalidId` if no event by that ID exists
+ /// or an `EventRecordingError::InvalidExtraKey` if the `extra` map could not be deserialized.
+ pub(crate) fn record_event_by_id(metric_id: u32, extra: HashMap<String, String>) -> Result<(), EventRecordingError> {
+ match metric_id {
+{% for metric_id, event in events_by_id.items() %}
+ {{metric_id}} => {
+ assert!(
+ extra_keys_len(&super::{{event}}) != 0 || extra.is_empty(),
+ "No extra keys allowed, but some were passed"
+ );
+
+ super::{{event}}.record_raw(extra);
+ Ok(())
+ }
+{% endfor %}
+ _ => Err(EventRecordingError::InvalidId),
+ }
+ }
+
+ /// Wrapper to record an event based on its metric ID, with a provided timestamp.
+ ///
+ /// # Arguments
+ ///
+ /// * `metric_id` - The metric's ID to look up
+ /// * `timestamp` - The time at which this event was recorded.
+ /// * `extra` - An map of (extra key id, string) pairs.
+ /// The map will be decoded into the appropriate `ExtraKeys` type.
+ /// # Returns
+ ///
+ /// Returns `Ok(())` if the event was found and `record` was called with the given `extra`,
+ /// or an `EventRecordingError::InvalidId` if no event by that ID exists
+ /// or an `EventRecordingError::InvalidExtraKey` if the event doesn't take extra pairs,
+ /// but some are passed in.
+ pub(crate) fn record_event_by_id_with_time(metric_id: MetricId, timestamp: u64, extra: HashMap<String, String>) -> Result<(), EventRecordingError> {
+ match metric_id {
+{% for metric_id, event in events_by_id.items() %}
+ MetricId({{metric_id}}) => {
+ if extra_keys_len(&super::{{event}}) == 0 && !extra.is_empty() {
+ return Err(EventRecordingError::InvalidExtraKey);
+ }
+
+ super::{{event}}.record_with_time(timestamp, extra);
+ Ok(())
+ }
+{% endfor %}
+ _ => Err(EventRecordingError::InvalidId),
+ }
+ }
+
+ /// Wrapper to get the currently stored events for event metric.
+ ///
+ /// # Arguments
+ ///
+ /// * `metric_id` - The metric's ID to look up
+ /// * `ping_name` - (Optional) The ping name to look into.
+ /// Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// Returns the recorded events or `None` if nothing stored.
+ ///
+ /// # Panics
+ ///
+ /// Panics if no event by the given metric ID could be found.
+ pub(crate) fn event_test_get_value_wrapper(metric_id: u32, ping_name: Option<String>) -> Option<Vec<RecordedEvent>> {
+ match metric_id {
+{% for metric_id, event in events_by_id.items() %}
+ {{metric_id}} => super::{{event}}.test_get_value(ping_name.as_deref()),
+{% endfor %}
+ _ => panic!("No event for metric id {}", metric_id),
+ }
+ }
+
+ /// Check the provided event for errors.
+ ///
+ /// # Arguments
+ ///
+ /// * `metric_id` - The metric's ID to look up
+ /// * `ping_name` - (Optional) The ping name to look into.
+ /// Defaults to the first value in `send_in_pings`.
+ ///
+ /// # Returns
+ ///
+ /// Returns a string for the recorded error or `None`.
+ ///
+ /// # Panics
+ ///
+ /// Panics if no event by the given metric ID could be found.
+ #[allow(unused_variables)]
+ pub(crate) fn event_test_get_error(metric_id: u32) -> Option<String> {
+ #[cfg(feature = "with_gecko")]
+ match metric_id {
+{% for metric_id, event in events_by_id.items() %}
+ {{metric_id}} => test_get_errors!(super::{{event}}),
+{% endfor %}
+ _ => panic!("No event for metric id {}", metric_id),
+ }
+
+ #[cfg(not(feature = "with_gecko"))]
+ {
+ return None;
+ }
+ }
+
+{% for labeled_type, labeleds_by_id in labeleds_by_id_by_type.items() %}
+ /// Gets the submetric from the specified labeled_{{labeled_type}} metric.
+ ///
+ /// # Arguments
+ ///
+ /// * `metric_id` - The metric's ID to look up
+ /// * `label` - The label identifying the {{labeled_type}} submetric.
+ ///
+ /// # Returns
+ ///
+ /// Returns the {{labeled_type}} submetric.
+ ///
+ /// # Panics
+ ///
+ /// Panics if no labeled_{{labeled_type}} by the given metric ID could be found.
+ #[allow(unused_variables)]
+ pub(crate) fn labeled_{{labeled_type}}_get(metric_id: u32, label: &str) -> Labeled{{labeled_type|Camelize}}Metric {
+ match metric_id {
+{% for metric_id, (labeled, _) in labeleds_by_id.items() %}
+ {{metric_id}} => super::{{labeled}}.get(label),
+{% endfor %}
+ _ => panic!("No labeled_{{labeled_type}} for metric id {}", metric_id),
+ }
+ }
+
+ /// Gets the submetric from the specified labeled_{{labeled_type}} metric, by enum.
+ ///
+ /// # Arguments
+ ///
+ /// * `metric_id` - The metric's ID to look up
+ /// * `label_enum` - The label enum identifying the {{labeled_type}} submetric.
+ ///
+ /// # Returns
+ ///
+ /// Returns the {{labeled_type}} submetric.
+ ///
+ /// # Panics
+ ///
+ /// Panics if no labeled_{{labeled_type}} by the given metric ID could be found.
+ #[allow(unused_variables)]
+ pub(crate) fn labeled_{{labeled_type}}_enum_get(metric_id: u32, label_enum: u16) -> Labeled{{labeled_type|Camelize}}Metric {
+ match metric_id {
+{% for metric_id, (labeled, has_enum) in labeleds_by_id.items() %}
+{% if has_enum %}
+ {{metric_id}} => super::{{labeled}}.get(labeled_enum_to_str(metric_id, label_enum)),
+{% endif %}
+{% endfor %}
+ _ => panic!("No labeled_{{labeled_type}} for metric id {}", metric_id),
+ }
+ }
+{% endfor %}
+
+ pub(crate) fn labeled_enum_to_str(metric_id: u32, label: u16) -> &'static str {
+ match metric_id {
+{% for category_name, objs in all_objs.items() %}
+{% for obj in objs.values() %}
+{% if obj.labeled and obj.labels and obj.labels|length %}
+ {{obj|metric_id}} => super::{{category_name|snake_case}}::{{obj.name|Camelize}}Label::from(label).into(),
+{% endif %}
+{% endfor %}
+{% endfor %}
+ _ => panic!("Can't turn label enum to string for metric {} which isn't a labeled metric with static labels", metric_id),
+ }
+ }
+
+ pub(crate) mod submetric_maps {
+ use std::sync::{
+ atomic::AtomicU32,
+ RwLock,
+ };
+ use super::*;
+
+ pub(crate) const SUBMETRIC_BIT: u32 = {{submetric_bit}};
+ pub(crate) static NEXT_LABELED_SUBMETRIC_ID: AtomicU32 = AtomicU32::new((1 << SUBMETRIC_BIT) + 1);
+ pub(crate) static LABELED_METRICS_TO_IDS: Lazy<RwLock<HashMap<(u32, String), u32>>> = Lazy::new(||
+ RwLock::new(HashMap::new())
+ );
+ pub(crate) static LABELED_ENUMS_TO_IDS: Lazy<RwLock<HashMap<(u32, u16), u32>>> = Lazy::new(||
+ RwLock::new(HashMap::new())
+ );
+
+{% for typ, metrics in metric_by_type.items() %}
+{% if typ.0 in ('BOOLEAN_MAP', 'COUNTER_MAP', 'STRING_MAP') %}
+ pub static {{typ.0}}: Lazy<RwLock<HashMap<MetricId, Labeled{{typ.1}}>>> = Lazy::new(||
+ RwLock::new(HashMap::new())
+ );
+{% endif %}
+{% endfor%}
+ }
+}
+{% endif %}
diff --git a/toolkit/components/glean/build_scripts/glean_parser_ext/templates/rust_pings.jinja2 b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/rust_pings.jinja2
new file mode 100644
index 0000000000..c041f663a6
--- /dev/null
+++ b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/rust_pings.jinja2
@@ -0,0 +1,77 @@
+// -*- mode: Rust -*-
+
+// AUTOGENERATED BY glean_parser. DO NOT EDIT.
+{# The rendered source is autogenerated, but this
+Jinja2 template is not. Please file bugs! #}
+
+/* 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/. */
+
+use crate::private::Ping;
+use once_cell::sync::Lazy;
+
+{% for obj in all_objs['pings'].values() %}
+#[allow(non_upper_case_globals)]
+/// {{ obj.description|wordwrap() | replace('\n', '\n/// ') }}
+pub static {{ obj.name|snake_case }}: Lazy<Ping> = Lazy::new(|| {
+ Ping::new(
+ "{{ obj.name }}",
+ {{ obj.include_client_id|rust }},
+ {{ obj.send_if_empty|rust }},
+ {{ obj.precise_timestamps|rust }},
+ {{ obj.reason_codes|rust }},
+ )
+});
+
+{% endfor %}
+
+/// Instantiate custom pings once to trigger registration.
+///
+/// # Arguments
+///
+/// application_id: If present, limit to only registering custom pings
+/// assigned to the identified application.
+#[doc(hidden)]
+pub fn register_pings(application_id: Option<&str>) {
+ match application_id {
+ {% for id, ping_names in ping_names_by_app_id.items() %}
+ Some("{{id}}") => {
+ log::info!("Registering pings {{ ping_names|join(', ') }} for {{id}}");
+ {% for ping_name in ping_names %}
+ let _ = &*{{ ping_name|snake_case }};
+ {% endfor %}
+ },
+ {% endfor %}
+ _ => {
+ {% for obj in all_objs['pings'].values() %}
+ let _ = &*{{ obj.name|snake_case }};
+ {% endfor %}
+ }
+ }
+}
+
+#[cfg(feature = "with_gecko")]
+pub(crate) fn submit_ping_by_id(id: u32, reason: Option<&str>) {
+ if id & (1 << crate::factory::DYNAMIC_PING_BIT) > 0 {
+ let map = crate::factory::__jog_metric_maps::PING_MAP
+ .read()
+ .expect("Read lock for dynamic ping map was poisoned!");
+ if let Some(ping) = map.get(&id) {
+ ping.submit(reason);
+ } else {
+ // TODO: instrument this error.
+ log::error!("Cannot submit unknown dynamic ping {} by id.", id);
+ }
+ return;
+ }
+ match id {
+{% for obj in all_objs['pings'].values() %}
+ {{ obj.name|ping_id }} => {{ obj.name | snake_case }}.submit(reason),
+{% endfor %}
+ _ => {
+ // TODO: instrument this error.
+ log::error!("Cannot submit unknown ping {} by id.", id);
+ }
+ }
+}