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 attribution_processing::AttributionDataProvider;
9use cobalt_client::traits::{AsEventCode, AsEventCodes};
10use futures::stream::StreamExt;
11use futures::{try_join, TryFutureExt};
12use memory_metrics_registry::cobalt_registry;
13use std::collections::HashMap;
14use std::sync::Arc;
15
16use cobalt_registry::MemoryLeakMigratedMetricDimensionTimeSinceBoot as TimeSinceBoot;
17use {fidl_fuchsia_kernel as fkernel, fidl_fuchsia_metrics as fmetrics};
18
19/// Sorted list mapping durations to the largest event that is lower.
20const UPTIME_LEVEL_INDEX: &[(zx::BootDuration, TimeSinceBoot)] = &[
21    (zx::BootDuration::from_minutes(1), TimeSinceBoot::Up),
22    (zx::BootDuration::from_minutes(30), TimeSinceBoot::UpOneMinute),
23    (zx::BootDuration::from_hours(1), TimeSinceBoot::UpThirtyMinutes),
24    (zx::BootDuration::from_hours(6), TimeSinceBoot::UpOneHour),
25    (zx::BootDuration::from_hours(12), TimeSinceBoot::UpSixHours),
26    (zx::BootDuration::from_hours(24), TimeSinceBoot::UpTwelveHours),
27    (zx::BootDuration::from_hours(48), TimeSinceBoot::UpOneDay),
28    (zx::BootDuration::from_hours(72), TimeSinceBoot::UpTwoDays),
29    (zx::BootDuration::from_hours(144), TimeSinceBoot::UpThreeDays),
30];
31
32/// Convert an instant to the code corresponding to the largest uptime that is smaller.
33fn get_uptime_event_code(capture_time: zx::BootInstant) -> TimeSinceBoot {
34    let uptime = zx::Duration::from_nanos(capture_time.into_nanos());
35    UPTIME_LEVEL_INDEX
36        .into_iter()
37        .find(|&&(time, _)| uptime < time)
38        .map(|(_, code)| *code)
39        .unwrap_or(TimeSinceBoot::UpSixDays)
40}
41
42fn kmem_events(kmem_stats: &fkernel::MemoryStats) -> impl Iterator<Item = fmetrics::MetricEvent> {
43    use cobalt_registry::MemoryGeneralBreakdownMigratedMetricDimensionGeneralBreakdown as Breakdown;
44    let make_event = |code: Breakdown, value| {
45        Some(fmetrics::MetricEvent {
46            metric_id: cobalt_registry::MEMORY_GENERAL_BREAKDOWN_MIGRATED_METRIC_ID,
47            event_codes: vec![code.as_event_code()],
48            payload: fmetrics::MetricEventPayload::IntegerValue(value? as i64),
49        })
50    };
51    vec![
52        make_event(Breakdown::TotalBytes, kmem_stats.total_bytes),
53        make_event(
54            Breakdown::UsedBytes,
55            (|| Some((kmem_stats.total_bytes? as i64 - kmem_stats.free_bytes? as i64) as u64))(),
56        ),
57        make_event(Breakdown::FreeBytes, kmem_stats.free_bytes),
58        make_event(Breakdown::VmoBytes, kmem_stats.vmo_bytes),
59        make_event(Breakdown::KernelFreeHeapBytes, kmem_stats.free_heap_bytes),
60        make_event(Breakdown::MmuBytes, kmem_stats.mmu_overhead_bytes),
61        make_event(Breakdown::IpcBytes, kmem_stats.ipc_bytes),
62        make_event(Breakdown::KernelTotalHeapBytes, kmem_stats.total_heap_bytes),
63        make_event(Breakdown::WiredBytes, kmem_stats.wired_bytes),
64        make_event(Breakdown::OtherBytes, kmem_stats.other_bytes),
65    ]
66    .into_iter()
67    .flatten()
68}
69
70fn kmem_events_with_uptime(
71    kmem_stats: &fkernel::MemoryStats,
72    capture_time: zx::BootInstant,
73) -> impl Iterator<Item = fmetrics::MetricEvent> {
74    use cobalt_registry::MemoryLeakMigratedMetricDimensionGeneralBreakdown as Breakdown;
75    let make_event = |code: Breakdown, value| {
76        Some(fmetrics::MetricEvent {
77            metric_id: cobalt_registry::MEMORY_LEAK_MIGRATED_METRIC_ID,
78            event_codes: cobalt_registry::MemoryLeakMigratedEventCodes {
79                general_breakdown: code,
80                time_since_boot: get_uptime_event_code(capture_time),
81            }
82            .as_event_codes(),
83            payload: fmetrics::MetricEventPayload::IntegerValue(value? as i64),
84        })
85    };
86    vec![
87        make_event(Breakdown::TotalBytes, kmem_stats.total_bytes),
88        make_event(
89            Breakdown::UsedBytes,
90            (|| Some((kmem_stats.total_bytes? as i64 - kmem_stats.free_bytes? as i64) as u64))(),
91        ),
92        make_event(Breakdown::FreeBytes, kmem_stats.free_bytes),
93        make_event(Breakdown::VmoBytes, kmem_stats.vmo_bytes),
94        make_event(Breakdown::KernelFreeHeapBytes, kmem_stats.free_heap_bytes),
95        make_event(Breakdown::MmuBytes, kmem_stats.mmu_overhead_bytes),
96        make_event(Breakdown::IpcBytes, kmem_stats.ipc_bytes),
97        make_event(Breakdown::KernelTotalHeapBytes, kmem_stats.total_heap_bytes),
98        make_event(Breakdown::WiredBytes, kmem_stats.wired_bytes),
99        make_event(Breakdown::OtherBytes, kmem_stats.other_bytes),
100    ]
101    .into_iter()
102    .flatten()
103}
104
105fn digest_events<'a>(
106    digest: Digest,
107    bucket_name_to_code: &'a HashMap<String, u32>,
108) -> impl 'a + Iterator<Item = fmetrics::MetricEvent> {
109    digest.buckets.into_iter().filter_map(|bucket| {
110        Some(fmetrics::MetricEvent {
111            metric_id: cobalt_registry::MEMORY_MIGRATED_METRIC_ID,
112            event_codes: vec![*bucket_name_to_code.get(&bucket.name)?],
113            payload: fmetrics::MetricEventPayload::IntegerValue(bucket.size as i64),
114        })
115    })
116}
117
118/// Periodically upload metrics related to memory usage.
119pub async fn collect_metrics_forever(
120    attribution_data_service: Arc<impl AttributionDataProvider + 'static>,
121    kernel_stats_proxy: fkernel::StatsProxy,
122    metric_event_logger: fmetrics::MetricEventLoggerProxy,
123    bucket_definitions: Arc<[BucketDefinition]>,
124) {
125    let mut bucket_name_to_code = HashMap::from([
126        (
127            "TotalBytes".to_string(),
128            cobalt_registry::MemoryMigratedMetricDimensionBucket::TotalBytes.as_event_code(),
129        ),
130        (
131            "Free".to_string(),
132            cobalt_registry::MemoryMigratedMetricDimensionBucket::Free.as_event_code(),
133        ),
134        (
135            "Kernel".to_string(),
136            cobalt_registry::MemoryMigratedMetricDimensionBucket::Kernel.as_event_code(),
137        ),
138        (
139            "Orphaned".to_string(),
140            cobalt_registry::MemoryMigratedMetricDimensionBucket::Orphaned.as_event_code(),
141        ),
142        (
143            "Undigested".to_string(),
144            cobalt_registry::MemoryMigratedMetricDimensionBucket::Undigested.as_event_code(),
145        ),
146        (
147            "[Addl]PagerTotal".to_string(),
148            cobalt_registry::MemoryMigratedMetricDimensionBucket::__Addl_PagerTotal.as_event_code(),
149        ),
150        (
151            "[Addl]PagerNewest".to_string(),
152            cobalt_registry::MemoryMigratedMetricDimensionBucket::__Addl_PagerNewest
153                .as_event_code(),
154        ),
155        (
156            "[Addl]PagerOldest".to_string(),
157            cobalt_registry::MemoryMigratedMetricDimensionBucket::__Addl_PagerOldest
158                .as_event_code(),
159        ),
160        (
161            "[Addl]DiscardableLocked".to_string(),
162            cobalt_registry::MemoryMigratedMetricDimensionBucket::__Addl_DiscardableLocked
163                .as_event_code(),
164        ),
165        (
166            "[Addl]DiscardableUnlocked".to_string(),
167            cobalt_registry::MemoryMigratedMetricDimensionBucket::__Addl_DiscardableUnlocked
168                .as_event_code(),
169        ),
170        (
171            "[Addl]ZramCompressedBytes".to_string(),
172            cobalt_registry::MemoryMigratedMetricDimensionBucket::__Addl_ZramCompressedBytes
173                .as_event_code(),
174        ),
175    ]);
176    bucket_definitions.iter().for_each(|bucket_definition| {
177        bucket_name_to_code
178            .entry(bucket_definition.name.clone())
179            .or_insert(bucket_definition.event_code as u32);
180    });
181    let mut timer = fuchsia_async::Interval::new(zx::Duration::from_minutes(5));
182    loop {
183        timer.next().await;
184
185        let result = collect_metrics_once(
186            &*attribution_data_service,
187            &kernel_stats_proxy,
188            &metric_event_logger,
189            &bucket_definitions,
190            &bucket_name_to_code,
191        )
192        .await;
193
194        if let Err(e) = result {
195            log::error!("Metrics collection failed: {e}");
196        }
197    }
198}
199
200async fn collect_metrics_once(
201    attribution_data_service: &impl AttributionDataProvider,
202    kernel_stats_proxy: &fkernel::StatsProxy,
203    metric_event_logger: &fmetrics::MetricEventLoggerProxy,
204    bucket_definitions: &[BucketDefinition],
205    bucket_name_to_code: &HashMap<String, u32>,
206) -> Result<()> {
207    let timestamp = zx::BootInstant::get();
208    let (kmem_stats, kmem_stats_compression) = try_join!(
209        kernel_stats_proxy.get_memory_stats().map_err(anyhow::Error::from),
210        kernel_stats_proxy.get_memory_stats_compression().map_err(anyhow::Error::from)
211    )?;
212    let digest = Digest::compute(
213        &*attribution_data_service,
214        &kmem_stats,
215        &kmem_stats_compression,
216        &bucket_definitions,
217    )?;
218
219    let events = kmem_events(&kmem_stats)
220        .chain(kmem_events_with_uptime(&kmem_stats, timestamp))
221        .chain(digest_events(digest, &bucket_name_to_code));
222    metric_event_logger
223        .log_metric_events(&events.collect::<Vec<fmetrics::MetricEvent>>())
224        .await?
225        .map_err(error_from_metrics_error)?;
226    Ok(())
227}
228
229#[cfg(test)]
230mod tests {
231    use super::*;
232    use anyhow::anyhow;
233    use attribution_processing::testing::FakeAttributionDataProvider;
234    use attribution_processing::{
235        Attribution, AttributionData, Principal, PrincipalDescription, PrincipalIdentifier,
236        PrincipalType, Resource, ResourceReference, ZXName,
237    };
238    use futures::task::Poll;
239    use futures::TryStreamExt;
240    use regex::bytes::Regex;
241    use std::time::Duration;
242    use {fidl_fuchsia_memory_attribution_plugin as fplugin, fuchsia_async as fasync};
243
244    fn get_attribution_data_provider() -> Arc<impl AttributionDataProvider + 'static> {
245        let attribution_data = AttributionData {
246            principals_vec: vec![Principal {
247                identifier: PrincipalIdentifier(1),
248                description: PrincipalDescription::Component("principal".to_owned()),
249                principal_type: PrincipalType::Runnable,
250                parent: None,
251            }],
252            resources_vec: vec![
253                // Orphaned VMO.
254                Resource {
255                    koid: 10,
256                    name_index: 0,
257                    resource_type: fplugin::ResourceType::Vmo(fplugin::Vmo {
258                        parent: None,
259                        private_committed_bytes: Some(1024),
260                        private_populated_bytes: Some(2048),
261                        scaled_committed_bytes: Some(1024),
262                        scaled_populated_bytes: Some(2048),
263                        total_committed_bytes: Some(1024),
264                        total_populated_bytes: Some(2048),
265                        ..Default::default()
266                    }),
267                },
268                // VMO belonging to bucket1.
269                Resource {
270                    koid: 20,
271                    name_index: 1,
272                    resource_type: fplugin::ResourceType::Vmo(fplugin::Vmo {
273                        parent: None,
274                        private_committed_bytes: Some(1024),
275                        private_populated_bytes: Some(2048),
276                        scaled_committed_bytes: Some(1024),
277                        scaled_populated_bytes: Some(2048),
278                        total_committed_bytes: Some(1024),
279                        total_populated_bytes: Some(2048),
280                        ..Default::default()
281                    }),
282                },
283                // Process owning bucket1's VMO.
284                Resource {
285                    koid: 30,
286                    name_index: 1,
287                    resource_type: fplugin::ResourceType::Process(fplugin::Process {
288                        vmos: Some(vec![20]),
289                        ..Default::default()
290                    }),
291                },
292            ],
293            resource_names: vec![
294                ZXName::from_string_lossy("resource"),
295                ZXName::from_string_lossy("bucket1_resource"),
296            ],
297            attributions: vec![Attribution {
298                source: PrincipalIdentifier(1),
299                subject: PrincipalIdentifier(1),
300                resources: vec![ResourceReference::KernelObject(10)],
301            }],
302        };
303        Arc::new(FakeAttributionDataProvider { attribution_data })
304    }
305
306    async fn serve_kernel_stats(
307        mut request_stream: fkernel::StatsRequestStream,
308    ) -> Result<(), fidl::Error> {
309        while let Some(request) = request_stream.try_next().await? {
310            match request {
311                fkernel::StatsRequest::GetMemoryStats { responder } => {
312                    responder
313                        .send(&fkernel::MemoryStats {
314                            total_bytes: Some(1),
315                            free_bytes: Some(2),
316                            wired_bytes: Some(3),
317                            total_heap_bytes: Some(4),
318                            free_heap_bytes: Some(5),
319                            vmo_bytes: Some(6),
320                            mmu_overhead_bytes: Some(7),
321                            ipc_bytes: Some(8),
322                            other_bytes: Some(9),
323                            free_loaned_bytes: Some(10),
324                            cache_bytes: Some(11),
325                            slab_bytes: Some(12),
326                            zram_bytes: Some(13),
327                            vmo_reclaim_total_bytes: Some(14),
328                            vmo_reclaim_newest_bytes: Some(15),
329                            vmo_reclaim_oldest_bytes: Some(16),
330                            vmo_reclaim_disabled_bytes: Some(17),
331                            vmo_discardable_locked_bytes: Some(18),
332                            vmo_discardable_unlocked_bytes: Some(19),
333                            ..Default::default()
334                        })
335                        .unwrap();
336                }
337                fkernel::StatsRequest::GetMemoryStatsExtended { responder: _ } => {
338                    unimplemented!("Deprecated call, should not be used")
339                }
340                fkernel::StatsRequest::GetMemoryStatsCompression { responder } => {
341                    responder
342                        .send(&fkernel::MemoryStatsCompression {
343                            uncompressed_storage_bytes: Some(20),
344                            compressed_storage_bytes: Some(21),
345                            compressed_fragmentation_bytes: Some(22),
346                            compression_time: Some(23),
347                            decompression_time: Some(24),
348                            total_page_compression_attempts: Some(25),
349                            failed_page_compression_attempts: Some(26),
350                            total_page_decompressions: Some(27),
351                            compressed_page_evictions: Some(28),
352                            eager_page_compressions: Some(29),
353                            memory_pressure_page_compressions: Some(30),
354                            critical_memory_page_compressions: Some(31),
355                            pages_decompressed_unit_ns: Some(32),
356                            pages_decompressed_within_log_time: Some([
357                                40, 41, 42, 43, 44, 45, 46, 47,
358                            ]),
359                            ..Default::default()
360                        })
361                        .unwrap();
362                }
363                fkernel::StatsRequest::GetCpuStats { responder: _ } => unimplemented!(),
364                fkernel::StatsRequest::GetCpuLoad { duration: _, responder: _ } => unimplemented!(),
365            }
366        }
367        Ok(())
368    }
369
370    #[test]
371    fn test_periodic_metrics_collection() -> anyhow::Result<()> {
372        // Setup executor.
373        let mut exec = fasync::TestExecutor::new_with_fake_time();
374
375        // Setup mock data providers.
376        let data_provider = get_attribution_data_provider();
377        let (stats_provider, stats_request_stream) =
378            fidl::endpoints::create_proxy_and_stream::<fkernel::StatsMarker>();
379        fasync::Task::spawn(async move {
380            serve_kernel_stats(stats_request_stream).await.unwrap();
381        })
382        .detach();
383        // Define a single bucket which matches a specific VMO.
384        let bucket_definitions = Arc::new([BucketDefinition {
385            name: "bucket1".to_string(),
386            vmo: Some(Regex::new("bucket1.*")?),
387            event_code: 1,
388            process: None,
389        }]);
390
391        // Setup test proxy to observe emitted events from the service.
392        let (metric_event_logger, metric_event_request_stream) =
393            fidl::endpoints::create_proxy_and_stream::<fmetrics::MetricEventLoggerMarker>();
394
395        // Service under test.
396        let mut metrics_collector = fuchsia_async::Task::spawn(collect_metrics_forever(
397            data_provider,
398            stats_provider,
399            metric_event_logger,
400            bucket_definitions,
401        ));
402
403        // Give the service the opportunity to run.
404        assert!(
405            exec.run_until_stalled(&mut metrics_collector).is_pending(),
406            "Metrics collection service returned unexpectedly early"
407        );
408
409        // Ensure no metrics has been uploaded yet.
410        let mut metric_event_request_future = metric_event_request_stream.into_future();
411        assert!(
412            exec.run_until_stalled(&mut metric_event_request_future).is_pending(),
413            "Metrics collection service returned unexpectedly early"
414        );
415
416        // Fake the passage of time, so that collect_metrics may do a capture.
417        assert!(
418            exec.run_until_stalled(&mut std::pin::pin!(fasync::TestExecutor::advance_to(
419                exec.now() + Duration::from_secs(5 * 60).into()
420            )))
421            .is_ready(),
422            "Failed to advance time"
423        );
424        let uptime = get_uptime_event_code(zx::BootInstant::get());
425
426        // Ensure we have one and only one event ready for consumption.
427        let Poll::Ready((event, metric_event_request_stream)) =
428            exec.run_until_stalled(&mut metric_event_request_future)
429        else {
430            panic!("Failed to receive metrics")
431        };
432        let event = event.ok_or_else(|| anyhow!("Metrics stream unexpectedly closed"))??;
433        match event {
434            fmetrics::MetricEventLoggerRequest::LogMetricEvents { events, .. } => {
435                // Kernel metrics
436                assert_eq!(
437                    &events[0..10],
438                    vec![
439                        fmetrics::MetricEvent {
440                            metric_id: cobalt_registry::MEMORY_GENERAL_BREAKDOWN_MIGRATED_METRIC_ID,
441                            event_codes: vec![cobalt_registry::MemoryGeneralBreakdownMigratedMetricDimensionGeneralBreakdown::TotalBytes.as_event_code()],
442                            payload: fmetrics::MetricEventPayload::IntegerValue(1)
443                        },
444                        fmetrics::MetricEvent {
445                            metric_id: cobalt_registry::MEMORY_GENERAL_BREAKDOWN_MIGRATED_METRIC_ID,
446                            event_codes: vec![cobalt_registry::MemoryGeneralBreakdownMigratedMetricDimensionGeneralBreakdown::UsedBytes.as_event_code()],
447                            payload: fmetrics::MetricEventPayload::IntegerValue(-1)
448                        },
449                        fmetrics::MetricEvent {
450                            metric_id: cobalt_registry::MEMORY_GENERAL_BREAKDOWN_MIGRATED_METRIC_ID,
451                            event_codes: vec![cobalt_registry::MemoryGeneralBreakdownMigratedMetricDimensionGeneralBreakdown::FreeBytes.as_event_code()],
452                            payload: fmetrics::MetricEventPayload::IntegerValue(2)
453                        },
454                        fmetrics::MetricEvent {
455                            metric_id: cobalt_registry::MEMORY_GENERAL_BREAKDOWN_MIGRATED_METRIC_ID,
456                            event_codes: vec![cobalt_registry::MemoryGeneralBreakdownMigratedMetricDimensionGeneralBreakdown::VmoBytes.as_event_code()],
457                            payload: fmetrics::MetricEventPayload::IntegerValue(6)
458                        },
459                        fmetrics::MetricEvent {
460                            metric_id: cobalt_registry::MEMORY_GENERAL_BREAKDOWN_MIGRATED_METRIC_ID,
461                            event_codes: vec![cobalt_registry::MemoryGeneralBreakdownMigratedMetricDimensionGeneralBreakdown::KernelFreeHeapBytes.as_event_code()],
462                            payload: fmetrics::MetricEventPayload::IntegerValue(5)
463                        },
464                        fmetrics::MetricEvent {
465                            metric_id: cobalt_registry::MEMORY_GENERAL_BREAKDOWN_MIGRATED_METRIC_ID,
466                            event_codes: vec![cobalt_registry::MemoryGeneralBreakdownMigratedMetricDimensionGeneralBreakdown::MmuBytes.as_event_code()],
467                            payload: fmetrics::MetricEventPayload::IntegerValue(7)
468                        },
469                        fmetrics::MetricEvent {
470                            metric_id: cobalt_registry::MEMORY_GENERAL_BREAKDOWN_MIGRATED_METRIC_ID,
471                            event_codes: vec![cobalt_registry::MemoryGeneralBreakdownMigratedMetricDimensionGeneralBreakdown::IpcBytes.as_event_code()],
472                            payload: fmetrics::MetricEventPayload::IntegerValue(8)
473                        },
474                        fmetrics::MetricEvent {
475                            metric_id: cobalt_registry::MEMORY_GENERAL_BREAKDOWN_MIGRATED_METRIC_ID,
476                            event_codes: vec![cobalt_registry::MemoryGeneralBreakdownMigratedMetricDimensionGeneralBreakdown::KernelTotalHeapBytes.as_event_code()],
477                            payload: fmetrics::MetricEventPayload::IntegerValue(4)
478                        },
479                        fmetrics::MetricEvent {
480                            metric_id: cobalt_registry::MEMORY_GENERAL_BREAKDOWN_MIGRATED_METRIC_ID,
481                            event_codes: vec![cobalt_registry::MemoryGeneralBreakdownMigratedMetricDimensionGeneralBreakdown::WiredBytes.as_event_code()],
482                            payload: fmetrics::MetricEventPayload::IntegerValue(3)
483                        },
484                        fmetrics::MetricEvent {
485                            metric_id: cobalt_registry::MEMORY_GENERAL_BREAKDOWN_MIGRATED_METRIC_ID,
486                            event_codes: vec![cobalt_registry::MemoryGeneralBreakdownMigratedMetricDimensionGeneralBreakdown::OtherBytes.as_event_code()],
487                            payload: fmetrics::MetricEventPayload::IntegerValue(9)
488                        },]);
489                // Kernel metrics with uptime
490                assert_eq!(
491                    &events[10..20],
492                    vec![
493                        fmetrics::MetricEvent {
494                            metric_id: cobalt_registry::MEMORY_LEAK_MIGRATED_METRIC_ID,
495                            event_codes: cobalt_registry::MemoryLeakMigratedEventCodes {
496                                general_breakdown: cobalt_registry::MemoryLeakMigratedMetricDimensionGeneralBreakdown::TotalBytes, time_since_boot: uptime}.as_event_codes(),
497                            payload: fmetrics::MetricEventPayload::IntegerValue(1)
498                        },
499                        fmetrics::MetricEvent {
500                            metric_id: cobalt_registry::MEMORY_LEAK_MIGRATED_METRIC_ID,
501                            event_codes: cobalt_registry::MemoryLeakMigratedEventCodes {
502                                general_breakdown: cobalt_registry::MemoryLeakMigratedMetricDimensionGeneralBreakdown::UsedBytes, time_since_boot:uptime}.as_event_codes(),
503                            payload: fmetrics::MetricEventPayload::IntegerValue(-1)
504                        },
505                        fmetrics::MetricEvent {
506                            metric_id: cobalt_registry::MEMORY_LEAK_MIGRATED_METRIC_ID,
507                            event_codes: cobalt_registry::MemoryLeakMigratedEventCodes {
508                                general_breakdown: cobalt_registry::MemoryLeakMigratedMetricDimensionGeneralBreakdown::FreeBytes, time_since_boot:uptime}.as_event_codes(),                            payload: fmetrics::MetricEventPayload::IntegerValue(2)
509                        },
510                        fmetrics::MetricEvent {
511                            metric_id: cobalt_registry::MEMORY_LEAK_MIGRATED_METRIC_ID,
512                            event_codes: cobalt_registry::MemoryLeakMigratedEventCodes {
513                                general_breakdown: cobalt_registry::MemoryLeakMigratedMetricDimensionGeneralBreakdown::VmoBytes, time_since_boot:uptime}.as_event_codes(),
514                            payload: fmetrics::MetricEventPayload::IntegerValue(6)
515                        },
516                        fmetrics::MetricEvent {
517                            metric_id: cobalt_registry::MEMORY_LEAK_MIGRATED_METRIC_ID,
518                            event_codes: cobalt_registry::MemoryLeakMigratedEventCodes {
519                                general_breakdown: cobalt_registry::MemoryLeakMigratedMetricDimensionGeneralBreakdown::KernelFreeHeapBytes, time_since_boot:uptime}.as_event_codes(),
520                            payload: fmetrics::MetricEventPayload::IntegerValue(5)
521                        },
522                        fmetrics::MetricEvent {
523                            metric_id: cobalt_registry::MEMORY_LEAK_MIGRATED_METRIC_ID,
524                            event_codes: cobalt_registry::MemoryLeakMigratedEventCodes {
525                                general_breakdown: cobalt_registry::MemoryLeakMigratedMetricDimensionGeneralBreakdown::MmuBytes, time_since_boot:uptime}.as_event_codes(),
526                            payload: fmetrics::MetricEventPayload::IntegerValue(7)
527                        },
528                        fmetrics::MetricEvent {
529                            metric_id: cobalt_registry::MEMORY_LEAK_MIGRATED_METRIC_ID,
530                            event_codes: cobalt_registry::MemoryLeakMigratedEventCodes {
531                                general_breakdown: cobalt_registry::MemoryLeakMigratedMetricDimensionGeneralBreakdown::IpcBytes, time_since_boot:uptime}.as_event_codes(),
532                            payload: fmetrics::MetricEventPayload::IntegerValue(8)
533                        },
534                        fmetrics::MetricEvent {
535                            metric_id: cobalt_registry::MEMORY_LEAK_MIGRATED_METRIC_ID,
536                            event_codes: cobalt_registry::MemoryLeakMigratedEventCodes {
537                                general_breakdown: cobalt_registry::MemoryLeakMigratedMetricDimensionGeneralBreakdown::KernelTotalHeapBytes, time_since_boot:uptime}.as_event_codes(),
538                            payload: fmetrics::MetricEventPayload::IntegerValue(4)
539                        },
540                        fmetrics::MetricEvent {
541                            metric_id: cobalt_registry::MEMORY_LEAK_MIGRATED_METRIC_ID,
542                            event_codes: cobalt_registry::MemoryLeakMigratedEventCodes {
543                                general_breakdown: cobalt_registry::MemoryLeakMigratedMetricDimensionGeneralBreakdown::WiredBytes, time_since_boot:uptime}.as_event_codes(),
544                            payload: fmetrics::MetricEventPayload::IntegerValue(3)
545                        },
546                        fmetrics::MetricEvent {
547                            metric_id: cobalt_registry::MEMORY_LEAK_MIGRATED_METRIC_ID,
548                            event_codes: cobalt_registry::MemoryLeakMigratedEventCodes {
549                                general_breakdown: cobalt_registry::MemoryLeakMigratedMetricDimensionGeneralBreakdown::OtherBytes, time_since_boot:uptime}.as_event_codes(),
550                            payload: fmetrics::MetricEventPayload::IntegerValue(9)
551                        },
552                    ]
553                );
554                // Digest metrics
555                assert_eq!(
556                    &events[20..],
557                    vec![
558                        // Buckets with custom definitions
559                        fmetrics::MetricEvent {
560                            metric_id: cobalt_registry::MEMORY_MIGRATED_METRIC_ID,
561                            event_codes: vec![1], // Corresponds to the "bucket1" bucket
562                            payload: fmetrics::MetricEventPayload::IntegerValue(1024)
563                        },
564                        // Default buckets
565                        fmetrics::MetricEvent {
566                            metric_id: cobalt_registry::MEMORY_MIGRATED_METRIC_ID,
567                            event_codes: vec![cobalt_registry::MemoryMigratedMetricDimensionBucket::Undigested.as_event_code()],
568                            payload: fmetrics::MetricEventPayload::IntegerValue(1024)
569                        },
570                        fmetrics::MetricEvent {
571                            metric_id: cobalt_registry::MEMORY_MIGRATED_METRIC_ID,
572                            event_codes: vec![cobalt_registry::MemoryMigratedMetricDimensionBucket::Orphaned.as_event_code()],
573                            payload: fmetrics::MetricEventPayload::IntegerValue(0)
574                        },
575                        fmetrics::MetricEvent {
576                            metric_id: cobalt_registry::MEMORY_MIGRATED_METRIC_ID,
577                            event_codes: vec![cobalt_registry::MemoryMigratedMetricDimensionBucket::Kernel.as_event_code()],
578                            payload: fmetrics::MetricEventPayload::IntegerValue(31)
579                        },
580                        fmetrics::MetricEvent {
581                            metric_id: cobalt_registry::MEMORY_MIGRATED_METRIC_ID,
582                            event_codes: vec![cobalt_registry::MemoryMigratedMetricDimensionBucket::Free.as_event_code()],
583                            payload: fmetrics::MetricEventPayload::IntegerValue(2)
584                        },
585                        fmetrics::MetricEvent {
586                            metric_id: cobalt_registry::MEMORY_MIGRATED_METRIC_ID,
587                            event_codes: vec![cobalt_registry::MemoryMigratedMetricDimensionBucket::__Addl_PagerTotal.as_event_code()],
588                            payload: fmetrics::MetricEventPayload::IntegerValue(14)
589                        },
590                        fmetrics::MetricEvent {
591                            metric_id: cobalt_registry::MEMORY_MIGRATED_METRIC_ID,
592                            event_codes: vec![cobalt_registry::MemoryMigratedMetricDimensionBucket::__Addl_PagerNewest.as_event_code()],
593                            payload: fmetrics::MetricEventPayload::IntegerValue(15)
594                        },
595                        fmetrics::MetricEvent {
596                            metric_id: cobalt_registry::MEMORY_MIGRATED_METRIC_ID,
597                            event_codes: vec![cobalt_registry::MemoryMigratedMetricDimensionBucket::__Addl_PagerOldest.as_event_code()],
598                            payload: fmetrics::MetricEventPayload::IntegerValue(16)
599                        },
600                        fmetrics::MetricEvent {
601                            metric_id: cobalt_registry::MEMORY_MIGRATED_METRIC_ID,
602                            event_codes: vec![cobalt_registry::MemoryMigratedMetricDimensionBucket::__Addl_DiscardableLocked.as_event_code()],
603                            payload: fmetrics::MetricEventPayload::IntegerValue(18)
604                        },
605                        fmetrics::MetricEvent {
606                            metric_id: cobalt_registry::MEMORY_MIGRATED_METRIC_ID,
607                            event_codes: vec![cobalt_registry::MemoryMigratedMetricDimensionBucket::__Addl_DiscardableUnlocked.as_event_code()],
608                            payload: fmetrics::MetricEventPayload::IntegerValue(19)
609                        },
610                        fmetrics::MetricEvent {
611                            metric_id: cobalt_registry::MEMORY_MIGRATED_METRIC_ID,
612                            event_codes: vec![cobalt_registry::MemoryMigratedMetricDimensionBucket::__Addl_ZramCompressedBytes.as_event_code()],
613                            payload: fmetrics::MetricEventPayload::IntegerValue(21)
614                        }
615                    ]
616                )
617            }
618            _ => panic!("Unexpected metric event"),
619        }
620
621        assert!(exec
622            .run_until_stalled(&mut metric_event_request_stream.into_future())
623            .is_pending());
624        Ok(())
625    }
626}