Skip to main content

traces/
kernel.rs

1// Copyright 2024 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.
4use anyhow::Result;
5use fuchsia_async::{Interval, MonotonicDuration};
6use fuchsia_trace::{category_enabled, counter};
7use fuchsia_trace_observer::TraceObserver;
8use futures::future::FutureExt;
9use futures::{StreamExt, select};
10use log::debug;
11use stalls::StallProvider;
12use stalls::refaults::RefaultProvider;
13use std::ffi::CStr;
14
15const CATEGORY_MEMORY_KERNEL: &'static CStr = c"memory:kernel";
16// LINT.IfChange
17const SAMPLE_INTERVAL: MonotonicDuration = MonotonicDuration::from_millis(200);
18// LINT.ThenChange(//src/performance/memory/attribution/monitor/tests/traces/lib.rs)
19
20// Continuously monitors the 'memory:kernel' trace category on a dedicated thread.
21// Once enabled, it periodically records memory statistics until the category is disabled.
22// This function runs indefinitely
23pub async fn serve_forever(
24    kernel_stats: impl fidl_fuchsia_kernel::StatsProxyInterface + Send + 'static,
25    stall_provider: impl StallProvider + Send + 'static,
26    page_refault_tracker: impl RefaultProvider + Send + 'static,
27) {
28    let (tx, rx) = futures::channel::oneshot::channel::<()>();
29    std::thread::Builder::new()
30        .name("memory:tracing".to_string())
31        .spawn(move || {
32            let mut executor = fuchsia_async::LocalExecutor::default();
33            executor.run_singlethreaded(serve_forever_loop(
34                kernel_stats,
35                stall_provider,
36                page_refault_tracker,
37            ));
38            // The task failed, send a message.
39            let _ = tx.send(());
40        })
41        .expect("failed to spawn memory:tracing thread");
42    let _ = rx.await;
43}
44
45// Internal loop, kept public for testing in
46// //src/performance/memory/attribution/monitor/tests/traces.
47pub async fn serve_forever_loop(
48    kernel_stats: impl fidl_fuchsia_kernel::StatsProxyInterface,
49    stall_provider: impl StallProvider,
50    page_refault_tracker: impl RefaultProvider,
51) {
52    fuchsia_trace_provider::trace_provider_create_with_fdio();
53    fuchsia_trace_provider::trace_provider_wait_for_init();
54    debug!("Start serving traces");
55    let trace_observer = TraceObserver::new();
56    loop {
57        let mut interval = Interval::new(SAMPLE_INTERVAL);
58        while category_enabled(CATEGORY_MEMORY_KERNEL) {
59            if let Err(err) = publish_one_sample(
60                &kernel_stats,
61                stall_provider.clone(),
62                page_refault_tracker.clone(),
63            )
64            .await
65            {
66                log::warn!("Failed to trace on category {:?} : {:?}", CATEGORY_MEMORY_KERNEL, err);
67            }
68            debug!("Wait for {} ms", SAMPLE_INTERVAL.into_millis());
69            select! {
70                _ = interval.next() => (),
71                _ = trace_observer.on_state_changed().fuse() => (),
72            };
73        }
74        debug!("Trace category {:?} not active. Waiting.", CATEGORY_MEMORY_KERNEL);
75        let _ = trace_observer.on_state_changed().await;
76        debug!("Trace event detected");
77    }
78}
79
80async fn publish_one_sample(
81    kernel_stats: &impl fidl_fuchsia_kernel::StatsProxyInterface,
82    stall_provider: impl StallProvider,
83    page_refault_tracker: impl RefaultProvider,
84) -> Result<()> {
85    debug!("Publish trace records for category {:?}", CATEGORY_MEMORY_KERNEL);
86    let mem_stats = kernel_stats.get_memory_stats().await?;
87    // Statistics are split into two records to comply with the 15-argument limit.
88    counter!(CATEGORY_MEMORY_KERNEL, c"kmem_stats_a",0,
89        "total_bytes"=>mem_stats.total_bytes.unwrap_or_default(),
90        "free_bytes"=>mem_stats.free_bytes.unwrap_or_default(),
91        "free_loaned_bytes"=>mem_stats.free_loaned_bytes.unwrap_or_default(),
92        "wired_bytes"=>mem_stats.wired_bytes.unwrap_or_default(),
93        "total_heap_bytes"=>mem_stats.total_heap_bytes.unwrap_or_default(),
94        "free_heap_bytes"=>mem_stats.free_heap_bytes.unwrap_or_default(),
95        "vmo_bytes"=>mem_stats.vmo_bytes.unwrap_or_default(),
96        "mmu_overhead_bytes"=>mem_stats.mmu_overhead_bytes.unwrap_or_default(),
97        "ipc_bytes"=>mem_stats.ipc_bytes.unwrap_or_default(),
98        "cache_bytes"=>mem_stats.cache_bytes.unwrap_or_default(),
99        "slab_bytes"=>mem_stats.slab_bytes.unwrap_or_default(),
100        "zram_bytes"=>mem_stats.zram_bytes.unwrap_or_default(),
101        "other_bytes"=>mem_stats.other_bytes.unwrap_or_default()
102    );
103    counter!(CATEGORY_MEMORY_KERNEL, c"kmem_stats_b", 0,
104        "vmo_reclaim_total_bytes"=>mem_stats.vmo_reclaim_total_bytes.unwrap_or_default(),
105        "vmo_reclaim_newest_bytes"=>mem_stats.vmo_reclaim_newest_bytes.unwrap_or_default(),
106        "vmo_reclaim_oldest_bytes"=>mem_stats.vmo_reclaim_oldest_bytes.unwrap_or_default(),
107        "vmo_reclaim_disabled_bytes"=>mem_stats.vmo_reclaim_disabled_bytes.unwrap_or_default(),
108        "vmo_discardable_locked_bytes"=>mem_stats.vmo_discardable_locked_bytes.unwrap_or_default(),
109        "vmo_discardable_unlocked_bytes"=>mem_stats.vmo_discardable_unlocked_bytes.unwrap_or_default()
110    );
111    let cmp_stats = kernel_stats.get_memory_stats_compression().await?;
112    counter!(CATEGORY_MEMORY_KERNEL, c"kmem_stats_compression", 0,
113        "uncompressed_storage_bytes"=>cmp_stats.uncompressed_storage_bytes.unwrap_or_default(),
114        "compressed_storage_bytes"=>cmp_stats.compressed_storage_bytes.unwrap_or_default(),
115        "compressed_fragmentation_bytes"=>cmp_stats.compressed_fragmentation_bytes.unwrap_or_default(),
116        "compression_time"=>cmp_stats.compression_time.unwrap_or_default(),
117        "decompression_time"=>cmp_stats.decompression_time.unwrap_or_default(),
118        "total_page_compression_attempts"=>cmp_stats.total_page_compression_attempts.unwrap_or_default(),
119        "failed_page_compression_attempts"=>cmp_stats.failed_page_compression_attempts.unwrap_or_default(),
120        "total_page_decompressions"=>cmp_stats.total_page_decompressions.unwrap_or_default(),
121        "compressed_page_evictions"=>cmp_stats.compressed_page_evictions.unwrap_or_default(),
122        "eager_page_compressions"=>cmp_stats.eager_page_compressions.unwrap_or_default(),
123        "memory_pressure_page_compressions"=>cmp_stats.memory_pressure_page_compressions.unwrap_or_default(),
124        "critical_memory_page_compressions"=>cmp_stats.critical_memory_page_compressions.unwrap_or_default()
125    );
126
127    if let Some(pd) = cmp_stats.pages_decompressed_within_log_time {
128        counter!(
129            CATEGORY_MEMORY_KERNEL,
130            c"kmem_stats_compression_time",0,
131            "pages_decompressed_unit_ns"=>cmp_stats.pages_decompressed_unit_ns.unwrap_or_default(),
132            "pages_decompressed_within_log_time[0]"=>pd[0],
133            "pages_decompressed_within_log_time[1]"=>pd[1],
134            "pages_decompressed_within_log_time[2]"=>pd[2],
135            "pages_decompressed_within_log_time[3]"=>pd[3],
136            "pages_decompressed_within_log_time[4]"=>pd[4],
137            "pages_decompressed_within_log_time[5]"=>pd[5],
138            "pages_decompressed_within_log_time[6]"=>pd[6],
139            "pages_decompressed_within_log_time[7]"=>pd[7]
140        );
141    }
142
143    let stall_info = stall_provider.get_stall_info()?;
144    counter!(
145        CATEGORY_MEMORY_KERNEL,
146        c"memory_stall",0,
147        "stall_time_some_ns"=>u64::try_from(stall_info.some.as_nanos())?,
148        "stall_time_full_ns"=>u64::try_from(stall_info.full.as_nanos())?,
149        "page_refaults"=>page_refault_tracker.get_count()
150    );
151
152    Ok(())
153}