use std::ffi::c_char;
use std::ffi::CStr;
use std::ptr;
use blazesym::BuildId;
use blazesym::__private::bytes_to_path;
use blazesym::helper::is_procmap_query_supported;
use blazesym::helper::read_elf_build_id;
use blazesym::Result;
use crate::blaze_err;
#[cfg(doc)]
use crate::blaze_err_last;
use crate::error::set_last_err;
#[no_mangle]
pub extern "C" fn blaze_supports_procmap_query() -> bool {
let result = is_procmap_query_supported();
let err = result
.as_ref()
.map(|_| blaze_err::OK)
.unwrap_or_else(|err| err.kind().into());
let () = set_last_err(err);
result.unwrap_or(false)
}
#[no_mangle]
pub unsafe extern "C" fn blaze_read_elf_build_id(path: *const c_char, len: *mut usize) -> *mut u8 {
#[inline]
fn inner(path: *const c_char, len: *mut usize) -> Result<Option<BuildId<'static>>> {
let path = unsafe { CStr::from_ptr(path) };
let path = bytes_to_path(path.to_bytes())?;
let build_id = read_elf_build_id(path)?;
if !len.is_null() {
let () = unsafe {
len.write(
build_id
.as_ref()
.map(|build_id| build_id.len())
.unwrap_or_default(),
)
};
}
Ok(build_id)
}
let result = inner(path, len);
let err = result
.as_ref()
.map(|_| blaze_err::OK)
.unwrap_or_else(|err| err.kind().into());
let () = set_last_err(err);
match result {
Ok(None) | Err(..) => ptr::null_mut(),
Ok(Some(build_id)) => {
let len = build_id.len();
let dst = unsafe { libc::malloc(len) }.cast::<u8>();
if dst.is_null() {
let () = set_last_err(blaze_err::OUT_OF_MEMORY);
} else {
let () = unsafe { ptr::copy_nonoverlapping(build_id.as_ptr(), dst, len) };
}
dst
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::ffi::CString;
use std::path::Path;
use std::slice;
use crate::blaze_err;
use crate::blaze_err_last;
#[test]
fn procmap_query_supported() {
let _supported = blaze_supports_procmap_query();
assert_eq!(blaze_err_last(), blaze_err::OK);
}
#[test]
fn build_id_reading() {
let mut len = 0;
let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
.join("..")
.join("data")
.join("libtest-so.so");
let path_c = CString::new(path.to_str().unwrap()).unwrap();
let build_id = unsafe { blaze_read_elf_build_id(path_c.as_ptr(), &mut len) };
assert!(!build_id.is_null());
assert_eq!(blaze_err_last(), blaze_err::OK);
assert_eq!(len, 20);
let build_id_rs = read_elf_build_id(&path).unwrap().unwrap();
assert_eq!(
unsafe { slice::from_raw_parts(build_id, len) },
&*build_id_rs
);
let () = unsafe { libc::free(build_id.cast()) };
let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
.join("..")
.join("data")
.join("libtest-so-no-separate-code.so");
let path_c = CString::new(path.to_str().unwrap()).unwrap();
let build_id = unsafe { blaze_read_elf_build_id(path_c.as_ptr(), &mut len) };
assert!(!build_id.is_null());
assert_eq!(blaze_err_last(), blaze_err::OK);
assert_eq!(len, 16);
let () = unsafe { libc::free(build_id.cast()) };
let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
.join("..")
.join("data")
.join("test-no-debug.bin");
let path_c = CString::new(path.to_str().unwrap()).unwrap();
let build_id = unsafe { blaze_read_elf_build_id(path_c.as_ptr(), &mut len) };
assert!(build_id.is_null());
assert_eq!(blaze_err_last(), blaze_err::OK);
let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
.join("..")
.join("data")
.join("does-not-exist");
let path_c = CString::new(path.to_str().unwrap()).unwrap();
let build_id = unsafe { blaze_read_elf_build_id(path_c.as_ptr(), &mut len) };
assert!(build_id.is_null());
assert_eq!(blaze_err_last(), blaze_err::NOT_FOUND);
}
}