Skip to main content

relay_profiling/
profile_chunk.rs

1use serde::Deserialize;
2
3use crate::{
4    AndroidProfileChunk, PerfettoProfileChunk, ProfileError, ProfileType, V2ProfileChunk, sample,
5};
6
7/// Minimum interface all profile chunk types must implement.
8pub trait ProfileChunk {
9    /// Returns the platform this profile chunk is associated with.
10    fn platform(&self) -> &str;
11
12    /// Returns the [`ProfileType`] of this profile chunk.
13    ///
14    /// By default this is inferred from the [`Self::platform`].
15    fn profile_type(&self) -> ProfileType {
16        ProfileType::from_platform(self.platform())
17    }
18
19    /// Normalizes the profile chunk.
20    fn normalize(&mut self) -> Result<(), ProfileError>;
21}
22
23/// Supported profile chunks for continous profiling.
24#[derive(Debug)]
25pub enum AnyProfileChunk {
26    Android(Box<AndroidProfileChunk>),
27    Perfetto(Box<PerfettoProfileChunk>),
28    V2(Box<V2ProfileChunk>),
29}
30
31impl From<Box<V2ProfileChunk>> for AnyProfileChunk {
32    fn from(chunk: Box<V2ProfileChunk>) -> Self {
33        Self::V2(chunk)
34    }
35}
36
37impl From<Box<AndroidProfileChunk>> for AnyProfileChunk {
38    fn from(chunk: Box<AndroidProfileChunk>) -> Self {
39        Self::Android(chunk)
40    }
41}
42
43impl From<Box<PerfettoProfileChunk>> for AnyProfileChunk {
44    fn from(chunk: Box<PerfettoProfileChunk>) -> Self {
45        Self::Perfetto(chunk)
46    }
47}
48
49impl From<AndroidOrV2ProfileChunk> for AnyProfileChunk {
50    fn from(chunk: AndroidOrV2ProfileChunk) -> Self {
51        match chunk {
52            AndroidOrV2ProfileChunk::Android(c) => Self::Android(c),
53            AndroidOrV2ProfileChunk::V2(c) => Self::V2(c),
54        }
55    }
56}
57
58impl ProfileChunk for AnyProfileChunk {
59    fn platform(&self) -> &str {
60        match self {
61            AnyProfileChunk::Android(chunk) => chunk.platform(),
62            AnyProfileChunk::Perfetto(chunk) => chunk.platform(),
63            AnyProfileChunk::V2(chunk) => chunk.platform(),
64        }
65    }
66
67    fn normalize(&mut self) -> Result<(), ProfileError> {
68        match self {
69            AnyProfileChunk::Android(chunk) => chunk.normalize(),
70            AnyProfileChunk::Perfetto(chunk) => chunk.normalize(),
71            AnyProfileChunk::V2(chunk) => chunk.normalize(),
72        }
73    }
74}
75
76impl relay_protocol::Getter for AnyProfileChunk {
77    fn get_value(&self, path: &str) -> Option<relay_protocol::Val<'_>> {
78        match self {
79            AnyProfileChunk::Android(chunk) => chunk.get_value(path),
80            AnyProfileChunk::Perfetto(chunk) => chunk.get_value(path),
81            AnyProfileChunk::V2(chunk) => chunk.get_value(path),
82        }
83    }
84}
85
86impl relay_filter::Filterable for AnyProfileChunk {
87    fn release(&self) -> Option<&str> {
88        match self {
89            AnyProfileChunk::Android(chunk) => chunk.release(),
90            AnyProfileChunk::Perfetto(chunk) => chunk.release(),
91            AnyProfileChunk::V2(chunk) => chunk.release(),
92        }
93    }
94}
95
96/// Either an [`AndroidProfileChunk`] or a [`V2ProfileChunk`].
97pub enum AndroidOrV2ProfileChunk {
98    Android(Box<AndroidProfileChunk>),
99    V2(Box<V2ProfileChunk>),
100}
101
102impl ProfileChunk for AndroidOrV2ProfileChunk {
103    fn platform(&self) -> &str {
104        match self {
105            AndroidOrV2ProfileChunk::Android(chunk) => chunk.platform(),
106            AndroidOrV2ProfileChunk::V2(chunk) => chunk.platform(),
107        }
108    }
109
110    fn normalize(&mut self) -> Result<(), ProfileError> {
111        match self {
112            AndroidOrV2ProfileChunk::Android(chunk) => chunk.normalize(),
113            AndroidOrV2ProfileChunk::V2(chunk) => chunk.normalize(),
114        }
115    }
116}
117
118impl AndroidOrV2ProfileChunk {
119    /// Parses either a [`AndroidOrV2ProfileChunk`] or [`ProfileChunk`] from a slice of bytes.
120    pub fn parse(data: &[u8]) -> Result<Self, ProfileError> {
121        #[derive(Debug, Deserialize)]
122        struct MinimalProfile {
123            platform: String,
124            #[serde(default)]
125            version: sample::Version,
126        }
127
128        let minimal: MinimalProfile = {
129            let d = &mut serde_json::Deserializer::from_slice(data);
130            serde_path_to_error::deserialize(d)
131        }?;
132
133        match (minimal.platform.as_str(), minimal.version) {
134            // This has always been parsed with higher priority than `v2`, so this was kept as-is
135            // when refactoring, but from the looks of it, this may cause issues with v2 profiles
136            // which happen to be sent from android.
137            ("android", _) => AndroidProfileChunk::parse(data)
138                .map(Box::new)
139                .map(Self::Android),
140            (_, sample::Version::V2) => V2ProfileChunk::parse(data).map(Box::new).map(Self::V2),
141            (_, sample::Version::V1) => Err(ProfileError::PlatformNotSupported),
142            (_, sample::Version::Unknown) => Err(ProfileError::PlatformNotSupported),
143        }
144    }
145}