Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/toolkit/crashreporter/crashping/src/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 21 kB image not shown  

Quelle  lib.rs

  Sprache: Rust
 

/* 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 https://mozilla.org/MPL/2.0/. */


//! The crashping crate allows populating and sending the crash ping, which contains all
//! ping-scoped crash annotations.

pub use glean::net;
pub use glean::ClientInfoMetrics;
use glean::{Configuration, ConfigurationBuilder};
use std::path::PathBuf;

mod glean_metrics {
    // Env variable set to the file generated by glean_rust.py (by build.rs).
    include!(env!("GLEAN_METRICS_FILE"));
}
mod annotations;
mod single_instance;

use annotations::ANNOTATIONS;

const TELEMETRY_SERVER: &str = "https://incoming.telemetry.mozilla.org";

/// Initialize the Glean ping. This must be called _before_ Glean initialization.
///
/// Since Glean v63.0.0, custom pings are required to be instantiated prior to Glean init
/// in order to ensure they are enabled and able to collect data. This is due to the data
/// collection state being determined at the ping level now instead of just by the global
/// Glean collection enabled flag. See Bug 1934931 for more information.
pub fn init() {
    _ = &*glean_metrics::crash;
}

/// Initialize Glean and the Glean ping.
///
/// If using this type, you do not need to call `init()`. This is intended for use in runtimes that
/// are only using Glean to send the crash ping.
///
/// You should be sure to set an `uploader` on the `configuration` before initializing. It is
/// recommended to set the `ClientInfoMetrics` fields to more useful values as well.
pub struct InitGlean {
    pub configuration: Configuration,
    pub client_info_metrics: ClientInfoMetrics,
    pub clear_uploader_for_tests: bool,
}

/// A handle taking ownership of the Glean store.
pub struct GleanHandle {
    single_instance: single_instance::SingleInstance,
}

impl GleanHandle {
    /// Own the Glean store for the lifetime of the application.
    pub fn application_lifetime(self) {
        self.single_instance.retain_until_application_exit();
    }
}

impl InitGlean {
    /// The data_dir should be a dedicated directory for use by Glean.
    pub fn new(data_dir: PathBuf, app_id: &str, client_info_metrics: ClientInfoMetrics) -> Self {
        InitGlean {
            configuration: ConfigurationBuilder::new(true, data_dir, app_id)
                .with_server_endpoint(TELEMETRY_SERVER)
                .with_use_core_mps(false)
                .with_internal_pings(false)
                .build(),
            client_info_metrics,
            clear_uploader_for_tests: true,
        }
    }

    pub fn initialize(self) -> std::io::Result<GleanHandle> {
        self.init_with(glean::initialize)
    }

    /// Initialize using glean::test_reset_glean.
    ///
    /// This will not do any process locking of the Glean store.
    pub fn test_reset_glean(self, clear_stores: bool) {
        self.init_with_no_lock(move |c, m| glean::test_reset_glean(c, m, clear_stores))
    }

    /// Initialize with the given function.
    ///
    /// This will take exclusive ownership of the Glean store for the current process (potentially
    /// blocking). The returned GleanHandle tracks ownership.
    pub fn init_with<F: FnOnce(Configuration, ClientInfoMetrics)>(
        self,
        f: F,
    ) -> std::io::Result<GleanHandle> {
        std::fs::create_dir_all(&self.configuration.data_path)?;
        let handle = GleanHandle {
            single_instance: single_instance::SingleInstance::acquire(
                &self.configuration.data_path.join("crashping.pid"),
            )?,
        };
        self.init_with_no_lock(f);
        Ok(handle)
    }

    pub fn init_with_no_lock<F: FnOnce(Configuration, ClientInfoMetrics)>(mut self, f: F) {
        // Clear the uploader for tests, if configured.
        // No need to check `cfg!(test)`, since we don't set an uploader in unit tests (and if we
        // did, it would be test-specific).
        let is_test = std::env::var_os("XPCSHELL_TEST_PROFILE_DIR").is_some()
            || std::env::var_os("MOZ_AUTOMATION").is_some()
            || std::env::var_os("MOZ_DISABLE_NONLOCAL_CONNECTIONS") == Some("1".into());
        if self.clear_uploader_for_tests && is_test {
            self.configuration.uploader = None;
            self.configuration.server_endpoint = None;
        }
        init();
        f(self.configuration, self.client_info_metrics);
    }
}

/// Send the Glean crash ping.
pub fn send(annotations: &serde_json::Value, reason: Option<&str>) -> anyhow::Result<()> {
    // The crash.time metric may be overwritten if a CrashTime annotation is present.
    glean_metrics::crash::time.set(None);
    set_metrics_from_annotations(annotations)?;
    log::debug!("submitting Glean crash ping");
    glean_metrics::crash.submit(reason);
    Ok(())
}

