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 {
464 pub const SUCCESS: blaze_symbolize_reason = blaze_symbolize_reason(0);
466 pub const UNMAPPED: blaze_symbolize_reason = blaze_symbolize_reason(1);
469 pub const INVALID_FILE_OFFSET: blaze_symbolize_reason = blaze_symbolize_reason(2);
471 pub const MISSING_COMPONENT: blaze_symbolize_reason = blaze_symbolize_reason(3);
475 pub const MISSING_SYMS: blaze_symbolize_reason = blaze_symbolize_reason(4);
477 pub const UNKNOWN_ADDR: blaze_symbolize_reason = blaze_symbolize_reason(5);
479 pub const UNSUPPORTED: blaze_symbolize_reason = blaze_symbolize_reason(6);
481}
482
483
484impl From<Reason> for blaze_symbolize_reason {
485 fn from(reason: Reason) -> Self {
486 match reason {
487 Reason::Unmapped => blaze_symbolize_reason::UNMAPPED,
488 Reason::InvalidFileOffset => blaze_symbolize_reason::INVALID_FILE_OFFSET,
489 Reason::MissingComponent => blaze_symbolize_reason::MISSING_COMPONENT,
490 Reason::MissingSyms => blaze_symbolize_reason::MISSING_SYMS,
491 Reason::Unsupported => blaze_symbolize_reason::UNSUPPORTED,
492 Reason::UnknownAddr => blaze_symbolize_reason::UNKNOWN_ADDR,
493 _ => unreachable!(),
494 }
495 }
496}
497
498
499#[no_mangle]
502pub extern "C" fn blaze_symbolize_reason_str(err: blaze_symbolize_reason) -> *const c_char {
503 match err {
504 blaze_symbolize_reason::SUCCESS => b"success\0".as_ptr().cast(),
505 blaze_symbolize_reason::UNMAPPED => Reason::Unmapped.as_bytes().as_ptr().cast(),
506 blaze_symbolize_reason::INVALID_FILE_OFFSET => {
507 Reason::InvalidFileOffset.as_bytes().as_ptr().cast()
508 }
509 blaze_symbolize_reason::MISSING_COMPONENT => {
510 Reason::MissingComponent.as_bytes().as_ptr().cast()
511 }
512 blaze_symbolize_reason::MISSING_SYMS => Reason::MissingSyms.as_bytes().as_ptr().cast(),
513 blaze_symbolize_reason::UNKNOWN_ADDR => Reason::UnknownAddr.as_bytes().as_ptr().cast(),
514 blaze_symbolize_reason::UNSUPPORTED => Reason::Unsupported.as_bytes().as_ptr().cast(),
515 _ => b"unknown reason\0".as_ptr().cast(),
516 }
517}
518
519
520#[repr(C)]
522#[derive(Debug)]
523pub struct blaze_symbolize_code_info {
524 pub dir: *const c_char,
528 pub file: *const c_char,
532 pub line: u32,
535 pub column: u16,
538 pub reserved: [u8; 10],
540}
541
542
543#[repr(C)]
545#[derive(Debug)]
546pub struct blaze_symbolize_inlined_fn {
547 pub name: *const c_char,
549 pub code_info: blaze_symbolize_code_info,
551 pub reserved: [u8; 8],
553}
554
555
556#[repr(C)]
561#[derive(Debug)]
562pub struct blaze_sym {
563 pub name: *const c_char,
569 pub module: *const c_char,
578 pub addr: Addr,
584 pub offset: usize,
594 pub size: isize,
601 pub code_info: blaze_symbolize_code_info,
603 pub inlined_cnt: usize,
605 pub inlined: *const blaze_symbolize_inlined_fn,
607 pub reason: blaze_symbolize_reason,
610 pub reserved: [u8; 15],
612}
613
614#[repr(C)]
619#[derive(Debug)]
620pub struct blaze_syms {
621 pub cnt: usize,
623 pub syms: [blaze_sym; 0],
628}
629
630pub(crate) unsafe fn from_cstr(cstr: *const c_char) -> PathBuf {
635 Path::new(OsStr::from_bytes(
636 unsafe { CStr::from_ptr(cstr) }.to_bytes(),
637 ))
638 .to_path_buf()
639}
640
641
642#[repr(C)]
644#[derive(Debug)]
645pub struct blaze_symbolizer_opts {
646 pub type_size: usize,
651 pub debug_dirs: *const *const c_char,
662 pub debug_dirs_len: usize,
664 pub auto_reload: bool,
668 pub code_info: bool,
675 pub inlined_fns: bool,
680 pub demangle: bool,
686 pub reserved: [u8; 20],
689}
690
691impl Default for blaze_symbolizer_opts {
692 fn default() -> Self {
693 Self {
694 type_size: mem::size_of::<Self>(),
695 debug_dirs: ptr::null(),
696 debug_dirs_len: 0,
697 auto_reload: false,
698 code_info: false,
699 inlined_fns: false,
700 demangle: false,
701 reserved: [0; 20],
702 }
703 }
704}
705
706
707#[no_mangle]
720pub extern "C" fn blaze_symbolizer_new() -> *mut blaze_symbolizer {
721 let symbolizer = Symbolizer::new();
722 let symbolizer_box = Box::new(symbolizer);
723 let () = set_last_err(blaze_err::OK);
724 Box::into_raw(symbolizer_box)
725}
726
727#[no_mangle]
740pub unsafe extern "C" fn blaze_symbolizer_new_opts(
741 opts: *const blaze_symbolizer_opts,
742) -> *mut blaze_symbolizer {
743 if !input_zeroed!(opts, blaze_symbolizer_opts) {
744 let () = set_last_err(blaze_err::INVALID_INPUT);
745 return ptr::null_mut()
746 }
747 let opts = input_sanitize!(opts, blaze_symbolizer_opts);
748
749 let blaze_symbolizer_opts {
750 type_size: _,
751 debug_dirs,
752 debug_dirs_len: _debug_dirs_len,
753 auto_reload,
754 code_info,
755 inlined_fns,
756 demangle,
757 reserved: _,
758 } = opts;
759
760 let builder = Symbolizer::builder()
761 .enable_auto_reload(auto_reload)
762 .enable_code_info(code_info)
763 .enable_inlined_fns(inlined_fns)
764 .enable_demangling(demangle);
765
766 let builder = if debug_dirs.is_null() {
767 builder
768 } else {
769 #[cfg(feature = "dwarf")]
770 {
771 let slice = unsafe { slice_from_user_array(debug_dirs, _debug_dirs_len) };
774 let iter = slice.iter().map(|cstr| {
775 Path::new(OsStr::from_bytes(
776 unsafe { CStr::from_ptr(cstr.cast()) }.to_bytes(),
779 ))
780 });
781
782 builder.set_debug_dirs(Some(iter))
783 }
784
785 #[cfg(not(feature = "dwarf"))]
786 {
787 builder
788 }
789 };
790
791 let symbolizer = builder.build();
792 let symbolizer_box = Box::new(symbolizer);
793 let () = set_last_err(blaze_err::OK);
794 Box::into_raw(symbolizer_box)
795}
796
797#[no_mangle]
803pub unsafe extern "C" fn blaze_symbolizer_free(symbolizer: *mut blaze_symbolizer) {
804 if !symbolizer.is_null() {
805 drop(unsafe { Box::from_raw(symbolizer) });
806 }
807}
808
809
810#[no_mangle]
823pub unsafe extern "C" fn blaze_symbolize_cache_elf(
824 symbolizer: *mut blaze_symbolizer,
825 cache: *const blaze_cache_src_elf,
826) {
827 if !input_zeroed!(cache, blaze_cache_src_elf) {
828 let () = set_last_err(blaze_err::INVALID_INPUT);
829 return
830 }
831 let cache = input_sanitize!(cache, blaze_cache_src_elf);
832 let cache = cache::Cache::from(cache::Elf::from(cache));
833
834 let symbolizer = unsafe { &*symbolizer };
836 let result = symbolizer.cache(&cache);
837 let err = result
838 .map(|()| blaze_err::OK)
839 .unwrap_or_else(|err| err.kind().into());
840 let () = set_last_err(err);
841}
842
843
844#[no_mangle]
864pub unsafe extern "C" fn blaze_symbolize_cache_process(
865 symbolizer: *mut blaze_symbolizer,
866 cache: *const blaze_cache_src_process,
867) {
868 if !input_zeroed!(cache, blaze_cache_src_process) {
869 let () = set_last_err(blaze_err::INVALID_INPUT);
870 return
871 }
872 let cache = input_sanitize!(cache, blaze_cache_src_process);
873 let cache = cache::Cache::from(cache::Process::from(cache));
874
875 let symbolizer = unsafe { &*symbolizer };
877 let result = symbolizer.cache(&cache);
878 let err = result
879 .map(|()| blaze_err::OK)
880 .unwrap_or_else(|err| err.kind().into());
881 let () = set_last_err(err);
882}
883
884fn make_cstr(src: &OsStr, cstr_last: &mut *mut c_char) -> *mut c_char {
885 let cstr = *cstr_last;
886 unsafe { ptr::copy_nonoverlapping(src.as_bytes().as_ptr(), cstr as *mut u8, src.len()) };
887 unsafe { *cstr.add(src.len()) = 0 };
888 *cstr_last = unsafe { cstr_last.add(src.len() + 1) };
889
890 cstr
891}
892
893fn convert_code_info(
894 code_info_in: &Option<CodeInfo>,
895 code_info_out: &mut blaze_symbolize_code_info,
896 cstr_last: &mut *mut c_char,
897) {
898 code_info_out.dir = code_info_in
899 .as_ref()
900 .and_then(|info| {
901 info.dir
902 .as_ref()
903 .map(|d| make_cstr(d.as_os_str(), cstr_last))
904 })
905 .unwrap_or_else(ptr::null_mut);
906 code_info_out.file = code_info_in
907 .as_ref()
908 .map(|info| make_cstr(&info.file, cstr_last))
909 .unwrap_or_else(ptr::null_mut);
910 code_info_out.line = code_info_in
911 .as_ref()
912 .and_then(|info| info.line)
913 .unwrap_or(0);
914 code_info_out.column = code_info_in
915 .as_ref()
916 .and_then(|info| info.column)
917 .unwrap_or(0);
918}
919
920
921pub(crate) fn convert_sym(
923 sym: &Sym,
924 sym_ref: &mut blaze_sym,
925 inlined_last: &mut *mut blaze_symbolize_inlined_fn,
926 cstr_last: &mut *mut c_char,
927) {
928 let name_ptr = make_cstr(OsStr::new(sym.name.as_ref()), cstr_last);
929 let module_ptr = sym
930 .module
931 .as_deref()
932 .map(|module| make_cstr(module, cstr_last))
933 .unwrap_or(ptr::null_mut());
934
935 sym_ref.name = name_ptr;
936 sym_ref.module = module_ptr;
937 sym_ref.addr = sym.addr;
938 sym_ref.offset = sym.offset;
939 sym_ref.size = sym
940 .size
941 .map(|size| isize::try_from(size).unwrap_or(isize::MAX))
942 .unwrap_or(-1);
943 convert_code_info(&sym.code_info, &mut sym_ref.code_info, cstr_last);
944 sym_ref.inlined_cnt = sym.inlined.len();
945 sym_ref.inlined = *inlined_last;
946 sym_ref.reason = blaze_symbolize_reason::SUCCESS;
947
948 for inlined in sym.inlined.iter() {
949 let inlined_ref = unsafe { &mut **inlined_last };
950
951 let name_ptr = make_cstr(OsStr::new(inlined.name.as_ref()), cstr_last);
952 inlined_ref.name = name_ptr;
953 convert_code_info(&inlined.code_info, &mut inlined_ref.code_info, cstr_last);
954
955 *inlined_last = unsafe { inlined_last.add(1) };
956 }
957}
958
959
960fn convert_symbolizedresults_to_c(results: Vec<Symbolized>) -> *const blaze_syms {
965 let (strtab_size, inlined_fn_cnt) = results.iter().fold((0, 0), |acc, sym| match sym {
968 Symbolized::Sym(sym) => (acc.0 + sym.c_str_size(), acc.1 + sym.inlined.len()),
969 Symbolized::Unknown(..) => acc,
970 });
971
972 let buf_size = mem::size_of::<u64>()
973 + strtab_size
974 + mem::size_of::<blaze_syms>()
975 + mem::size_of::<blaze_sym>() * results.len()
976 + mem::size_of::<blaze_symbolize_inlined_fn>() * inlined_fn_cnt;
977 let buf = unsafe { alloc(Layout::from_size_align(buf_size, 8).unwrap()) };
978 if buf.is_null() {
979 return ptr::null()
980 }
981
982 unsafe { *buf.cast::<u64>() = buf_size as u64 };
984
985 let syms_buf = unsafe { buf.add(mem::size_of::<u64>()) };
986
987 let syms_ptr = syms_buf.cast::<blaze_syms>();
988 let mut syms_last = unsafe { (*syms_ptr).syms.as_mut_ptr() };
989 let mut inlined_last = unsafe {
990 syms_buf.add(mem::size_of::<blaze_syms>() + mem::size_of::<blaze_sym>() * results.len())
991 } as *mut blaze_symbolize_inlined_fn;
992 let mut cstr_last = unsafe {
993 syms_buf.add(
994 mem::size_of::<blaze_syms>()
995 + mem::size_of::<blaze_sym>() * results.len()
996 + mem::size_of::<blaze_symbolize_inlined_fn>() * inlined_fn_cnt,
997 )
998 } as *mut c_char;
999
1000 unsafe { (*syms_ptr).cnt = results.len() };
1001
1002 for sym in results {
1004 match sym {
1005 Symbolized::Sym(sym) => {
1006 let sym_ref = unsafe { &mut *syms_last };
1007 let () = convert_sym(&sym, sym_ref, &mut inlined_last, &mut cstr_last);
1008 }
1009 Symbolized::Unknown(reason) => {
1010 let () = unsafe { syms_last.write_bytes(0, 1) };
1015 let sym_ref = unsafe { &mut *syms_last };
1016 sym_ref.reason = reason.into();
1017 }
1018 }
1019
1020 syms_last = unsafe { syms_last.add(1) };
1021 }
1022
1023 syms_ptr
1024}
1025
1026unsafe fn blaze_symbolize_impl(
1027 symbolizer: *mut blaze_symbolizer,
1028 src: Source<'_>,
1029 inputs: Input<*const u64>,
1030 input_cnt: usize,
1031) -> *const blaze_syms {
1032 let symbolizer = unsafe { &*symbolizer };
1034 let addrs = unsafe { slice_from_user_array(*inputs.as_inner_ref(), input_cnt) };
1037
1038 let input = match inputs {
1039 Input::AbsAddr(..) => Input::AbsAddr(addrs.deref()),
1040 Input::VirtOffset(..) => Input::VirtOffset(addrs.deref()),
1041 Input::FileOffset(..) => Input::FileOffset(addrs.deref()),
1042 };
1043
1044 let result = symbolizer.symbolize(&src, input);
1045 match result {
1046 Ok(results) if results.is_empty() => {
1047 let () = set_last_err(blaze_err::OK);
1048 ptr::null()
1049 }
1050 Ok(results) => {
1051 let result = convert_symbolizedresults_to_c(results);
1052 if result.is_null() {
1053 let () = set_last_err(blaze_err::OUT_OF_MEMORY);
1054 } else {
1055 let () = set_last_err(blaze_err::OK);
1056 }
1057 result
1058 }
1059 Err(err) => {
1060 let () = set_last_err(err.kind().into());
1061 ptr::null_mut()
1062 }
1063 }
1064}
1065
1066
1067#[no_mangle]
1083pub unsafe extern "C" fn blaze_symbolize_process_abs_addrs(
1084 symbolizer: *mut blaze_symbolizer,
1085 src: *const blaze_symbolize_src_process,
1086 abs_addrs: *const Addr,
1087 abs_addr_cnt: usize,
1088) -> *const blaze_syms {
1089 if !input_zeroed!(src, blaze_symbolize_src_process) {
1090 let () = set_last_err(blaze_err::INVALID_INPUT);
1091 return ptr::null()
1092 }
1093 let src = input_sanitize!(src, blaze_symbolize_src_process);
1094 let src = Source::from(Process::from(src));
1095
1096 unsafe { blaze_symbolize_impl(symbolizer, src, Input::AbsAddr(abs_addrs), abs_addr_cnt) }
1097}
1098
1099
1100#[no_mangle]
1116pub unsafe extern "C" fn blaze_symbolize_kernel_abs_addrs(
1117 symbolizer: *mut blaze_symbolizer,
1118 src: *const blaze_symbolize_src_kernel,
1119 abs_addrs: *const Addr,
1120 abs_addr_cnt: usize,
1121) -> *const blaze_syms {
1122 if !input_zeroed!(src, blaze_symbolize_src_kernel) {
1123 let () = set_last_err(blaze_err::INVALID_INPUT);
1124 return ptr::null()
1125 }
1126 let src = input_sanitize!(src, blaze_symbolize_src_kernel);
1127 let src = Source::from(Kernel::from(src));
1128
1129 unsafe { blaze_symbolize_impl(symbolizer, src, Input::AbsAddr(abs_addrs), abs_addr_cnt) }
1130}
1131
1132
1133#[no_mangle]
1149pub unsafe extern "C" fn blaze_symbolize_elf_virt_offsets(
1150 symbolizer: *mut blaze_symbolizer,
1151 src: *const blaze_symbolize_src_elf,
1152 virt_offsets: *const Addr,
1153 virt_offset_cnt: usize,
1154) -> *const blaze_syms {
1155 if !input_zeroed!(src, blaze_symbolize_src_elf) {
1156 let () = set_last_err(blaze_err::INVALID_INPUT);
1157 return ptr::null()
1158 }
1159 let src = input_sanitize!(src, blaze_symbolize_src_elf);
1160 let src = Source::from(Elf::from(src));
1161
1162 unsafe {
1163 blaze_symbolize_impl(
1164 symbolizer,
1165 src,
1166 Input::VirtOffset(virt_offsets),
1167 virt_offset_cnt,
1168 )
1169 }
1170}
1171
1172#[no_mangle]
1188pub unsafe extern "C" fn blaze_symbolize_elf_file_offsets(
1189 symbolizer: *mut blaze_symbolizer,
1190 src: *const blaze_symbolize_src_elf,
1191 file_offsets: *const Addr,
1192 file_offset_cnt: usize,
1193) -> *const blaze_syms {
1194 if !input_zeroed!(src, blaze_symbolize_src_elf) {
1195 let () = set_last_err(blaze_err::INVALID_INPUT);
1196 return ptr::null()
1197 }
1198 let src = input_sanitize!(src, blaze_symbolize_src_elf);
1199 let src = Source::from(Elf::from(src));
1200
1201 unsafe {
1202 blaze_symbolize_impl(
1203 symbolizer,
1204 src,
1205 Input::FileOffset(file_offsets),
1206 file_offset_cnt,
1207 )
1208 }
1209}
1210
1211
1212#[no_mangle]
1228pub unsafe extern "C" fn blaze_symbolize_gsym_data_virt_offsets(
1229 symbolizer: *mut blaze_symbolizer,
1230 src: *const blaze_symbolize_src_gsym_data,
1231 virt_offsets: *const Addr,
1232 virt_offset_cnt: usize,
1233) -> *const blaze_syms {
1234 if !input_zeroed!(src, blaze_symbolize_src_gsym_data) {
1235 let () = set_last_err(blaze_err::INVALID_INPUT);
1236 return ptr::null()
1237 }
1238 let src = input_sanitize!(src, blaze_symbolize_src_gsym_data);
1239 let src = Source::from(GsymData::from(src));
1240 unsafe {
1241 blaze_symbolize_impl(
1242 symbolizer,
1243 src,
1244 Input::VirtOffset(virt_offsets),
1245 virt_offset_cnt,
1246 )
1247 }
1248}
1249
1250
1251#[no_mangle]
1267pub unsafe extern "C" fn blaze_symbolize_gsym_file_virt_offsets(
1268 symbolizer: *mut blaze_symbolizer,
1269 src: *const blaze_symbolize_src_gsym_file,
1270 virt_offsets: *const Addr,
1271 virt_offset_cnt: usize,
1272) -> *const blaze_syms {
1273 if !input_zeroed!(src, blaze_symbolize_src_gsym_file) {
1274 let () = set_last_err(blaze_err::INVALID_INPUT);
1275 return ptr::null()
1276 }
1277 let src = input_sanitize!(src, blaze_symbolize_src_gsym_file);
1278 let src = Source::from(GsymFile::from(src));
1279
1280 unsafe {
1281 blaze_symbolize_impl(
1282 symbolizer,
1283 src,
1284 Input::VirtOffset(virt_offsets),
1285 virt_offset_cnt,
1286 )
1287 }
1288}
1289
1290
1291#[no_mangle]
1297pub unsafe extern "C" fn blaze_syms_free(syms: *const blaze_syms) {
1298 if syms.is_null() {
1299 return
1300 }
1301
1302 let buf = unsafe { syms.byte_sub(mem::size_of::<u64>()).cast::<u8>().cast_mut() };
1304 let size = unsafe { *buf.cast::<u64>() } as usize;
1305 unsafe { dealloc(buf, Layout::from_size_align(size, 8).unwrap()) };
1306}
1307
1308
1309#[cfg(test)]
1310mod tests {
1311 use super::*;
1312
1313 use std::borrow::Cow;
1314 use std::ffi::CString;
1315 use std::fs::copy;
1316 use std::fs::read as read_file;
1317 use std::fs::remove_file;
1318 use std::hint::black_box;
1319 use std::io::Error;
1320 use std::os::unix::ffi::OsStringExt as _;
1321 use std::slice;
1322
1323 use blazesym::inspect;
1324 use blazesym::normalize;
1325 use blazesym::symbolize::InlinedFn;
1326 use blazesym::symbolize::Reason;
1327 use blazesym::Pid;
1328
1329 use tempfile::tempdir;
1330
1331 use test_tag::tag;
1332
1333 use crate::blaze_err_last;
1334
1335
1336 #[tag(miri)]
1338 #[test]
1339 #[cfg(target_pointer_width = "64")]
1340 fn type_sizes() {
1341 assert_eq!(mem::size_of::<blaze_cache_src_elf>(), 32);
1342 assert_eq!(mem::size_of::<blaze_cache_src_process>(), 32);
1343 assert_eq!(mem::size_of::<blaze_symbolize_src_elf>(), 40);
1344 assert_eq!(mem::size_of::<blaze_symbolize_src_kernel>(), 48);
1345 assert_eq!(mem::size_of::<blaze_symbolize_src_process>(), 32);
1346 assert_eq!(mem::size_of::<blaze_symbolize_src_gsym_data>(), 40);
1347 assert_eq!(mem::size_of::<blaze_symbolize_src_gsym_file>(), 32);
1348 assert_eq!(mem::size_of::<blaze_symbolizer_opts>(), 48);
1349 assert_eq!(mem::size_of::<blaze_symbolize_code_info>(), 32);
1350 assert_eq!(mem::size_of::<blaze_symbolize_inlined_fn>(), 48);
1351 assert_eq!(mem::size_of::<blaze_sym>(), 104);
1352 }
1353
1354 #[tag(miri)]
1356 #[test]
1357 fn debug_repr() {
1358 let elf = blaze_symbolize_src_elf {
1359 type_size: 24,
1360 ..Default::default()
1361 };
1362 assert_eq!(
1363 format!("{elf:?}"),
1364 "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] }"
1365 );
1366
1367 let kernel = blaze_symbolize_src_kernel {
1368 type_size: 32,
1369 debug_syms: true,
1370 ..Default::default()
1371 };
1372 assert_eq!(
1373 format!("{kernel:?}"),
1374 "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] }"
1375 );
1376
1377 let process = blaze_symbolize_src_process {
1378 type_size: 16,
1379 pid: 1337,
1380 debug_syms: true,
1381 ..Default::default()
1382 };
1383 assert_eq!(
1384 format!("{process:?}"),
1385 "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] }"
1386 );
1387
1388 let gsym_data = blaze_symbolize_src_gsym_data {
1389 type_size: 24,
1390 data: ptr::null(),
1391 data_len: 0,
1392 reserved: [0; 16],
1393 };
1394 assert_eq!(
1395 format!("{gsym_data:?}"),
1396 "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] }"
1397 );
1398
1399 let gsym_file = blaze_symbolize_src_gsym_file {
1400 type_size: 16,
1401 path: ptr::null(),
1402 reserved: [0; 16],
1403 };
1404 assert_eq!(
1405 format!("{gsym_file:?}"),
1406 "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] }"
1407 );
1408
1409 let sym = blaze_sym {
1410 name: ptr::null(),
1411 module: ptr::null(),
1412 addr: 0x1337,
1413 offset: 24,
1414 size: 16,
1415 code_info: blaze_symbolize_code_info {
1416 dir: ptr::null(),
1417 file: ptr::null(),
1418 line: 42,
1419 column: 1,
1420 reserved: [0; 10],
1421 },
1422 inlined_cnt: 0,
1423 inlined: ptr::null(),
1424 reason: blaze_symbolize_reason::UNSUPPORTED,
1425 reserved: [0; 15],
1426 };
1427 assert_eq!(
1428 format!("{sym:?}"),
1429 "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] }"
1430 );
1431
1432 let inlined = blaze_symbolize_inlined_fn {
1433 name: ptr::null(),
1434 code_info: blaze_symbolize_code_info {
1435 dir: ptr::null(),
1436 file: ptr::null(),
1437 line: 42,
1438 column: 1,
1439 reserved: [0; 10],
1440 },
1441 reserved: [0; 8],
1442 };
1443 assert_eq!(
1444 format!("{inlined:?}"),
1445 "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] }"
1446 );
1447
1448 let syms = blaze_syms { cnt: 0, syms: [] };
1449 assert_eq!(format!("{syms:?}"), "blaze_syms { cnt: 0, syms: [] }");
1450
1451 let opts = blaze_symbolizer_opts {
1452 type_size: 16,
1453 demangle: true,
1454 ..Default::default()
1455 };
1456 assert_eq!(
1457 format!("{opts:?}"),
1458 "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] }"
1459 );
1460 }
1461
1462 #[tag(miri)]
1464 #[test]
1465 fn reason_stringification() {
1466 let data = [
1467 (Reason::Unmapped, blaze_symbolize_reason::UNMAPPED),
1468 (
1469 Reason::InvalidFileOffset,
1470 blaze_symbolize_reason::INVALID_FILE_OFFSET,
1471 ),
1472 (
1473 Reason::MissingComponent,
1474 blaze_symbolize_reason::MISSING_COMPONENT,
1475 ),
1476 (Reason::MissingSyms, blaze_symbolize_reason::MISSING_SYMS),
1477 (Reason::Unsupported, blaze_symbolize_reason::UNSUPPORTED),
1478 (Reason::UnknownAddr, blaze_symbolize_reason::UNKNOWN_ADDR),
1479 ];
1480
1481 for (reason, expected) in data {
1482 assert_eq!(blaze_symbolize_reason::from(reason), expected);
1483 let cstr = unsafe { CStr::from_ptr(blaze_symbolize_reason_str(expected)) };
1484 let expected = CStr::from_bytes_with_nul(reason.as_bytes()).unwrap();
1485 assert_eq!(cstr, expected);
1486 }
1487 }
1488
1489
1490 #[tag(miri)]
1493 #[test]
1494 fn kernel_conversion() {
1495 let kernel = blaze_symbolize_src_kernel::default();
1496 let kernel = Kernel::from(kernel);
1497 assert_eq!(kernel.kallsyms, MaybeDefault::Default);
1498 assert_eq!(kernel.vmlinux, MaybeDefault::Default);
1499
1500 let kernel = blaze_symbolize_src_kernel {
1501 kallsyms: b"\0" as *const _ as *const c_char,
1502 vmlinux: b"\0" as *const _ as *const c_char,
1503 ..Default::default()
1504 };
1505 let kernel = Kernel::from(kernel);
1506 assert_eq!(kernel.kallsyms, MaybeDefault::None);
1507 assert_eq!(kernel.vmlinux, MaybeDefault::None);
1508
1509 let kernel = blaze_symbolize_src_kernel {
1510 kallsyms: b"/proc/kallsyms\0" as *const _ as *const c_char,
1511 vmlinux: b"/boot/vmlinux\0" as *const _ as *const c_char,
1512 debug_syms: false,
1513 ..Default::default()
1514 };
1515
1516 let kernel = Kernel::from(kernel);
1517 assert_eq!(
1518 kernel.kallsyms,
1519 MaybeDefault::Some(PathBuf::from("/proc/kallsyms"))
1520 );
1521 assert_eq!(
1522 kernel.vmlinux,
1523 MaybeDefault::Some(PathBuf::from("/boot/vmlinux"))
1524 );
1525 }
1526
1527 #[tag(miri)]
1530 #[test]
1531 fn cache_process_conversion() {
1532 let process = blaze_cache_src_process {
1533 pid: 42,
1534 cache_vmas: false,
1535 ..Default::default()
1536 };
1537 let process = cache::Process::from(process);
1538 assert_eq!(process.pid, Pid::from(42))
1539 }
1540
1541 #[tag(miri)]
1543 #[test]
1544 fn symbol_conversion() {
1545 fn touch<X: Clone>(x: &X) {
1546 let x = x.clone();
1547 let _x = black_box(x);
1548 }
1549
1550 fn touch_cstr(s: *const c_char) {
1551 if !s.is_null() {
1552 let s = unsafe { CStr::from_ptr(s) }.to_bytes();
1553 let _x = black_box(s);
1554 }
1555 }
1556
1557 fn touch_code_info(code_info: &blaze_symbolize_code_info) {
1558 let blaze_symbolize_code_info {
1559 dir,
1560 file,
1561 line,
1562 column,
1563 reserved: _,
1564 } = code_info;
1565
1566 let _x = touch_cstr(*dir);
1567 let _x = touch_cstr(*file);
1568 let _x = touch(line);
1569 let _x = touch(column);
1570 }
1571
1572 fn touch_syms(syms: *const blaze_syms) {
1574 let syms = unsafe { &*syms };
1575 for i in 0..syms.cnt {
1576 let sym = unsafe { &*syms.syms.as_slice().as_ptr().add(i) };
1577 let blaze_sym {
1578 name,
1579 module,
1580 addr,
1581 offset,
1582 size,
1583 code_info,
1584 inlined_cnt,
1585 inlined,
1586 reason,
1587 reserved: _,
1588 } = sym;
1589
1590 let () = touch_cstr(*name);
1591 let () = touch_cstr(*module);
1592 let _x = touch(addr);
1593 let _x = touch(offset);
1594 let _x = touch(size);
1595 let () = touch_code_info(code_info);
1596
1597 for j in 0..*inlined_cnt {
1598 let inlined_fn = unsafe { &*inlined.add(j) };
1599 let blaze_symbolize_inlined_fn {
1600 name,
1601 code_info,
1602 reserved: _,
1603 } = inlined_fn;
1604 let () = touch_cstr(*name);
1605 let () = touch_code_info(code_info);
1606 }
1607 let () = touch(reason);
1608 }
1609 }
1610
1611 let results = vec![];
1613 let syms = convert_symbolizedresults_to_c(results);
1614 assert!(!syms.is_null());
1615
1616 let () = touch_syms(syms);
1617 let () = unsafe { blaze_syms_free(syms) };
1618
1619 let results = vec![Symbolized::Sym(Sym {
1621 name: "test".into(),
1622 module: Some(Cow::from(OsStr::new("module"))),
1623 addr: 0x1337,
1624 offset: 0x1338,
1625 size: Some(42),
1626 code_info: Some(CodeInfo {
1627 dir: None,
1628 file: OsStr::new("a-file").into(),
1629 line: Some(42),
1630 column: Some(43),
1631 _non_exhaustive: (),
1632 }),
1633 inlined: vec![InlinedFn {
1634 name: "inlined_fn".into(),
1635 code_info: Some(CodeInfo {
1636 dir: Some(Path::new("/some/dir").into()),
1637 file: OsStr::new("another-file").into(),
1638 line: Some(42),
1639 column: Some(43),
1640 _non_exhaustive: (),
1641 }),
1642 _non_exhaustive: (),
1643 }]
1644 .into_boxed_slice(),
1645 _non_exhaustive: (),
1646 })];
1647 let syms = convert_symbolizedresults_to_c(results);
1648 assert!(!syms.is_null());
1649 let () = touch_syms(syms);
1650 let () = unsafe { blaze_syms_free(syms) };
1651
1652 let results = vec![
1654 Symbolized::Unknown(Reason::UnknownAddr),
1655 Symbolized::Sym(Sym {
1656 name: "test".into(),
1657 module: Some(Cow::from(OsStr::new("module"))),
1658 addr: 0x1337,
1659 offset: 0x1338,
1660 size: None,
1661 code_info: None,
1662 inlined: vec![InlinedFn {
1663 name: "inlined_fn".into(),
1664 code_info: None,
1665 _non_exhaustive: (),
1666 }]
1667 .into_boxed_slice(),
1668 _non_exhaustive: (),
1669 }),
1670 Symbolized::Unknown(Reason::InvalidFileOffset),
1671 ];
1672 let syms = convert_symbolizedresults_to_c(results);
1673 assert!(!syms.is_null());
1674 let () = touch_syms(syms);
1675 let () = unsafe { blaze_syms_free(syms) };
1676 }
1677
1678 #[tag(miri)]
1680 #[test]
1681 fn symbolizer_creation() {
1682 let symbolizer = blaze_symbolizer_new();
1683 let () = unsafe { blaze_symbolizer_free(symbolizer) };
1684 }
1685
1686 #[tag(miri)]
1689 #[test]
1690 fn symbolizer_creation_with_opts() {
1691 let opts = blaze_symbolizer_opts {
1692 demangle: true,
1693 ..Default::default()
1694 };
1695
1696 let symbolizer = unsafe { blaze_symbolizer_new_opts(&opts) };
1697 let () = unsafe { blaze_symbolizer_free(symbolizer) };
1698 }
1699
1700 #[test]
1703 fn symbolize_elf_dwarf_gsym() {
1704 fn test<F>(symbolize: F, has_code_info: bool)
1705 where
1706 F: FnOnce(*mut blaze_symbolizer, *const Addr, usize) -> *const blaze_syms,
1707 {
1708 let symbolizer = blaze_symbolizer_new();
1709 let addrs = [0x2000200];
1710 let result = symbolize(symbolizer, addrs.as_ptr(), addrs.len());
1711
1712 assert!(!result.is_null());
1713
1714 let result = unsafe { &*result };
1715 assert_eq!(result.cnt, 1);
1716 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
1717 let sym = &syms[0];
1718 assert_eq!(
1719 unsafe { CStr::from_ptr(sym.name) },
1720 CStr::from_bytes_with_nul(b"factorial\0").unwrap()
1721 );
1722 assert_eq!(sym.reason, blaze_symbolize_reason::SUCCESS);
1723 assert_eq!(sym.addr, 0x2000200);
1724 assert_eq!(sym.offset, 0);
1725 assert!(sym.size > 0);
1726
1727 if has_code_info {
1728 assert!(!sym.code_info.dir.is_null());
1729 assert!(!sym.code_info.file.is_null());
1730 assert_eq!(
1731 unsafe { CStr::from_ptr(sym.code_info.file) },
1732 CStr::from_bytes_with_nul(b"test-stable-addrs.c\0").unwrap()
1733 );
1734 assert_eq!(sym.code_info.line, 10);
1735 } else {
1736 assert!(sym.code_info.dir.is_null());
1737 assert!(sym.code_info.file.is_null());
1738 assert_eq!(sym.code_info.line, 0);
1739 }
1740
1741 let () = unsafe { blaze_syms_free(result) };
1742 let () = unsafe { blaze_symbolizer_free(symbolizer) };
1743 }
1744
1745 let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
1746 .join("..")
1747 .join("data")
1748 .join("test-stable-addrs-no-dwarf.bin");
1749 let path_c = CString::new(path.to_str().unwrap()).unwrap();
1750 let elf_src = blaze_symbolize_src_elf {
1751 path: path_c.as_ptr(),
1752 debug_syms: true,
1753 ..Default::default()
1754 };
1755
1756 let symbolize = |symbolizer, addrs, addr_cnt| unsafe {
1757 blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs, addr_cnt)
1758 };
1759 test(symbolize, false);
1760
1761 let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
1762 .join("..")
1763 .join("data")
1764 .join("test-stable-addrs-stripped-elf-with-dwarf.bin");
1765 let path_c = CString::new(path.to_str().unwrap()).unwrap();
1766 let elf_src = blaze_symbolize_src_elf {
1767 path: path_c.as_ptr(),
1768 debug_syms: true,
1769 ..Default::default()
1770 };
1771
1772 let symbolize = |symbolizer, addrs, addr_cnt| unsafe {
1773 blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs, addr_cnt)
1774 };
1775 test(symbolize, true);
1776
1777 let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
1778 .join("..")
1779 .join("data")
1780 .join("test-stable-addrs.gsym");
1781 let path_c = CString::new(path.to_str().unwrap()).unwrap();
1782 let gsym_src = blaze_symbolize_src_gsym_file {
1783 path: path_c.as_ptr(),
1784 ..Default::default()
1785 };
1786
1787 let symbolize = |symbolizer, addrs, addr_cnt| unsafe {
1788 blaze_symbolize_gsym_file_virt_offsets(symbolizer, &gsym_src, addrs, addr_cnt)
1789 };
1790 test(symbolize, true);
1791
1792 let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
1793 .join("..")
1794 .join("data")
1795 .join("test-stable-addrs.gsym");
1796 let data = read_file(path).unwrap();
1797 let gsym_src = blaze_symbolize_src_gsym_data {
1798 data: data.as_ptr(),
1799 data_len: data.len(),
1800 ..Default::default()
1801 };
1802
1803 let symbolize = |symbolizer, addrs, addr_cnt| unsafe {
1804 blaze_symbolize_gsym_data_virt_offsets(symbolizer, &gsym_src, addrs, addr_cnt)
1805 };
1806 test(symbolize, true);
1807 }
1808
1809
1810 #[test]
1812 #[cfg_attr(
1813 not(target_pointer_width = "64"),
1814 ignore = "loads 64 bit shared object"
1815 )]
1816 fn symbolize_elf_file_offset() {
1817 let test_so = Path::new(&env!("CARGO_MANIFEST_DIR"))
1818 .join("..")
1819 .join("data")
1820 .join("libtest-so.so")
1821 .canonicalize()
1822 .unwrap();
1823 let so_cstr = CString::new(test_so.clone().into_os_string().into_vec()).unwrap();
1824 let handle = unsafe { libc::dlopen(so_cstr.as_ptr(), libc::RTLD_NOW) };
1825 assert!(!handle.is_null());
1826
1827 let the_answer_addr = unsafe { libc::dlsym(handle, "the_answer\0".as_ptr().cast()) };
1828 assert!(!the_answer_addr.is_null());
1829
1830 let normalizer = normalize::Normalizer::new();
1831 let normalized = normalizer
1832 .normalize_user_addrs(Pid::Slf, [the_answer_addr as Addr].as_slice())
1833 .unwrap();
1834 assert_eq!(normalized.outputs.len(), 1);
1835 assert_eq!(normalized.meta.len(), 1);
1836
1837 let rc = unsafe { libc::dlclose(handle) };
1838 assert_eq!(rc, 0, "{}", Error::last_os_error());
1839
1840 let output = normalized.outputs[0];
1841 let meta = &normalized.meta[output.1];
1842 assert_eq!(meta.as_elf().unwrap().path, test_so);
1843
1844 let symbolizer = blaze_symbolizer_new();
1845 let elf_src = blaze_symbolize_src_elf {
1846 path: so_cstr.as_ptr(),
1847 ..Default::default()
1848 };
1849 let offsets = [output.0];
1850 let result = unsafe {
1851 blaze_symbolize_elf_file_offsets(
1852 symbolizer,
1853 &elf_src,
1854 offsets.as_slice().as_ptr(),
1855 offsets.len(),
1856 )
1857 };
1858 assert!(!result.is_null());
1859
1860 let result = unsafe { &*result };
1861 assert_eq!(result.cnt, 1);
1862
1863 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
1864 let sym = &syms[0];
1865 assert!(!sym.name.is_null());
1866 assert!(!sym.module.is_null());
1867 assert!(sym.size > 0);
1868 assert_eq!(
1869 unsafe { CStr::from_ptr(sym.name) },
1870 CStr::from_bytes_with_nul(b"the_answer\0").unwrap()
1871 );
1872
1873 let () = unsafe { blaze_syms_free(result) };
1874 let () = unsafe { blaze_symbolizer_free(symbolizer) };
1875 }
1876
1877 #[tag(other_os)]
1880 #[test]
1881 fn symbolize_elf_cached() {
1882 let dir = tempdir().unwrap();
1883 let path__ = Path::new(&env!("CARGO_MANIFEST_DIR"))
1884 .join("..")
1885 .join("data")
1886 .join("test-stable-addrs.bin");
1887 let path = dir.path().join("test-stable-addrs-temporary.bin");
1888 let _count = copy(&path__, &path).unwrap();
1889
1890 let symbolizer = blaze_symbolizer_new();
1891
1892 let path_c = CString::new(path.to_str().unwrap()).unwrap();
1893 let cache = blaze_cache_src_elf {
1894 path: path_c.as_ptr(),
1895 ..Default::default()
1896 };
1897 let () = unsafe { blaze_symbolize_cache_elf(symbolizer, &cache) };
1898 assert_eq!(blaze_err_last(), blaze_err::OK);
1899
1900 let () = remove_file(&path).unwrap();
1901
1902 let src = blaze_symbolize_src_elf {
1903 path: path_c.as_ptr(),
1904 debug_syms: false,
1905 ..Default::default()
1906 };
1907 let addrs = [0x2000200];
1908 let result = unsafe {
1909 blaze_symbolize_elf_virt_offsets(symbolizer, &src, addrs.as_ptr(), addrs.len())
1910 };
1911 assert!(!result.is_null());
1912
1913 let result = unsafe { &*result };
1914 assert_eq!(result.cnt, 1);
1915 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
1916 let sym = &syms[0];
1917
1918 assert_eq!(
1919 unsafe { CStr::from_ptr(sym.name) },
1920 CStr::from_bytes_with_nul(b"factorial\0").unwrap()
1921 );
1922 assert_eq!(sym.reason, blaze_symbolize_reason::SUCCESS);
1923 assert_eq!(sym.addr, 0x2000200);
1924
1925 let () = unsafe { blaze_syms_free(result) };
1926 let () = unsafe { blaze_symbolizer_free(symbolizer) };
1927 }
1928
1929 #[test]
1932 fn symbolize_dwarf_demangle() {
1933 fn test(path: &Path, addr: Addr) -> Result<(), ()> {
1934 let opts = blaze_symbolizer_opts {
1935 code_info: true,
1936 inlined_fns: true,
1937 ..Default::default()
1938 };
1939
1940 let path_c = CString::new(path.to_str().unwrap()).unwrap();
1941 let elf_src = blaze_symbolize_src_elf {
1942 path: path_c.as_ptr(),
1943 debug_syms: true,
1944 ..Default::default()
1945 };
1946
1947 let symbolizer = unsafe { blaze_symbolizer_new_opts(&opts) };
1948 let addrs = [addr];
1949 let result = unsafe {
1950 blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs.as_ptr(), addrs.len())
1951 };
1952 assert!(!result.is_null());
1953
1954 let result = unsafe { &*result };
1955 assert_eq!(result.cnt, 1);
1956 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
1957 let sym = &syms[0];
1958 let name = unsafe { CStr::from_ptr(sym.name) };
1959 assert!(
1960 name.to_str().unwrap().contains("test13test_function"),
1961 "{name:?}"
1962 );
1963
1964 if sym.inlined_cnt == 0 {
1965 let () = unsafe { blaze_syms_free(result) };
1966 let () = unsafe { blaze_symbolizer_free(symbolizer) };
1967 return Err(())
1968 }
1969
1970 assert_eq!(sym.inlined_cnt, 1);
1971 let name = unsafe { CStr::from_ptr((*sym.inlined).name) };
1972 assert!(
1973 name.to_str().unwrap().contains("test12inlined_call"),
1974 "{name:?}"
1975 );
1976
1977 let () = unsafe { blaze_syms_free(result) };
1978 let () = unsafe { blaze_symbolizer_free(symbolizer) };
1979
1980 let opts = blaze_symbolizer_opts {
1982 code_info: true,
1983 inlined_fns: true,
1984 demangle: true,
1985 ..Default::default()
1986 };
1987
1988 let symbolizer = unsafe { blaze_symbolizer_new_opts(&opts) };
1989 let addrs = [addr];
1990 let result = unsafe {
1991 blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs.as_ptr(), addrs.len())
1992 };
1993 assert!(!result.is_null());
1994
1995 let result = unsafe { &*result };
1996 assert_eq!(result.cnt, 1);
1997 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
1998 let sym = &syms[0];
1999 assert_eq!(
2000 unsafe { CStr::from_ptr(sym.name) },
2001 CStr::from_bytes_with_nul(b"test::test_function\0").unwrap()
2002 );
2003 assert_eq!(unsafe { CStr::from_ptr(sym.module) }, &*path_c);
2004
2005 assert_eq!(sym.inlined_cnt, 1);
2006 assert_eq!(
2007 unsafe { CStr::from_ptr((*sym.inlined).name) },
2008 CStr::from_bytes_with_nul(b"test::inlined_call\0").unwrap()
2009 );
2010
2011 let () = unsafe { blaze_syms_free(result) };
2012 let () = unsafe { blaze_symbolizer_free(symbolizer) };
2013 Ok(())
2014 }
2015
2016 let test_dwarf = Path::new(&env!("CARGO_MANIFEST_DIR"))
2017 .join("..")
2018 .join("data")
2019 .join("test-rs.bin");
2020 let elf = inspect::source::Elf::new(&test_dwarf);
2021 let src = inspect::source::Source::Elf(elf);
2022
2023 let inspector = inspect::Inspector::new();
2024 let results = inspector
2025 .lookup(&src, &["_RNvCs69hjMPjVIJK_4test13test_function"])
2026 .unwrap()
2027 .into_iter()
2028 .flatten()
2029 .collect::<Vec<_>>();
2030 assert!(!results.is_empty());
2031
2032 let addr = results[0].addr;
2033 let src = Source::Elf(Elf::new(&test_dwarf));
2034 let symbolizer = Symbolizer::builder().enable_demangling(false).build();
2035 let result = symbolizer
2036 .symbolize_single(&src, Input::VirtOffset(addr))
2037 .unwrap()
2038 .into_sym()
2039 .unwrap();
2040
2041 let addr = result.addr;
2042 let size = result.size.unwrap() as u64;
2043 for inst_addr in addr..addr + size {
2044 if test(&test_dwarf, inst_addr).is_ok() {
2045 return
2046 }
2047 }
2048
2049 panic!("failed to find inlined function call");
2050 }
2051
2052 #[test]
2054 fn symbolize_in_process() {
2055 let process_src = blaze_symbolize_src_process {
2056 pid: 0,
2057 debug_syms: true,
2058 perf_map: true,
2059 ..Default::default()
2060 };
2061
2062 let symbolizer = blaze_symbolizer_new();
2063 let addrs = [blaze_symbolizer_new as Addr];
2064 let result = unsafe {
2065 blaze_symbolize_process_abs_addrs(symbolizer, &process_src, addrs.as_ptr(), addrs.len())
2066 };
2067
2068 assert!(!result.is_null());
2069
2070 let result = unsafe { &*result };
2071 assert_eq!(result.cnt, 1);
2072 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
2073 let sym = &syms[0];
2074 assert_eq!(
2075 unsafe { CStr::from_ptr(sym.name) },
2076 CStr::from_bytes_with_nul(b"blaze_symbolizer_new\0").unwrap()
2077 );
2078
2079 let () = unsafe { blaze_syms_free(result) };
2080 let () = unsafe { blaze_symbolizer_free(symbolizer) };
2081 }
2082
2083 #[test]
2086 fn symbolize_in_process_cached() {
2087 let symbolizer = blaze_symbolizer_new();
2088 let cache = blaze_cache_src_process {
2089 pid: 0,
2090 cache_vmas: true,
2091 ..Default::default()
2092 };
2093 let () = unsafe { blaze_symbolize_cache_process(symbolizer, &cache) };
2094 assert_eq!(blaze_err_last(), blaze_err::OK);
2095
2096 let src = blaze_symbolize_src_process {
2097 pid: 0,
2098 debug_syms: true,
2099 perf_map: true,
2100 ..Default::default()
2101 };
2102 let addrs = [blaze_symbolizer_new as Addr];
2103 let result = unsafe {
2104 blaze_symbolize_process_abs_addrs(symbolizer, &src, addrs.as_ptr(), addrs.len())
2105 };
2106
2107 assert!(!result.is_null());
2108
2109 let result = unsafe { &*result };
2110 assert_eq!(result.cnt, 1);
2111 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
2112 let sym = &syms[0];
2113 assert_eq!(
2114 unsafe { CStr::from_ptr(sym.name) },
2115 CStr::from_bytes_with_nul(b"blaze_symbolizer_new\0").unwrap()
2116 );
2117
2118 let () = unsafe { blaze_syms_free(result) };
2119 let () = unsafe { blaze_symbolizer_free(symbolizer) };
2120 }
2121
2122 #[test]
2125 fn symbolize_in_kernel() {
2126 let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
2127 .join("..")
2128 .join("data")
2129 .join("kallsyms");
2130 let path_c = CString::new(path.to_str().unwrap()).unwrap();
2131 let src = blaze_symbolize_src_kernel {
2132 kallsyms: path_c.as_ptr(),
2133 vmlinux: b"\0" as *const _ as *const c_char,
2134 ..Default::default()
2135 };
2136
2137 let symbolizer = blaze_symbolizer_new();
2138 let addrs = [0xc080a470];
2139 let result = unsafe {
2140 blaze_symbolize_kernel_abs_addrs(symbolizer, &src, addrs.as_ptr(), addrs.len())
2141 };
2142
2143 assert!(!result.is_null());
2144
2145 let result = unsafe { &*result };
2146 assert_eq!(result.cnt, 1);
2147 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
2148 let sym = &syms[0];
2149 assert_eq!(
2150 unsafe { CStr::from_ptr(sym.name) },
2151 CStr::from_bytes_with_nul(b"init_task\0").unwrap()
2152 );
2153 assert_eq!(sym.module, ptr::null_mut());
2154
2155 let () = unsafe { blaze_syms_free(result) };
2156 let () = unsafe { blaze_symbolizer_free(symbolizer) };
2157 }
2158
2159 #[test]
2162 fn symbolize_elf_non_existent() {
2163 let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
2164 .join("..")
2165 .join("data")
2166 .join("does-not-actually-exist.bin");
2167 let path_c = CString::new(path.to_str().unwrap()).unwrap();
2168 let elf_src = blaze_symbolize_src_elf {
2169 path: path_c.as_ptr(),
2170 debug_syms: true,
2171 ..Default::default()
2172 };
2173
2174 let symbolizer = blaze_symbolizer_new();
2175 let addrs = [blaze_symbolizer_new as Addr];
2176 let result = unsafe {
2177 blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs.as_ptr(), addrs.len())
2178 };
2179 assert!(result.is_null());
2180 assert_eq!(blaze_err_last(), blaze_err::NOT_FOUND);
2181
2182 let () = unsafe { blaze_syms_free(result) };
2183 let () = unsafe { blaze_symbolizer_free(symbolizer) };
2184 }
2185
2186 #[test]
2188 fn symbolize_no_debug_dirs() {
2189 let dir = tempdir().unwrap();
2190 let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
2191 .join("..")
2192 .join("data")
2193 .join("test-stable-addrs-stripped-with-link.bin");
2194 let dst = dir.path().join("test-stable-addrs-stripped-with-link.bin");
2195 let _count = copy(path, &dst).unwrap();
2196
2197 let debug_dirs = [];
2198 let opts = blaze_symbolizer_opts {
2199 debug_dirs: debug_dirs.as_ptr(),
2200 debug_dirs_len: debug_dirs.len(),
2201 code_info: true,
2202 inlined_fns: true,
2203 demangle: true,
2204 ..Default::default()
2205 };
2206 let symbolizer = unsafe { blaze_symbolizer_new_opts(&opts) };
2207
2208 let path_c = CString::new(dst.to_str().unwrap()).unwrap();
2209 let elf_src = blaze_symbolize_src_elf {
2210 path: path_c.as_ptr(),
2211 debug_syms: true,
2212 ..Default::default()
2213 };
2214 let addrs = [0x2000200];
2215 let result = unsafe {
2216 blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs.as_ptr(), addrs.len())
2217 };
2218 assert!(!result.is_null());
2219
2220 let result = unsafe { &*result };
2221 assert_eq!(result.cnt, 1);
2222 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
2223 let sym = &syms[0];
2224 assert_eq!(sym.name, ptr::null());
2227 assert_eq!(sym.module, ptr::null());
2228 assert_eq!(sym.reason, blaze_symbolize_reason::MISSING_SYMS);
2229
2230 let () = unsafe { blaze_syms_free(result) };
2231 let () = unsafe { blaze_symbolizer_free(symbolizer) };
2232 }
2233
2234 #[test]
2236 fn symbolize_configurable_debug_dirs() {
2237 let debug_dir1 = tempdir().unwrap();
2238 let debug_dir1_c = CString::new(debug_dir1.path().to_str().unwrap()).unwrap();
2239 let debug_dir2 = tempdir().unwrap();
2240 let debug_dir2_c = CString::new(debug_dir2.path().to_str().unwrap()).unwrap();
2241
2242 let src = Path::new(&env!("CARGO_MANIFEST_DIR"))
2243 .join("..")
2244 .join("data")
2245 .join("test-stable-addrs-dwarf-only.dbg");
2246 let dst = debug_dir2.path().join("test-stable-addrs-dwarf-only.dbg");
2247 let _count = copy(src, dst).unwrap();
2248
2249 let debug_dirs = [debug_dir1_c.as_ptr(), debug_dir2_c.as_ptr()];
2250 let opts = blaze_symbolizer_opts {
2251 debug_dirs: debug_dirs.as_ptr(),
2252 debug_dirs_len: debug_dirs.len(),
2253 code_info: true,
2254 inlined_fns: true,
2255 demangle: true,
2256 ..Default::default()
2257 };
2258 let symbolizer = unsafe { blaze_symbolizer_new_opts(&opts) };
2259
2260 let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
2261 .join("..")
2262 .join("data")
2263 .join("test-stable-addrs-stripped-with-link.bin");
2264 let path_c = CString::new(path.to_str().unwrap()).unwrap();
2265 let elf_src = blaze_symbolize_src_elf {
2266 path: path_c.as_ptr(),
2267 debug_syms: true,
2268 ..Default::default()
2269 };
2270 let addrs = [0x2000200];
2271 let result = unsafe {
2272 blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs.as_ptr(), addrs.len())
2273 };
2274 assert!(!result.is_null());
2275
2276 let result = unsafe { &*result };
2277 assert_eq!(result.cnt, 1);
2278 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
2279 let sym = &syms[0];
2280 let name = unsafe { CStr::from_ptr(sym.name) };
2281 assert_eq!(name.to_str().unwrap(), "factorial");
2282 let module = unsafe { CStr::from_ptr(sym.module) };
2283 assert!(
2284 module
2285 .to_str()
2286 .unwrap()
2287 .ends_with("test-stable-addrs-stripped-with-link.bin"),
2288 "{module:?}"
2289 );
2290
2291 let () = unsafe { blaze_syms_free(result) };
2292 let () = unsafe { blaze_symbolizer_free(symbolizer) };
2293 }
2294}