1use std::{
4 alloc::Layout,
5 io::Write,
6 panic::catch_unwind,
7 process::abort,
8 sync::atomic::{AtomicBool, AtomicU64, Ordering},
9};
10
11use jevmalloc::{
12 Jemalloc,
13 global::hook::{ALLOC, ALLOC_ZEROED},
14 stats::print as print_stats,
15};
16pub use jevmalloc::{arenas::trim, background_thread_enable};
17use libc::{STDOUT_FILENO, c_void, write};
18
19use crate::{arrayvec::ArrayVec, utils::BoolExt};
20
21type TraceLine = ArrayVec<u8, 128>;
24
25#[cfg(feature = "jemalloc_conf")]
32#[used]
33#[unsafe(no_mangle)]
34pub static malloc_conf: &[u8] = const_str::concat_bytes!(
35 "tcache:true",
36 ",percpu_arena:percpu",
37 ",metadata_thp:always",
38 ",background_thread:true",
39 ",max_background_threads:-1",
40 ",lg_extent_max_active_fit:4",
41 ",oversize_threshold:2097152",
42 ",tcache_max:524288",
43 ",dirty_decay_ms:16000",
44 ",muzzy_decay_ms:144000",
45 0
47);
48
49#[cfg(all(
50 feature = "jemalloc_conf",
51 feature = "jemalloc_prof",
52 target_arch = "x86_64",
53))]
54const _MALLOC_CONF_PROF: &str = ",prof_active:false";
55#[cfg(all(
56 feature = "jemalloc_conf",
57 any(not(feature = "jemalloc_prof"), not(target_arch = "x86_64")),
58))]
59const _MALLOC_CONF_PROF: &str = "";
60
61#[global_allocator]
62static JEMALLOC: Jemalloc = Jemalloc;
63
64static GLOBAL_ALLOCS: AtomicU64 = AtomicU64::new(0);
65static COUNT_GLOBAL_ALLOCS: AtomicBool = AtomicBool::new(false);
66static TRACE_GLOBAL_ALLOCS: AtomicBool = AtomicBool::new(false);
67
68#[crate::ctor(unsafe)]
74fn _static_initialization() {
75 unsafe { ALLOC = Some(global_alloc_hook) };
78
79 unsafe { ALLOC_ZEROED = Some(global_alloc_zeroed_hook) };
81}
82
83fn global_alloc_hook(layout: Layout) {
84 catch_unwind(move || handle_global_alloc(layout))
85 .map_err(|_| abort())
86 .ok();
87}
88
89fn global_alloc_zeroed_hook(layout: Layout) {
90 catch_unwind(move || handle_global_alloc(layout))
91 .map_err(|_| abort())
92 .ok();
93}
94
95fn handle_global_alloc(layout: Layout) {
96 let do_count = COUNT_GLOBAL_ALLOCS.load(Ordering::Relaxed);
97 let count = GLOBAL_ALLOCS.fetch_add(do_count.into(), Ordering::Relaxed);
98
99 if TRACE_GLOBAL_ALLOCS.load(Ordering::Relaxed) {
100 let mut buf = TraceLine::new();
101
102 writeln!(&mut buf, "{count} align={} size={}", layout.align(), layout.size())
103 .expect("writeln! to buffer failed");
104
105 unsafe { write(STDOUT_FILENO, buf.as_ptr().cast::<c_void>(), buf.len()) }
107 .ge(&0)
108 .into_result()
109 .expect("write(2) error");
110 }
111}
112
113#[inline]
119#[must_use]
120pub fn global_alloc_count() -> u64 { GLOBAL_ALLOCS.load(Ordering::Relaxed) }
121
122#[must_use]
127pub fn memory_stats(opts: &str) -> Option<String> {
128 const MAX_LENGTH: usize = 1_048_576;
129
130 let mut stats = vec![0; MAX_LENGTH];
131 let length = print_stats(opts, &mut stats).ok()?.len();
132 if length == 0 {
133 return None;
134 }
135
136 stats.truncate(length);
137 String::from_utf8(stats).ok()
138}
139
140pub mod this_thread {
145 pub use jevmalloc::thread::this::{decay, set_muzzy_decay};
146}