Skip to main content

attribution_processing/
fplugin_serde.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::fkernel_serde;
6use fidl_fuchsia_kernel__common as fkernel;
7use fidl_fuchsia_memory_attribution_plugin__common as fplugin;
8use serde::{Deserialize, Serialize};
9
10#[derive(Serialize, Deserialize)]
11#[serde(remote = "fplugin::PerformanceImpactMetrics")]
12pub struct PerformanceImpactMetricsDef {
13    pub some_memory_stalls_ns: Option<i64>,
14    pub full_memory_stalls_ns: Option<i64>,
15    pub page_refaults: Option<u64>,
16    #[doc(hidden)]
17    #[serde(skip)]
18    pub __source_breaking: fidl::marker::SourceBreaking,
19}
20
21// TODO(https://github.com/serde-rs/serde/issues/723): Use remote serialization
22// with fkernel::KernelStatistics when supported inside options.
23#[derive(Default, PartialEq, Debug, Clone, Serialize, Deserialize)]
24pub struct KernelStatistics {
25    #[serde(with = "fkernel_serde::MemoryStatsDef")]
26    pub memory_statistics: fkernel::MemoryStats,
27    #[serde(with = "fkernel_serde::MemoryStatsCompressionDef")]
28    pub compression_statistics: fkernel::MemoryStatsCompression,
29}
30
31impl From<fplugin::KernelStatistics> for KernelStatistics {
32    fn from(value: fplugin::KernelStatistics) -> KernelStatistics {
33        KernelStatistics {
34            memory_statistics: value.memory_stats.unwrap(),
35            compression_statistics: value.compression_stats.unwrap(),
36        }
37    }
38}
39
40impl Into<fplugin::KernelStatistics> for KernelStatistics {
41    fn into(self) -> fplugin::KernelStatistics {
42        fplugin::KernelStatistics {
43            memory_stats: Some(self.memory_statistics),
44            compression_stats: Some(self.compression_statistics),
45            ..Default::default()
46        }
47    }
48}
49
50// TODO(https://github.com/serde-rs/serde/issues/723): Use remote serialization
51// with fkernel::KernelStatistics when supported inside options.
52#[derive(PartialEq, Debug, Clone, Serialize)]
53#[serde(remote = "fplugin::ResourceType")]
54pub enum ResourceTypeDef {
55    #[serde(with = "JobDef")]
56    Job(fplugin::Job),
57    #[serde(with = "ProcessDef")]
58    Process(fplugin::Process),
59    #[serde(with = "VmoDef")]
60    Vmo(fplugin::Vmo),
61    #[doc(hidden)]
62    #[serde(skip)]
63    __SourceBreaking { unknown_ordinal: u64 },
64}
65
66#[derive(PartialEq, Debug, Clone, Serialize)]
67#[serde(remote = "fplugin::Job")]
68pub struct JobDef {
69    pub child_jobs: Option<Vec<u64>>,
70    pub processes: Option<Vec<u64>>,
71    #[serde(skip)]
72    pub __source_breaking: fidl::marker::SourceBreaking,
73}
74
75#[derive(PartialEq, Debug, Clone, Serialize)]
76#[serde(remote = "fplugin::Process")]
77pub struct ProcessDef {
78    pub vmos: Option<Vec<u64>>,
79    #[serde(with = "option_vec_mapping_def")]
80    pub mappings: Option<Vec<fplugin::Mapping>>,
81    #[serde(skip)]
82    pub __source_breaking: fidl::marker::SourceBreaking,
83}
84
85// As [Process::mappings] is an Option<Vec<fplugin::Mapping>> instead of a pure struct, we can't
86// easily derive a serializer and need to provide a custom one.
87mod option_vec_mapping_def {
88    use super::{MappingDef, fplugin};
89    use serde::ser::SerializeSeq;
90    use serde::{Serialize, Serializer};
91
92    pub fn serialize<S>(
93        opt_vec: &Option<Vec<fplugin::Mapping>>,
94        serializer: S,
95    ) -> Result<S::Ok, S::Error>
96    where
97        S: Serializer,
98    {
99        #[derive(Serialize)]
100        struct Wrapper<'a>(#[serde(with = "MappingDef")] &'a fplugin::Mapping);
101
102        match opt_vec {
103            Some(vec) => {
104                let mut seq = serializer.serialize_seq(Some(vec.len()))?;
105                for element in vec {
106                    seq.serialize_element(&Wrapper(element))?;
107                }
108                seq.end()
109            }
110            None => serializer.serialize_none(),
111        }
112    }
113}
114
115#[derive(PartialEq, Debug, Clone, Serialize)]
116#[serde(remote = "fplugin::Mapping")]
117pub struct MappingDef {
118    pub vmo: Option<u64>,
119    pub address_base: Option<u64>,
120    pub size: Option<u64>,
121    #[serde(skip)]
122    pub __source_breaking: fidl::marker::SourceBreaking,
123}
124
125#[derive(PartialEq, Debug, Clone, Serialize)]
126#[serde(remote = "fplugin::Vmo")]
127pub struct VmoDef {
128    pub parent: Option<u64>,
129    pub private_committed_bytes: Option<u64>,
130    pub private_populated_bytes: Option<u64>,
131    pub scaled_committed_bytes: Option<u64>,
132    pub scaled_populated_bytes: Option<u64>,
133    pub total_committed_bytes: Option<u64>,
134    pub total_populated_bytes: Option<u64>,
135    pub flags: Option<u32>,
136    #[serde(skip)]
137    pub __source_breaking: fidl::marker::SourceBreaking,
138}
139
140#[cfg(test)]
141mod test {
142    use super::*;
143    use crate::fplugin;
144    #[test]
145    fn test_convert() {
146        let fplugin_kernel_statistics = fplugin::KernelStatistics {
147            memory_stats: Some(fkernel::MemoryStats {
148                total_bytes: Some(1),
149                free_bytes: Some(2),
150                free_loaned_bytes: Some(3),
151                wired_bytes: Some(4),
152                total_heap_bytes: Some(5),
153                free_heap_bytes: Some(6),
154                vmo_bytes: Some(7),
155                mmu_overhead_bytes: Some(8),
156                ipc_bytes: Some(9),
157                cache_bytes: Some(10),
158                slab_bytes: Some(11),
159                zram_bytes: Some(12),
160                other_bytes: Some(13),
161                vmo_reclaim_total_bytes: Some(14),
162                vmo_reclaim_newest_bytes: Some(15),
163                vmo_reclaim_oldest_bytes: Some(16),
164                vmo_reclaim_disabled_bytes: Some(17),
165                vmo_discardable_locked_bytes: Some(18),
166                vmo_discardable_unlocked_bytes: Some(19),
167                ..Default::default()
168            }),
169            compression_stats: Some(fkernel::MemoryStatsCompression {
170                uncompressed_storage_bytes: Some(15),
171                compressed_storage_bytes: Some(16),
172                compressed_fragmentation_bytes: Some(17),
173                compression_time: Some(18),
174                decompression_time: Some(19),
175                total_page_compression_attempts: Some(20),
176                failed_page_compression_attempts: Some(21),
177                total_page_decompressions: Some(22),
178                compressed_page_evictions: Some(23),
179                eager_page_compressions: Some(24),
180                memory_pressure_page_compressions: Some(25),
181                critical_memory_page_compressions: Some(26),
182                pages_decompressed_unit_ns: Some(27),
183                pages_decompressed_within_log_time: Some([0, 1, 2, 3, 4, 5, 6, 7]),
184                ..Default::default()
185            }),
186            ..Default::default()
187        };
188
189        let kernel_statistics: KernelStatistics = fplugin_kernel_statistics.clone().into();
190
191        assert_eq!(kernel_statistics.memory_statistics.total_bytes, Some(1));
192        assert_eq!(kernel_statistics.memory_statistics.free_bytes, Some(2));
193
194        assert_eq!(kernel_statistics.compression_statistics.uncompressed_storage_bytes, Some(15));
195
196        assert_eq!(fplugin_kernel_statistics, kernel_statistics.into());
197    }
198}