Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(mobile): Synthesize device.class based on specs from device context #1895

Merged
merged 11 commits into from
Mar 6, 2023
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- Strip quotes from client hint values. ([#1874](https://github.com/getsentry/relay/pull/1874))
- Add Dotnet, Javascript and PHP support for profiling. ([#1871](https://github.com/getsentry/relay/pull/1871), [#1876](https://github.com/getsentry/relay/pull/1876), [#1885](https://github.com/getsentry/relay/pull/1885))
- Scrub `span.data.http.query` with default scrubbers. ([#1889](https://github.com/getsentry/relay/pull/1889))
- Synthesize new class attribute in device context using specs found on the device, such as processor_count, memory_size, etc. ([#1895]https://github.com/getsentry/relay/pull/1895)

**Bug Fixes**:

Expand Down
30 changes: 30 additions & 0 deletions relay-general/src/protocol/contexts/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,29 @@ use crate::protocol::FromUserAgentInfo;
use crate::store::user_agent::is_known;
use crate::types::{Annotated, Object, Value};
use crate::user_agent::{parse_device, ClientHints};
use serde::{Deserialize, Serialize};

#[derive(
Clone,
Copy,
Debug,
Deserialize,
Eq,
PartialEq,
Serialize,
Empty,
FromValue,
IntoValue,
ProcessValue,
)]
#[cfg_attr(feature = "jsonschema", derive(JsonSchema))]
pub struct DeviceClass(pub u64);

impl DeviceClass {
pub const LOW: Self = Self(1);
pub const MEDIUM: Self = Self(2);
pub const HIGH: Self = Self(3);
}

/// Device information.
///
Expand Down Expand Up @@ -160,6 +183,10 @@ pub struct DeviceContext {
/// Whether location support is available on the device.
pub supports_location_service: Annotated<bool>,

// The performance class of the device, stored as a number.
// This value is synthesized from the device's specs in normalize_device_context.
pub class: Annotated<DeviceClass>,

/// Additional arbitrary fields for forwards compatibility
#[metastructure(additional_properties, retain = "true", pii = "maybe")]
pub other: Object<Value>,
Expand Down Expand Up @@ -204,6 +231,7 @@ impl FromUserAgentInfo for DeviceContext {

#[cfg(test)]
mod tests {
use crate::protocol::contexts::device::DeviceClass;
use crate::protocol::{DeviceContext, FromUserAgentInfo};
use crate::protocol::{Headers, PairList};
use crate::types::{Annotated, Object, Value};
Expand Down Expand Up @@ -316,6 +344,7 @@ mod tests {
"supports_gyroscope": true,
"supports_audio": true,
"supports_location_service": true,
"class": 1,
"other": "value",
"type": "device"
}"#;
Expand Down Expand Up @@ -359,6 +388,7 @@ mod tests {
supports_gyroscope: Annotated::new(true),
supports_audio: Annotated::new(true),
supports_location_service: Annotated::new(true),
class: Annotated::new(DeviceClass::LOW),
other: {
let mut map = Object::new();
map.insert(
Expand Down
128 changes: 127 additions & 1 deletion relay-general/src/store/normalize/contexts.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use once_cell::sync::Lazy;
use regex::Regex;

use crate::protocol::{Context, OsContext, ResponseContext, RuntimeContext};
use crate::protocol::{
Context, DeviceClass, DeviceContext, OsContext, ResponseContext, RuntimeContext,
};
use crate::types::{Annotated, Empty};

/// Environment.OSVersion (GetVersionEx) or RuntimeInformation.OSDescription on Windows
Expand Down Expand Up @@ -37,6 +39,8 @@ static OS_UNAME_REGEX: Lazy<Regex> = Lazy::new(|| {
static RUNTIME_DOTNET_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new(r#"^(?P<name>.*) (?P<version>\d+\.\d+(\.\d+){0,2}).*$"#).unwrap());

const GIB: u64 = 1024 * 1024 * 1024;

fn normalize_runtime_context(runtime: &mut RuntimeContext) {
if runtime.name.value().is_empty() && runtime.version.value().is_empty() {
if let Some(raw_description) = runtime.raw_description.as_str() {
Expand Down Expand Up @@ -199,11 +203,41 @@ fn normalize_response(response: &mut ResponseContext) {
}
}

// Reads device specs (family, memory, cpu, etc) and sets the device class to high, medium, or low.
fn normalize_device_context(device: &mut DeviceContext) {
if let Some(family) = device.family.value() {
if family == "iPhone" || family == "iOS" || family == "iOS-Device" {
if let Some(processor_frequency) = device.processor_frequency.value() {
if processor_frequency < &2000 {
device.class = DeviceClass::LOW.into();
} else if processor_frequency < &3000 {
device.class = DeviceClass::MEDIUM.into();
} else {
device.class = DeviceClass::HIGH.into();
}
}
} else if let (Some(&freq), Some(&proc), Some(&mem)) = (
device.processor_frequency.value(),
device.processor_count.value(),
device.memory_size.value(),
) {
if freq < 2000 || proc < 8 || mem < 4 * GIB {
device.class = DeviceClass::LOW.into();
} else if freq < 2500 || mem < 6 * GIB {
device.class = DeviceClass::MEDIUM.into();
} else {
device.class = DeviceClass::HIGH.into();
}
}
}
}

pub fn normalize_context(context: &mut Context) {
match context {
Context::Runtime(runtime) => normalize_runtime_context(runtime),
Context::Os(os) => normalize_os_context(os),
Context::Response(response) => normalize_response(response),
Context::Device(device) => normalize_device_context(device),
_ => (),
}
}
Expand Down Expand Up @@ -580,4 +614,96 @@ mod tests {
assert_eq!(Some("15.0"), os.kernel_version.as_str());
assert_eq!(None, os.build.value());
}

#[test]
fn test_apple_no_device_class() {
let mut device = DeviceContext {
family: "iPhone".to_string().into(),
..DeviceContext::default()
};
normalize_device_context(&mut device);
assert_eq!(None, device.class.value());
}

#[test]
fn test_apple_low_device_class() {
let mut device = DeviceContext {
family: "iPhone".to_string().into(),
processor_frequency: 1000.into(),
..DeviceContext::default()
};
normalize_device_context(&mut device);
assert_eq!(DeviceClass::LOW, *device.class.value().unwrap());
}

#[test]
fn test_apple_medium_device_class() {
let mut device = DeviceContext {
family: "iPhone".to_string().into(),
processor_frequency: 2000.into(),
..DeviceContext::default()
};
normalize_device_context(&mut device);
assert_eq!(DeviceClass::MEDIUM, *device.class.value().unwrap());
}

#[test]
fn test_apple_high_device_class() {
let mut device = DeviceContext {
family: "iPhone".to_string().into(),
processor_frequency: 3000.into(),
..DeviceContext::default()
};
normalize_device_context(&mut device);
assert_eq!(DeviceClass::HIGH, *device.class.value().unwrap());
}

#[test]
fn test_android_no_device_class() {
let mut device = DeviceContext {
family: "android".to_string().into(),
..DeviceContext::default()
};
normalize_device_context(&mut device);
assert_eq!(None, device.class.value());
}

#[test]
fn test_android_low_device_class() {
let mut device = DeviceContext {
family: "android".to_string().into(),
processor_frequency: 1000.into(),
processor_count: 6.into(),
memory_size: (2 * GIB).into(),
..DeviceContext::default()
};
normalize_device_context(&mut device);
assert_eq!(DeviceClass::LOW, *device.class.value().unwrap());
}

#[test]
fn test_android_medium_device_class() {
let mut device = DeviceContext {
family: "android".to_string().into(),
processor_frequency: 2000.into(),
processor_count: 8.into(),
memory_size: (6 * GIB).into(),
..DeviceContext::default()
};
normalize_device_context(&mut device);
assert_eq!(DeviceClass::MEDIUM, *device.class.value().unwrap());
}

#[test]
fn test_android_high_device_class() {
let mut device = DeviceContext {
family: "android".to_string().into(),
processor_frequency: 2500.into(),
processor_count: 8.into(),
memory_size: (6 * GIB).into(),
..DeviceContext::default()
};
normalize_device_context(&mut device);
assert_eq!(DeviceClass::HIGH, *device.class.value().unwrap());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1117,6 +1117,15 @@ expression: "relay_general::protocol::event_json_schema()"
"null"
]
},
"class": {
"default": null,
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 0.0
},
"cpu_description": {
"description": " CPU description.\n\n For example, Intel(R) Core(TM)2 Quad CPU Q6600 @ 2.40GHz.",
"default": null,
Expand Down