malloc_size_of/
lib.rs

1// Copyright 2016-2017 The Servo Project Developers. See the COPYRIGHT
2// file at the top-level directory of this distribution and at
3// http://rust-lang.org/COPYRIGHT.
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
11//! A reduced fork of Firefox's malloc_size_of crate, for bundling with
12//! WebRender.
13use cfg_if::cfg_if;
14use cfx_types::{
15    AddressWithSpace, AllChainID, Space, SpaceMap, H160, H256, H512, U256, U512,
16};
17use hashbrown::HashMap as FastHashMap;
18use slab::Slab;
19use std::{
20    cmp::Reverse,
21    collections::{BinaryHeap, HashSet, VecDeque},
22    hash::{BuildHasher, Hash},
23    mem::{self, size_of},
24    ops::Range,
25    os::raw::c_void,
26    sync::Arc,
27};
28
29/// A C function that takes a pointer to a heap allocation and returns its size.
30type VoidPtrToSizeFn = unsafe extern "C" fn(ptr: *const c_void) -> usize;
31
32/// Operations used when measuring heap usage of data structures.
33pub struct MallocSizeOfOps {
34    /// A function that returns the size of a heap allocation.
35    pub size_of_op: VoidPtrToSizeFn,
36
37    /// Like `size_of_op`, but can take an interior pointer. Optional because
38    /// not all allocators support this operation. If it's not provided, some
39    /// memory measurements will actually be computed estimates rather than
40    /// real and accurate measurements.
41    pub enclosing_size_of_op: Option<VoidPtrToSizeFn>,
42
43    pub visited: HashSet<usize>,
44}
45
46impl MallocSizeOfOps {
47    pub fn new(
48        size_of: VoidPtrToSizeFn,
49        malloc_enclosing_size_of: Option<VoidPtrToSizeFn>,
50    ) -> Self {
51        MallocSizeOfOps {
52            size_of_op: size_of,
53            enclosing_size_of_op: malloc_enclosing_size_of,
54            visited: HashSet::new(),
55        }
56    }
57
58    /// Check if an allocation is empty. This relies on knowledge of how Rust
59    /// handles empty allocations, which may change in the future.
60    fn is_empty<T: ?Sized>(ptr: *const T) -> bool {
61        // The correct condition is this:
62        //   `ptr as usize <= ::std::mem::align_of::<T>()`
63        // But we can't call align_of() on a ?Sized T. So we approximate it
64        // with the following. 256 is large enough that it should always be
65        // larger than the required alignment, but small enough that it is
66        // always in the first page of memory and therefore not a legitimate
67        // address.
68        ptr as *const usize as usize <= 256
69    }
70
71    /// Call `size_of_op` on `ptr`, first checking that the allocation isn't
72    /// empty, because some types (such as `Vec`) utilize empty allocations.
73    pub unsafe fn malloc_size_of<T: ?Sized>(&self, ptr: *const T) -> usize {
74        if MallocSizeOfOps::is_empty(ptr) {
75            0
76        } else {
77            (self.size_of_op)(ptr as *const c_void)
78        }
79    }
80
81    /// Is an `enclosing_size_of_op` available?
82    pub fn has_malloc_enclosing_size_of(&self) -> bool {
83        self.enclosing_size_of_op.is_some()
84    }
85
86    /// Call `enclosing_size_of_op`, which must be available, on `ptr`, which
87    /// must not be empty.
88    pub unsafe fn malloc_enclosing_size_of<T>(&self, ptr: *const T) -> usize {
89        assert!(!MallocSizeOfOps::is_empty(ptr));
90        (self.enclosing_size_of_op.unwrap())(ptr as *const c_void)
91    }
92}
93
94/// Trait for measuring the "deep" heap usage of a data structure. This is the
95/// most commonly-used of the traits.
96pub trait MallocSizeOf {
97    /// Measure the heap usage of all descendant heap-allocated structures, but
98    /// not the space taken up by the value itself.
99    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize;
100}
101
102/// Trait for measuring the "shallow" heap usage of a container.
103pub trait MallocShallowSizeOf {
104    /// Measure the heap usage of immediate heap-allocated descendant
105    /// structures, but not the space taken up by the value itself. Anything
106    /// beyond the immediate descendants must be measured separately, using
107    /// iteration.
108    fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize;
109}
110
111impl MallocSizeOf for String {
112    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
113        unsafe { ops.malloc_size_of(self.as_ptr()) }
114    }
115}
116
117impl<T: MallocSizeOf> MallocSizeOf for SpaceMap<T> {
118    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
119        self.map_sum(|x| x.size_of(ops))
120    }
121}
122
123impl<T: ?Sized> MallocShallowSizeOf for Box<T> {
124    fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
125        unsafe { ops.malloc_size_of(&**self) }
126    }
127}
128
129impl<T: MallocSizeOf + ?Sized> MallocSizeOf for Box<T> {
130    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
131        self.shallow_size_of(ops) + (**self).size_of(ops)
132    }
133}
134
135impl MallocSizeOf for () {
136    fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { 0 }
137}
138
139impl<T1, T2> MallocSizeOf for (T1, T2)
140where
141    T1: MallocSizeOf,
142    T2: MallocSizeOf,
143{
144    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
145        self.0.size_of(ops) + self.1.size_of(ops)
146    }
147}
148
149impl<T1, T2, T3> MallocSizeOf for (T1, T2, T3)
150where
151    T1: MallocSizeOf,
152    T2: MallocSizeOf,
153    T3: MallocSizeOf,
154{
155    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
156        self.0.size_of(ops) + self.1.size_of(ops) + self.2.size_of(ops)
157    }
158}
159
160impl<T1, T2, T3, T4> MallocSizeOf for (T1, T2, T3, T4)
161where
162    T1: MallocSizeOf,
163    T2: MallocSizeOf,
164    T3: MallocSizeOf,
165    T4: MallocSizeOf,
166{
167    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
168        self.0.size_of(ops)
169            + self.1.size_of(ops)
170            + self.2.size_of(ops)
171            + self.3.size_of(ops)
172    }
173}
174
175impl<T: MallocSizeOf> MallocSizeOf for Option<T> {
176    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
177        if let Some(val) = self.as_ref() {
178            val.size_of(ops)
179        } else {
180            0
181        }
182    }
183}
184
185impl<T: MallocSizeOf, E: MallocSizeOf> MallocSizeOf for Result<T, E> {
186    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
187        match *self {
188            Ok(ref x) => x.size_of(ops),
189            Err(ref e) => e.size_of(ops),
190        }
191    }
192}
193
194impl<T: MallocSizeOf + Copy> MallocSizeOf for std::cell::Cell<T> {
195    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
196        self.get().size_of(ops)
197    }
198}
199
200impl<T: MallocSizeOf> MallocSizeOf for std::cell::RefCell<T> {
201    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
202        self.borrow().size_of(ops)
203    }
204}
205
206impl<T: MallocSizeOf> MallocSizeOf for std::cell::UnsafeCell<T> {
207    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
208        unsafe { &*self.get() }.size_of(ops)
209    }
210}
211
212impl<'a, B: ?Sized + ToOwned> MallocSizeOf for std::borrow::Cow<'a, B>
213where B::Owned: MallocSizeOf
214{
215    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
216        match *self {
217            std::borrow::Cow::Borrowed(_) => 0,
218            std::borrow::Cow::Owned(ref b) => b.size_of(ops),
219        }
220    }
221}
222
223impl<T: MallocSizeOf> MallocSizeOf for [T] {
224    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
225        let mut n = 0;
226        for elem in self.iter() {
227            n += elem.size_of(ops);
228        }
229        n
230    }
231}
232
233impl<T> MallocShallowSizeOf for Vec<T> {
234    fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
235        unsafe { ops.malloc_size_of(self.as_ptr()) }
236    }
237}
238
239impl<T: MallocSizeOf> MallocSizeOf for Vec<T> {
240    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
241        let mut n = self.shallow_size_of(ops);
242        for elem in self.iter() {
243            n += elem.size_of(ops);
244        }
245        n
246    }
247}
248
249#[allow(dead_code)]
250#[derive(Clone)]
251enum Entry<T> {
252    Vacant(usize),
253    Occupied(T),
254}
255
256impl<T> MallocShallowSizeOf for Slab<T> {
257    fn shallow_size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
258        mem::size_of::<Entry<T>>() * self.capacity()
259    }
260}
261
262impl<T: MallocSizeOf> MallocSizeOf for Slab<T> {
263    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
264        let mut n = self.shallow_size_of(ops);
265        for (_, elem) in self.iter() {
266            n += elem.size_of(ops);
267        }
268        n
269    }
270}
271
272impl<T> MallocShallowSizeOf for BinaryHeap<T> {
273    fn shallow_size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
274        mem::size_of::<T>() * self.capacity()
275    }
276}
277
278impl<T: MallocSizeOf> MallocSizeOf for BinaryHeap<T> {
279    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
280        let mut n = self.shallow_size_of(ops);
281        for elem in self.iter() {
282            n += elem.size_of(ops);
283        }
284        n
285    }
286}
287
288impl<T> MallocShallowSizeOf for VecDeque<T> {
289    fn shallow_size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
290        mem::size_of::<T>() * self.capacity()
291    }
292}
293
294impl<T: MallocSizeOf> MallocSizeOf for VecDeque<T> {
295    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
296        let mut n = self.shallow_size_of(ops);
297        for elem in self.iter() {
298            n += elem.size_of(ops);
299        }
300        n
301    }
302}
303
304/// This is only for estimating memory size in Cache Manager
305impl<T: MallocSizeOf> MallocSizeOf for Arc<T> {
306    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
307        let ptr = self.as_ref() as *const T as usize;
308        if ops.visited.contains(&ptr) {
309            return 0;
310        }
311        ops.visited.insert(ptr);
312        mem::size_of::<T>() + self.as_ref().size_of(ops)
313    }
314}
315
316macro_rules! malloc_size_of_hash_set {
317    ($ty:ty) => {
318        impl<T, S> MallocShallowSizeOf for $ty
319        where
320            T: Eq + Hash,
321            S: BuildHasher,
322        {
323            fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
324                if ops.has_malloc_enclosing_size_of() {
325                    // The first value from the iterator gives us an interior
326                    // pointer. `ops.malloc_enclosing_size_of()`
327                    // then gives us the storage size. This assumes
328                    // that the `HashSet`'s contents (values and hashes)
329                    // are all stored in a single contiguous heap allocation.
330                    self.iter().next().map_or(0, |t| unsafe {
331                        ops.malloc_enclosing_size_of(t)
332                    })
333                } else {
334                    // An estimate.
335                    self.capacity() * (size_of::<T>() + size_of::<usize>())
336                }
337            }
338        }
339
340        impl<T, S> MallocSizeOf for $ty
341        where
342            T: Eq + Hash + MallocSizeOf,
343            S: BuildHasher,
344        {
345            fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
346                let mut n = self.shallow_size_of(ops);
347                for t in self.iter() {
348                    n += t.size_of(ops);
349                }
350                n
351            }
352        }
353    };
354}
355
356malloc_size_of_hash_set!(std::collections::HashSet<T, S>);
357
358macro_rules! malloc_size_of_hash_map {
359    ($ty:ty) => {
360        impl<K, V, S> MallocShallowSizeOf for $ty
361        where
362            K: Eq + Hash,
363            S: BuildHasher,
364        {
365            fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
366                // See the implementation for std::collections::HashSet for
367                // details.
368                if ops.has_malloc_enclosing_size_of() {
369                    self.values().next().map_or(0, |v| unsafe {
370                        ops.malloc_enclosing_size_of(v)
371                    })
372                } else {
373                    self.capacity()
374                        * (size_of::<V>() + size_of::<K>() + size_of::<usize>())
375                }
376            }
377        }
378
379        impl<K, V, S> MallocSizeOf for $ty
380        where
381            K: Eq + Hash + MallocSizeOf,
382            V: MallocSizeOf,
383            S: BuildHasher,
384        {
385            fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
386                let mut n = self.shallow_size_of(ops);
387                for (k, v) in self.iter() {
388                    n += k.size_of(ops);
389                    n += v.size_of(ops);
390                }
391                n
392            }
393        }
394    };
395}
396
397malloc_size_of_hash_map!(std::collections::HashMap<K, V, S>);
398malloc_size_of_hash_map!(FastHashMap<K, V, S>);
399
400// PhantomData is always 0.
401impl<T> MallocSizeOf for std::marker::PhantomData<T> {
402    fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { 0 }
403}
404
405impl<T: MallocSizeOf> MallocSizeOf for std::sync::Mutex<T> {
406    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
407        self.lock().unwrap().size_of(ops)
408    }
409}
410
411impl<T: MallocSizeOf> MallocSizeOf for parking_lot::Mutex<T> {
412    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
413        self.lock().size_of(ops)
414    }
415}
416
417impl<T: MallocSizeOf> MallocSizeOf for std::sync::RwLock<T> {
418    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
419        self.read().unwrap().size_of(ops)
420    }
421}
422
423impl<T: MallocSizeOf> MallocSizeOf for parking_lot::RwLock<T> {
424    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
425        self.read().size_of(ops)
426    }
427}
428
429impl<T: MallocSizeOf> MallocSizeOf for Reverse<T> {
430    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
431        self.0.size_of(ops)
432    }
433}
434
435macro_rules! impl_smallvec {
436    ($size:expr) => {
437        impl<T> MallocSizeOf for smallvec::SmallVec<[T; $size]>
438        where T: MallocSizeOf
439        {
440            fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
441                let mut n = if self.spilled() {
442                    self.capacity() * core::mem::size_of::<T>()
443                } else {
444                    0
445                };
446                for elem in self.iter() {
447                    n += elem.size_of(ops);
448                }
449                n
450            }
451        }
452    };
453}
454
455impl_smallvec!(32); // kvdb uses this
456
457/// For use on types where size_of() returns 0.
458#[macro_export]
459macro_rules! malloc_size_of_is_0(
460    ($($ty:ty),+) => (
461        $(
462            impl $crate::MallocSizeOf for $ty {
463                #[inline(always)]
464                fn size_of(&self, _: &mut $crate::MallocSizeOfOps) -> usize {
465                    0
466                }
467            }
468        )+
469    );
470    ($($ty:ident<$($gen:ident),+>),+) => (
471        $(
472        impl<$($gen: $crate::MallocSizeOf),+> $crate::MallocSizeOf for $ty<$($gen),+> {
473            #[inline(always)]
474            fn size_of(&self, _: &mut $crate::MallocSizeOfOps) -> usize {
475                0
476            }
477        }
478        )+
479    );
480);
481
482malloc_size_of_is_0!(bool, char, str);
483malloc_size_of_is_0!(u8, u16, u32, u64, u128, usize);
484malloc_size_of_is_0!(i8, i16, i32, i64, i128, isize);
485malloc_size_of_is_0!(f32, f64);
486
487malloc_size_of_is_0!(std::sync::atomic::AtomicBool);
488malloc_size_of_is_0!(std::sync::atomic::AtomicIsize);
489malloc_size_of_is_0!(std::sync::atomic::AtomicUsize);
490
491malloc_size_of_is_0!(std::num::NonZeroUsize);
492malloc_size_of_is_0!(std::num::NonZeroU32);
493
494malloc_size_of_is_0!(std::time::Duration);
495malloc_size_of_is_0!(std::time::Instant);
496malloc_size_of_is_0!(std::time::SystemTime);
497
498malloc_size_of_is_0!(
499    Range<u8>,
500    Range<u16>,
501    Range<u32>,
502    Range<u64>,
503    Range<usize>
504);
505malloc_size_of_is_0!(
506    Range<i8>,
507    Range<i16>,
508    Range<i32>,
509    Range<i64>,
510    Range<isize>
511);
512malloc_size_of_is_0!(Range<f32>, Range<f64>);
513
514malloc_size_of_is_0!(
515    H256,
516    U256,
517    H512,
518    H160,
519    U512,
520    AddressWithSpace,
521    AllChainID,
522    Space
523);
524
525mod usable_size {
526
527    use super::*;
528
529    cfg_if! {
530        if #[cfg(target_os = "windows")] {
531
532            // default windows allocator
533            extern crate winapi;
534
535            use self::winapi::um::heapapi::{GetProcessHeap, HeapSize, HeapValidate};
536
537            /// Get the size of a heap block.
538            /// Call windows allocator through `winapi` crate
539            pub unsafe extern "C" fn malloc_usable_size(mut ptr: *const c_void) -> usize {
540
541                let heap = GetProcessHeap();
542
543                if HeapValidate(heap, 0, ptr) == 0 {
544                    ptr = *(ptr as *const *const c_void).offset(-1);
545                }
546
547                HeapSize(heap, 0, ptr) as usize
548            }
549
550        } else if #[cfg(feature = "jemalloc")] {
551
552            /// Use of jemalloc usable size C function through jemallocator crate call.
553            pub unsafe extern "C" fn malloc_usable_size(ptr: *const c_void) -> usize {
554                tikv_jemallocator::usable_size(ptr)
555            }
556
557        } else if #[cfg(target_os = "linux")] {
558
559            // Linux call system allocator (currently malloc).
560            extern "C" {
561                pub fn malloc_usable_size(ptr: *const c_void) -> usize;
562            }
563
564        } else if #[cfg(target_os = "macos")] {
565
566            // Linux call system allocator (currently malloc).
567            extern "C" {
568                #[link_name = "malloc_size"]
569                pub fn malloc_usable_size(ptr: *const c_void) -> usize;
570            }
571
572
573        } else {
574            pub unsafe extern "C" fn malloc_usable_size(_ptr: *const c_void) -> usize {
575                unreachable!("estimate heapsize or feature allocator needed")
576            }
577
578        }
579
580    }
581
582    /// No enclosing function defined.
583    #[inline]
584    pub fn new_enclosing_size_fn() -> Option<VoidPtrToSizeFn> { None }
585}
586
587/// Get a new instance of a MallocSizeOfOps
588pub fn new_malloc_size_ops() -> MallocSizeOfOps {
589    MallocSizeOfOps::new(
590        usable_size::malloc_usable_size,
591        usable_size::new_enclosing_size_fn(),
592    )
593}