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