/// Set whether upload is enabled or not.
pub fn set_collection_enabled(enabled: bool) {
    glean::set_collection_enabled(enabled);
}

/// **Test-only API**
///
/// Register a callback that will be called before the next ping is sent.
pub fn test_before_next_send<F: FnOnce(Option<&str>) + Send + 'static>(cb: F) {
    glean_metrics::crash.test_before_next_submit(cb);
}

/// **Test-only API**
///
/// Get all metric values as a JSON object.
pub fn test_get_metric_values() -> serde_json::Value {
    let mut ret: serde_json::Map<String, serde_json::Value> = Default::default();
    for annotation in ANNOTATIONS {
        if let Some(value) = (annotation.test_get_glean_value)() {
            ret.insert(annotation.glean_key.into(), value);
        }
    }
    ret.into()
}

/// Set Glean metrics from the given annotations.
fn set_metrics_from_annotations(annotations: &serde_json::Value) -> anyhow::Result<()> {
    for annotation in ANNOTATIONS {
        if let Some(value) = annotations.get(annotation.key) {
            (annotation.set_glean_metric)(value)?;
        }
    }

    Ok(())
}

#[cfg(test)]
mod test {
    use super::{send, test_before_next_send, ANNOTATIONS};
    use std::sync::{
        atomic::{AtomicBool, Ordering::Relaxed},
        Arc, Mutex,
    };

    static TEST_LOCK: Mutex<()> = Mutex::new(());

