_core_rustc_static/
lib.rs

1// Copyright 2023 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 lazy_static::lazy_static;
6use std::cell::RefCell;
7use std::ffi::c_char;
8use zx::sys::zx_handle_t;
9use zx::{self as zx, AsHandleRef};
10
11mod allocations_table;
12mod profiler;
13mod recursion_guard;
14mod resources_table;
15mod waiter_list;
16
17// Do not include the hooks in the tests' executable, to avoid instrumenting the test framework.
18#[cfg(not(test))]
19mod hooks;
20
21use crate::profiler::{PerThreadData, Profiler};
22use crate::recursion_guard::with_recursion_guard;
23
24// WARNING! Do not change this to use once_cell: once_cell uses parking_lot, which may allocate in
25// the contended case.
26lazy_static! {
27    static ref PROFILER: Profiler = with_recursion_guard(Default::default);
28}
29
30thread_local! {
31    static THREAD_DATA: RefCell<PerThreadData> = with_recursion_guard(Default::default);
32}
33
34#[derive(Clone, Copy, Default)]
35#[repr(C)]
36pub struct heapdump_global_stats {
37    pub total_allocated_bytes: u64,
38    pub total_deallocated_bytes: u64,
39}
40
41#[derive(Clone, Copy, Default)]
42#[repr(C)]
43pub struct heapdump_thread_local_stats {
44    pub total_allocated_bytes: u64,
45    pub total_deallocated_bytes: u64,
46}
47
48// Calls `f` under a recursion guard, giving it access to the Profiler and the current thread's
49// PerThreadData.
50pub fn with_profiler(f: impl FnOnce(&Profiler, &mut PerThreadData)) {
51    let profiler = &*PROFILER;
52    THREAD_DATA.with(|thread_data| {
53        with_recursion_guard(|| {
54            f(profiler, &mut thread_data.borrow_mut());
55        })
56    })
57}
58
59/// # Safety
60/// The caller must pass either a channel handle or an invalid handle.
61#[no_mangle]
62pub unsafe extern "C" fn heapdump_bind_with_channel(registry_channel: zx_handle_t) {
63    let handle = zx::Handle::from_raw(registry_channel);
64    if !handle.is_invalid() {
65        assert_eq!(handle.basic_info().unwrap().object_type, zx::ObjectType::CHANNEL);
66    }
67
68    PROFILER.bind(handle.into());
69}
70
71/// # Safety
72/// The caller must pass suitably-aligned and writable areas of memory to store the stats into.
73#[no_mangle]
74pub unsafe extern "C" fn heapdump_get_stats(
75    global: *mut heapdump_global_stats,
76    local: *mut heapdump_thread_local_stats,
77) {
78    with_profiler(|profiler, thread_data| {
79        if global != std::ptr::null_mut() {
80            *global = profiler.get_global_stats();
81        }
82        if local != std::ptr::null_mut() {
83            *local = thread_data.get_local_stats();
84        }
85    });
86}
87
88/// # Safety
89/// The caller must pass a nul-terminated string whose length is not greater than ZX_MAX_NAME_LEN.
90#[no_mangle]
91pub unsafe extern "C" fn heapdump_take_named_snapshot(name: *const c_char) {
92    let name_cstr = std::ffi::CStr::from_ptr(name);
93    let name_str = name_cstr.to_str().expect("name contains invalid characters");
94    assert!(name_str.len() <= zx::sys::ZX_MAX_NAME_LEN, "name is too long");
95
96    PROFILER.publish_named_snapshot(name_str);
97}