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}