    #[must_use]
    fn test_init_glean() -> std::sync::MutexGuard<'static, ()> {
        let guard = TEST_LOCK.lock().unwrap();
        let glean_path = std::env::temp_dir().join("crashping_glean");
        super::InitGlean::new(
            glean_path,
            "crashping.test",
            super::ClientInfoMetrics::unknown(),
        )
        .test_reset_glean(true);
        return guard;
    }

    /// Run a test that uses Glean.
    ///
    /// This function ensures that Glean tests run sequentially.
    fn glean_test<F: FnOnce() + std::panic::UnwindSafe>(f: F) {
        let _ = env_logger::builder()
            .filter_level(log::LevelFilter::Debug)
            .is_test(true)
            .try_init();
        let res = {
            let _guard = test_init_glean();
            // Catch panics so that we don't poison the mutex (so other tests can run).
            std::panic::catch_unwind(f)
        };
        if let Err(e) = res {
            std::panic::resume_unwind(e);
        }
    }

    #[derive(Clone)]
    struct SoftAssert {
        failed: Arc<AtomicBool>,
        message: &'static str,
    }

    impl SoftAssert {
        fn new(failed: bool, message: &'static str) -> Self {
            SoftAssert {
                failed: Arc::new(AtomicBool::new(failed)),
                message,
            }
        }

        fn assert<T: std::fmt::Display>(&self, value: bool, msg: T) {
            if !value {
                eprintln!("{}", msg);
                self.failed.store(true, Relaxed);
            }
        }

        fn clear(&self) {
            self.failed.store(false, Relaxed);
        }
    }

    impl Drop for SoftAssert {
        fn drop(&mut self) {
            assert!(!self.failed.load(Relaxed), "{}"self.message);
        }
    }

    #[test]
    fn all_annotations() {
        glean_test(|| {
            // Set test values here if the default values aren't sufficient nor applicable.
            let mut annotations = serde_json::json!({
                "AsyncShutdownTimeout""{\"phase\":\"abcd\",\"conditions\":[{\"foo\":\"bar\"}],\"brokenAddBlockers\":[\"foo\"]}",
                "BlockedDllList""Foo.dll;bar.dll;rawr.dll",
                "CrashTime""1234",
                "LastInteractionDuration""100",
                "NimbusEnrollments""a,b,c,d,e",
                "QuotaManagerShutdownTimeout""line1\nline2\nline3",
                "JavaException": r#"{
                    "exception": {
                        "values": [{
                            "stacktrace": {
                                "value""something went wrong",
                                "module""foo.bar",
                                "type""FooBarType",
                                "frames": [{
                                    "module""org.mozilla",
                                    "function""FooBar",
                                    "in_app"true,
                                    "lineno"42,
                                    "file""FooBar.java"
                                }]
                            }
                        }]
                    }
                }"#,
                "SecondsSinceLastCrash""50000",
                // Add extraneous field `foobar` to ensure it doesn't affect setting the metric
                "StackTraces": r#"{
                    "foobar""baz",
                    "crash_type""bad crash",
                    "crash_address""0xcafe",
                    "crash_thread"1,
                    "main_module"0,
                    "modules": [{
                        "base_address""0xcafe",
                        "end_address""0xf000",
                        "code_id""CODEID",
                        "debug_file""debug_file.so",
                        "debug_id""DEBUGID",
                        "filename""file.so",
                        "version""1.0.0"
                    }],
                    "threads": [
                        {"frames": [
                            {
                                "ip""0xf00",
                                "trust""crash",
                                "module_index"0
                            }
                        ]},
                        {"frames": [
                            {
                                "ip""0x0",
                                "trust""crash",
                                "module_index"0
                            },
                            {
                                "ip""0xbadf00d",
                                "trust""cfi",
                                "module_index"0
                            }
                        ]}
                    ]
                }"#,
                "UptimeTS""400.5",
                "UtilityActorsName""abc,def",
                "WindowsFileDialogErrorCode""40",
            });

            // For convenience, automatically populate example values for the simple cases.
            for annotation in ANNOTATIONS {
                if !annotations
                    .as_object()
                    .unwrap()
                    .contains_key(annotation.key)
                {
                    let default_val: Option<serde_json::Value> = match annotation.convert_fn {
                        "convert_boolean_to_boolean" => Some("1".into()),
                        "convert_string_to_string" => Some("some_string".into()),
                        "convert_u64_to_quantity" => Some("42".into()),
                        _ => None,
                    };
                    if let Some(val) = default_val {
                        annotations
                            .as_object_mut()
                            .unwrap()
                            .insert(annotation.key.to_owned(), val);
                    }
                }
            }

            let success = SoftAssert::new(false"one or more failures occurred");

            // Ensure all annotations have a test value.
            for annotation in ANNOTATIONS {
                success.assert(
                    annotations
                        .as_object()
                        .unwrap()
                        .contains_key(annotation.key),
                    format!("{} test value is not set", annotation.key),
                );
            }

            // Ensure all metrics are set.
            let metrics_tested = SoftAssert::new(true"test_before_next_send did not run");
            {
                let success = success.clone();
                let metrics_tested = metrics_tested.clone();
                test_before_next_send(move |_| {
                    for annotation in ANNOTATIONS {
                        success.assert(
                            (annotation.test_get_glean_value)().is_some(),
                            format!("{} not set", annotation.glean_key),
                        );
                    }
                    metrics_tested.clear();
                });
            }

            send(&annotations, Some("crash")).expect("failed to set metrics");
        });
    }

    fn test_annotation(
        annotation: &'static crate::annotations::Annotation,
        value: serde_json::Value,
        expected: serde_json::Value,
    ) {
        glean_test(|| {
            let annotations = serde_json::json!({annotation.key: value});

            let sent = SoftAssert::new(true"test_before_next_send did not run");
            let check = SoftAssert::new(false"annotation check failed");
            let sent_inner = sent.clone();
            let check_inner = check.clone();
            let input_str = annotations.to_string();
            test_before_next_send(move |_| {
                sent_inner.clear();
                // Use a SoftAssert rather than `assert_eq!` so that we don't panic in the callback
                // (which will poison the mutex that Glean uses, making other tests fail
                // unnecessarily).
                let actual = (annotation.test_get_glean_value)();
                if let Some(actual) = actual {
                    check_inner.assert(actual == expected, "value mismatch");
                } else {
                    check_inner.assert(
                        false,
                        format!(
                            "missing value for {} with input {}",
                            annotation.glean_key, input_str,
                        ),
                    );
                }
            });

            send(&annotations, Some("crash")).expect("failed to set metrics");
        });
    }

    macro_rules! test_annotations {
        ( ) => {};
        ( $test_name:ident ($annotation:ident) { $($value:tt => $expected:tt),+ } $($rest:tt)* ) => {
            #[test]
            fn $test_name() {
                $(test_annotation(&crate::annotations::$annotation, serde_json::json!($value), serde_json::json!($expected));)*
            }

            test_annotations! { $($rest)* }
        };
    }

    test_annotations! {
        test_async_shutdown_timeout(AsyncShutdownTimeout) {
            r#"{"phase":"AddonManager: Waiting to start provider shutdown.","conditions":[{"name":"AddonRepository Background Updater","state":"(none)","filename":"resource://gre/modules/addons/AddonRepository.sys.mjs","lineNumber":576,"stack":["resource://gre/modules/addons/AddonRepository.sys.mjs:backgroundUpdateCheck:576","resource://gre/modules/AddonManager.sys.mjs:backgroundUpdateCheck/buPromise<:1269"]}],"brokenAddBlockers":["JSON store: writing data for 'creditcards' - IOUtils: waiting for profileBeforeChange IO to complete finished","StorageSyncService: shutdown - profile-change-teardown finished"]}"#
            => {
                "phase""AddonManager: Waiting to start provider shutdown.",
                "conditions": serde_json::json!([
                    {
                        "name""AddonRepository Background Updater",
                        "state""(none)",
                        "filename""resource://gre/modules/addons/AddonRepository.sys.mjs",
                        "lineNumber"576,
                        "stack": [
                            "resource://gre/modules/addons/AddonRepository.sys.mjs:backgroundUpdateCheck:576",
                            "resource://gre/modules/AddonManager.sys.mjs:backgroundUpdateCheck/buPromise<:1269",
                        ]
                    }
                ]).to_string(),
                "broken_add_blockers": [
                    "JSON store: writing data for 'creditcards' - IOUtils: waiting for profileBeforeChange IO to complete finished",
                    "StorageSyncService: shutdown - profile-change-teardown finished"
                ]
            }
        }

        test_blocked_dll_list(BlockedDllList) {
            "Foo.dll;bar.dll;rawr.dll" => ["Foo.dll""bar.dll""rawr.dll"]
        }

        test_java_exception(JavaException) {
            r#"{
                "exception": {
                    "values": [{
                        "stacktrace": {
                            "value""something went wrong",
                            "module""foo.bar",
                            "type""FooBarType",
                            "frames": [{
                                "module""org.mozilla",
                                "function""FooBar",
                                "in_app"true,
                                "lineno"42,
                                "filename""FooBar.java"
                            }]
                        }
                    }]
                }
            }"#
            => {
                "throwables": [
                    {
                        "message""something went wrong",
                        "type_name""foo.bar.FooBarType",
                        "stack": [
                            {
                                "class_name""org.mozilla",
                                "method_name""FooBar",
                                "file""FooBar.java",
                                "is_native"false,
                                "line"42
                            }
                        ]
                    }
                ]
            }
        }

        test_nimbus_enrollments(NimbusEnrollments) {
            "foo:control,bar:treatment-a" => ["foo:control""bar:treatment-a"]
        }

        test_quota_manager_shutdown_timeout(QuotaManagerShutdownTimeout) {
            "foo\nbar\nbaz" => ["foo""bar""baz"]
        }

        test_stack_traces(StackTraces) {
            r#"{
                "crash_type""main",
                "crash_address""0xf001ba11",
                "crash_thread"1,
                "main_module"0,
                "modules": [
                    {
                        "base_address""0x00000000",
                        "end_address""0x00004000",
                        "code_id""8675309",
                        "debug_file""",
                        "debug_id""18675309",
                        "filename""foo.exe",
                        "version""1.0.0"
                    },
                    {
                        "base_address""0x00004000",
                        "end_address""0x00008000",
                        "code_id""42",
                        "debug_file""foo.pdb",
                        "debug_id""43",
                        "filename""foo.dll",
                        "version""1.1.0"
                    }
                ],
                "threads": [
                    {
                        "frames": [
                            { "module_index"0"ip""0x10""trust""context" },
                            { "module_index"0"ip""0x20""trust""cfi" }
                        ]
                    },
                    {
                        "frames": [
                            { "module_index"1"ip""0x4010""trust""context" },
                            { "module_index"0"ip""0x30""trust""cfi" }
                        ]
                    }
                ]
            }"#
            => {
                "crash_type""main",
                "crash_address""0xf001ba11",
                "crash_thread"1,
                "main_module"0,
                "modules": [
                    {
                        "base_address""0x00000000",
                        "end_address""0x00004000",
                        "code_id""8675309",
                        "debug_file""",
                        "debug_id""18675309",
                        "filename""foo.exe",
                        "version""1.0.0",
                    },
                    {
                        "base_address""0x00004000",
                        "end_address""0x00008000",
                        "code_id""42",
                        "debug_file""foo.pdb",
                        "debug_id""43",
                        "filename""foo.dll",
                        "version""1.1.0",
                    },
                ],
                "threads": [
                    {
                        "frames": [
                            { "module_index"0"ip""0x10""trust""context" },
                            { "module_index"0"ip""0x20""trust""cfi" },
                        ],
                    },
                    {
                        "frames": [
                            { "module_index"1"ip""0x4010""trust""context" },
                            { "module_index"0"ip""0x30""trust""cfi" },
                        ],
                    },
                ],
            }
        }

        test_utility_actors_name(UtilityActorsName) {
            "audio-decoder-generic,js-oracle" => ["audio-decoder-generic","js-oracle"]
        }
    }
}

Messung V0.5 in Prozent
C=91 H=98 G=94

¤ Dauer der Verarbeitung: 0.2 Sekunden  (vorverarbeitet am  2026-08-25) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.