Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions glean-core/glean-sym/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ pub mod metrics;
pub mod types;
mod util;

pub use types::PingType;

static GLEAN: LazyLock<metrics::GleanSym> = LazyLock::new(|| metrics::GleanSym::load().unwrap());

// This boilerplate is usually generated by `uniffi::setup_scaffolding!()`
Expand Down
126 changes: 125 additions & 1 deletion glean-core/glean-sym/src/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,118 @@
use crate::types::*;
use crate::util::*;

#[derive(uniffi::Record)]
pub(crate) struct PingType {
handle: u64,
}
impl PingType {
unsafe fn clone_handle(&self) -> u64 {
unsafe {
let mut call_status = uniffi::RustCallStatus::default();
(crate::GLEAN
.uniffi_glean_core_fn_clone_pingtype)(self.handle, &mut call_status)
}
}
}
impl PingType {
pub fn new(
name: String,
include_client_id: bool,
send_if_empty: bool,
precise_timestamps: bool,
include_info_sections: bool,
enabled: bool,
schedules_pings: Vec<String>,
reason_codes: Vec<String>,
follows_collection_enabled: bool,
uploader_capabilities: Vec<String>,
) -> Self {
unsafe {
let name = uniffi::FfiConverter::<crate::UniFfiTag>::lower(name);
let include_client_id = uniffi::FfiConverter::<
crate::UniFfiTag,
>::lower(include_client_id);
let send_if_empty = uniffi::FfiConverter::<
crate::UniFfiTag,
>::lower(send_if_empty);
let precise_timestamps = uniffi::FfiConverter::<
crate::UniFfiTag,
>::lower(precise_timestamps);
let include_info_sections = uniffi::FfiConverter::<
crate::UniFfiTag,
>::lower(include_info_sections);
let enabled = uniffi::FfiConverter::<crate::UniFfiTag>::lower(enabled);
let schedules_pings = uniffi::FfiConverter::<
crate::UniFfiTag,
>::lower(schedules_pings);
let reason_codes = uniffi::FfiConverter::<
crate::UniFfiTag,
>::lower(reason_codes);
let follows_collection_enabled = uniffi::FfiConverter::<
crate::UniFfiTag,
>::lower(follows_collection_enabled);
let uploader_capabilities = uniffi::FfiConverter::<
crate::UniFfiTag,
>::lower(uploader_capabilities);
let mut call_status = uniffi::RustCallStatus::default();
let handle = (crate::GLEAN
.uniffi_glean_core_fn_constructor_pingtype_new)(
name.clone_for_ffi(),
include_client_id.clone_for_ffi(),
send_if_empty.clone_for_ffi(),
precise_timestamps.clone_for_ffi(),
include_info_sections.clone_for_ffi(),
enabled.clone_for_ffi(),
schedules_pings.clone_for_ffi(),
reason_codes.clone_for_ffi(),
follows_collection_enabled.clone_for_ffi(),
uploader_capabilities.clone_for_ffi(),
&mut call_status,
);
uploader_capabilities.destroy();
follows_collection_enabled.destroy();
reason_codes.destroy();
schedules_pings.destroy();
enabled.destroy();
include_info_sections.destroy();
precise_timestamps.destroy();
send_if_empty.destroy();
include_client_id.destroy();
name.destroy();
Self { handle }
}
}
pub fn submit(&self, reason: Option<String>) -> () {
unsafe {
let this = self.clone_handle();
let reason = uniffi::FfiConverter::<crate::UniFfiTag>::lower(reason);
let mut call_status = uniffi::RustCallStatus::default();
let res = (crate::GLEAN
.uniffi_glean_core_fn_method_pingtype_submit)(
this,
reason.clone_for_ffi(),
&mut call_status,
);
reason.destroy();
crate::util::LocalTryLift::try_lift(res).unwrap()
}
}
pub fn set_enabled(&self, enabled: bool) -> () {
unsafe {
let this = self.clone_handle();
let enabled = uniffi::FfiConverter::<crate::UniFfiTag>::lower(enabled);
let mut call_status = uniffi::RustCallStatus::default();
let res = (crate::GLEAN
.uniffi_glean_core_fn_method_pingtype_set_enabled)(
this,
enabled.clone_for_ffi(),
&mut call_status,
);
enabled.destroy();
crate::util::LocalTryLift::try_lift(res).unwrap()
}
}
}
#[derive(uniffi::Record)]
pub struct CounterMetric {
handle: u64,
Expand Down Expand Up @@ -1694,8 +1806,20 @@ library_binding! {
& mut ::uniffi::RustCallStatus) -> ::uniffi::RustBuffer; fn
ffi_glean_core_uniffi_contract_version() -> u32; fn
ffi_glean_core_rustbuffer_free(bytes : ::uniffi::RustBuffer, call_status : & mut
::uniffi::RustCallStatus); fn uniffi_glean_core_fn_clone_countermetric(handle : u64,
::uniffi::RustCallStatus); fn uniffi_glean_core_fn_clone_pingtype(handle : u64,
call_status : & mut ::uniffi::RustCallStatus) -> u64; fn
uniffi_glean_core_fn_constructor_pingtype_new(name : uniffi::RustBuffer,
include_client_id : i8, send_if_empty : i8, precise_timestamps : i8,
include_info_sections : i8, enabled : i8, schedules_pings : uniffi::RustBuffer,
reason_codes : uniffi::RustBuffer, follows_collection_enabled : i8,
uploader_capabilities : uniffi::RustBuffer, call_status : & mut
::uniffi::RustCallStatus) -> u64; fn
uniffi_glean_core_fn_method_pingtype_submit(handle : u64, reason :
uniffi::RustBuffer, call_status : & mut ::uniffi::RustCallStatus) -> (); fn
uniffi_glean_core_fn_method_pingtype_set_enabled(handle : u64, enabled : i8,
call_status : & mut ::uniffi::RustCallStatus) -> (); fn
uniffi_glean_core_fn_clone_countermetric(handle : u64, call_status : & mut
::uniffi::RustCallStatus) -> u64; fn
uniffi_glean_core_fn_constructor_countermetric_new(meta : uniffi::RustBuffer,
call_status : & mut ::uniffi::RustCallStatus) -> u64; fn
uniffi_glean_core_fn_method_countermetric_add(handle : u64, amount : i32, call_status
Expand Down
58 changes: 58 additions & 0 deletions glean-core/glean-sym/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,3 +206,61 @@ impl<K: ExtraKeys> EventMetric<K> {
self.inner.test_get_num_recorded_errors(error)
}
}

pub struct PingType {
inner: crate::metrics::PingType,
}

impl PingType {
/// Creates a new ping type.
///
/// # Arguments
///
/// * `name` - The name of the ping.
/// * `include_client_id` - Whether to include the client ID in the assembled ping when.
/// * `send_if_empty` - Whether the ping should be sent empty or not.
/// * `precise_timestamps` - Whether the ping should use precise timestamps for the start and end time.
/// * `include_info_sections` - Whether the ping should include the client/ping_info sections.
/// * `enabled` - Whether or not this ping is enabled. Note: Data that would be sent on a disabled
/// ping will still be collected and is discarded instead of being submitted.
/// * `schedules_pings` - A list of pings which are triggered for submission when this ping is
/// submitted.
/// * `reason_codes` - The valid reason codes for this ping.
/// * `uploader_capabilities` - The capabilities required during this ping's upload.
#[allow(clippy::too_many_arguments)]
pub fn new<A: Into<String>>(
name: A,
include_client_id: bool,
send_if_empty: bool,
precise_timestamps: bool,
include_info_sections: bool,
enabled: bool,
schedules_pings: Vec<String>,
reason_codes: Vec<String>,
follows_collection_enabled: bool,
uploader_capabilities: Vec<String>,
) -> Self {
let inner = crate::metrics::PingType::new(
name.into(),
include_client_id,
send_if_empty,
precise_timestamps,
include_info_sections,
enabled,
schedules_pings,
reason_codes,
follows_collection_enabled,
uploader_capabilities,
);

Self { inner }
}

pub fn submit(&self, reason: Option<&str>) {
self.inner.submit(reason.map(|s| s.to_string()))
}

pub fn set_enabled(&self, enabled: bool) {
self.inner.set_enabled(enabled)
}
}
47 changes: 28 additions & 19 deletions samples/glean-sym-test/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,27 @@ def library_name(name):
suffix = "dylib" if platform.system() == "Darwin" else "so"
return f"lib{name}.{suffix}"

def check_ping_data(ping_type, sent_ping, amount):
data = open(sent_ping).read()
end_first_object = data.find("}")
payload = json.loads(data[end_first_object + 1 :])
counter = payload["metrics"]["counter"]
events = payload["events"]

if ping_type == "prototype":
assert 1 == counter["test.metrics.sample_counter"]

assert amount == counter["dylib.counting"]

assert 2 == len(events)

no_extra = events[0]
assert "event" == no_extra["name"]

with_extra = events[1]
assert "event_with_extras" == with_extra["name"]
extras = with_extra["extra"]
assert "true", extras["is_set"]

def test_run():
xul = cdll.LoadLibrary(library_name("xul"))
Expand All @@ -39,25 +60,13 @@ def test_run():
# * It contains several metrics with the expected values
path = os.path.join(data_path, "sent_pings")
for root, dirs, files in os.walk(path):
assert len(files) == 1
assert "prototype-" in files[0]
assert len(files) == 2
files = sorted(files)

assert "prototype-" in files[0]
sent_ping = os.path.join(path, files[0])
data = open(sent_ping).read()
end_first_object = data.find("}")
payload = json.loads(data[end_first_object + 1 :])
counter = payload["metrics"]["counter"]
events = payload["events"]

assert 1 == counter["test.metrics.sample_counter"]
assert amount == counter["dylib.counting"]

assert 2 == len(events)

no_extra = events[0]
assert "event" == no_extra["name"]
check_ping_data("prototype", sent_ping, amount)

with_extra = events[1]
assert "event_with_extras" == with_extra["name"]
extras = with_extra["extra"]
assert "true", extras["is_set"]
assert "services-info-" in files[1]
sent_ping = os.path.join(path, files[1])
check_ping_data("services-info", sent_ping, amount)
1 change: 1 addition & 0 deletions samples/glean-sym-test/services/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use glean_build::Builder;
fn main() {
Builder::default()
.file("metrics.yaml")
.file("pings.yaml")
.format("rust_sym")
.generate()
.expect("Error generating Glean Rust bindings");
Expand Down
3 changes: 3 additions & 0 deletions samples/glean-sym-test/services/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub mod glean_metrics {
#[unsafe(no_mangle)]
unsafe extern "C" fn record(amount: i32) {
env_logger::init();
let _ = &*glean_metrics::services_info;
log::info!("Record invoked");

// A timer ID is passed through a `RustBuffer`,
Expand All @@ -37,4 +38,6 @@ unsafe extern "C" fn record(amount: i32) {
glean_metrics::dylib::event_with_extras.record(extra);

glean_metrics::dylib::timing.stop_and_accumulate(tid);

glean_metrics::services_info.submit(Some("recorded"));
}
5 changes: 5 additions & 0 deletions samples/glean-sym-test/services/metrics.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ dylib:
expires: never
send_in_pings:
- prototype
- services-info

timing:
type: timing_distribution
Expand All @@ -34,6 +35,7 @@ dylib:
expires: never
send_in_pings:
- prototype
- services-info

data:
type: string
Expand All @@ -48,6 +50,7 @@ dylib:
expires: never
send_in_pings:
- prototype
- services-info

event:
type: event
Expand All @@ -62,6 +65,7 @@ dylib:
expires: never
send_in_pings:
- prototype
- services-info

event_with_extras:
type: event
Expand All @@ -76,6 +80,7 @@ dylib:
expires: never
send_in_pings:
- prototype
- services-info
extra_keys:
is_set:
type: boolean
Expand Down
25 changes: 25 additions & 0 deletions samples/glean-sym-test/services/pings.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# 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 defines the pings that are recorded by the Glean SDK.
# Their code APIs is automatically generated, at build time using,
# the `glean_parser` PyPI package.

---
$schema: moz://mozilla.org/schemas/glean/pings/2-0-0

services-info:
description: |
A test ping sent from a Rust library.
include_client_id: true
send_if_empty: false
bugs:
- https://bugzilla.mozilla.org/123456789
data_reviews:
- N/A
notification_emails:
- CHANGE-ME@example.com
reasons:
recorded: |
Recorded from Rust
9 changes: 4 additions & 5 deletions tools/glean-sym-parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,17 +84,16 @@ pub fn generate(content: &str) -> String {
for elem in parsed {
let Interface(iface) = elem else { continue };
let ident = iface.identifier;
if !ident.0.ends_with("Metric") {
if !ident.0.ends_with("Metric") && ident.0 != "PingType" {
continue;
}

let structname = ident.0.to_lowercase().replace("_", "");
let ident = format_ident!("{}", ident.0);
let extern_fn_ident = format_ident!("uniffi_glean_core_fn_clone_{}", structname);
let visibility = if structname == "eventmetric" {
quote! { pub(crate) }
} else {
quote! { pub }
let visibility = match &*structname {
"eventmetric" | "pingtype" => quote! { pub(crate) },
_ => quote! { pub },
};
tokens.push(quote! {
#[derive(uniffi::Record)]
Expand Down
Loading