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