diff options
Diffstat (limited to 'toolkit/components/glean/build_scripts/glean_parser_ext/templates')
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); + } + } +} |