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