cobalt/
buckets.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::error_from_metrics_error;
6use anyhow::Result;
7use attribution_processing::digest::{BucketDefinition, Digest};
8use cobalt_client::traits::{AsEventCode, AsEventCodes};
9use cobalt_registry::MemoryLeakMigratedMetricDimensionTimeSinceBoot as TimeSinceBoot;
10use memory_metrics_registry::cobalt_registry;
11use std::collections::HashMap;
12use {fidl_fuchsia_kernel as fkernel, fidl_fuchsia_metrics as fmetrics};
13
14/// Sorted list mapping durations to the largest event that is lower.
15const UPTIME_LEVEL_INDEX: &[(zx::BootDuration, TimeSinceBoot)] = &[
16    (zx::BootDuration::from_minutes(1), TimeSinceBoot::Up),
17    (zx::BootDuration::from_minutes(30), TimeSinceBoot::UpOneMinute),
18    (zx::BootDuration::from_hours(1), TimeSinceBoot::UpThirtyMinutes),
19    (zx::BootDuration::from_hours(6), TimeSinceBoot::UpOneHour),
20    (zx::BootDuration::from_hours(12), TimeSinceBoot::UpSixHours),
21    (zx::BootDuration::from_hours(24), TimeSinceBoot::UpTwelveHours),
22    (zx::BootDuration::from_hours(48), TimeSinceBoot::UpOneDay),
23    (zx::BootDuration::from_hours(72), TimeSinceBoot::UpTwoDays),
24    (zx::BootDuration::from_hours(144), TimeSinceBoot::UpThreeDays),
25];
26
27/// Convert an instant to the code corresponding to the largest uptime that is smaller.
28fn get_uptime_event_code(capture_time: zx::BootInstant) -> TimeSinceBoot {
29    let uptime = zx::Duration::from_nanos(capture_time.into_nanos());
30    UPTIME_LEVEL_INDEX
31        .into_iter()
32        .find(|&&(time, _)| uptime < time)
33        .map(|(_, code)| *code)
34        .unwrap_or(TimeSinceBoot::UpSixDays)
35}
36
37fn kmem_events(kmem_stats: &fkernel::MemoryStats) -> impl Iterator<Item = fmetrics::MetricEvent> {
38    use cobalt_registry::MemoryGeneralBreakdownMigratedMetricDimensionGeneralBreakdown as Breakdown;
39    let make_event = |code: Breakdown, value| {
40        Some(fmetrics::MetricEvent {
41            metric_id: cobalt_registry::MEMORY_GENERAL_BREAKDOWN_MIGRATED_METRIC_ID,
42            event_codes: vec![code.as_event_code()],
43            payload: fmetrics::MetricEventPayload::IntegerValue(value? as i64),
44        })
45    };
46    vec![
47        make_event(Breakdown::TotalBytes, kmem_stats.total_bytes),
48        make_event(
49            Breakdown::UsedBytes,
50            (|| Some((kmem_stats.total_bytes? as i64 - kmem_stats.free_bytes? as i64) as u64))(),
51        ),
52        make_event(Breakdown::FreeBytes, kmem_stats.free_bytes),
53        make_event(Breakdown::VmoBytes, kmem_stats.vmo_bytes),
54        make_event(Breakdown::KernelFreeHeapBytes, kmem_stats.free_heap_bytes),
55        make_event(Breakdown::MmuBytes, kmem_stats.mmu_overhead_bytes),
56        make_event(Breakdown::IpcBytes, kmem_stats.ipc_bytes),
57        make_event(Breakdown::KernelTotalHeapBytes, kmem_stats.total_heap_bytes),
58        make_event(Breakdown::WiredBytes, kmem_stats.wired_bytes),
59        make_event(Breakdown::OtherBytes, kmem_stats.other_bytes),
60    ]
61    .into_iter()
62    .flatten()
63}
64
65fn kmem_events_with_uptime(
66    kmem_stats: &fkernel::MemoryStats,
67    capture_time: zx::BootInstant,
68) -> impl Iterator<Item = fmetrics::MetricEvent> {
69    use cobalt_registry::MemoryLeakMigratedMetricDimensionGeneralBreakdown as Breakdown;
70    let make_event = |code: Breakdown, value| {
71        Some(fmetrics::MetricEvent {
72            metric_id: cobalt_registry::MEMORY_LEAK_MIGRATED_METRIC_ID,
73            event_codes: cobalt_registry::MemoryLeakMigratedEventCodes {
74                general_breakdown: code,
75                time_since_boot: get_uptime_event_code(capture_time),
76            }
77            .as_event_codes(),
78            payload: fmetrics::MetricEventPayload::IntegerValue(value? as i64),
79        })
80    };
81    vec![
82        make_event(Breakdown::TotalBytes, kmem_stats.total_bytes),
83        make_event(
84            Breakdown::UsedBytes,
85            (|| Some((kmem_stats.total_bytes? as i64 - kmem_stats.free_bytes? as i64) as u64))(),
86        ),
87        make_event(Breakdown::FreeBytes, kmem_stats.free_bytes),
88        make_event(Breakdown::VmoBytes, kmem_stats.vmo_bytes),
89        make_event(Breakdown::KernelFreeHeapBytes, kmem_stats.free_heap_bytes),
90        make_event(Breakdown::MmuBytes, kmem_stats.mmu_overhead_bytes),
91        make_event(Breakdown::IpcBytes, kmem_stats.ipc_bytes),
92        make_event(Breakdown::KernelTotalHeapBytes, kmem_stats.total_heap_bytes),
93        make_event(Breakdown::WiredBytes, kmem_stats.wired_bytes),
94        make_event(Breakdown::OtherBytes, kmem_stats.other_bytes),
95    ]
96    .into_iter()
97    .flatten()
98}
99
100fn digest_events<'a>(
101    digest: &'a Digest,
102    bucket_name_to_code: &'a HashMap<String, u32>,
103) -> impl 'a + Iterator<Item = fmetrics::MetricEvent> {
104    digest.buckets.iter().filter_map(|bucket| {
105        Some(fmetrics::MetricEvent {
106            metric_id: cobalt_registry::MEMORY_MIGRATED_METRIC_ID,
107            event_codes: vec![*bucket_name_to_code.get(&bucket.name)?],
108            payload: fmetrics::MetricEventPayload::IntegerValue(bucket.populated_size as i64),
109        })
110    })
111}
112
113pub fn prepare_bucket_codes(bucket_definitions: &[BucketDefinition]) -> HashMap<String, u32> {
114    let mut bucket_name_to_code = HashMap::from([
115        (
116            "TotalBytes".to_string(),
117            cobalt_registry::MemoryMigratedMetricDimensionBucket::TotalBytes.as_event_code(),
118        ),
119        (
120            "Free".to_string(),
121            cobalt_registry::MemoryMigratedMetricDimensionBucket::Free.as_event_code(),
122        ),
123        (
124            "Kernel".to_string(),
125            cobalt_registry::MemoryMigratedMetricDimensionBucket::Kernel.as_event_code(),
126        ),
127        (
128            "Orphaned".to_string(),
129            cobalt_registry::MemoryMigratedMetricDimensionBucket::Orphaned.as_event_code(),
130        ),
131        (
132            "Undigested".to_string(),
133            cobalt_registry::MemoryMigratedMetricDimensionBucket::Undigested.as_event_code(),
134        ),
135        (
136            "[Addl]PagerTotal".to_string(),
137            cobalt_registry::MemoryMigratedMetricDimensionBucket::__Addl_PagerTotal.as_event_code(),
138        ),
139        (
140            "[Addl]PagerNewest".to_string(),
141            cobalt_registry::MemoryMigratedMetricDimensionBucket::__Addl_PagerNewest
142                .as_event_code(),
143        ),
144        (
145            "[Addl]PagerOldest".to_string(),
146            cobalt_registry::MemoryMigratedMetricDimensionBucket::__Addl_PagerOldest
147                .as_event_code(),
148        ),
149        (
150            "[Addl]DiscardableLocked".to_string(),
151            cobalt_registry::MemoryMigratedMetricDimensionBucket::__Addl_DiscardableLocked
152                .as_event_code(),
153        ),
154        (
155            "[Addl]DiscardableUnlocked".to_string(),
156            cobalt_registry::MemoryMigratedMetricDimensionBucket::__Addl_DiscardableUnlocked
157                .as_event_code(),
158        ),
159        (
160            "[Addl]ZramCompressedBytes".to_string(),
161            cobalt_registry::MemoryMigratedMetricDimensionBucket::__Addl_ZramCompressedBytes
162                .as_event_code(),
163        ),
164    ]);
165    bucket_definitions.iter().for_each(|bucket_definition| {
166        bucket_name_to_code
167            .entry(bucket_definition.name.clone())
168            .or_insert(bucket_definition.event_code as u32);
169    });
170    bucket_name_to_code
171}
172
173/// Upload cobalt data based on collected memory data.
174pub async fn upload_metrics(
175    timestamp: zx::BootInstant,
176    kmem_stats: &fkernel::MemoryStats,
177    metric_event_logger: &fmetrics::MetricEventLoggerProxy,
178    digest: &Digest,
179    bucket_codes: &HashMap<String, u32>,
180) -> Result<()> {
181    let events = kmem_events(kmem_stats)
182        .chain(kmem_events_with_uptime(kmem_stats, timestamp))
183        .chain(digest_events(digest, &bucket_codes));
184    metric_event_logger
185        .log_metric_events(&events.collect::<Vec<fmetrics::MetricEvent>>())
186        .await?
187        .map_err(error_from_metrics_error)?;
188    Ok(())
189}
190
191#[cfg(test)]
192mod tests {
193    use super::*;
194    use anyhow::anyhow;
195    use attribution_processing::{
196        Attribution, AttributionData, GlobalPrincipalIdentifier, Principal, PrincipalDescription,
197        PrincipalType, ProcessedAttributionData, Resource, ResourceReference, ZXName,
198        attribute_vmos,
199    };
200    use fidl_fuchsia_memory_attribution_plugin as fplugin;
201    use futures::{TryFutureExt, TryStreamExt, try_join};
202    use regex::bytes::Regex;
203
204    fn get_data() -> ProcessedAttributionData {
205        let attribution_data = AttributionData {
206            principals_vec: vec![Principal {
207                identifier: GlobalPrincipalIdentifier::new_for_test(1),
208                description: Some(PrincipalDescription::Component("principal".to_owned())),
209                principal_type: PrincipalType::Runnable,
210                parent: None,
211            }],
212            resources_vec: vec![
213                // Orphaned VMO.
214                Resource {
215                    koid: 10,
216                    name_index: 0,
217                    resource_type: fplugin::ResourceType::Vmo(fplugin::Vmo {
218                        parent: None,
219                        private_committed_bytes: Some(1024),
220                        private_populated_bytes: Some(2048),
221                        scaled_committed_bytes: Some(1024),
222                        scaled_populated_bytes: Some(2048),
223                        total_committed_bytes: Some(1024),
224                        total_populated_bytes: Some(2048),
225                        ..Default::default()
226                    }),
227                },
228                // VMO belonging to bucket1.
229                Resource {
230                    koid: 20,
231                    name_index: 1,
232                    resource_type: fplugin::ResourceType::Vmo(fplugin::Vmo {
233                        parent: None,
234                        private_committed_bytes: Some(1024),
235                        private_populated_bytes: Some(2048),
236                        scaled_committed_bytes: Some(1024),
237                        scaled_populated_bytes: Some(2048),
238                        total_committed_bytes: Some(1024),
239                        total_populated_bytes: Some(2048),
240                        ..Default::default()
241                    }),
242                },
243                // Process owning bucket1's VMO.
244                Resource {
245                    koid: 30,
246                    name_index: 1,
247                    resource_type: fplugin::ResourceType::Process(fplugin::Process {
248                        vmos: Some(vec![20]),
249                        ..Default::default()
250                    }),
251                },
252            ],
253            resource_names: vec![
254                ZXName::from_string_lossy("resource"),
255                ZXName::from_string_lossy("bucket1_resource"),
256            ],
257            attributions: vec![Attribution {
258                source: GlobalPrincipalIdentifier::new_for_test(1),
259                subject: GlobalPrincipalIdentifier::new_for_test(1),
260                resources: vec![ResourceReference::KernelObject(10)],
261            }],
262        };
263        attribute_vmos(attribution_data)
264    }
265
266    fn get_kernel_stats() -> (fkernel::MemoryStats, fkernel::MemoryStatsCompression) {
267        (
268            fkernel::MemoryStats {
269                total_bytes: Some(1),
270                free_bytes: Some(2),
271                wired_bytes: Some(3),
272                total_heap_bytes: Some(4),
273                free_heap_bytes: Some(5),
274                vmo_bytes: Some(6),
275                mmu_overhead_bytes: Some(7),
276                ipc_bytes: Some(8),
277                other_bytes: Some(9),
278                free_loaned_bytes: Some(10),
279                cache_bytes: Some(11),
280                slab_bytes: Some(12),
281                zram_bytes: Some(13),
282                vmo_reclaim_total_bytes: Some(14),
283                vmo_reclaim_newest_bytes: Some(15),
284                vmo_reclaim_oldest_bytes: Some(16),
285                vmo_reclaim_disabled_bytes: Some(17),
286                vmo_discardable_locked_bytes: Some(18),
287                vmo_discardable_unlocked_bytes: Some(19),
288                ..Default::default()
289            },
290            fkernel::MemoryStatsCompression {
291                uncompressed_storage_bytes: Some(20),
292                compressed_storage_bytes: Some(21),
293                compressed_fragmentation_bytes: Some(22),
294                compression_time: Some(23),
295                decompression_time: Some(24),
296                total_page_compression_attempts: Some(25),
297                failed_page_compression_attempts: Some(26),
298                total_page_decompressions: Some(27),
299                compressed_page_evictions: Some(28),
300                eager_page_compressions: Some(29),
301                memory_pressure_page_compressions: Some(30),
302                critical_memory_page_compressions: Some(31),
303                pages_decompressed_unit_ns: Some(32),
304                pages_decompressed_within_log_time: Some([40, 41, 42, 43, 44, 45, 46, 47]),
305                ..Default::default()
306            },
307        )
308    }
309
310    #[fuchsia::test]
311    async fn test_upload_metrics() -> anyhow::Result<()> {
312        let bucket_definitions = [BucketDefinition {
313            name: "bucket1".to_string(),
314            vmo: Some(Regex::new("bucket1.*")?),
315            event_code: 1,
316            process: None,
317            principal: None,
318        }];
319
320        let (metric_event_logger, mut metric_event_request_stream) =
321            fidl::endpoints::create_proxy_and_stream::<fmetrics::MetricEventLoggerMarker>();
322
323        let (kmem_stats, kmem_stats_compression) = get_kernel_stats();
324        let digest = Digest::compute(
325            &get_data(),
326            &kmem_stats,
327            &kmem_stats_compression,
328            &bucket_definitions,
329            false,
330        )?;
331        let bucket_codes = prepare_bucket_codes(&bucket_definitions);
332        let upload = upload_metrics(
333            zx::BootInstant::get(),
334            &kmem_stats,
335            &metric_event_logger,
336            &digest,
337            &bucket_codes,
338        );
339        let uptime = get_uptime_event_code(zx::BootInstant::get());
340        try_join!(metric_event_request_stream.try_next().and_then(|event| async {
341            match event.unwrap() {
342                fmetrics::MetricEventLoggerRequest::LogMetricEvents { events, responder } => {
343                    responder.send(Ok(()))?;
344                    // Kernel metrics
345                    assert_eq!(
346                        &events[0..10],
347                        vec![
348                            fmetrics::MetricEvent {
349                                metric_id: cobalt_registry::MEMORY_GENERAL_BREAKDOWN_MIGRATED_METRIC_ID,
350                                event_codes: vec![cobalt_registry::MemoryGeneralBreakdownMigratedMetricDimensionGeneralBreakdown::TotalBytes.as_event_code()],
351                                payload: fmetrics::MetricEventPayload::IntegerValue(1)
352                            },
353                            fmetrics::MetricEvent {
354                                metric_id: cobalt_registry::MEMORY_GENERAL_BREAKDOWN_MIGRATED_METRIC_ID,
355                                event_codes: vec![cobalt_registry::MemoryGeneralBreakdownMigratedMetricDimensionGeneralBreakdown::UsedBytes.as_event_code()],
356                                payload: fmetrics::MetricEventPayload::IntegerValue(-1)
357                            },
358                            fmetrics::MetricEvent {
359                                metric_id: cobalt_registry::MEMORY_GENERAL_BREAKDOWN_MIGRATED_METRIC_ID,
360                                event_codes: vec![cobalt_registry::MemoryGeneralBreakdownMigratedMetricDimensionGeneralBreakdown::FreeBytes.as_event_code()],
361                                payload: fmetrics::MetricEventPayload::IntegerValue(2)
362                            },
363                            fmetrics::MetricEvent {
364                                metric_id: cobalt_registry::MEMORY_GENERAL_BREAKDOWN_MIGRATED_METRIC_ID,
365                                event_codes: vec![cobalt_registry::MemoryGeneralBreakdownMigratedMetricDimensionGeneralBreakdown::VmoBytes.as_event_code()],
366                                payload: fmetrics::MetricEventPayload::IntegerValue(6)
367                            },
368                            fmetrics::MetricEvent {
369                                metric_id: cobalt_registry::MEMORY_GENERAL_BREAKDOWN_MIGRATED_METRIC_ID,
370                                event_codes: vec![cobalt_registry::MemoryGeneralBreakdownMigratedMetricDimensionGeneralBreakdown::KernelFreeHeapBytes.as_event_code()],
371                                payload: fmetrics::MetricEventPayload::IntegerValue(5)
372                            },
373                            fmetrics::MetricEvent {
374                                metric_id: cobalt_registry::MEMORY_GENERAL_BREAKDOWN_MIGRATED_METRIC_ID,
375                                event_codes: vec![cobalt_registry::MemoryGeneralBreakdownMigratedMetricDimensionGeneralBreakdown::MmuBytes.as_event_code()],
376                                payload: fmetrics::MetricEventPayload::IntegerValue(7)
377                            },
378                            fmetrics::MetricEvent {
379                                metric_id: cobalt_registry::MEMORY_GENERAL_BREAKDOWN_MIGRATED_METRIC_ID,
380                                event_codes: vec![cobalt_registry::MemoryGeneralBreakdownMigratedMetricDimensionGeneralBreakdown::IpcBytes.as_event_code()],
381                                payload: fmetrics::MetricEventPayload::IntegerValue(8)
382                            },
383                            fmetrics::MetricEvent {
384                                metric_id: cobalt_registry::MEMORY_GENERAL_BREAKDOWN_MIGRATED_METRIC_ID,
385                                event_codes: vec![cobalt_registry::MemoryGeneralBreakdownMigratedMetricDimensionGeneralBreakdown::KernelTotalHeapBytes.as_event_code()],
386                                payload: fmetrics::MetricEventPayload::IntegerValue(4)
387                            },
388                            fmetrics::MetricEvent {
389                                metric_id: cobalt_registry::MEMORY_GENERAL_BREAKDOWN_MIGRATED_METRIC_ID,
390                                event_codes: vec![cobalt_registry::MemoryGeneralBreakdownMigratedMetricDimensionGeneralBreakdown::WiredBytes.as_event_code()],
391                                payload: fmetrics::MetricEventPayload::IntegerValue(3)
392                            },
393                            fmetrics::MetricEvent {
394                                metric_id: cobalt_registry::MEMORY_GENERAL_BREAKDOWN_MIGRATED_METRIC_ID,
395                                event_codes: vec![cobalt_registry::MemoryGeneralBreakdownMigratedMetricDimensionGeneralBreakdown::OtherBytes.as_event_code()],
396                                payload: fmetrics::MetricEventPayload::IntegerValue(9)
397                            },]);
398                    // Kernel metrics with uptime
399                    assert_eq!(
400                        &events[10..20],
401                        vec![
402                            fmetrics::MetricEvent {
403                                metric_id: cobalt_registry::MEMORY_LEAK_MIGRATED_METRIC_ID,
404                                event_codes: cobalt_registry::MemoryLeakMigratedEventCodes {
405                                    general_breakdown: cobalt_registry::MemoryLeakMigratedMetricDimensionGeneralBreakdown::TotalBytes, time_since_boot: uptime}.as_event_codes(),
406                                payload: fmetrics::MetricEventPayload::IntegerValue(1)
407                            },
408                            fmetrics::MetricEvent {
409                                metric_id: cobalt_registry::MEMORY_LEAK_MIGRATED_METRIC_ID,
410                                event_codes: cobalt_registry::MemoryLeakMigratedEventCodes {
411                                    general_breakdown: cobalt_registry::MemoryLeakMigratedMetricDimensionGeneralBreakdown::UsedBytes, time_since_boot:uptime}.as_event_codes(),
412                                payload: fmetrics::MetricEventPayload::IntegerValue(-1)
413                            },
414                            fmetrics::MetricEvent {
415                                metric_id: cobalt_registry::MEMORY_LEAK_MIGRATED_METRIC_ID,
416                                event_codes: cobalt_registry::MemoryLeakMigratedEventCodes {
417                                    general_breakdown: cobalt_registry::MemoryLeakMigratedMetricDimensionGeneralBreakdown::FreeBytes, time_since_boot:uptime}.as_event_codes(),
418                                payload: fmetrics::MetricEventPayload::IntegerValue(2)
419                            },
420                            fmetrics::MetricEvent {
421                                metric_id: cobalt_registry::MEMORY_LEAK_MIGRATED_METRIC_ID,
422                                event_codes: cobalt_registry::MemoryLeakMigratedEventCodes {
423                                    general_breakdown: cobalt_registry::MemoryLeakMigratedMetricDimensionGeneralBreakdown::VmoBytes, time_since_boot:uptime}.as_event_codes(),
424                                payload: fmetrics::MetricEventPayload::IntegerValue(6)
425                            },
426                            fmetrics::MetricEvent {
427                                metric_id: cobalt_registry::MEMORY_LEAK_MIGRATED_METRIC_ID,
428                                event_codes: cobalt_registry::MemoryLeakMigratedEventCodes {
429                                    general_breakdown: cobalt_registry::MemoryLeakMigratedMetricDimensionGeneralBreakdown::KernelFreeHeapBytes, time_since_boot:uptime}.as_event_codes(),
430                                payload: fmetrics::MetricEventPayload::IntegerValue(5)
431                            },
432                            fmetrics::MetricEvent {
433                                metric_id: cobalt_registry::MEMORY_LEAK_MIGRATED_METRIC_ID,
434                                event_codes: cobalt_registry::MemoryLeakMigratedEventCodes {
435                                    general_breakdown: cobalt_registry::MemoryLeakMigratedMetricDimensionGeneralBreakdown::MmuBytes, time_since_boot:uptime}.as_event_codes(),
436                                payload: fmetrics::MetricEventPayload::IntegerValue(7)
437                            },
438                            fmetrics::MetricEvent {
439                                metric_id: cobalt_registry::MEMORY_LEAK_MIGRATED_METRIC_ID,
440                                event_codes: cobalt_registry::MemoryLeakMigratedEventCodes {
441                                    general_breakdown: cobalt_registry::MemoryLeakMigratedMetricDimensionGeneralBreakdown::IpcBytes, time_since_boot:uptime}.as_event_codes(),
442                                payload: fmetrics::MetricEventPayload::IntegerValue(8)
443                            },
444                            fmetrics::MetricEvent {
445                                metric_id: cobalt_registry::MEMORY_LEAK_MIGRATED_METRIC_ID,
446                                event_codes: cobalt_registry::MemoryLeakMigratedEventCodes {
447                                    general_breakdown: cobalt_registry::MemoryLeakMigratedMetricDimensionGeneralBreakdown::KernelTotalHeapBytes, time_since_boot:uptime}.as_event_codes(),
448                                payload: fmetrics::MetricEventPayload::IntegerValue(4)
449                            },
450                            fmetrics::MetricEvent {
451                                metric_id: cobalt_registry::MEMORY_LEAK_MIGRATED_METRIC_ID,
452                                event_codes: cobalt_registry::MemoryLeakMigratedEventCodes {
453                                    general_breakdown: cobalt_registry::MemoryLeakMigratedMetricDimensionGeneralBreakdown::WiredBytes, time_since_boot:uptime}.as_event_codes(),
454                                payload: fmetrics::MetricEventPayload::IntegerValue(3)
455                            },
456                            fmetrics::MetricEvent {
457                                metric_id: cobalt_registry::MEMORY_LEAK_MIGRATED_METRIC_ID,
458                                event_codes: cobalt_registry::MemoryLeakMigratedEventCodes {
459                                    general_breakdown: cobalt_registry::MemoryLeakMigratedMetricDimensionGeneralBreakdown::OtherBytes, time_since_boot:uptime}.as_event_codes(),
460                                payload: fmetrics::MetricEventPayload::IntegerValue(9)
461                            },
462                        ]
463                    );
464                    // Digest metrics
465                    assert_eq!(
466                        &events[20..],
467                        vec![
468                            // Buckets with custom definitions
469                            fmetrics::MetricEvent {
470                                metric_id: cobalt_registry::MEMORY_MIGRATED_METRIC_ID,
471                                event_codes: vec![1], // Corresponds to the "bucket1" bucket
472                                payload: fmetrics::MetricEventPayload::IntegerValue(2048)
473                            },
474                            // Default buckets
475                            fmetrics::MetricEvent {
476                                metric_id: cobalt_registry::MEMORY_MIGRATED_METRIC_ID,
477                                event_codes: vec![cobalt_registry::MemoryMigratedMetricDimensionBucket::Undigested.as_event_code()],
478                                payload: fmetrics::MetricEventPayload::IntegerValue(2048)
479                            },
480                            fmetrics::MetricEvent {
481                                metric_id: cobalt_registry::MEMORY_MIGRATED_METRIC_ID,
482                                event_codes: vec![cobalt_registry::MemoryMigratedMetricDimensionBucket::Orphaned.as_event_code()],
483                                payload: fmetrics::MetricEventPayload::IntegerValue(0)
484                            },
485                            fmetrics::MetricEvent {
486                                metric_id: cobalt_registry::MEMORY_MIGRATED_METRIC_ID,
487                                event_codes: vec![cobalt_registry::MemoryMigratedMetricDimensionBucket::Kernel.as_event_code()],
488                                payload: fmetrics::MetricEventPayload::IntegerValue(31)
489                            },
490                            fmetrics::MetricEvent {
491                                metric_id: cobalt_registry::MEMORY_MIGRATED_METRIC_ID,
492                                event_codes: vec![cobalt_registry::MemoryMigratedMetricDimensionBucket::Free.as_event_code()],
493                                payload: fmetrics::MetricEventPayload::IntegerValue(2)
494                            },
495                            fmetrics::MetricEvent {
496                                metric_id: cobalt_registry::MEMORY_MIGRATED_METRIC_ID,
497                                event_codes: vec![cobalt_registry::MemoryMigratedMetricDimensionBucket::__Addl_PagerTotal.as_event_code()],
498                                payload: fmetrics::MetricEventPayload::IntegerValue(14)
499                            },
500                            fmetrics::MetricEvent {
501                                metric_id: cobalt_registry::MEMORY_MIGRATED_METRIC_ID,
502                                event_codes: vec![cobalt_registry::MemoryMigratedMetricDimensionBucket::__Addl_PagerNewest.as_event_code()],
503                                payload: fmetrics::MetricEventPayload::IntegerValue(15)
504                            },
505                            fmetrics::MetricEvent {
506                                metric_id: cobalt_registry::MEMORY_MIGRATED_METRIC_ID,
507                                event_codes: vec![cobalt_registry::MemoryMigratedMetricDimensionBucket::__Addl_PagerOldest.as_event_code()],
508                                payload: fmetrics::MetricEventPayload::IntegerValue(16)
509                            },
510                            fmetrics::MetricEvent {
511                                metric_id: cobalt_registry::MEMORY_MIGRATED_METRIC_ID,
512                                event_codes: vec![cobalt_registry::MemoryMigratedMetricDimensionBucket::__Addl_DiscardableLocked.as_event_code()],
513                                payload: fmetrics::MetricEventPayload::IntegerValue(18)
514                            },
515                            fmetrics::MetricEvent {
516                                metric_id: cobalt_registry::MEMORY_MIGRATED_METRIC_ID,
517                                event_codes: vec![cobalt_registry::MemoryMigratedMetricDimensionBucket::__Addl_DiscardableUnlocked.as_event_code()],
518                                payload: fmetrics::MetricEventPayload::IntegerValue(19)
519                            },
520                            fmetrics::MetricEvent {
521                                metric_id: cobalt_registry::MEMORY_MIGRATED_METRIC_ID,
522                                event_codes: vec![cobalt_registry::MemoryMigratedMetricDimensionBucket::__Addl_ZramCompressedBytes.as_event_code()],
523                                payload: fmetrics::MetricEventPayload::IntegerValue(21)
524                            }
525                        ]
526                    )
527                }
528                _ => panic!("Unexpected metric event"),
529            }
530            Ok(())}).map_err(|err| anyhow!(err)), upload)?;
531        Ok(())
532    }
533}