1use std::alloc::alloc;
2use std::alloc::dealloc;
3use std::alloc::Layout;
4use std::ffi::CStr;
5use std::ffi::OsStr;
6use std::fmt::Debug;
7use std::mem;
8use std::ops::Deref as _;
9use std::os::raw::c_char;
10use std::os::unix::ffi::OsStrExt as _;
11use std::path::Path;
12use std::path::PathBuf;
13use std::ptr;
14
15use blazesym::symbolize::cache;
16use blazesym::symbolize::source::Elf;
17use blazesym::symbolize::source::GsymData;
18use blazesym::symbolize::source::GsymFile;
19use blazesym::symbolize::source::Kernel;
20use blazesym::symbolize::source::Process;
21use blazesym::symbolize::source::Source;
22use blazesym::symbolize::CodeInfo;
23use blazesym::symbolize::Input;
24use blazesym::symbolize::Reason;
25use blazesym::symbolize::Sym;
26use blazesym::symbolize::Symbolized;
27use blazesym::symbolize::Symbolizer;
28use blazesym::Addr;
29use blazesym::MaybeDefault;
30
31use crate::blaze_err;
32#[cfg(doc)]
33use crate::blaze_err_last;
34use crate::set_last_err;
35use crate::util::slice_from_aligned_user_array;
36use crate::util::slice_from_user_array;
37use crate::util::DynSize as _;
38
39
40#[repr(C)]
42#[derive(Debug)]
43pub struct blaze_cache_src_elf {
44 pub type_size: usize,
49 pub path: *const c_char,
51 pub reserved: [u8; 16],
54}
55
56impl Default for blaze_cache_src_elf {
57 fn default() -> Self {
58 Self {
59 type_size: mem::size_of::<Self>(),
60 path: ptr::null(),
61 reserved: [0; 16],
62 }
63 }
64}
65
66impl From<blaze_cache_src_elf> for cache::Elf {
67 fn from(elf: blaze_cache_src_elf) -> Self {
68 let blaze_cache_src_elf {
69 type_size: _,
70 path,
71 reserved: _,
72 } = elf;
73 Self {
74 path: unsafe { from_cstr(path) },
75 _non_exhaustive: (),
76 }
77 }
78}
79
80
81#[repr(C)]
83#[derive(Debug)]
84pub struct blaze_cache_src_process {
85 pub type_size: usize,
90 pub pid: u32,
92 pub cache_vmas: bool,
111 pub reserved: [u8; 19],
114}
115
116impl Default for blaze_cache_src_process {
117 fn default() -> Self {
118 Self {
119 type_size: mem::size_of::<Self>(),
120 pid: 0,
121 cache_vmas: false,
122 reserved: [0; 19],
123 }
124 }
125}
126
127impl From<blaze_cache_src_process> for cache::Process {
128 fn from(process: blaze_cache_src_process) -> Self {
129 let blaze_cache_src_process {
130 type_size: _,
131 pid,
132 cache_vmas,
133 reserved: _,
134 } = process;
135 Self {
136 pid: pid.into(),
137 cache_vmas,
138 _non_exhaustive: (),
139 }
140 }
141}
142
143
144#[repr(C)]
149#[derive(Debug)]
150pub struct blaze_symbolize_src_elf {
151 pub type_size: usize,
156 pub path: *const c_char,
163 pub debug_syms: bool,
166 pub reserved: [u8; 23],
169}
170
171impl Default for blaze_symbolize_src_elf {
172 fn default() -> Self {
173 Self {
174 type_size: mem::size_of::<Self>(),
175 path: ptr::null(),
176 debug_syms: false,
177 reserved: [0; 23],
178 }
179 }
180}
181
182impl From<blaze_symbolize_src_elf> for Elf {
183 fn from(elf: blaze_symbolize_src_elf) -> Self {
184 let blaze_symbolize_src_elf {
185 type_size: _,
186 path,
187 debug_syms,
188 reserved: _,
189 } = elf;
190 Self {
191 path: unsafe { from_cstr(path) },
192 debug_syms,
193 _non_exhaustive: (),
194 }
195 }
196}
197
198
199#[repr(C)]
201#[derive(Debug)]
202pub struct blaze_symbolize_src_kernel {
203 pub type_size: usize,
208 pub kallsyms: *const c_char,
218 pub vmlinux: *const c_char,
234 pub debug_syms: bool,
237 pub reserved: [u8; 23],
240}
241
242impl Default for blaze_symbolize_src_kernel {
243 fn default() -> Self {
244 Self {
245 type_size: mem::size_of::<Self>(),
246 kallsyms: ptr::null(),
247 vmlinux: ptr::null(),
248 debug_syms: false,
249 reserved: [0; 23],
250 }
251 }
252}
253
254impl From<blaze_symbolize_src_kernel> for Kernel {
255 fn from(kernel: blaze_symbolize_src_kernel) -> Self {
256 fn to_maybe_path(path: *const c_char) -> MaybeDefault<PathBuf> {
257 if !path.is_null() {
258 let path = unsafe { from_cstr(path) };
259 if path.as_os_str().is_empty() {
260 MaybeDefault::None
261 } else {
262 MaybeDefault::Some(path)
263 }
264 } else {
265 MaybeDefault::Default
266 }
267 }
268
269 let blaze_symbolize_src_kernel {
270 type_size: _,
271 kallsyms,
272 vmlinux,
273 debug_syms,
274 reserved: _,
275 } = kernel;
276 Self {
277 kallsyms: to_maybe_path(kallsyms),
278 vmlinux: to_maybe_path(vmlinux),
279 kaslr_offset: None,
280 debug_syms,
281 _non_exhaustive: (),
282 }
283 }
284}
285
286
287#[repr(C)]
292#[derive(Debug)]
293pub struct blaze_symbolize_src_process {
294 pub type_size: usize,
299 pub pid: u32,
301 pub debug_syms: bool,
304 pub perf_map: bool,
307 pub no_map_files: bool,
316 pub no_vdso: bool,
319 pub reserved: [u8; 16],
322}
323
324impl Default for blaze_symbolize_src_process {
325 fn default() -> Self {
326 Self {
327 type_size: mem::size_of::<Self>(),
328 pid: 0,
329 debug_syms: false,
330 perf_map: false,
331 no_map_files: false,
332 no_vdso: false,
333 reserved: [0; 16],
334 }
335 }
336}
337
338impl From<blaze_symbolize_src_process> for Process {
339 fn from(process: blaze_symbolize_src_process) -> Self {
340 let blaze_symbolize_src_process {
341 type_size: _,
342 pid,
343 debug_syms,
344 perf_map,
345 no_map_files,
346 no_vdso,
347 reserved: _,
348 } = process;
349 Self {
350 pid: pid.into(),
351 debug_syms,
352 perf_map,
353 map_files: !no_map_files,
354 vdso: !no_vdso,
355 _non_exhaustive: (),
356 }
357 }
358}
359
360
361#[repr(C)]
363#[derive(Debug)]
364pub struct blaze_symbolize_src_gsym_data {
365 pub type_size: usize,
370 pub data: *const u8,
372 pub data_len: usize,
374 pub reserved: [u8; 16],
377}
378
379impl Default for blaze_symbolize_src_gsym_data {
380 fn default() -> Self {
381 Self {
382 type_size: mem::size_of::<Self>(),
383 data: ptr::null(),
384 data_len: 0,
385 reserved: [0; 16],
386 }
387 }
388}
389
390impl From<blaze_symbolize_src_gsym_data> for GsymData<'_> {
391 fn from(gsym: blaze_symbolize_src_gsym_data) -> Self {
392 let blaze_symbolize_src_gsym_data {
393 type_size: _,
394 data,
395 data_len,
396 reserved: _,
397 } = gsym;
398 Self {
399 data: unsafe { slice_from_aligned_user_array(data, data_len) },
400 _non_exhaustive: (),
401 }
402 }
403}
404
405
406#[repr(C)]
408#[derive(Debug)]
409pub struct blaze_symbolize_src_gsym_file {
410 pub type_size: usize,
415 pub path: *const c_char,
417 pub reserved: [u8; 16],
420}
421
422impl Default for blaze_symbolize_src_gsym_file {
423 fn default() -> Self {
424 Self {
425 type_size: mem::size_of::<Self>(),
426 path: ptr::null(),
427 reserved: [0; 16],
428 }
429 }
430}
431
432impl From<blaze_symbolize_src_gsym_file> for GsymFile {
433 fn from(gsym: blaze_symbolize_src_gsym_file) -> Self {
434 let blaze_symbolize_src_gsym_file {
435 type_size: _,
436 path,
437 reserved: _,
438 } = gsym;
439 Self {
440 path: unsafe { from_cstr(path) },
441 _non_exhaustive: (),
442 }
443 }
444}
445
446
447pub type blaze_symbolizer = Symbolizer;
452
453
454#[repr(transparent)]
460#[derive(Copy, Clone, Debug, Eq, PartialEq)]
461pub struct blaze_symbolize_reason(u8);
462
463impl blaze_symbolize_reason {
466 pub const SUCCESS: blaze_symbolize_reason = blaze_symbolize_reason(0);
468 pub const UNMAPPED: blaze_symbolize_reason = blaze_symbolize_reason(1);
471 pub const INVALID_FILE_OFFSET: blaze_symbolize_reason = blaze_symbolize_reason(2);
473 pub const MISSING_COMPONENT: blaze_symbolize_reason = blaze_symbolize_reason(3);
477 pub const MISSING_SYMS: blaze_symbolize_reason = blaze_symbolize_reason(4);
479 pub const UNKNOWN_ADDR: blaze_symbolize_reason = blaze_symbolize_reason(5);
481 pub const UNSUPPORTED: blaze_symbolize_reason = blaze_symbolize_reason(6);
483}
484
485
486impl From<Reason> for blaze_symbolize_reason {
487 fn from(reason: Reason) -> Self {
488 match reason {
489 Reason::Unmapped => blaze_symbolize_reason::UNMAPPED,
490 Reason::InvalidFileOffset => blaze_symbolize_reason::INVALID_FILE_OFFSET,
491 Reason::MissingComponent => blaze_symbolize_reason::MISSING_COMPONENT,
492 Reason::MissingSyms => blaze_symbolize_reason::MISSING_SYMS,
493 Reason::Unsupported => blaze_symbolize_reason::UNSUPPORTED,
494 Reason::UnknownAddr => blaze_symbolize_reason::UNKNOWN_ADDR,
495 _ => unreachable!(),
496 }
497 }
498}
499
500
501#[no_mangle]
504pub extern "C" fn blaze_symbolize_reason_str(reason: blaze_symbolize_reason) -> *const c_char {
505 match reason {
506 blaze_symbolize_reason::SUCCESS => b"success\0".as_ptr().cast(),
507 blaze_symbolize_reason::UNMAPPED => Reason::Unmapped.as_bytes().as_ptr().cast(),
508 blaze_symbolize_reason::INVALID_FILE_OFFSET => {
509 Reason::InvalidFileOffset.as_bytes().as_ptr().cast()
510 }
511 blaze_symbolize_reason::MISSING_COMPONENT => {
512 Reason::MissingComponent.as_bytes().as_ptr().cast()
513 }
514 blaze_symbolize_reason::MISSING_SYMS => Reason::MissingSyms.as_bytes().as_ptr().cast(),
515 blaze_symbolize_reason::UNKNOWN_ADDR => Reason::UnknownAddr.as_bytes().as_ptr().cast(),
516 blaze_symbolize_reason::UNSUPPORTED => Reason::Unsupported.as_bytes().as_ptr().cast(),
517 _ => b"unknown reason\0".as_ptr().cast(),
518 }
519}
520
521
522#[repr(C)]
524#[derive(Debug)]
525pub struct blaze_symbolize_code_info {
526 pub dir: *const c_char,
530 pub file: *const c_char,
534 pub line: u32,
537 pub column: u16,
540 pub reserved: [u8; 10],
542}
543
544
545#[repr(C)]
547#[derive(Debug)]
548pub struct blaze_symbolize_inlined_fn {
549 pub name: *const c_char,
551 pub code_info: blaze_symbolize_code_info,
553 pub reserved: [u8; 8],
555}
556
557
558#[repr(C)]
563#[derive(Debug)]
564pub struct blaze_sym {
565 pub name: *const c_char,
571 pub module: *const c_char,
580 pub addr: Addr,
586 pub offset: usize,
596 pub size: isize,
603 pub code_info: blaze_symbolize_code_info,
605 pub inlined_cnt: usize,
607 pub inlined: *const blaze_symbolize_inlined_fn,
609 pub reason: blaze_symbolize_reason,
612 pub reserved: [u8; 15],
614}
615
616#[repr(C)]
621#[derive(Debug)]
622pub struct blaze_syms {
623 pub cnt: usize,
625 pub syms: [blaze_sym; 0],
630}
631
632pub(crate) unsafe fn from_cstr(cstr: *const c_char) -> PathBuf {
637 Path::new(OsStr::from_bytes(
638 unsafe { CStr::from_ptr(cstr) }.to_bytes(),
639 ))
640 .to_path_buf()
641}
642
643
644#[repr(C)]
646#[derive(Debug)]
647pub struct blaze_symbolizer_opts {
648 pub type_size: usize,
653 pub debug_dirs: *const *const c_char,
664 pub debug_dirs_len: usize,
666 pub auto_reload: bool,
670 pub code_info: bool,
677 pub inlined_fns: bool,
682 pub demangle: bool,
688 pub reserved: [u8; 20],
691}
692
693impl Default for blaze_symbolizer_opts {
694 fn default() -> Self {
695 Self {
696 type_size: mem::size_of::<Self>(),
697 debug_dirs: ptr::null(),
698 debug_dirs_len: 0,
699 auto_reload: false,
700 code_info: false,
701 inlined_fns: false,
702 demangle: false,
703 reserved: [0; 20],
704 }
705 }
706}
707
708
709#[no_mangle]
722pub extern "C" fn blaze_symbolizer_new() -> *mut blaze_symbolizer {
723 let symbolizer = Symbolizer::new();
724 let symbolizer_box = Box::new(symbolizer);
725 let () = set_last_err(blaze_err::OK);
726 Box::into_raw(symbolizer_box)
727}
728
729#[no_mangle]
742pub unsafe extern "C" fn blaze_symbolizer_new_opts(
743 opts: *const blaze_symbolizer_opts,
744) -> *mut blaze_symbolizer {
745 if !input_zeroed!(opts, blaze_symbolizer_opts) {
746 let () = set_last_err(blaze_err::INVALID_INPUT);
747 return ptr::null_mut()
748 }
749 let opts = input_sanitize!(opts, blaze_symbolizer_opts);
750
751 let blaze_symbolizer_opts {
752 type_size: _,
753 debug_dirs,
754 debug_dirs_len: _debug_dirs_len,
755 auto_reload,
756 code_info,
757 inlined_fns,
758 demangle,
759 reserved: _,
760 } = opts;
761
762 let builder = Symbolizer::builder()
763 .enable_auto_reload(auto_reload)
764 .enable_code_info(code_info)
765 .enable_inlined_fns(inlined_fns)
766 .enable_demangling(demangle);
767
768 let builder = if debug_dirs.is_null() {
769 builder
770 } else {
771 #[cfg(feature = "dwarf")]
772 {
773 let slice = unsafe { slice_from_user_array(debug_dirs, _debug_dirs_len) };
776 let iter = slice.iter().map(|cstr| {
777 Path::new(OsStr::from_bytes(
778 unsafe { CStr::from_ptr(cstr.cast()) }.to_bytes(),
781 ))
782 });
783
784 builder.set_debug_dirs(Some(iter))
785 }
786
787 #[cfg(not(feature = "dwarf"))]
788 {
789 builder
790 }
791 };
792
793 let symbolizer = builder.build();
794 let symbolizer_box = Box::new(symbolizer);
795 let () = set_last_err(blaze_err::OK);
796 Box::into_raw(symbolizer_box)
797}
798
799#[no_mangle]
805pub unsafe extern "C" fn blaze_symbolizer_free(symbolizer: *mut blaze_symbolizer) {
806 if !symbolizer.is_null() {
807 drop(unsafe { Box::from_raw(symbolizer) });
808 }
809}
810
811
812#[no_mangle]
825pub unsafe extern "C" fn blaze_symbolize_cache_elf(
826 symbolizer: *mut blaze_symbolizer,
827 cache: *const blaze_cache_src_elf,
828) {
829 if !input_zeroed!(cache, blaze_cache_src_elf) {
830 let () = set_last_err(blaze_err::INVALID_INPUT);
831 return
832 }
833 let cache = input_sanitize!(cache, blaze_cache_src_elf);
834 let cache = cache::Cache::from(cache::Elf::from(cache));
835
836 let symbolizer = unsafe { &*symbolizer };
838 let result = symbolizer.cache(&cache);
839 let err = result
840 .map(|()| blaze_err::OK)
841 .unwrap_or_else(|err| err.kind().into());
842 let () = set_last_err(err);
843}
844
845
846#[no_mangle]
866pub unsafe extern "C" fn blaze_symbolize_cache_process(
867 symbolizer: *mut blaze_symbolizer,
868 cache: *const blaze_cache_src_process,
869) {
870 if !input_zeroed!(cache, blaze_cache_src_process) {
871 let () = set_last_err(blaze_err::INVALID_INPUT);
872 return
873 }
874 let cache = input_sanitize!(cache, blaze_cache_src_process);
875 let cache = cache::Cache::from(cache::Process::from(cache));
876
877 let symbolizer = unsafe { &*symbolizer };
879 let result = symbolizer.cache(&cache);
880 let err = result
881 .map(|()| blaze_err::OK)
882 .unwrap_or_else(|err| err.kind().into());
883 let () = set_last_err(err);
884}
885
886fn make_cstr(src: &OsStr, cstr_last: &mut *mut c_char) -> *mut c_char {
887 let cstr = *cstr_last;
888 unsafe { ptr::copy_nonoverlapping(src.as_bytes().as_ptr(), cstr as *mut u8, src.len()) };
889 unsafe { *cstr.add(src.len()) = 0 };
890 *cstr_last = unsafe { cstr_last.add(src.len() + 1) };
891
892 cstr
893}
894
895fn convert_code_info(
896 code_info_in: Option<&CodeInfo>,
897 code_info_out: &mut blaze_symbolize_code_info,
898 cstr_last: &mut *mut c_char,
899) {
900 code_info_out.dir = code_info_in
901 .as_ref()
902 .and_then(|info| {
903 info.dir
904 .as_ref()
905 .map(|d| make_cstr(d.as_os_str(), cstr_last))
906 })
907 .unwrap_or_else(ptr::null_mut);
908 code_info_out.file = code_info_in
909 .as_ref()
910 .map(|info| make_cstr(&info.file, cstr_last))
911 .unwrap_or_else(ptr::null_mut);
912 code_info_out.line = code_info_in
913 .as_ref()
914 .and_then(|info| info.line)
915 .unwrap_or(0);
916 code_info_out.column = code_info_in
917 .as_ref()
918 .and_then(|info| info.column)
919 .unwrap_or(0);
920}
921
922
923pub(crate) fn convert_sym(
925 sym: &Sym,
926 sym_ref: &mut blaze_sym,
927 inlined_last: &mut *mut blaze_symbolize_inlined_fn,
928 cstr_last: &mut *mut c_char,
929) {
930 let name_ptr = make_cstr(OsStr::new(sym.name.as_ref()), cstr_last);
931 let module_ptr = sym
932 .module
933 .as_deref()
934 .map(|module| make_cstr(module, cstr_last))
935 .unwrap_or(ptr::null_mut());
936
937 sym_ref.name = name_ptr;
938 sym_ref.module = module_ptr;
939 sym_ref.addr = sym.addr;
940 sym_ref.offset = sym.offset;
941 sym_ref.size = sym
942 .size
943 .map(|size| isize::try_from(size).unwrap_or(isize::MAX))
944 .unwrap_or(-1);
945 convert_code_info(sym.code_info.as_deref(), &mut sym_ref.code_info, cstr_last);
946 sym_ref.inlined_cnt = sym.inlined.len();
947 sym_ref.inlined = *inlined_last;
948 sym_ref.reason = blaze_symbolize_reason::SUCCESS;
949
950 for inlined in sym.inlined.iter() {
951 let inlined_ref = unsafe { &mut **inlined_last };
952
953 let name_ptr = make_cstr(OsStr::new(inlined.name.as_ref()), cstr_last);
954 inlined_ref.name = name_ptr;
955 convert_code_info(
956 inlined.code_info.as_ref(),
957 &mut inlined_ref.code_info,
958 cstr_last,
959 );
960
961 *inlined_last = unsafe { inlined_last.add(1) };
962 }
963}
964
965
966fn convert_symbolizedresults_to_c(results: Vec<Symbolized>) -> *const blaze_syms {
971 let (strtab_size, inlined_fn_cnt) = results.iter().fold((0, 0), |acc, sym| match sym {
974 Symbolized::Sym(sym) => (acc.0 + sym.c_str_size(), acc.1 + sym.inlined.len()),
975 Symbolized::Unknown(..) => acc,
976 });
977
978 let buf_size = mem::size_of::<u64>()
979 + strtab_size
980 + mem::size_of::<blaze_syms>()
981 + mem::size_of::<blaze_sym>() * results.len()
982 + mem::size_of::<blaze_symbolize_inlined_fn>() * inlined_fn_cnt;
983 let buf = unsafe { alloc(Layout::from_size_align(buf_size, 8).unwrap()) };
984 if buf.is_null() {
985 return ptr::null()
986 }
987
988 unsafe { *buf.cast::<u64>() = buf_size as u64 };
990
991 let syms_buf = unsafe { buf.add(mem::size_of::<u64>()) };
992
993 let syms_ptr = syms_buf.cast::<blaze_syms>();
994 let mut syms_last = unsafe { (*syms_ptr).syms.as_mut_ptr() };
995 let mut inlined_last = unsafe {
996 syms_buf.add(mem::size_of::<blaze_syms>() + mem::size_of::<blaze_sym>() * results.len())
997 } as *mut blaze_symbolize_inlined_fn;
998 let mut cstr_last = unsafe {
999 syms_buf.add(
1000 mem::size_of::<blaze_syms>()
1001 + mem::size_of::<blaze_sym>() * results.len()
1002 + mem::size_of::<blaze_symbolize_inlined_fn>() * inlined_fn_cnt,
1003 )
1004 } as *mut c_char;
1005
1006 unsafe { (*syms_ptr).cnt = results.len() };
1007
1008 for sym in results {
1010 match sym {
1011 Symbolized::Sym(sym) => {
1012 let sym_ref = unsafe { &mut *syms_last };
1013 let () = convert_sym(&sym, sym_ref, &mut inlined_last, &mut cstr_last);
1014 }
1015 Symbolized::Unknown(reason) => {
1016 let () = unsafe { syms_last.write_bytes(0, 1) };
1021 let sym_ref = unsafe { &mut *syms_last };
1022 sym_ref.reason = reason.into();
1023 }
1024 }
1025
1026 syms_last = unsafe { syms_last.add(1) };
1027 }
1028
1029 syms_ptr
1030}
1031
1032unsafe fn blaze_symbolize_impl(
1033 symbolizer: *mut blaze_symbolizer,
1034 src: Source<'_>,
1035 inputs: Input<*const u64>,
1036 input_cnt: usize,
1037) -> *const blaze_syms {
1038 let symbolizer = unsafe { &*symbolizer };
1040 let addrs = unsafe { slice_from_user_array(*inputs.as_inner_ref(), input_cnt) };
1043
1044 let input = match inputs {
1045 Input::AbsAddr(..) => Input::AbsAddr(addrs.deref()),
1046 Input::VirtOffset(..) => Input::VirtOffset(addrs.deref()),
1047 Input::FileOffset(..) => Input::FileOffset(addrs.deref()),
1048 };
1049
1050 let result = symbolizer.symbolize(&src, input);
1051 match result {
1052 Ok(results) if results.is_empty() => {
1053 let () = set_last_err(blaze_err::OK);
1054 ptr::null()
1055 }
1056 Ok(results) => {
1057 let result = convert_symbolizedresults_to_c(results);
1058 if result.is_null() {
1059 let () = set_last_err(blaze_err::OUT_OF_MEMORY);
1060 } else {
1061 let () = set_last_err(blaze_err::OK);
1062 }
1063 result
1064 }
1065 Err(err) => {
1066 let () = set_last_err(err.kind().into());
1067 ptr::null_mut()
1068 }
1069 }
1070}
1071
1072
1073#[no_mangle]
1089pub unsafe extern "C" fn blaze_symbolize_process_abs_addrs(
1090 symbolizer: *mut blaze_symbolizer,
1091 src: *const blaze_symbolize_src_process,
1092 abs_addrs: *const Addr,
1093 abs_addr_cnt: usize,
1094) -> *const blaze_syms {
1095 if !input_zeroed!(src, blaze_symbolize_src_process) {
1096 let () = set_last_err(blaze_err::INVALID_INPUT);
1097 return ptr::null()
1098 }
1099 let src = input_sanitize!(src, blaze_symbolize_src_process);
1100 let src = Source::from(Process::from(src));
1101
1102 unsafe { blaze_symbolize_impl(symbolizer, src, Input::AbsAddr(abs_addrs), abs_addr_cnt) }
1103}
1104
1105
1106#[no_mangle]
1122pub unsafe extern "C" fn blaze_symbolize_kernel_abs_addrs(
1123 symbolizer: *mut blaze_symbolizer,
1124 src: *const blaze_symbolize_src_kernel,
1125 abs_addrs: *const Addr,
1126 abs_addr_cnt: usize,
1127) -> *const blaze_syms {
1128 if !input_zeroed!(src, blaze_symbolize_src_kernel) {
1129 let () = set_last_err(blaze_err::INVALID_INPUT);
1130 return ptr::null()
1131 }
1132 let src = input_sanitize!(src, blaze_symbolize_src_kernel);
1133 let src = Source::from(Kernel::from(src));
1134
1135 unsafe { blaze_symbolize_impl(symbolizer, src, Input::AbsAddr(abs_addrs), abs_addr_cnt) }
1136}
1137
1138
1139#[no_mangle]
1155pub unsafe extern "C" fn blaze_symbolize_elf_virt_offsets(
1156 symbolizer: *mut blaze_symbolizer,
1157 src: *const blaze_symbolize_src_elf,
1158 virt_offsets: *const Addr,
1159 virt_offset_cnt: usize,
1160) -> *const blaze_syms {
1161 if !input_zeroed!(src, blaze_symbolize_src_elf) {
1162 let () = set_last_err(blaze_err::INVALID_INPUT);
1163 return ptr::null()
1164 }
1165 let src = input_sanitize!(src, blaze_symbolize_src_elf);
1166 let src = Source::from(Elf::from(src));
1167
1168 unsafe {
1169 blaze_symbolize_impl(
1170 symbolizer,
1171 src,
1172 Input::VirtOffset(virt_offsets),
1173 virt_offset_cnt,
1174 )
1175 }
1176}
1177
1178#[no_mangle]
1194pub unsafe extern "C" fn blaze_symbolize_elf_file_offsets(
1195 symbolizer: *mut blaze_symbolizer,
1196 src: *const blaze_symbolize_src_elf,
1197 file_offsets: *const Addr,
1198 file_offset_cnt: usize,
1199) -> *const blaze_syms {
1200 if !input_zeroed!(src, blaze_symbolize_src_elf) {
1201 let () = set_last_err(blaze_err::INVALID_INPUT);
1202 return ptr::null()
1203 }
1204 let src = input_sanitize!(src, blaze_symbolize_src_elf);
1205 let src = Source::from(Elf::from(src));
1206
1207 unsafe {
1208 blaze_symbolize_impl(
1209 symbolizer,
1210 src,
1211 Input::FileOffset(file_offsets),
1212 file_offset_cnt,
1213 )
1214 }
1215}
1216
1217
1218#[no_mangle]
1234pub unsafe extern "C" fn blaze_symbolize_gsym_data_virt_offsets(
1235 symbolizer: *mut blaze_symbolizer,
1236 src: *const blaze_symbolize_src_gsym_data,
1237 virt_offsets: *const Addr,
1238 virt_offset_cnt: usize,
1239) -> *const blaze_syms {
1240 if !input_zeroed!(src, blaze_symbolize_src_gsym_data) {
1241 let () = set_last_err(blaze_err::INVALID_INPUT);
1242 return ptr::null()
1243 }
1244 let src = input_sanitize!(src, blaze_symbolize_src_gsym_data);
1245 let src = Source::from(GsymData::from(src));
1246 unsafe {
1247 blaze_symbolize_impl(
1248 symbolizer,
1249 src,
1250 Input::VirtOffset(virt_offsets),
1251 virt_offset_cnt,
1252 )
1253 }
1254}
1255
1256
1257#[no_mangle]
1273pub unsafe extern "C" fn blaze_symbolize_gsym_file_virt_offsets(
1274 symbolizer: *mut blaze_symbolizer,
1275 src: *const blaze_symbolize_src_gsym_file,
1276 virt_offsets: *const Addr,
1277 virt_offset_cnt: usize,
1278) -> *const blaze_syms {
1279 if !input_zeroed!(src, blaze_symbolize_src_gsym_file) {
1280 let () = set_last_err(blaze_err::INVALID_INPUT);
1281 return ptr::null()
1282 }
1283 let src = input_sanitize!(src, blaze_symbolize_src_gsym_file);
1284 let src = Source::from(GsymFile::from(src));
1285
1286 unsafe {
1287 blaze_symbolize_impl(
1288 symbolizer,
1289 src,
1290 Input::VirtOffset(virt_offsets),
1291 virt_offset_cnt,
1292 )
1293 }
1294}
1295
1296
1297#[no_mangle]
1303pub unsafe extern "C" fn blaze_syms_free(syms: *const blaze_syms) {
1304 if syms.is_null() {
1305 return
1306 }
1307
1308 let buf = unsafe { syms.byte_sub(mem::size_of::<u64>()).cast::<u8>().cast_mut() };
1310 let size = unsafe { *buf.cast::<u64>() } as usize;
1311 unsafe { dealloc(buf, Layout::from_size_align(size, 8).unwrap()) };
1312}
1313
1314
1315#[cfg(test)]
1316mod tests {
1317 use super::*;
1318
1319 use std::borrow::Cow;
1320 use std::ffi::CString;
1321 use std::fs::copy;
1322 use std::fs::read as read_file;
1323 use std::fs::remove_file;
1324 use std::hint::black_box;
1325 use std::io::Error;
1326 use std::os::unix::ffi::OsStringExt as _;
1327 use std::slice;
1328
1329 use blazesym::inspect;
1330 use blazesym::normalize;
1331 use blazesym::symbolize::InlinedFn;
1332 use blazesym::symbolize::Reason;
1333 use blazesym::Pid;
1334
1335 use tempfile::tempdir;
1336
1337 use test_tag::tag;
1338
1339 use crate::blaze_err_last;
1340
1341
1342 #[tag(miri)]
1344 #[test]
1345 #[cfg(target_pointer_width = "64")]
1346 fn type_sizes() {
1347 assert_eq!(mem::size_of::<blaze_cache_src_elf>(), 32);
1348 assert_eq!(mem::size_of::<blaze_cache_src_process>(), 32);
1349 assert_eq!(mem::size_of::<blaze_symbolize_src_elf>(), 40);
1350 assert_eq!(mem::size_of::<blaze_symbolize_src_kernel>(), 48);
1351 assert_eq!(mem::size_of::<blaze_symbolize_src_process>(), 32);
1352 assert_eq!(mem::size_of::<blaze_symbolize_src_gsym_data>(), 40);
1353 assert_eq!(mem::size_of::<blaze_symbolize_src_gsym_file>(), 32);
1354 assert_eq!(mem::size_of::<blaze_symbolizer_opts>(), 48);
1355 assert_eq!(mem::size_of::<blaze_symbolize_code_info>(), 32);
1356 assert_eq!(mem::size_of::<blaze_symbolize_inlined_fn>(), 48);
1357 assert_eq!(mem::size_of::<blaze_sym>(), 104);
1358 }
1359
1360 #[tag(miri)]
1362 #[test]
1363 fn debug_repr() {
1364 let elf = blaze_symbolize_src_elf {
1365 type_size: 24,
1366 ..Default::default()
1367 };
1368 assert_eq!(
1369 format!("{elf:?}"),
1370 "blaze_symbolize_src_elf { type_size: 24, path: 0x0, debug_syms: false, reserved: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }"
1371 );
1372
1373 let kernel = blaze_symbolize_src_kernel {
1374 type_size: 32,
1375 debug_syms: true,
1376 ..Default::default()
1377 };
1378 assert_eq!(
1379 format!("{kernel:?}"),
1380 "blaze_symbolize_src_kernel { type_size: 32, kallsyms: 0x0, vmlinux: 0x0, debug_syms: true, reserved: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }"
1381 );
1382
1383 let process = blaze_symbolize_src_process {
1384 type_size: 16,
1385 pid: 1337,
1386 debug_syms: true,
1387 ..Default::default()
1388 };
1389 assert_eq!(
1390 format!("{process:?}"),
1391 "blaze_symbolize_src_process { type_size: 16, pid: 1337, debug_syms: true, perf_map: false, no_map_files: false, no_vdso: false, reserved: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }"
1392 );
1393
1394 let gsym_data = blaze_symbolize_src_gsym_data {
1395 type_size: 24,
1396 data: ptr::null(),
1397 data_len: 0,
1398 reserved: [0; 16],
1399 };
1400 assert_eq!(
1401 format!("{gsym_data:?}"),
1402 "blaze_symbolize_src_gsym_data { type_size: 24, data: 0x0, data_len: 0, reserved: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }"
1403 );
1404
1405 let gsym_file = blaze_symbolize_src_gsym_file {
1406 type_size: 16,
1407 path: ptr::null(),
1408 reserved: [0; 16],
1409 };
1410 assert_eq!(
1411 format!("{gsym_file:?}"),
1412 "blaze_symbolize_src_gsym_file { type_size: 16, path: 0x0, reserved: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }"
1413 );
1414
1415 let sym = blaze_sym {
1416 name: ptr::null(),
1417 module: ptr::null(),
1418 addr: 0x1337,
1419 offset: 24,
1420 size: 16,
1421 code_info: blaze_symbolize_code_info {
1422 dir: ptr::null(),
1423 file: ptr::null(),
1424 line: 42,
1425 column: 1,
1426 reserved: [0; 10],
1427 },
1428 inlined_cnt: 0,
1429 inlined: ptr::null(),
1430 reason: blaze_symbolize_reason::UNSUPPORTED,
1431 reserved: [0; 15],
1432 };
1433 assert_eq!(
1434 format!("{sym:?}"),
1435 "blaze_sym { name: 0x0, module: 0x0, addr: 4919, offset: 24, size: 16, code_info: blaze_symbolize_code_info { dir: 0x0, file: 0x0, line: 42, column: 1, reserved: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, inlined_cnt: 0, inlined: 0x0, reason: blaze_symbolize_reason(6), reserved: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }"
1436 );
1437
1438 let inlined = blaze_symbolize_inlined_fn {
1439 name: ptr::null(),
1440 code_info: blaze_symbolize_code_info {
1441 dir: ptr::null(),
1442 file: ptr::null(),
1443 line: 42,
1444 column: 1,
1445 reserved: [0; 10],
1446 },
1447 reserved: [0; 8],
1448 };
1449 assert_eq!(
1450 format!("{inlined:?}"),
1451 "blaze_symbolize_inlined_fn { name: 0x0, code_info: blaze_symbolize_code_info { dir: 0x0, file: 0x0, line: 42, column: 1, reserved: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, reserved: [0, 0, 0, 0, 0, 0, 0, 0] }"
1452 );
1453
1454 let syms = blaze_syms { cnt: 0, syms: [] };
1455 assert_eq!(format!("{syms:?}"), "blaze_syms { cnt: 0, syms: [] }");
1456
1457 let opts = blaze_symbolizer_opts {
1458 type_size: 16,
1459 demangle: true,
1460 ..Default::default()
1461 };
1462 assert_eq!(
1463 format!("{opts:?}"),
1464 "blaze_symbolizer_opts { type_size: 16, debug_dirs: 0x0, debug_dirs_len: 0, auto_reload: false, code_info: false, inlined_fns: false, demangle: true, reserved: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }"
1465 );
1466 }
1467
1468 #[tag(miri)]
1470 #[test]
1471 fn reason_stringification() {
1472 let data = [
1473 (Reason::Unmapped, blaze_symbolize_reason::UNMAPPED),
1474 (
1475 Reason::InvalidFileOffset,
1476 blaze_symbolize_reason::INVALID_FILE_OFFSET,
1477 ),
1478 (
1479 Reason::MissingComponent,
1480 blaze_symbolize_reason::MISSING_COMPONENT,
1481 ),
1482 (Reason::MissingSyms, blaze_symbolize_reason::MISSING_SYMS),
1483 (Reason::Unsupported, blaze_symbolize_reason::UNSUPPORTED),
1484 (Reason::UnknownAddr, blaze_symbolize_reason::UNKNOWN_ADDR),
1485 ];
1486
1487 for (reason, expected) in data {
1488 assert_eq!(blaze_symbolize_reason::from(reason), expected);
1489 let cstr = unsafe { CStr::from_ptr(blaze_symbolize_reason_str(expected)) };
1490 let expected = CStr::from_bytes_with_nul(reason.as_bytes()).unwrap();
1491 assert_eq!(cstr, expected);
1492 }
1493 }
1494
1495
1496 #[tag(miri)]
1499 #[test]
1500 fn kernel_conversion() {
1501 let kernel = blaze_symbolize_src_kernel::default();
1502 let kernel = Kernel::from(kernel);
1503 assert_eq!(kernel.kallsyms, MaybeDefault::Default);
1504 assert_eq!(kernel.vmlinux, MaybeDefault::Default);
1505
1506 let kernel = blaze_symbolize_src_kernel {
1507 kallsyms: b"\0" as *const _ as *const c_char,
1508 vmlinux: b"\0" as *const _ as *const c_char,
1509 ..Default::default()
1510 };
1511 let kernel = Kernel::from(kernel);
1512 assert_eq!(kernel.kallsyms, MaybeDefault::None);
1513 assert_eq!(kernel.vmlinux, MaybeDefault::None);
1514
1515 let kernel = blaze_symbolize_src_kernel {
1516 kallsyms: b"/proc/kallsyms\0" as *const _ as *const c_char,
1517 vmlinux: b"/boot/vmlinux\0" as *const _ as *const c_char,
1518 debug_syms: false,
1519 ..Default::default()
1520 };
1521
1522 let kernel = Kernel::from(kernel);
1523 assert_eq!(
1524 kernel.kallsyms,
1525 MaybeDefault::Some(PathBuf::from("/proc/kallsyms"))
1526 );
1527 assert_eq!(
1528 kernel.vmlinux,
1529 MaybeDefault::Some(PathBuf::from("/boot/vmlinux"))
1530 );
1531 }
1532
1533 #[tag(miri)]
1536 #[test]
1537 fn cache_process_conversion() {
1538 let process = blaze_cache_src_process {
1539 pid: 42,
1540 cache_vmas: false,
1541 ..Default::default()
1542 };
1543 let process = cache::Process::from(process);
1544 assert_eq!(process.pid, Pid::from(42))
1545 }
1546
1547 #[tag(miri)]
1549 #[test]
1550 fn symbol_conversion() {
1551 fn touch<X: Clone>(x: &X) {
1552 let x = x.clone();
1553 let _x = black_box(x);
1554 }
1555
1556 fn touch_cstr(s: *const c_char) {
1557 if !s.is_null() {
1558 let s = unsafe { CStr::from_ptr(s) }.to_bytes();
1559 let _x = black_box(s);
1560 }
1561 }
1562
1563 fn touch_code_info(code_info: &blaze_symbolize_code_info) {
1564 let blaze_symbolize_code_info {
1565 dir,
1566 file,
1567 line,
1568 column,
1569 reserved: _,
1570 } = code_info;
1571
1572 let _x = touch_cstr(*dir);
1573 let _x = touch_cstr(*file);
1574 let _x = touch(line);
1575 let _x = touch(column);
1576 }
1577
1578 fn touch_syms(syms: *const blaze_syms) {
1580 let syms = unsafe { &*syms };
1581 for i in 0..syms.cnt {
1582 let sym = unsafe { &*syms.syms.as_slice().as_ptr().add(i) };
1583 let blaze_sym {
1584 name,
1585 module,
1586 addr,
1587 offset,
1588 size,
1589 code_info,
1590 inlined_cnt,
1591 inlined,
1592 reason,
1593 reserved: _,
1594 } = sym;
1595
1596 let () = touch_cstr(*name);
1597 let () = touch_cstr(*module);
1598 let _x = touch(addr);
1599 let _x = touch(offset);
1600 let _x = touch(size);
1601 let () = touch_code_info(code_info);
1602
1603 for j in 0..*inlined_cnt {
1604 let inlined_fn = unsafe { &*inlined.add(j) };
1605 let blaze_symbolize_inlined_fn {
1606 name,
1607 code_info,
1608 reserved: _,
1609 } = inlined_fn;
1610 let () = touch_cstr(*name);
1611 let () = touch_code_info(code_info);
1612 }
1613 let () = touch(reason);
1614 }
1615 }
1616
1617 let results = vec![];
1619 let syms = convert_symbolizedresults_to_c(results);
1620 assert!(!syms.is_null());
1621
1622 let () = touch_syms(syms);
1623 let () = unsafe { blaze_syms_free(syms) };
1624
1625 let results = vec![Symbolized::Sym(Sym {
1627 name: "test".into(),
1628 module: Some(Cow::from(OsStr::new("module"))),
1629 addr: 0x1337,
1630 offset: 0x1338,
1631 size: Some(42),
1632 code_info: Some(Box::new(CodeInfo {
1633 dir: None,
1634 file: OsStr::new("a-file").into(),
1635 line: Some(42),
1636 column: Some(43),
1637 _non_exhaustive: (),
1638 })),
1639 inlined: vec![InlinedFn {
1640 name: "inlined_fn".into(),
1641 code_info: Some(CodeInfo {
1642 dir: Some(Path::new("/some/dir").into()),
1643 file: OsStr::new("another-file").into(),
1644 line: Some(42),
1645 column: Some(43),
1646 _non_exhaustive: (),
1647 }),
1648 _non_exhaustive: (),
1649 }]
1650 .into_boxed_slice(),
1651 _non_exhaustive: (),
1652 })];
1653 let syms = convert_symbolizedresults_to_c(results);
1654 assert!(!syms.is_null());
1655 let () = touch_syms(syms);
1656 let () = unsafe { blaze_syms_free(syms) };
1657
1658 let results = vec![
1660 Symbolized::Unknown(Reason::UnknownAddr),
1661 Symbolized::Sym(Sym {
1662 name: "test".into(),
1663 module: Some(Cow::from(OsStr::new("module"))),
1664 addr: 0x1337,
1665 offset: 0x1338,
1666 size: None,
1667 code_info: None,
1668 inlined: vec![InlinedFn {
1669 name: "inlined_fn".into(),
1670 code_info: None,
1671 _non_exhaustive: (),
1672 }]
1673 .into_boxed_slice(),
1674 _non_exhaustive: (),
1675 }),
1676 Symbolized::Unknown(Reason::InvalidFileOffset),
1677 ];
1678 let syms = convert_symbolizedresults_to_c(results);
1679 assert!(!syms.is_null());
1680 let () = touch_syms(syms);
1681 let () = unsafe { blaze_syms_free(syms) };
1682 }
1683
1684 #[tag(miri)]
1686 #[test]
1687 fn symbolizer_creation() {
1688 let symbolizer = blaze_symbolizer_new();
1689 let () = unsafe { blaze_symbolizer_free(symbolizer) };
1690 }
1691
1692 #[tag(miri)]
1695 #[test]
1696 fn symbolizer_creation_with_opts() {
1697 let opts = blaze_symbolizer_opts {
1698 demangle: true,
1699 ..Default::default()
1700 };
1701
1702 let symbolizer = unsafe { blaze_symbolizer_new_opts(&opts) };
1703 let () = unsafe { blaze_symbolizer_free(symbolizer) };
1704 }
1705
1706 #[test]
1709 fn symbolize_elf_dwarf_gsym() {
1710 fn test<F>(symbolize: F, has_code_info: bool)
1711 where
1712 F: FnOnce(*mut blaze_symbolizer, *const Addr, usize) -> *const blaze_syms,
1713 {
1714 let symbolizer = blaze_symbolizer_new();
1715 let addrs = [0x2000200];
1716 let result = symbolize(symbolizer, addrs.as_ptr(), addrs.len());
1717
1718 assert!(!result.is_null());
1719
1720 let result = unsafe { &*result };
1721 assert_eq!(result.cnt, 1);
1722 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
1723 let sym = &syms[0];
1724 assert_eq!(
1725 unsafe { CStr::from_ptr(sym.name) },
1726 CStr::from_bytes_with_nul(b"factorial\0").unwrap()
1727 );
1728 assert_eq!(sym.reason, blaze_symbolize_reason::SUCCESS);
1729 assert_eq!(sym.addr, 0x2000200);
1730 assert_eq!(sym.offset, 0);
1731 assert!(sym.size > 0);
1732
1733 if has_code_info {
1734 assert!(!sym.code_info.dir.is_null());
1735 assert!(!sym.code_info.file.is_null());
1736 assert_eq!(
1737 unsafe { CStr::from_ptr(sym.code_info.file) },
1738 CStr::from_bytes_with_nul(b"test-stable-addrs.c\0").unwrap()
1739 );
1740 assert_eq!(sym.code_info.line, 10);
1741 } else {
1742 assert!(sym.code_info.dir.is_null());
1743 assert!(sym.code_info.file.is_null());
1744 assert_eq!(sym.code_info.line, 0);
1745 }
1746
1747 let () = unsafe { blaze_syms_free(result) };
1748 let () = unsafe { blaze_symbolizer_free(symbolizer) };
1749 }
1750
1751 let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
1752 .join("..")
1753 .join("data")
1754 .join("test-stable-addrs-no-dwarf.bin");
1755 let path_c = CString::new(path.to_str().unwrap()).unwrap();
1756 let elf_src = blaze_symbolize_src_elf {
1757 path: path_c.as_ptr(),
1758 debug_syms: true,
1759 ..Default::default()
1760 };
1761
1762 let symbolize = |symbolizer, addrs, addr_cnt| unsafe {
1763 blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs, addr_cnt)
1764 };
1765 test(symbolize, false);
1766
1767 let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
1768 .join("..")
1769 .join("data")
1770 .join("test-stable-addrs-stripped-elf-with-dwarf.bin");
1771 let path_c = CString::new(path.to_str().unwrap()).unwrap();
1772 let elf_src = blaze_symbolize_src_elf {
1773 path: path_c.as_ptr(),
1774 debug_syms: true,
1775 ..Default::default()
1776 };
1777
1778 let symbolize = |symbolizer, addrs, addr_cnt| unsafe {
1779 blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs, addr_cnt)
1780 };
1781 test(symbolize, true);
1782
1783 let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
1784 .join("..")
1785 .join("data")
1786 .join("test-stable-addrs.gsym");
1787 let path_c = CString::new(path.to_str().unwrap()).unwrap();
1788 let gsym_src = blaze_symbolize_src_gsym_file {
1789 path: path_c.as_ptr(),
1790 ..Default::default()
1791 };
1792
1793 let symbolize = |symbolizer, addrs, addr_cnt| unsafe {
1794 blaze_symbolize_gsym_file_virt_offsets(symbolizer, &gsym_src, addrs, addr_cnt)
1795 };
1796 test(symbolize, true);
1797
1798 let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
1799 .join("..")
1800 .join("data")
1801 .join("test-stable-addrs.gsym");
1802 let data = read_file(path).unwrap();
1803 let gsym_src = blaze_symbolize_src_gsym_data {
1804 data: data.as_ptr(),
1805 data_len: data.len(),
1806 ..Default::default()
1807 };
1808
1809 let symbolize = |symbolizer, addrs, addr_cnt| unsafe {
1810 blaze_symbolize_gsym_data_virt_offsets(symbolizer, &gsym_src, addrs, addr_cnt)
1811 };
1812 test(symbolize, true);
1813 }
1814
1815
1816 #[test]
1818 #[cfg_attr(
1819 not(target_pointer_width = "64"),
1820 ignore = "loads 64 bit shared object"
1821 )]
1822 fn symbolize_elf_file_offset() {
1823 let test_so = Path::new(&env!("CARGO_MANIFEST_DIR"))
1824 .join("..")
1825 .join("data")
1826 .join("libtest-so.so")
1827 .canonicalize()
1828 .unwrap();
1829 let so_cstr = CString::new(test_so.clone().into_os_string().into_vec()).unwrap();
1830 let handle = unsafe { libc::dlopen(so_cstr.as_ptr(), libc::RTLD_NOW) };
1831 assert!(!handle.is_null());
1832
1833 let the_answer_addr = unsafe { libc::dlsym(handle, "the_answer\0".as_ptr().cast()) };
1834 assert!(!the_answer_addr.is_null());
1835
1836 let normalizer = normalize::Normalizer::new();
1837 let normalized = normalizer
1838 .normalize_user_addrs(Pid::Slf, [the_answer_addr as Addr].as_slice())
1839 .unwrap();
1840 assert_eq!(normalized.outputs.len(), 1);
1841 assert_eq!(normalized.meta.len(), 1);
1842
1843 let rc = unsafe { libc::dlclose(handle) };
1844 assert_eq!(rc, 0, "{}", Error::last_os_error());
1845
1846 let output = normalized.outputs[0];
1847 let meta = &normalized.meta[output.1];
1848 assert_eq!(meta.as_elf().unwrap().path, test_so);
1849
1850 let symbolizer = blaze_symbolizer_new();
1851 let elf_src = blaze_symbolize_src_elf {
1852 path: so_cstr.as_ptr(),
1853 ..Default::default()
1854 };
1855 let offsets = [output.0];
1856 let result = unsafe {
1857 blaze_symbolize_elf_file_offsets(
1858 symbolizer,
1859 &elf_src,
1860 offsets.as_slice().as_ptr(),
1861 offsets.len(),
1862 )
1863 };
1864 assert!(!result.is_null());
1865
1866 let result = unsafe { &*result };
1867 assert_eq!(result.cnt, 1);
1868
1869 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
1870 let sym = &syms[0];
1871 assert!(!sym.name.is_null());
1872 assert!(!sym.module.is_null());
1873 assert!(sym.size > 0);
1874 assert_eq!(
1875 unsafe { CStr::from_ptr(sym.name) },
1876 CStr::from_bytes_with_nul(b"the_answer\0").unwrap()
1877 );
1878
1879 let () = unsafe { blaze_syms_free(result) };
1880 let () = unsafe { blaze_symbolizer_free(symbolizer) };
1881 }
1882
1883 #[tag(other_os)]
1886 #[test]
1887 fn symbolize_elf_cached() {
1888 let dir = tempdir().unwrap();
1889 let path__ = Path::new(&env!("CARGO_MANIFEST_DIR"))
1890 .join("..")
1891 .join("data")
1892 .join("test-stable-addrs.bin");
1893 let path = dir.path().join("test-stable-addrs-temporary.bin");
1894 let _count = copy(&path__, &path).unwrap();
1895
1896 let symbolizer = blaze_symbolizer_new();
1897
1898 let path_c = CString::new(path.to_str().unwrap()).unwrap();
1899 let cache = blaze_cache_src_elf {
1900 path: path_c.as_ptr(),
1901 ..Default::default()
1902 };
1903 let () = unsafe { blaze_symbolize_cache_elf(symbolizer, &cache) };
1904 assert_eq!(blaze_err_last(), blaze_err::OK);
1905
1906 let () = remove_file(&path).unwrap();
1907
1908 let src = blaze_symbolize_src_elf {
1909 path: path_c.as_ptr(),
1910 debug_syms: false,
1911 ..Default::default()
1912 };
1913 let addrs = [0x2000200];
1914 let result = unsafe {
1915 blaze_symbolize_elf_virt_offsets(symbolizer, &src, addrs.as_ptr(), addrs.len())
1916 };
1917 assert!(!result.is_null());
1918
1919 let result = unsafe { &*result };
1920 assert_eq!(result.cnt, 1);
1921 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
1922 let sym = &syms[0];
1923
1924 assert_eq!(
1925 unsafe { CStr::from_ptr(sym.name) },
1926 CStr::from_bytes_with_nul(b"factorial\0").unwrap()
1927 );
1928 assert_eq!(sym.reason, blaze_symbolize_reason::SUCCESS);
1929 assert_eq!(sym.addr, 0x2000200);
1930
1931 let () = unsafe { blaze_syms_free(result) };
1932 let () = unsafe { blaze_symbolizer_free(symbolizer) };
1933 }
1934
1935 #[test]
1938 fn symbolize_dwarf_demangle() {
1939 fn test(path: &Path, addr: Addr) -> Result<(), ()> {
1940 let opts = blaze_symbolizer_opts {
1941 code_info: true,
1942 inlined_fns: true,
1943 ..Default::default()
1944 };
1945
1946 let path_c = CString::new(path.to_str().unwrap()).unwrap();
1947 let elf_src = blaze_symbolize_src_elf {
1948 path: path_c.as_ptr(),
1949 debug_syms: true,
1950 ..Default::default()
1951 };
1952
1953 let symbolizer = unsafe { blaze_symbolizer_new_opts(&opts) };
1954 let addrs = [addr];
1955 let result = unsafe {
1956 blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs.as_ptr(), addrs.len())
1957 };
1958 assert!(!result.is_null());
1959
1960 let result = unsafe { &*result };
1961 assert_eq!(result.cnt, 1);
1962 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
1963 let sym = &syms[0];
1964 let name = unsafe { CStr::from_ptr(sym.name) };
1965 assert!(
1966 name.to_str().unwrap().contains("test13test_function"),
1967 "{name:?}"
1968 );
1969
1970 if sym.inlined_cnt == 0 {
1971 let () = unsafe { blaze_syms_free(result) };
1972 let () = unsafe { blaze_symbolizer_free(symbolizer) };
1973 return Err(())
1974 }
1975
1976 assert_eq!(sym.inlined_cnt, 1);
1977 let name = unsafe { CStr::from_ptr((*sym.inlined).name) };
1978 assert!(
1979 name.to_str().unwrap().contains("test12inlined_call"),
1980 "{name:?}"
1981 );
1982
1983 let () = unsafe { blaze_syms_free(result) };
1984 let () = unsafe { blaze_symbolizer_free(symbolizer) };
1985
1986 let opts = blaze_symbolizer_opts {
1988 code_info: true,
1989 inlined_fns: true,
1990 demangle: true,
1991 ..Default::default()
1992 };
1993
1994 let symbolizer = unsafe { blaze_symbolizer_new_opts(&opts) };
1995 let addrs = [addr];
1996 let result = unsafe {
1997 blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs.as_ptr(), addrs.len())
1998 };
1999 assert!(!result.is_null());
2000
2001 let result = unsafe { &*result };
2002 assert_eq!(result.cnt, 1);
2003 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
2004 let sym = &syms[0];
2005 assert_eq!(
2006 unsafe { CStr::from_ptr(sym.name) },
2007 CStr::from_bytes_with_nul(b"test::test_function\0").unwrap()
2008 );
2009 assert_eq!(unsafe { CStr::from_ptr(sym.module) }, &*path_c);
2010
2011 assert_eq!(sym.inlined_cnt, 1);
2012 assert_eq!(
2013 unsafe { CStr::from_ptr((*sym.inlined).name) },
2014 CStr::from_bytes_with_nul(b"test::inlined_call\0").unwrap()
2015 );
2016
2017 let () = unsafe { blaze_syms_free(result) };
2018 let () = unsafe { blaze_symbolizer_free(symbolizer) };
2019 Ok(())
2020 }
2021
2022 let test_dwarf = Path::new(&env!("CARGO_MANIFEST_DIR"))
2023 .join("..")
2024 .join("data")
2025 .join("test-rs.bin");
2026 let elf = inspect::source::Elf::new(&test_dwarf);
2027 let src = inspect::source::Source::Elf(elf);
2028
2029 let inspector = inspect::Inspector::new();
2030 let results = inspector
2031 .lookup(&src, &["_RNvCs69hjMPjVIJK_4test13test_function"])
2032 .unwrap()
2033 .into_iter()
2034 .flatten()
2035 .collect::<Vec<_>>();
2036 assert!(!results.is_empty());
2037
2038 let addr = results[0].addr;
2039 let src = Source::Elf(Elf::new(&test_dwarf));
2040 let symbolizer = Symbolizer::builder().enable_demangling(false).build();
2041 let result = symbolizer
2042 .symbolize_single(&src, Input::VirtOffset(addr))
2043 .unwrap()
2044 .into_sym()
2045 .unwrap();
2046
2047 let addr = result.addr;
2048 let size = result.size.unwrap() as u64;
2049 for inst_addr in addr..addr + size {
2050 if test(&test_dwarf, inst_addr).is_ok() {
2051 return
2052 }
2053 }
2054
2055 panic!("failed to find inlined function call");
2056 }
2057
2058 #[test]
2060 fn symbolize_in_process() {
2061 let process_src = blaze_symbolize_src_process {
2062 pid: 0,
2063 debug_syms: true,
2064 perf_map: true,
2065 ..Default::default()
2066 };
2067
2068 let symbolizer = blaze_symbolizer_new();
2069 let addrs = [blaze_symbolizer_new as Addr];
2070 let result = unsafe {
2071 blaze_symbolize_process_abs_addrs(symbolizer, &process_src, addrs.as_ptr(), addrs.len())
2072 };
2073
2074 assert!(!result.is_null());
2075
2076 let result = unsafe { &*result };
2077 assert_eq!(result.cnt, 1);
2078 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
2079 let sym = &syms[0];
2080 assert_eq!(
2081 unsafe { CStr::from_ptr(sym.name) },
2082 CStr::from_bytes_with_nul(b"blaze_symbolizer_new\0").unwrap()
2083 );
2084
2085 let () = unsafe { blaze_syms_free(result) };
2086 let () = unsafe { blaze_symbolizer_free(symbolizer) };
2087 }
2088
2089 #[test]
2092 fn symbolize_in_process_cached() {
2093 let symbolizer = blaze_symbolizer_new();
2094 let cache = blaze_cache_src_process {
2095 pid: 0,
2096 cache_vmas: true,
2097 ..Default::default()
2098 };
2099 let () = unsafe { blaze_symbolize_cache_process(symbolizer, &cache) };
2100 assert_eq!(blaze_err_last(), blaze_err::OK);
2101
2102 let src = blaze_symbolize_src_process {
2103 pid: 0,
2104 debug_syms: true,
2105 perf_map: true,
2106 ..Default::default()
2107 };
2108 let addrs = [blaze_symbolizer_new as Addr];
2109 let result = unsafe {
2110 blaze_symbolize_process_abs_addrs(symbolizer, &src, addrs.as_ptr(), addrs.len())
2111 };
2112
2113 assert!(!result.is_null());
2114
2115 let result = unsafe { &*result };
2116 assert_eq!(result.cnt, 1);
2117 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
2118 let sym = &syms[0];
2119 assert_eq!(
2120 unsafe { CStr::from_ptr(sym.name) },
2121 CStr::from_bytes_with_nul(b"blaze_symbolizer_new\0").unwrap()
2122 );
2123
2124 let () = unsafe { blaze_syms_free(result) };
2125 let () = unsafe { blaze_symbolizer_free(symbolizer) };
2126 }
2127
2128 #[test]
2131 fn symbolize_in_kernel() {
2132 let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
2133 .join("..")
2134 .join("data")
2135 .join("kallsyms");
2136 let path_c = CString::new(path.to_str().unwrap()).unwrap();
2137 let src = blaze_symbolize_src_kernel {
2138 kallsyms: path_c.as_ptr(),
2139 vmlinux: b"\0" as *const _ as *const c_char,
2140 ..Default::default()
2141 };
2142
2143 let symbolizer = blaze_symbolizer_new();
2144 let addrs = [0xc080a470];
2145 let result = unsafe {
2146 blaze_symbolize_kernel_abs_addrs(symbolizer, &src, addrs.as_ptr(), addrs.len())
2147 };
2148
2149 assert!(!result.is_null());
2150
2151 let result = unsafe { &*result };
2152 assert_eq!(result.cnt, 1);
2153 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
2154 let sym = &syms[0];
2155 assert_eq!(
2156 unsafe { CStr::from_ptr(sym.name) },
2157 CStr::from_bytes_with_nul(b"init_task\0").unwrap()
2158 );
2159 assert_eq!(sym.module, ptr::null_mut());
2160
2161 let () = unsafe { blaze_syms_free(result) };
2162 let () = unsafe { blaze_symbolizer_free(symbolizer) };
2163 }
2164
2165 #[test]
2168 fn symbolize_elf_non_existent() {
2169 let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
2170 .join("..")
2171 .join("data")
2172 .join("does-not-actually-exist.bin");
2173 let path_c = CString::new(path.to_str().unwrap()).unwrap();
2174 let elf_src = blaze_symbolize_src_elf {
2175 path: path_c.as_ptr(),
2176 debug_syms: true,
2177 ..Default::default()
2178 };
2179
2180 let symbolizer = blaze_symbolizer_new();
2181 let addrs = [blaze_symbolizer_new as Addr];
2182 let result = unsafe {
2183 blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs.as_ptr(), addrs.len())
2184 };
2185 assert!(result.is_null());
2186 assert_eq!(blaze_err_last(), blaze_err::NOT_FOUND);
2187
2188 let () = unsafe { blaze_syms_free(result) };
2189 let () = unsafe { blaze_symbolizer_free(symbolizer) };
2190 }
2191
2192 #[test]
2194 fn symbolize_no_debug_dirs() {
2195 let dir = tempdir().unwrap();
2196 let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
2197 .join("..")
2198 .join("data")
2199 .join("test-stable-addrs-stripped-with-link.bin");
2200 let dst = dir.path().join("test-stable-addrs-stripped-with-link.bin");
2201 let _count = copy(path, &dst).unwrap();
2202
2203 let debug_dirs = [];
2204 let opts = blaze_symbolizer_opts {
2205 debug_dirs: debug_dirs.as_ptr(),
2206 debug_dirs_len: debug_dirs.len(),
2207 code_info: true,
2208 inlined_fns: true,
2209 demangle: true,
2210 ..Default::default()
2211 };
2212 let symbolizer = unsafe { blaze_symbolizer_new_opts(&opts) };
2213
2214 let path_c = CString::new(dst.to_str().unwrap()).unwrap();
2215 let elf_src = blaze_symbolize_src_elf {
2216 path: path_c.as_ptr(),
2217 debug_syms: true,
2218 ..Default::default()
2219 };
2220 let addrs = [0x2000200];
2221 let result = unsafe {
2222 blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs.as_ptr(), addrs.len())
2223 };
2224 assert!(!result.is_null());
2225
2226 let result = unsafe { &*result };
2227 assert_eq!(result.cnt, 1);
2228 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
2229 let sym = &syms[0];
2230 assert_eq!(sym.name, ptr::null());
2233 assert_eq!(sym.module, ptr::null());
2234 assert_eq!(sym.reason, blaze_symbolize_reason::MISSING_SYMS);
2235
2236 let () = unsafe { blaze_syms_free(result) };
2237 let () = unsafe { blaze_symbolizer_free(symbolizer) };
2238 }
2239
2240 #[test]
2242 fn symbolize_configurable_debug_dirs() {
2243 let debug_dir1 = tempdir().unwrap();
2244 let debug_dir1_c = CString::new(debug_dir1.path().to_str().unwrap()).unwrap();
2245 let debug_dir2 = tempdir().unwrap();
2246 let debug_dir2_c = CString::new(debug_dir2.path().to_str().unwrap()).unwrap();
2247
2248 let src = Path::new(&env!("CARGO_MANIFEST_DIR"))
2249 .join("..")
2250 .join("data")
2251 .join("test-stable-addrs-dwarf-only.dbg");
2252 let dst = debug_dir2.path().join("test-stable-addrs-dwarf-only.dbg");
2253 let _count = copy(src, dst).unwrap();
2254
2255 let debug_dirs = [debug_dir1_c.as_ptr(), debug_dir2_c.as_ptr()];
2256 let opts = blaze_symbolizer_opts {
2257 debug_dirs: debug_dirs.as_ptr(),
2258 debug_dirs_len: debug_dirs.len(),
2259 code_info: true,
2260 inlined_fns: true,
2261 demangle: true,
2262 ..Default::default()
2263 };
2264 let symbolizer = unsafe { blaze_symbolizer_new_opts(&opts) };
2265
2266 let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
2267 .join("..")
2268 .join("data")
2269 .join("test-stable-addrs-stripped-with-link.bin");
2270 let path_c = CString::new(path.to_str().unwrap()).unwrap();
2271 let elf_src = blaze_symbolize_src_elf {
2272 path: path_c.as_ptr(),
2273 debug_syms: true,
2274 ..Default::default()
2275 };
2276 let addrs = [0x2000200];
2277 let result = unsafe {
2278 blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs.as_ptr(), addrs.len())
2279 };
2280 assert!(!result.is_null());
2281
2282 let result = unsafe { &*result };
2283 assert_eq!(result.cnt, 1);
2284 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
2285 let sym = &syms[0];
2286 let name = unsafe { CStr::from_ptr(sym.name) };
2287 assert_eq!(name.to_str().unwrap(), "factorial");
2288 let module = unsafe { CStr::from_ptr(sym.module) };
2289 assert!(
2290 module
2291 .to_str()
2292 .unwrap()
2293 .ends_with("test-stable-addrs-stripped-with-link.bin"),
2294 "{module:?}"
2295 );
2296
2297 let () = unsafe { blaze_syms_free(result) };
2298 let () = unsafe { blaze_symbolizer_free(symbolizer) };
2299 }
2300}