Skip to content

Commit

Permalink
capi: Add blaze_normalizer_new_opts() constructor
Browse files Browse the repository at this point in the history
This change adds the blaze_normalizer_new_opts() constructor for
blaze_normalizer objects to the C API. Using it users can control the
availability of build IDs as part of the normalization output.

Signed-off-by: Daniel Müller <deso@posteo.net>
  • Loading branch information
d-e-s-o authored and danielocfb committed Jan 29, 2024
1 parent ab76985 commit d35aa2d
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 10 deletions.
1 change: 1 addition & 0 deletions capi/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
Unreleased
----------
- Added `blaze_normalizer_new_opts` function and `blaze_normalizer_opts` type
- Renamed various symbolization functions to closer reflect Rust
terminology
- Renamed `BLAZE_SYM_UNKNOWN` enum variant to `BLAZE_SYM_UNDEFINED`
Expand Down
29 changes: 26 additions & 3 deletions capi/include/blazesym.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,17 @@ typedef struct blaze_inspect_elf_src {
*/
typedef struct blaze_normalizer blaze_normalizer;

/**
* Options for configuring [`blaze_normalizer`] objects.
*/
typedef struct blaze_normalizer_opts {
/**
* Whether to read and report build IDs as part of the normalization
* process.
*/
bool build_ids;
} blaze_normalizer_opts;

/**
* C compatible version of [`Apk`].
*/
Expand Down Expand Up @@ -224,7 +235,7 @@ typedef struct blaze_normalized_user_output {
typedef struct blaze_symbolizer blaze_symbolizer;

/**
* Options for configuring `blaze_symbolizer` objects.
* Options for configuring [`blaze_symbolizer`] objects.
*/
typedef struct blaze_symbolizer_opts {
/**
Expand Down Expand Up @@ -519,11 +530,23 @@ void blaze_inspector_free(blaze_inspector *inspector);
/**
* Create an instance of a blazesym normalizer.
*
* The returned pointer should be released using
* [`blaze_normalizer_free`] once it is no longer needed.
* The returned pointer should be released using [`blaze_normalizer_free`] once
* it is no longer needed.
*/
blaze_normalizer *blaze_normalizer_new(void);

/**
* Create an instance of a blazesym normalizer.
*
* The returned pointer should be released using [`blaze_normalizer_free`] once
* it is no longer needed.
*
* # Safety
* The provided pointer needs to point to a valid [`blaze_normalizer_opts`]
* instance.
*/
blaze_normalizer *blaze_normalizer_new_opts(const struct blaze_normalizer_opts *opts);

/**
* Free a blazesym normalizer.
*
Expand Down
111 changes: 108 additions & 3 deletions capi/src/normalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,20 @@ use crate::slice_from_user_array;
pub type blaze_normalizer = Normalizer;


/// Options for configuring [`blaze_normalizer`] objects.
#[repr(C)]
#[derive(Debug)]
pub struct blaze_normalizer_opts {
/// Whether to read and report build IDs as part of the normalization
/// process.
pub build_ids: bool,
}


/// Create an instance of a blazesym normalizer.
///
/// The returned pointer should be released using
/// [`blaze_normalizer_free`] once it is no longer needed.
/// The returned pointer should be released using [`blaze_normalizer_free`] once
/// it is no longer needed.
#[no_mangle]
pub extern "C" fn blaze_normalizer_new() -> *mut blaze_normalizer {
let normalizer = Normalizer::new();
Expand All @@ -37,6 +47,28 @@ pub extern "C" fn blaze_normalizer_new() -> *mut blaze_normalizer {
}


/// Create an instance of a blazesym normalizer.
///
/// The returned pointer should be released using [`blaze_normalizer_free`] once
/// it is no longer needed.
///
/// # Safety
/// The provided pointer needs to point to a valid [`blaze_normalizer_opts`]
/// instance.
#[no_mangle]
pub unsafe extern "C" fn blaze_normalizer_new_opts(
opts: *const blaze_normalizer_opts,
) -> *mut blaze_normalizer {
// SAFETY: The caller ensures that the pointer is valid.
let opts = unsafe { &*opts };
let blaze_normalizer_opts { build_ids } = opts;

let normalizer = Normalizer::builder().enable_build_ids(*build_ids).build();
let normalizer_box = Box::new(normalizer);
Box::into_raw(normalizer_box)
}


/// Free a blazesym normalizer.
///
/// Release resources associated with a normalizer as created by
Expand Down Expand Up @@ -76,7 +108,7 @@ impl From<(u64, usize)> for blaze_normalized_output {

/// The valid variant kind in [`blaze_user_meta`].
#[repr(C)]
#[derive(Debug)]
#[derive(Debug, PartialEq)]
pub enum blaze_user_meta_kind {
/// [`blaze_user_meta_variant::unknown`] is valid.
BLAZE_USER_META_UNKNOWN,
Expand Down Expand Up @@ -453,6 +485,12 @@ pub unsafe extern "C" fn blaze_user_output_free(output: *mut blaze_normalized_us
mod tests {
use super::*;

use std::ffi::CStr;
use std::io;
use std::path::Path;

use blazesym::helper::read_elf_build_id;


/// Exercise the `Debug` representation of various types.
#[test]
Expand Down Expand Up @@ -625,4 +663,71 @@ mod tests {
let () = unsafe { blaze_user_output_free(result) };
let () = unsafe { blaze_normalizer_free(normalizer) };
}

/// Check that we can enable/disable the reading of build IDs.
#[test]
fn normalize_build_id_reading() {
fn test(read_build_ids: bool) {
let test_so = Path::new(&env!("CARGO_MANIFEST_DIR"))
.join("..")
.join("data")
.join("libtest-so.so")
.canonicalize()
.unwrap();
let so_cstr = CString::new(test_so.clone().into_os_string().into_vec()).unwrap();
let handle = unsafe { libc::dlopen(so_cstr.as_ptr(), libc::RTLD_NOW) };
assert!(!handle.is_null());

let the_answer_addr = unsafe { libc::dlsym(handle, "the_answer\0".as_ptr().cast()) };
assert!(!the_answer_addr.is_null());

let opts = blaze_normalizer_opts {
build_ids: read_build_ids,
};
let normalizer = unsafe { blaze_normalizer_new_opts(&opts) };
assert!(!normalizer.is_null());

let addrs = [the_answer_addr as Addr];
let result = unsafe {
blaze_normalize_user_addrs_sorted(
normalizer,
0,
addrs.as_slice().as_ptr(),
addrs.len(),
)
};
assert!(!result.is_null());

let normalized = unsafe { &*result };
assert_eq!(normalized.meta_cnt, 1);
assert_eq!(normalized.output_cnt, 1);

let rc = unsafe { libc::dlclose(handle) };
assert_eq!(rc, 0, "{}", io::Error::last_os_error());

let output = unsafe { &*normalized.outputs.add(0) };
let meta = unsafe { &*normalized.metas.add(output.meta_idx) };
assert_eq!(meta.kind, blaze_user_meta_kind::BLAZE_USER_META_ELF);

let elf = unsafe { &meta.variant.elf };

assert!(!elf.path.is_null());
let path = unsafe { CStr::from_ptr(elf.path) };
assert_eq!(path, so_cstr.as_ref());

if read_build_ids {
let expected = read_elf_build_id(&test_so).unwrap().unwrap();
let build_id = unsafe { slice_from_user_array(elf.build_id, elf.build_id_len) };
assert_eq!(build_id, &expected);
} else {
assert!(elf.build_id.is_null());
}

let () = unsafe { blaze_user_output_free(result) };
let () = unsafe { blaze_normalizer_free(normalizer) };
}

test(true);
test(false);
}
}
2 changes: 1 addition & 1 deletion capi/src/symbolize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ pub(crate) unsafe fn from_cstr(cstr: *const c_char) -> PathBuf {
}


/// Options for configuring `blaze_symbolizer` objects.
/// Options for configuring [`blaze_symbolizer`] objects.
#[repr(C)]
#[derive(Debug)]
pub struct blaze_symbolizer_opts {
Expand Down
4 changes: 2 additions & 2 deletions src/normalize/normalizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ pub struct Output<M> {
/// By default all features are enabled.
#[derive(Clone, Debug)]
pub struct Builder {
/// Whether to read and report build IDs as part of the
/// normalization process.
/// Whether to read and report build IDs as part of the normalization
/// process.
build_ids: bool,
}

Expand Down
2 changes: 1 addition & 1 deletion tests/blazesym.rs
Original file line number Diff line number Diff line change
Expand Up @@ -650,7 +650,7 @@ fn normalize_elf_addr() {

/// Check that we can enable/disable the reading of build IDs.
#[test]
fn normalize_build_id_rading() {
fn normalize_build_id_reading() {
fn test(read_build_ids: bool) {
let test_so = Path::new(&env!("CARGO_MANIFEST_DIR"))
.join("data")
Expand Down

0 comments on commit d35aa2d

Please sign in to comment.