summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/client/app/src/net/legacy_telemetry.rs
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/crashreporter/client/app/src/net/legacy_telemetry.rs')
-rw-r--r--toolkit/crashreporter/client/app/src/net/legacy_telemetry.rs177
1 files changed, 177 insertions, 0 deletions
diff --git a/toolkit/crashreporter/client/app/src/net/legacy_telemetry.rs b/toolkit/crashreporter/client/app/src/net/legacy_telemetry.rs
new file mode 100644
index 0000000000..680f1614b0
--- /dev/null
+++ b/toolkit/crashreporter/client/app/src/net/legacy_telemetry.rs
@@ -0,0 +1,177 @@
+/* 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/. */
+
+//! Support for legacy telemetry ping creation. The ping support serialization which should be used
+//! when submitting.
+
+use anyhow::Context;
+use serde::Serialize;
+use std::collections::BTreeMap;
+use uuid::Uuid;
+
+const TELEMETRY_VERSION: u64 = 4;
+const PAYLOAD_VERSION: u64 = 1;
+
+// Generated by `build.rs`.
+// static PING_ANNOTATIONS: phf::Set<&'static str>;
+include!(concat!(env!("OUT_DIR"), "/ping_annotations.rs"));
+
+#[derive(Serialize)]
+#[serde(tag = "type", rename_all = "camelCase")]
+pub enum Ping<'a> {
+ Crash {
+ id: Uuid,
+ version: u64,
+ #[serde(with = "time::serde::rfc3339")]
+ creation_date: time::OffsetDateTime,
+ client_id: &'a str,
+ #[serde(skip_serializing_if = "serde_json::Value::is_null")]
+ environment: serde_json::Value,
+ payload: Payload<'a>,
+ application: Application<'a>,
+ },
+}
+
+time::serde::format_description!(date_format, Date, "[year]-[month]-[day]");
+
+#[derive(Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct Payload<'a> {
+ session_id: &'a str,
+ version: u64,
+ #[serde(with = "date_format")]
+ crash_date: time::Date,
+ #[serde(with = "time::serde::rfc3339")]
+ crash_time: time::OffsetDateTime,
+ has_crash_environment: bool,
+ crash_id: &'a str,
+ minidump_sha256_hash: Option<&'a str>,
+ process_type: &'a str,
+ #[serde(skip_serializing_if = "serde_json::Value::is_null")]
+ stack_traces: serde_json::Value,
+ metadata: BTreeMap<&'a str, &'a str>,
+}
+
+#[derive(Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct Application<'a> {
+ vendor: &'a str,
+ name: &'a str,
+ build_id: &'a str,
+ display_version: String,
+ platform_version: String,
+ version: &'a str,
+ channel: &'a str,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ architecture: Option<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ xpcom_abi: Option<String>,
+}
+
+impl<'a> Ping<'a> {
+ pub fn crash(
+ extra: &'a serde_json::Value,
+ crash_id: &'a str,
+ minidump_sha256_hash: Option<&'a str>,
+ ) -> anyhow::Result<Self> {
+ let now: time::OffsetDateTime = crate::std::time::SystemTime::now().into();
+ let environment: serde_json::Value = extra["TelemetryEnvironment"]
+ .as_str()
+ .and_then(|estr| serde_json::from_str(estr).ok())
+ .unwrap_or_default();
+
+ // The subset of extra file entries (crash annotations) which are allowed in pings.
+ let metadata = extra
+ .as_object()
+ .map(|map| {
+ map.iter()
+ .filter_map(|(k, v)| {
+ PING_ANNOTATIONS
+ .contains(k)
+ .then(|| k.as_str())
+ .zip(v.as_str())
+ })
+ .collect()
+ })
+ .unwrap_or_default();
+
+ let display_version = environment
+ .pointer("/build/displayVersion")
+ .and_then(|s| s.as_str())
+ .unwrap_or_default()
+ .to_owned();
+ let platform_version = environment
+ .pointer("/build/platformVersion")
+ .and_then(|s| s.as_str())
+ .unwrap_or_default()
+ .to_owned();
+ let architecture = environment
+ .pointer("/build/architecture")
+ .and_then(|s| s.as_str())
+ .map(ToOwned::to_owned);
+ let xpcom_abi = environment
+ .pointer("/build/xpcomAbi")
+ .and_then(|s| s.as_str())
+ .map(ToOwned::to_owned);
+
+ Ok(Ping::Crash {
+ id: crate::std::mock::hook(Uuid::new_v4(), "ping_uuid"),
+ version: TELEMETRY_VERSION,
+ creation_date: now,
+ client_id: extra["TelemetryClientId"]
+ .as_str()
+ .context("missing TelemetryClientId")?,
+ environment,
+ payload: Payload {
+ session_id: extra["TelemetrySessionId"]
+ .as_str()
+ .context("missing TelemetrySessionId")?,
+ version: PAYLOAD_VERSION,
+ crash_date: now.date(),
+ crash_time: now,
+ has_crash_environment: true,
+ crash_id,
+ minidump_sha256_hash,
+ process_type: "main",
+ stack_traces: extra["StackTraces"].clone(),
+ metadata,
+ },
+ application: Application {
+ vendor: extra["Vendor"].as_str().unwrap_or_default(),
+ name: extra["ProductName"].as_str().unwrap_or_default(),
+ build_id: extra["BuildID"].as_str().unwrap_or_default(),
+ display_version,
+ platform_version,
+ version: extra["Version"].as_str().unwrap_or_default(),
+ channel: extra["ReleaseChannel"].as_str().unwrap_or_default(),
+ architecture,
+ xpcom_abi,
+ },
+ })
+ }
+
+ /// Generate the telemetry URL for submitting this ping.
+ pub fn submission_url(&self, extra: &serde_json::Value) -> anyhow::Result<String> {
+ let url = extra["TelemetryServerURL"]
+ .as_str()
+ .context("missing TelemetryServerURL")?;
+ let id = self.id();
+ let name = extra["ProductName"]
+ .as_str()
+ .context("missing ProductName")?;
+ let version = extra["Version"].as_str().context("missing Version")?;
+ let channel = extra["ReleaseChannel"]
+ .as_str()
+ .context("missing ReleaseChannel")?;
+ let buildid = extra["BuildID"].as_str().context("missing BuildID")?;
+ Ok(format!("{url}/submit/telemetry/{id}/crash/{name}/{version}/{channel}/{buildid}?v={TELEMETRY_VERSION}"))
+ }
+
+ /// Get the ping identifier.
+ pub fn id(&self) -> &Uuid {
+ match self {
+ Ping::Crash { id, .. } => id,
+ }
+ }
+}