tikv_jemalloc_ctl/
raw.rs

1//! Raw `unsafe` access to the `malloctl` API.
2
3use crate::error::{cvt, Result};
4use crate::{mem, ptr, slice};
5use libc::c_char;
6
7/// Translates `name` to a `mib` (Management Information Base)
8///
9/// `mib`s are used to avoid repeated name lookups for applications that
10/// repeatedly query the same portion of `jemalloc`s `mallctl` namespace.
11///
12/// On success, `mib` contains an array of integers. It is possible to pass
13/// `mib` with a length smaller than the number of period-separated name
14/// components. This results in a partial MIB that can be used as the basis for
15/// constructing a complete MIB.
16///
17/// For name components that are integers (e.g. the `2` in `arenas.bin.2.size`),
18/// the corresponding MIB component will always be that integer. Therefore, it
19/// is legitimate to construct code like the following:
20///
21/// ```
22/// #[global_allocator]
23/// static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
24///
25/// fn main() {
26///     use tikv_jemalloc_ctl::raw;
27///     use libc::{c_uint, c_char};
28///     unsafe {
29///         let mut mib = [0; 4];
30///         let nbins: c_uint = raw::read(b"arenas.nbins\0").unwrap();
31///         raw::name_to_mib(b"arenas.bin.0.size\0", &mut mib).unwrap();
32///         for i in 0..4 {
33///             mib[2] = i;
34///             let bin_size: usize = raw::read_mib(&mut mib).unwrap();
35///             println!("arena bin {} has size {}", i, bin_size);
36///         }
37///     }
38/// }
39/// ```
40pub fn name_to_mib(name: &[u8], mib: &mut [usize]) -> Result<()> {
41    unsafe {
42        validate_name(name);
43
44        let mut len = mib.len();
45        cvt(tikv_jemalloc_sys::mallctlnametomib(
46            name as *const _ as *const c_char,
47            mib.as_mut_ptr(),
48            &mut len,
49        ))?;
50        assert_eq!(mib.len(), len);
51        Ok(())
52    }
53}
54
55/// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and reads its value.
56///
57/// The [`name_to_mib`] API translates a string of the key (e.g. `arenas.nbins`)
58/// to a `mib` (Management Information Base).
59///
60/// # Safety
61///
62/// This function is `unsafe` because it is possible to use it to construct an
63/// invalid `T`, for example, by passing `T=bool` for a key returning `u8`. The
64/// sizes of `bool` and `u8` match, but `bool` cannot represent all values that
65/// `u8` can.
66pub unsafe fn read_mib<T: Copy>(mib: &[usize]) -> Result<T> {
67    let mut value = MaybeUninit { init: () };
68    let mut len = mem::size_of::<T>();
69    cvt(tikv_jemalloc_sys::mallctlbymib(
70        mib.as_ptr(),
71        mib.len(),
72        &mut value.init as *mut _ as *mut _,
73        &mut len,
74        ptr::null_mut(),
75        0,
76    ))?;
77    assert_eq!(len, mem::size_of::<T>());
78    Ok(value.maybe_uninit)
79}
80
81/// Uses the null-terminated string `name` as key to the _MALLCTL NAMESPACE_ and
82/// reads its value.
83///
84/// # Safety
85///
86/// This function is `unsafe` because it is possible to use it to construct an
87/// invalid `T`, for example, by passing `T=bool` for a key returning `u8`. The
88/// sizes of `bool` and `u8` match, but `bool` cannot represent all values that
89/// `u8` can.
90pub unsafe fn read<T: Copy>(name: &[u8]) -> Result<T> {
91    validate_name(name);
92
93    let mut value = MaybeUninit { init: () };
94    let mut len = mem::size_of::<T>();
95    cvt(tikv_jemalloc_sys::mallctl(
96        name as *const _ as *const c_char,
97        &mut value.init as *mut _ as *mut _,
98        &mut len,
99        ptr::null_mut(),
100        0,
101    ))?;
102    assert_eq!(len, mem::size_of::<T>());
103    Ok(value.maybe_uninit)
104}
105
106/// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and writes its `value`.
107///
108/// The [`name_to_mib`] API translates a string of the key (e.g. `arenas.nbins`)
109/// to a `mib` (Management Information Base).
110///
111/// # Safety
112///
113/// This function is `unsafe` because it is possible to use it to construct an
114/// invalid `T`, for example, by passing `T=u8` for a key expecting `bool`. The
115/// sizes of `bool` and `u8` match, but `bool` cannot represent all values that
116/// `u8` can.
117pub unsafe fn write_mib<T>(mib: &[usize], mut value: T) -> Result<()> {
118    cvt(tikv_jemalloc_sys::mallctlbymib(
119        mib.as_ptr(),
120        mib.len(),
121        ptr::null_mut(),
122        ptr::null_mut(),
123        &mut value as *mut _ as *mut _,
124        mem::size_of::<T>(),
125    ))
126}
127
128/// Uses the null-terminated string `name` as the key to the _MALLCTL NAMESPACE_
129/// and writes it `value`
130///
131/// # Safety
132///
133/// This function is `unsafe` because it is possible to use it to construct an
134/// invalid `T`, for example, by passing `T=u8` for a key expecting `bool`. The
135/// sizes of `bool` and `u8` match, but `bool` cannot represent all values that
136/// `u8` can.
137pub unsafe fn write<T>(name: &[u8], mut value: T) -> Result<()> {
138    validate_name(name);
139
140    cvt(tikv_jemalloc_sys::mallctl(
141        name as *const _ as *const c_char,
142        ptr::null_mut(),
143        ptr::null_mut(),
144        &mut value as *mut _ as *mut _,
145        mem::size_of::<T>(),
146    ))
147}
148
149/// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and writes its `value`
150/// returning its previous value.
151///
152/// The [`name_to_mib`] API translates a string of the key (e.g. `arenas.nbins`)
153/// to a `mib` (Management Information Base).
154///
155/// # Safety
156///
157/// This function is `unsafe` because it is possible to use it to construct an
158/// invalid `T`, for example, by passing `T=u8` for a key expecting `bool`. The
159/// sizes of `bool` and `u8` match, but `bool` cannot represent all values that
160/// `u8` can.
161pub unsafe fn update_mib<T>(mib: &[usize], mut value: T) -> Result<T> {
162    let mut len = mem::size_of::<T>();
163    cvt(tikv_jemalloc_sys::mallctlbymib(
164        mib.as_ptr(),
165        mib.len(),
166        &mut value as *mut _ as *mut _,
167        &mut len,
168        &mut value as *mut _ as *mut _,
169        len,
170    ))?;
171    assert_eq!(len, mem::size_of::<T>());
172    Ok(value)
173}
174
175/// Uses the null-terminated string `name` as key to the _MALLCTL NAMESPACE_ and
176/// writes its `value` returning its previous value.
177///
178/// # Safety
179///
180/// This function is `unsafe` because it is possible to use it to construct an
181/// invalid `T`, for example, by passing `T=u8` for a key expecting `bool`. The
182/// sizes of `bool` and `u8` match, but `bool` cannot represent all values that
183/// `u8` can.
184pub unsafe fn update<T>(name: &[u8], mut value: T) -> Result<T> {
185    validate_name(name);
186
187    let mut len = mem::size_of::<T>();
188    cvt(tikv_jemalloc_sys::mallctl(
189        name as *const _ as *const c_char,
190        &mut value as *mut _ as *mut _,
191        &mut len,
192        &mut value as *mut _ as *mut _,
193        len,
194    ))?;
195    assert_eq!(len, mem::size_of::<T>());
196    Ok(value)
197}
198
199/// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and reads its value.
200///
201/// The [`name_to_mib`] API translates a string of the key (e.g. `arenas.nbins`)
202/// to a `mib` (Management Information Base).
203///
204/// # Safety
205///
206/// This function is unsafe because if the key does not return a pointer to a
207/// null-terminated string the behavior is undefined.
208///
209/// For example, a key for a `u64` value can be used to read a pointer on 64-bit
210/// platform, where this pointer will point to the address denoted by the `u64`s
211/// representation. Also, a key to a `*mut extent_hooks_t` will return a pointer
212/// that will not point to a null-terminated string.
213///
214/// This function needs to compute the length of the string by looking for the
215/// null-terminator: `\0`. This requires reading the memory behind the pointer.
216///
217/// If the pointer is invalid (e.g. because it was converted from a `u64` that
218/// does not represent a valid address), reading the string to look for `\0`
219/// will dereference a non-dereferenceable pointer, which is undefined behavior.
220///
221/// If the pointer is valid but it does not point to a null-terminated string,
222/// looking for `\0` will read garbage and might end up reading out-of-bounds,
223/// which is undefined behavior.
224pub unsafe fn read_str_mib(mib: &[usize]) -> Result<&'static [u8]> {
225    let ptr: *const c_char = read_mib(mib)?;
226    Ok(ptr2str(ptr))
227}
228
229/// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and writes its `value`.
230///
231/// The [`name_to_mib`] API translates a string of the key (e.g. `arenas.nbins`)
232/// to a `mib` (Management Information Base).
233///
234/// # Panics
235///
236/// If `value` is not a non-empty null-terminated string.
237pub fn write_str_mib(mib: &[usize], value: &'static [u8]) -> Result<()> {
238    assert!(!value.is_empty(), "value cannot be empty");
239    assert_eq!(*value.last().unwrap(), b'\0');
240    // This is safe because `value` will always point to a null-terminated
241    // string, which makes it safe for all key value types: pointers to
242    // null-terminated strings, pointers, pointer-sized integers, etc.
243    unsafe { write_mib(mib, value.as_ptr() as *const c_char) }
244}
245
246/// Uses the MIB `mib` as key to the _MALLCTL NAMESPACE_ and writes its `value`
247/// returning its previous value.
248///
249/// The [`name_to_mib`] API translates a string of the key (e.g. `arenas.nbins`)
250/// to a `mib` (Management Information Base).
251///
252/// # Safety
253///
254/// This function is unsafe because if the key does not return a pointer to a
255/// null-terminated string the behavior is undefined.
256///
257/// For example, a key for a `u64` value can be used to read a pointer on 64-bit
258/// platform, where this pointer will point to the address denoted by the `u64`s
259/// representation. Also, a key to a `*mut extent_hooks_t` will return a pointer
260/// that will not point to a null-terminated string.
261///
262/// This function needs to compute the length of the string by looking for the
263/// null-terminator: `\0`. This requires reading the memory behind the pointer.
264///
265/// If the pointer is invalid (e.g. because it was converted from a `u64` that
266/// does not represent a valid address), reading the string to look for `\0`
267/// will dereference a non-dereferenceable pointer, which is undefined behavior.
268///
269/// If the pointer is valid but it does not point to a null-terminated string,
270/// looking for `\0` will read garbage and might end up reading out-of-bounds,
271/// which is undefined behavior.
272pub unsafe fn update_str_mib(
273    mib: &[usize],
274    value: &'static [u8],
275) -> Result<&'static [u8]> {
276    let ptr: *const c_char = update_mib(mib, value.as_ptr() as *const c_char)?;
277    Ok(ptr2str(ptr))
278}
279
280/// Uses the null-terminated string `name` as key to the _MALLCTL NAMESPACE_ and
281/// reads its value.
282///
283/// # Safety
284///
285/// This function is unsafe because if the key does not return a pointer to a
286/// null-terminated string the behavior is undefined.
287///
288/// For example, a key for a `u64` value can be used to read a pointer on 64-bit
289/// platform, where this pointer will point to the address denoted by the `u64`s
290/// representation. Also, a key to a `*mut extent_hooks_t` will return a pointer
291/// that will not point to a null-terminated string.
292///
293/// This function needs to compute the length of the string by looking for the
294/// null-terminator: `\0`. This requires reading the memory behind the pointer.
295///
296/// If the pointer is invalid (e.g. because it was converted from a `u64` that
297/// does not represent a valid address), reading the string to look for `\0`
298/// will dereference a non-dereferenceable pointer, which is undefined behavior.
299///
300/// If the pointer is valid but it does not point to a null-terminated string,
301/// looking for `\0` will read garbage and might end up reading out-of-bounds,
302/// which is undefined behavior.
303pub unsafe fn read_str(name: &[u8]) -> Result<&'static [u8]> {
304    let ptr: *const c_char = read(name)?;
305    Ok(ptr2str(ptr))
306}
307
308/// Uses the null-terminated string `name` as key to the _MALLCTL NAMESPACE_ and
309/// writes its `value`.
310pub fn write_str(name: &[u8], value: &'static [u8]) -> Result<()> {
311    assert!(!value.is_empty(), "value cannot be empty");
312    assert_eq!(*value.last().unwrap(), b'\0');
313    // This is safe because `value` will always point to a null-terminated
314    // string, which makes it safe for all key value types: pointers to
315    // null-terminated strings, pointers, pointer-sized integers, etc.
316    unsafe { write(name, value.as_ptr() as *const c_char) }
317}
318
319/// Uses the null-terminated string `name` as key to the _MALLCTL NAMESPACE_ and
320/// writes its `value` returning its previous value.
321///
322/// # Safety
323///
324/// This function is unsafe because if the key does not return a pointer to a
325/// null-terminated string the behavior is undefined.
326///
327/// For example, a key for a `u64` value can be used to read a pointer on 64-bit
328/// platform, where this pointer will point to the address denoted by the `u64`s
329/// representation. Also, a key to a `*mut extent_hooks_t` will return a pointer
330/// that will not point to a null-terminated string.
331///
332/// This function needs to compute the length of the string by looking for the
333/// null-terminator: `\0`. This requires reading the memory behind the pointer.
334///
335/// If the pointer is invalid (e.g. because it was converted from a `u64` that
336/// does not represent a valid address), reading the string to look for `\0`
337/// will dereference a non-dereferenceable pointer, which is undefined behavior.
338///
339/// If the pointer is valid but it does not point to a null-terminated string,
340/// looking for `\0` will read garbage and might end up reading out-of-bounds,
341/// which is undefined behavior.
342pub unsafe fn update_str(
343    name: &[u8],
344    value: &'static [u8],
345) -> Result<&'static [u8]> {
346    let ptr: *const c_char = update(name, value.as_ptr() as *const c_char)?;
347    Ok(ptr2str(ptr))
348}
349
350/// Converts a non-empty null-terminated character string at `ptr` into a valid
351/// null-terminated UTF-8 string.
352///
353/// # Panics
354///
355/// If `ptr.is_null()`.
356///
357/// # Safety
358///
359/// If `ptr` does not point to a null-terminated character string the behavior
360/// is undefined.
361unsafe fn ptr2str(ptr: *const c_char) -> &'static [u8] {
362    assert!(
363        !ptr.is_null(),
364        "attempt to convert a null-ptr to a UTF-8 string"
365    );
366    let len = libc::strlen(ptr);
367    slice::from_raw_parts(ptr as *const u8, len + 1)
368}
369
370fn validate_name(name: &[u8]) {
371    assert!(!name.is_empty(), "empty byte string");
372    assert_eq!(
373        *name.last().unwrap(),
374        b'\0',
375        "non-null terminated byte string"
376    );
377}
378
379union MaybeUninit<T: Copy> {
380    init: (),
381    maybe_uninit: T,
382}
383
384#[cfg(test)]
385mod tests {
386    use super::*;
387    #[test]
388    #[cfg(not(target_arch = "mips64el"))] // FIXME: SIGFPE
389    fn test_ptr2str() {
390        unsafe {
391            //{ // This is undefined behavior:
392            //    let cstr = b"";
393            //    let rstr = ptr2str(cstr as *const _ as *const c_char);
394            //    assert!(rstr.is_err());
395            // }
396            {
397                let cstr = b"\0";
398                let rstr = ptr2str(cstr as *const _ as *const c_char);
399                assert_eq!(rstr.len(), 1);
400                assert_eq!(rstr, b"\0");
401            }
402            {
403                let cstr = b"foo  baaar\0";
404                let rstr = ptr2str(cstr as *const _ as *const c_char);
405                assert_eq!(rstr.len(), b"foo  baaar\0".len());
406                assert_eq!(rstr, b"foo  baaar\0");
407            }
408        }
409    }
410}