Skip to main content

servo_allocator/
lib.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! Selecting the default global allocator for Servo, and exposing common
6//! allocator introspection APIs for memory profiling.
7
8use std::os::raw::c_void;
9
10#[cfg(not(feature = "allocation-tracking"))]
11#[global_allocator]
12static ALLOC: Allocator = Allocator;
13
14#[cfg(feature = "allocation-tracking")]
15#[global_allocator]
16static ALLOC: crate::tracking::AccountingAlloc<Allocator> =
17    crate::tracking::AccountingAlloc::with_allocator(Allocator);
18
19#[cfg(feature = "allocation-tracking")]
20mod tracking;
21
22pub fn is_tracking_unmeasured() -> bool {
23    cfg!(feature = "allocation-tracking")
24}
25
26pub fn dump_unmeasured(_writer: impl std::io::Write) {
27    #[cfg(feature = "allocation-tracking")]
28    ALLOC.dump_unmeasured_allocations(_writer);
29}
30
31pub use crate::platform::*;
32
33type EnclosingSizeFn = unsafe extern "C" fn(*const c_void) -> usize;
34
35/// # Safety
36/// No restrictions. The passed pointer is never dereferenced.
37/// This function is only marked unsafe because the MallocSizeOfOps APIs
38/// requires an unsafe function pointer.
39#[cfg(feature = "allocation-tracking")]
40unsafe extern "C" fn enclosing_size_impl(ptr: *const c_void) -> usize {
41    let (adjusted, size) = crate::ALLOC.enclosing_size(ptr);
42    if size != 0 {
43        crate::ALLOC.note_allocation(adjusted, size);
44    }
45    size
46}
47
48#[expect(non_upper_case_globals)]
49#[cfg(feature = "allocation-tracking")]
50pub static enclosing_size: Option<EnclosingSizeFn> = Some(crate::enclosing_size_impl);
51
52#[expect(non_upper_case_globals)]
53#[cfg(not(feature = "allocation-tracking"))]
54pub static enclosing_size: Option<EnclosingSizeFn> = None;
55
56#[cfg(not(any(windows, feature = "use-system-allocator", target_env = "ohos")))]
57mod platform {
58    use std::os::raw::c_void;
59
60    pub use tikv_jemallocator::Jemalloc as Allocator;
61
62    /// Get the size of a heap block.
63    ///
64    /// # Safety
65    ///
66    /// Passing a non-heap allocated pointer to this function results in undefined behavior.
67    pub unsafe extern "C" fn usable_size(ptr: *const c_void) -> usize {
68        let size = unsafe { tikv_jemallocator::usable_size(ptr) };
69        #[cfg(feature = "allocation-tracking")]
70        crate::ALLOC.note_allocation(ptr, size);
71        size
72    }
73
74    /// Memory allocation APIs compatible with libc
75    pub mod libc_compat {
76        pub use tikv_jemalloc_sys::{free, malloc, realloc};
77    }
78}
79
80#[cfg(all(
81    not(windows),
82    any(feature = "use-system-allocator", target_env = "ohos")
83))]
84mod platform {
85    pub use std::alloc::System as Allocator;
86    use std::os::raw::c_void;
87
88    /// Get the size of a heap block.
89    ///
90    /// # Safety
91    ///
92    /// Passing a non-heap allocated pointer to this function results in undefined behavior.
93    pub unsafe extern "C" fn usable_size(ptr: *const c_void) -> usize {
94        #[cfg(target_vendor = "apple")]
95        unsafe {
96            let size = libc::malloc_size(ptr);
97            #[cfg(feature = "allocation-tracking")]
98            crate::ALLOC.note_allocation(ptr, size);
99            size
100        }
101
102        #[cfg(not(target_vendor = "apple"))]
103        unsafe {
104            let size = libc::malloc_usable_size(ptr as *mut _);
105            #[cfg(feature = "allocation-tracking")]
106            crate::ALLOC.note_allocation(ptr, size);
107            size
108        }
109    }
110
111    pub mod libc_compat {
112        pub use libc::{free, malloc, realloc};
113    }
114}
115
116#[cfg(windows)]
117mod platform {
118    pub use std::alloc::System as Allocator;
119    use std::os::raw::c_void;
120
121    use windows_sys::Win32::Foundation::FALSE;
122    use windows_sys::Win32::System::Memory::{GetProcessHeap, HeapSize, HeapValidate};
123
124    /// Get the size of a heap block.
125    ///
126    /// # Safety
127    ///
128    /// Passing a non-heap allocated pointer to this function results in undefined behavior.
129    pub unsafe extern "C" fn usable_size(mut ptr: *const c_void) -> usize {
130        unsafe {
131            let heap = GetProcessHeap();
132
133            if HeapValidate(heap, 0, ptr) == FALSE {
134                ptr = *(ptr as *const *const c_void).offset(-1)
135            }
136
137            let size = HeapSize(heap, 0, ptr) as usize;
138            #[cfg(feature = "allocation-tracking")]
139            crate::ALLOC.note_allocation(ptr, size);
140            size
141        }
142    }
143}