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::InlinedFn;
24use blazesym::symbolize::Input;
25use blazesym::symbolize::Reason;
26use blazesym::symbolize::Sym;
27use blazesym::symbolize::Symbolized;
28use blazesym::symbolize::Symbolizer;
29use blazesym::Addr;
30use blazesym::MaybeDefault;
31
32use crate::blaze_err;
33#[cfg(doc)]
34use crate::blaze_err_last;
35use crate::set_last_err;
36use crate::util::slice_from_aligned_user_array;
37use crate::util::slice_from_user_array;
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 reserved: [u8; 17],
319}
320
321impl Default for blaze_symbolize_src_process {
322 fn default() -> Self {
323 Self {
324 type_size: mem::size_of::<Self>(),
325 pid: 0,
326 debug_syms: false,
327 perf_map: false,
328 no_map_files: false,
329 reserved: [0; 17],
330 }
331 }
332}
333
334impl From<blaze_symbolize_src_process> for Process {
335 fn from(process: blaze_symbolize_src_process) -> Self {
336 let blaze_symbolize_src_process {
337 type_size: _,
338 pid,
339 debug_syms,
340 perf_map,
341 no_map_files,
342 reserved: _,
343 } = process;
344 Self {
345 pid: pid.into(),
346 debug_syms,
347 perf_map,
348 map_files: !no_map_files,
349 _non_exhaustive: (),
350 }
351 }
352}
353
354
355#[repr(C)]
357#[derive(Debug)]
358pub struct blaze_symbolize_src_gsym_data {
359 pub type_size: usize,
364 pub data: *const u8,
366 pub data_len: usize,
368 pub reserved: [u8; 16],
371}
372
373impl Default for blaze_symbolize_src_gsym_data {
374 fn default() -> Self {
375 Self {
376 type_size: mem::size_of::<Self>(),
377 data: ptr::null(),
378 data_len: 0,
379 reserved: [0; 16],
380 }
381 }
382}
383
384impl From<blaze_symbolize_src_gsym_data> for GsymData<'_> {
385 fn from(gsym: blaze_symbolize_src_gsym_data) -> Self {
386 let blaze_symbolize_src_gsym_data {
387 type_size: _,
388 data,
389 data_len,
390 reserved: _,
391 } = gsym;
392 Self {
393 data: unsafe { slice_from_aligned_user_array(data, data_len) },
394 _non_exhaustive: (),
395 }
396 }
397}
398
399
400#[repr(C)]
402#[derive(Debug)]
403pub struct blaze_symbolize_src_gsym_file {
404 pub type_size: usize,
409 pub path: *const c_char,
411 pub reserved: [u8; 16],
414}
415
416impl Default for blaze_symbolize_src_gsym_file {
417 fn default() -> Self {
418 Self {
419 type_size: mem::size_of::<Self>(),
420 path: ptr::null(),
421 reserved: [0; 16],
422 }
423 }
424}
425
426impl From<blaze_symbolize_src_gsym_file> for GsymFile {
427 fn from(gsym: blaze_symbolize_src_gsym_file) -> Self {
428 let blaze_symbolize_src_gsym_file {
429 type_size: _,
430 path,
431 reserved: _,
432 } = gsym;
433 Self {
434 path: unsafe { from_cstr(path) },
435 _non_exhaustive: (),
436 }
437 }
438}
439
440
441pub type blaze_symbolizer = Symbolizer;
446
447
448#[repr(transparent)]
454#[derive(Copy, Clone, Debug, PartialEq)]
455pub struct blaze_symbolize_reason(u8);
456
457impl blaze_symbolize_reason {
458 pub const SUCCESS: blaze_symbolize_reason = blaze_symbolize_reason(0);
460 pub const UNMAPPED: blaze_symbolize_reason = blaze_symbolize_reason(1);
463 pub const INVALID_FILE_OFFSET: blaze_symbolize_reason = blaze_symbolize_reason(2);
465 pub const MISSING_COMPONENT: blaze_symbolize_reason = blaze_symbolize_reason(3);
469 pub const MISSING_SYMS: blaze_symbolize_reason = blaze_symbolize_reason(4);
471 pub const UNKNOWN_ADDR: blaze_symbolize_reason = blaze_symbolize_reason(5);
473 pub const UNSUPPORTED: blaze_symbolize_reason = blaze_symbolize_reason(6);
475}
476
477
478impl From<Reason> for blaze_symbolize_reason {
479 fn from(reason: Reason) -> Self {
480 match reason {
481 Reason::Unmapped => blaze_symbolize_reason::UNMAPPED,
482 Reason::InvalidFileOffset => blaze_symbolize_reason::INVALID_FILE_OFFSET,
483 Reason::MissingComponent => blaze_symbolize_reason::MISSING_COMPONENT,
484 Reason::MissingSyms => blaze_symbolize_reason::MISSING_SYMS,
485 Reason::Unsupported => blaze_symbolize_reason::UNSUPPORTED,
486 Reason::UnknownAddr => blaze_symbolize_reason::UNKNOWN_ADDR,
487 _ => unreachable!(),
488 }
489 }
490}
491
492
493#[no_mangle]
496pub extern "C" fn blaze_symbolize_reason_str(err: blaze_symbolize_reason) -> *const c_char {
497 match err {
498 blaze_symbolize_reason::SUCCESS => b"success\0".as_ptr().cast(),
499 blaze_symbolize_reason::UNMAPPED => Reason::Unmapped.as_bytes().as_ptr().cast(),
500 blaze_symbolize_reason::INVALID_FILE_OFFSET => {
501 Reason::InvalidFileOffset.as_bytes().as_ptr().cast()
502 }
503 blaze_symbolize_reason::MISSING_COMPONENT => {
504 Reason::MissingComponent.as_bytes().as_ptr().cast()
505 }
506 blaze_symbolize_reason::MISSING_SYMS => Reason::MissingSyms.as_bytes().as_ptr().cast(),
507 blaze_symbolize_reason::UNKNOWN_ADDR => Reason::UnknownAddr.as_bytes().as_ptr().cast(),
508 blaze_symbolize_reason::UNSUPPORTED => Reason::Unsupported.as_bytes().as_ptr().cast(),
509 _ => b"unknown reason\0".as_ptr().cast(),
510 }
511}
512
513
514#[repr(C)]
516#[derive(Debug)]
517pub struct blaze_symbolize_code_info {
518 pub dir: *const c_char,
522 pub file: *const c_char,
526 pub line: u32,
529 pub column: u16,
532 pub reserved: [u8; 10],
534}
535
536
537#[repr(C)]
539#[derive(Debug)]
540pub struct blaze_symbolize_inlined_fn {
541 pub name: *const c_char,
543 pub code_info: blaze_symbolize_code_info,
545 pub reserved: [u8; 8],
547}
548
549
550#[repr(C)]
555#[derive(Debug)]
556pub struct blaze_sym {
557 pub name: *const c_char,
563 pub module: *const c_char,
572 pub addr: Addr,
578 pub offset: usize,
588 pub size: isize,
595 pub code_info: blaze_symbolize_code_info,
597 pub inlined_cnt: usize,
599 pub inlined: *const blaze_symbolize_inlined_fn,
601 pub reason: blaze_symbolize_reason,
604 pub reserved: [u8; 15],
606}
607
608#[repr(C)]
613#[derive(Debug)]
614pub struct blaze_syms {
615 pub cnt: usize,
617 pub syms: [blaze_sym; 0],
622}
623
624pub(crate) unsafe fn from_cstr(cstr: *const c_char) -> PathBuf {
629 Path::new(OsStr::from_bytes(
630 unsafe { CStr::from_ptr(cstr) }.to_bytes(),
631 ))
632 .to_path_buf()
633}
634
635
636#[repr(C)]
638#[derive(Debug)]
639pub struct blaze_symbolizer_opts {
640 pub type_size: usize,
645 pub debug_dirs: *const *const c_char,
656 pub debug_dirs_len: usize,
658 pub auto_reload: bool,
662 pub code_info: bool,
666 pub inlined_fns: bool,
668 pub demangle: bool,
674 pub reserved: [u8; 20],
677}
678
679impl Default for blaze_symbolizer_opts {
680 fn default() -> Self {
681 Self {
682 type_size: mem::size_of::<Self>(),
683 debug_dirs: ptr::null(),
684 debug_dirs_len: 0,
685 auto_reload: false,
686 code_info: false,
687 inlined_fns: false,
688 demangle: false,
689 reserved: [0; 20],
690 }
691 }
692}
693
694
695#[no_mangle]
708pub extern "C" fn blaze_symbolizer_new() -> *mut blaze_symbolizer {
709 let symbolizer = Symbolizer::new();
710 let symbolizer_box = Box::new(symbolizer);
711 let () = set_last_err(blaze_err::OK);
712 Box::into_raw(symbolizer_box)
713}
714
715#[no_mangle]
728pub unsafe extern "C" fn blaze_symbolizer_new_opts(
729 opts: *const blaze_symbolizer_opts,
730) -> *mut blaze_symbolizer {
731 if !input_zeroed!(opts, blaze_symbolizer_opts) {
732 let () = set_last_err(blaze_err::INVALID_INPUT);
733 return ptr::null_mut()
734 }
735 let opts = input_sanitize!(opts, blaze_symbolizer_opts);
736
737 let blaze_symbolizer_opts {
738 type_size: _,
739 debug_dirs,
740 debug_dirs_len: _debug_dirs_len,
741 auto_reload,
742 code_info,
743 inlined_fns,
744 demangle,
745 reserved: _,
746 } = opts;
747
748 let builder = Symbolizer::builder()
749 .enable_auto_reload(auto_reload)
750 .enable_code_info(code_info)
751 .enable_inlined_fns(inlined_fns)
752 .enable_demangling(demangle);
753
754 let builder = if debug_dirs.is_null() {
755 builder
756 } else {
757 #[cfg(feature = "dwarf")]
758 {
759 let slice = unsafe { slice_from_user_array(debug_dirs, _debug_dirs_len) };
762 let iter = slice.iter().map(|cstr| {
763 Path::new(OsStr::from_bytes(
764 unsafe { CStr::from_ptr(cstr.cast()) }.to_bytes(),
767 ))
768 });
769
770 builder.set_debug_dirs(Some(iter))
771 }
772
773 #[cfg(not(feature = "dwarf"))]
774 {
775 builder
776 }
777 };
778
779 let symbolizer = builder.build();
780 let symbolizer_box = Box::new(symbolizer);
781 let () = set_last_err(blaze_err::OK);
782 Box::into_raw(symbolizer_box)
783}
784
785#[no_mangle]
791pub unsafe extern "C" fn blaze_symbolizer_free(symbolizer: *mut blaze_symbolizer) {
792 if !symbolizer.is_null() {
793 drop(unsafe { Box::from_raw(symbolizer) });
794 }
795}
796
797
798#[no_mangle]
811pub unsafe extern "C" fn blaze_symbolize_cache_elf(
812 symbolizer: *mut blaze_symbolizer,
813 cache: *const blaze_cache_src_elf,
814) {
815 if !input_zeroed!(cache, blaze_cache_src_elf) {
816 let () = set_last_err(blaze_err::INVALID_INPUT);
817 return
818 }
819 let cache = input_sanitize!(cache, blaze_cache_src_elf);
820 let cache = cache::Cache::from(cache::Elf::from(cache));
821
822 let symbolizer = unsafe { &*symbolizer };
824 let result = symbolizer.cache(&cache);
825 let err = result
826 .map(|()| blaze_err::OK)
827 .unwrap_or_else(|err| err.kind().into());
828 let () = set_last_err(err);
829}
830
831
832#[no_mangle]
852pub unsafe extern "C" fn blaze_symbolize_cache_process(
853 symbolizer: *mut blaze_symbolizer,
854 cache: *const blaze_cache_src_process,
855) {
856 if !input_zeroed!(cache, blaze_cache_src_process) {
857 let () = set_last_err(blaze_err::INVALID_INPUT);
858 return
859 }
860 let cache = input_sanitize!(cache, blaze_cache_src_process);
861 let cache = cache::Cache::from(cache::Process::from(cache));
862
863 let symbolizer = unsafe { &*symbolizer };
865 let result = symbolizer.cache(&cache);
866 let err = result
867 .map(|()| blaze_err::OK)
868 .unwrap_or_else(|err| err.kind().into());
869 let () = set_last_err(err);
870}
871
872fn code_info_strtab_size(code_info: &Option<CodeInfo>) -> usize {
873 code_info
874 .as_ref()
875 .and_then(|info| info.dir.as_ref().map(|d| d.as_os_str().len() + 1))
876 .unwrap_or(0)
877 + code_info
878 .as_ref()
879 .map(|info| info.file.len() + 1)
880 .unwrap_or(0)
881}
882
883fn inlined_fn_strtab_size(inlined_fn: &InlinedFn) -> usize {
884 inlined_fn.name.len() + 1 + code_info_strtab_size(&inlined_fn.code_info)
885}
886
887fn sym_strtab_size(sym: &Sym) -> usize {
888 sym.name.len()
889 + 1
890 + sym
891 .module
892 .as_deref()
893 .map(|module| module.len() + 1)
894 .unwrap_or(0)
895 + code_info_strtab_size(&sym.code_info)
896 + sym
897 .inlined
898 .iter()
899 .map(inlined_fn_strtab_size)
900 .sum::<usize>()
901}
902
903fn convert_code_info(
904 code_info_in: &Option<CodeInfo>,
905 code_info_out: &mut blaze_symbolize_code_info,
906 mut make_cstr: impl FnMut(&OsStr) -> *mut c_char,
907) {
908 code_info_out.dir = code_info_in
909 .as_ref()
910 .and_then(|info| info.dir.as_ref().map(|d| make_cstr(d.as_os_str())))
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))
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
926fn convert_symbolizedresults_to_c(results: Vec<Symbolized>) -> *const blaze_syms {
931 let (strtab_size, inlined_fn_cnt) = results.iter().fold((0, 0), |acc, sym| match sym {
934 Symbolized::Sym(sym) => (acc.0 + sym_strtab_size(sym), acc.1 + sym.inlined.len()),
935 Symbolized::Unknown(..) => acc,
936 });
937
938 let buf_size = mem::size_of::<u64>()
939 + strtab_size
940 + mem::size_of::<blaze_syms>()
941 + mem::size_of::<blaze_sym>() * results.len()
942 + mem::size_of::<blaze_symbolize_inlined_fn>() * inlined_fn_cnt;
943 let raw_buf_with_sz = unsafe { alloc(Layout::from_size_align(buf_size, 8).unwrap()) };
944 if raw_buf_with_sz.is_null() {
945 return ptr::null()
946 }
947
948 unsafe { *(raw_buf_with_sz as *mut u64) = buf_size as u64 };
950
951 let raw_buf = unsafe { raw_buf_with_sz.add(mem::size_of::<u64>()) };
952
953 let syms_ptr = raw_buf as *mut blaze_syms;
954 let mut syms_last = unsafe { (*syms_ptr).syms.as_mut_slice().as_mut_ptr() };
955 let mut inlined_last = unsafe {
956 raw_buf.add(mem::size_of::<blaze_syms>() + mem::size_of::<blaze_sym>() * results.len())
957 } as *mut blaze_symbolize_inlined_fn;
958 let mut cstr_last = unsafe {
959 raw_buf.add(
960 mem::size_of::<blaze_syms>()
961 + mem::size_of::<blaze_sym>() * results.len()
962 + mem::size_of::<blaze_symbolize_inlined_fn>() * inlined_fn_cnt,
963 )
964 } as *mut c_char;
965
966 let mut make_cstr = |src: &OsStr| {
967 let cstr = cstr_last;
968 unsafe { ptr::copy_nonoverlapping(src.as_bytes().as_ptr(), cstr as *mut u8, src.len()) };
969 unsafe { *cstr.add(src.len()) = 0 };
970 cstr_last = unsafe { cstr_last.add(src.len() + 1) };
971
972 cstr
973 };
974
975 unsafe { (*syms_ptr).cnt = results.len() };
976
977 for sym in results {
979 match sym {
980 Symbolized::Sym(sym) => {
981 let sym_ref = unsafe { &mut *syms_last };
982 let name_ptr = make_cstr(OsStr::new(sym.name.as_ref()));
983 let module_ptr = sym
984 .module
985 .as_deref()
986 .map(&mut make_cstr)
987 .unwrap_or(ptr::null_mut());
988
989 sym_ref.name = name_ptr;
990 sym_ref.module = module_ptr;
991 sym_ref.addr = sym.addr;
992 sym_ref.offset = sym.offset;
993 sym_ref.size = sym
994 .size
995 .map(|size| isize::try_from(size).unwrap_or(isize::MAX))
996 .unwrap_or(-1);
997 convert_code_info(&sym.code_info, &mut sym_ref.code_info, &mut make_cstr);
998 sym_ref.inlined_cnt = sym.inlined.len();
999 sym_ref.inlined = inlined_last;
1000 sym_ref.reason = blaze_symbolize_reason::SUCCESS;
1001
1002 for inlined in sym.inlined.iter() {
1003 let inlined_ref = unsafe { &mut *inlined_last };
1004
1005 let name_ptr = make_cstr(OsStr::new(inlined.name.as_ref()));
1006 inlined_ref.name = name_ptr;
1007 convert_code_info(
1008 &inlined.code_info,
1009 &mut inlined_ref.code_info,
1010 &mut make_cstr,
1011 );
1012
1013 inlined_last = unsafe { inlined_last.add(1) };
1014 }
1015 }
1016 Symbolized::Unknown(reason) => {
1017 let () = unsafe { syms_last.write_bytes(0, 1) };
1022 let sym_ref = unsafe { &mut *syms_last };
1023 sym_ref.reason = reason.into();
1024 }
1025 }
1026
1027 syms_last = unsafe { syms_last.add(1) };
1028 }
1029
1030 syms_ptr
1031}
1032
1033unsafe fn blaze_symbolize_impl(
1034 symbolizer: *mut blaze_symbolizer,
1035 src: Source<'_>,
1036 inputs: Input<*const u64>,
1037 input_cnt: usize,
1038) -> *const blaze_syms {
1039 let symbolizer = unsafe { &*symbolizer };
1041 let addrs = unsafe { slice_from_user_array(*inputs.as_inner_ref(), input_cnt) };
1044
1045 let input = match inputs {
1046 Input::AbsAddr(..) => Input::AbsAddr(addrs.deref()),
1047 Input::VirtOffset(..) => Input::VirtOffset(addrs.deref()),
1048 Input::FileOffset(..) => Input::FileOffset(addrs.deref()),
1049 };
1050
1051 let result = symbolizer.symbolize(&src, input);
1052 match result {
1053 Ok(results) if results.is_empty() => {
1054 let () = set_last_err(blaze_err::OK);
1055 ptr::null()
1056 }
1057 Ok(results) => {
1058 let result = convert_symbolizedresults_to_c(results);
1059 if result.is_null() {
1060 let () = set_last_err(blaze_err::OUT_OF_MEMORY);
1061 } else {
1062 let () = set_last_err(blaze_err::OK);
1063 }
1064 result
1065 }
1066 Err(err) => {
1067 let () = set_last_err(err.kind().into());
1068 ptr::null_mut()
1069 }
1070 }
1071}
1072
1073
1074#[no_mangle]
1090pub unsafe extern "C" fn blaze_symbolize_process_abs_addrs(
1091 symbolizer: *mut blaze_symbolizer,
1092 src: *const blaze_symbolize_src_process,
1093 abs_addrs: *const Addr,
1094 abs_addr_cnt: usize,
1095) -> *const blaze_syms {
1096 if !input_zeroed!(src, blaze_symbolize_src_process) {
1097 let () = set_last_err(blaze_err::INVALID_INPUT);
1098 return ptr::null()
1099 }
1100 let src = input_sanitize!(src, blaze_symbolize_src_process);
1101 let src = Source::from(Process::from(src));
1102
1103 unsafe { blaze_symbolize_impl(symbolizer, src, Input::AbsAddr(abs_addrs), abs_addr_cnt) }
1104}
1105
1106
1107#[no_mangle]
1123pub unsafe extern "C" fn blaze_symbolize_kernel_abs_addrs(
1124 symbolizer: *mut blaze_symbolizer,
1125 src: *const blaze_symbolize_src_kernel,
1126 abs_addrs: *const Addr,
1127 abs_addr_cnt: usize,
1128) -> *const blaze_syms {
1129 if !input_zeroed!(src, blaze_symbolize_src_kernel) {
1130 let () = set_last_err(blaze_err::INVALID_INPUT);
1131 return ptr::null()
1132 }
1133 let src = input_sanitize!(src, blaze_symbolize_src_kernel);
1134 let src = Source::from(Kernel::from(src));
1135
1136 unsafe { blaze_symbolize_impl(symbolizer, src, Input::AbsAddr(abs_addrs), abs_addr_cnt) }
1137}
1138
1139
1140#[no_mangle]
1156pub unsafe extern "C" fn blaze_symbolize_elf_virt_offsets(
1157 symbolizer: *mut blaze_symbolizer,
1158 src: *const blaze_symbolize_src_elf,
1159 virt_offsets: *const Addr,
1160 virt_offset_cnt: usize,
1161) -> *const blaze_syms {
1162 if !input_zeroed!(src, blaze_symbolize_src_elf) {
1163 let () = set_last_err(blaze_err::INVALID_INPUT);
1164 return ptr::null()
1165 }
1166 let src = input_sanitize!(src, blaze_symbolize_src_elf);
1167 let src = Source::from(Elf::from(src));
1168
1169 unsafe {
1170 blaze_symbolize_impl(
1171 symbolizer,
1172 src,
1173 Input::VirtOffset(virt_offsets),
1174 virt_offset_cnt,
1175 )
1176 }
1177}
1178
1179#[no_mangle]
1195pub unsafe extern "C" fn blaze_symbolize_elf_file_offsets(
1196 symbolizer: *mut blaze_symbolizer,
1197 src: *const blaze_symbolize_src_elf,
1198 file_offsets: *const Addr,
1199 file_offset_cnt: usize,
1200) -> *const blaze_syms {
1201 if !input_zeroed!(src, blaze_symbolize_src_elf) {
1202 let () = set_last_err(blaze_err::INVALID_INPUT);
1203 return ptr::null()
1204 }
1205 let src = input_sanitize!(src, blaze_symbolize_src_elf);
1206 let src = Source::from(Elf::from(src));
1207
1208 unsafe {
1209 blaze_symbolize_impl(
1210 symbolizer,
1211 src,
1212 Input::FileOffset(file_offsets),
1213 file_offset_cnt,
1214 )
1215 }
1216}
1217
1218
1219#[no_mangle]
1235pub unsafe extern "C" fn blaze_symbolize_gsym_data_virt_offsets(
1236 symbolizer: *mut blaze_symbolizer,
1237 src: *const blaze_symbolize_src_gsym_data,
1238 virt_offsets: *const Addr,
1239 virt_offset_cnt: usize,
1240) -> *const blaze_syms {
1241 if !input_zeroed!(src, blaze_symbolize_src_gsym_data) {
1242 let () = set_last_err(blaze_err::INVALID_INPUT);
1243 return ptr::null()
1244 }
1245 let src = input_sanitize!(src, blaze_symbolize_src_gsym_data);
1246 let src = Source::from(GsymData::from(src));
1247 unsafe {
1248 blaze_symbolize_impl(
1249 symbolizer,
1250 src,
1251 Input::VirtOffset(virt_offsets),
1252 virt_offset_cnt,
1253 )
1254 }
1255}
1256
1257
1258#[no_mangle]
1274pub unsafe extern "C" fn blaze_symbolize_gsym_file_virt_offsets(
1275 symbolizer: *mut blaze_symbolizer,
1276 src: *const blaze_symbolize_src_gsym_file,
1277 virt_offsets: *const Addr,
1278 virt_offset_cnt: usize,
1279) -> *const blaze_syms {
1280 if !input_zeroed!(src, blaze_symbolize_src_gsym_file) {
1281 let () = set_last_err(blaze_err::INVALID_INPUT);
1282 return ptr::null()
1283 }
1284 let src = input_sanitize!(src, blaze_symbolize_src_gsym_file);
1285 let src = Source::from(GsymFile::from(src));
1286
1287 unsafe {
1288 blaze_symbolize_impl(
1289 symbolizer,
1290 src,
1291 Input::VirtOffset(virt_offsets),
1292 virt_offset_cnt,
1293 )
1294 }
1295}
1296
1297
1298#[no_mangle]
1304pub unsafe extern "C" fn blaze_syms_free(syms: *const blaze_syms) {
1305 if syms.is_null() {
1306 return
1307 }
1308
1309 let raw_buf_with_sz = unsafe { (syms as *mut u8).offset(-(mem::size_of::<u64>() as isize)) };
1310 let sz = unsafe { *(raw_buf_with_sz as *mut u64) } as usize;
1311 unsafe { dealloc(raw_buf_with_sz, Layout::from_size_align(sz, 8).unwrap()) };
1312}
1313
1314
1315#[cfg(test)]
1316mod tests {
1317 use super::*;
1318
1319 use std::borrow::Cow;
1320 use std::ffi::CString;
1321 use std::fs::copy;
1322 use std::fs::read as read_file;
1323 use std::fs::remove_file;
1324 use std::hint::black_box;
1325 use std::io::Error;
1326 use std::os::unix::ffi::OsStringExt as _;
1327 use std::slice;
1328
1329 use blazesym::inspect;
1330 use blazesym::normalize;
1331 use blazesym::symbolize::Reason;
1332 use blazesym::Pid;
1333
1334 use tempfile::tempdir;
1335
1336 use test_tag::tag;
1337
1338 use crate::blaze_err_last;
1339
1340
1341 #[tag(miri)]
1343 #[test]
1344 #[cfg(target_pointer_width = "64")]
1345 fn type_sizes() {
1346 assert_eq!(mem::size_of::<blaze_cache_src_elf>(), 32);
1347 assert_eq!(mem::size_of::<blaze_cache_src_process>(), 32);
1348 assert_eq!(mem::size_of::<blaze_symbolize_src_elf>(), 40);
1349 assert_eq!(mem::size_of::<blaze_symbolize_src_kernel>(), 48);
1350 assert_eq!(mem::size_of::<blaze_symbolize_src_process>(), 32);
1351 assert_eq!(mem::size_of::<blaze_symbolize_src_gsym_data>(), 40);
1352 assert_eq!(mem::size_of::<blaze_symbolize_src_gsym_file>(), 32);
1353 assert_eq!(mem::size_of::<blaze_symbolizer_opts>(), 48);
1354 assert_eq!(mem::size_of::<blaze_symbolize_code_info>(), 32);
1355 assert_eq!(mem::size_of::<blaze_symbolize_inlined_fn>(), 48);
1356 assert_eq!(mem::size_of::<blaze_sym>(), 104);
1357 }
1358
1359 #[tag(miri)]
1361 #[test]
1362 fn debug_repr() {
1363 let elf = blaze_symbolize_src_elf {
1364 type_size: 24,
1365 ..Default::default()
1366 };
1367 assert_eq!(
1368 format!("{elf:?}"),
1369 "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] }"
1370 );
1371
1372 let kernel = blaze_symbolize_src_kernel {
1373 type_size: 32,
1374 debug_syms: true,
1375 ..Default::default()
1376 };
1377 assert_eq!(
1378 format!("{kernel:?}"),
1379 "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] }"
1380 );
1381
1382 let process = blaze_symbolize_src_process {
1383 type_size: 16,
1384 pid: 1337,
1385 debug_syms: true,
1386 ..Default::default()
1387 };
1388 assert_eq!(
1389 format!("{process:?}"),
1390 "blaze_symbolize_src_process { type_size: 16, pid: 1337, debug_syms: true, perf_map: false, no_map_files: false, reserved: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }"
1391 );
1392
1393 let gsym_data = blaze_symbolize_src_gsym_data {
1394 type_size: 24,
1395 data: ptr::null(),
1396 data_len: 0,
1397 reserved: [0; 16],
1398 };
1399 assert_eq!(
1400 format!("{gsym_data:?}"),
1401 "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] }"
1402 );
1403
1404 let gsym_file = blaze_symbolize_src_gsym_file {
1405 type_size: 16,
1406 path: ptr::null(),
1407 reserved: [0; 16],
1408 };
1409 assert_eq!(
1410 format!("{gsym_file:?}"),
1411 "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] }"
1412 );
1413
1414 let sym = blaze_sym {
1415 name: ptr::null(),
1416 module: ptr::null(),
1417 addr: 0x1337,
1418 offset: 24,
1419 size: 16,
1420 code_info: blaze_symbolize_code_info {
1421 dir: ptr::null(),
1422 file: ptr::null(),
1423 line: 42,
1424 column: 1,
1425 reserved: [0; 10],
1426 },
1427 inlined_cnt: 0,
1428 inlined: ptr::null(),
1429 reason: blaze_symbolize_reason::UNSUPPORTED,
1430 reserved: [0; 15],
1431 };
1432 assert_eq!(
1433 format!("{sym:?}"),
1434 "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] }"
1435 );
1436
1437 let inlined = blaze_symbolize_inlined_fn {
1438 name: ptr::null(),
1439 code_info: blaze_symbolize_code_info {
1440 dir: ptr::null(),
1441 file: ptr::null(),
1442 line: 42,
1443 column: 1,
1444 reserved: [0; 10],
1445 },
1446 reserved: [0; 8],
1447 };
1448 assert_eq!(
1449 format!("{inlined:?}"),
1450 "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] }"
1451 );
1452
1453 let syms = blaze_syms { cnt: 0, syms: [] };
1454 assert_eq!(format!("{syms:?}"), "blaze_syms { cnt: 0, syms: [] }");
1455
1456 let opts = blaze_symbolizer_opts {
1457 type_size: 16,
1458 demangle: true,
1459 ..Default::default()
1460 };
1461 assert_eq!(
1462 format!("{opts:?}"),
1463 "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] }"
1464 );
1465 }
1466
1467 #[tag(miri)]
1469 #[test]
1470 fn reason_stringification() {
1471 let data = [
1472 (Reason::Unmapped, blaze_symbolize_reason::UNMAPPED),
1473 (
1474 Reason::InvalidFileOffset,
1475 blaze_symbolize_reason::INVALID_FILE_OFFSET,
1476 ),
1477 (
1478 Reason::MissingComponent,
1479 blaze_symbolize_reason::MISSING_COMPONENT,
1480 ),
1481 (Reason::MissingSyms, blaze_symbolize_reason::MISSING_SYMS),
1482 (Reason::Unsupported, blaze_symbolize_reason::UNSUPPORTED),
1483 (Reason::UnknownAddr, blaze_symbolize_reason::UNKNOWN_ADDR),
1484 ];
1485
1486 for (reason, expected) in data {
1487 assert_eq!(blaze_symbolize_reason::from(reason), expected);
1488 let cstr = unsafe { CStr::from_ptr(blaze_symbolize_reason_str(expected)) };
1489 let expected = CStr::from_bytes_with_nul(reason.as_bytes()).unwrap();
1490 assert_eq!(cstr, expected);
1491 }
1492 }
1493
1494
1495 #[tag(miri)]
1498 #[test]
1499 fn kernel_conversion() {
1500 let kernel = blaze_symbolize_src_kernel::default();
1501 let kernel = Kernel::from(kernel);
1502 assert_eq!(kernel.kallsyms, MaybeDefault::Default);
1503 assert_eq!(kernel.vmlinux, MaybeDefault::Default);
1504
1505 let kernel = blaze_symbolize_src_kernel {
1506 kallsyms: b"\0" as *const _ as *const c_char,
1507 vmlinux: b"\0" as *const _ as *const c_char,
1508 ..Default::default()
1509 };
1510 let kernel = Kernel::from(kernel);
1511 assert_eq!(kernel.kallsyms, MaybeDefault::None);
1512 assert_eq!(kernel.vmlinux, MaybeDefault::None);
1513
1514 let kernel = blaze_symbolize_src_kernel {
1515 kallsyms: b"/proc/kallsyms\0" as *const _ as *const c_char,
1516 vmlinux: b"/boot/vmlinux\0" as *const _ as *const c_char,
1517 debug_syms: false,
1518 ..Default::default()
1519 };
1520
1521 let kernel = Kernel::from(kernel);
1522 assert_eq!(
1523 kernel.kallsyms,
1524 MaybeDefault::Some(PathBuf::from("/proc/kallsyms"))
1525 );
1526 assert_eq!(
1527 kernel.vmlinux,
1528 MaybeDefault::Some(PathBuf::from("/boot/vmlinux"))
1529 );
1530 }
1531
1532 #[tag(miri)]
1535 #[test]
1536 fn cache_process_conversion() {
1537 let process = blaze_cache_src_process {
1538 pid: 42,
1539 cache_vmas: false,
1540 ..Default::default()
1541 };
1542 let process = cache::Process::from(process);
1543 assert_eq!(process.pid, Pid::from(42))
1544 }
1545
1546 #[tag(miri)]
1548 #[test]
1549 fn symbol_conversion() {
1550 fn touch<X: Clone>(x: &X) {
1551 let x = x.clone();
1552 let _x = black_box(x);
1553 }
1554
1555 fn touch_cstr(s: *const c_char) {
1556 if !s.is_null() {
1557 let s = unsafe { CStr::from_ptr(s) }.to_bytes();
1558 let _x = black_box(s);
1559 }
1560 }
1561
1562 fn touch_code_info(code_info: &blaze_symbolize_code_info) {
1563 let blaze_symbolize_code_info {
1564 dir,
1565 file,
1566 line,
1567 column,
1568 reserved: _,
1569 } = code_info;
1570
1571 let _x = touch_cstr(*dir);
1572 let _x = touch_cstr(*file);
1573 let _x = touch(line);
1574 let _x = touch(column);
1575 }
1576
1577 fn touch_syms(syms: *const blaze_syms) {
1579 let syms = unsafe { &*syms };
1580 for i in 0..syms.cnt {
1581 let sym = unsafe { &*syms.syms.as_slice().as_ptr().add(i) };
1582 let blaze_sym {
1583 name,
1584 module,
1585 addr,
1586 offset,
1587 size,
1588 code_info,
1589 inlined_cnt,
1590 inlined,
1591 reason,
1592 reserved: _,
1593 } = sym;
1594
1595 let () = touch_cstr(*name);
1596 let () = touch_cstr(*module);
1597 let _x = touch(addr);
1598 let _x = touch(offset);
1599 let _x = touch(size);
1600 let () = touch_code_info(code_info);
1601
1602 for j in 0..*inlined_cnt {
1603 let inlined_fn = unsafe { &*inlined.add(j) };
1604 let blaze_symbolize_inlined_fn {
1605 name,
1606 code_info,
1607 reserved: _,
1608 } = inlined_fn;
1609 let () = touch_cstr(*name);
1610 let () = touch_code_info(code_info);
1611 }
1612 let () = touch(reason);
1613 }
1614 }
1615
1616 let results = vec![];
1618 let syms = convert_symbolizedresults_to_c(results);
1619 assert!(!syms.is_null());
1620
1621 let () = touch_syms(syms);
1622 let () = unsafe { blaze_syms_free(syms) };
1623
1624 let results = vec![Symbolized::Sym(Sym {
1626 name: "test".into(),
1627 module: Some(Cow::from(OsStr::new("module"))),
1628 addr: 0x1337,
1629 offset: 0x1338,
1630 size: Some(42),
1631 code_info: Some(CodeInfo {
1632 dir: None,
1633 file: OsStr::new("a-file").into(),
1634 line: Some(42),
1635 column: Some(43),
1636 _non_exhaustive: (),
1637 }),
1638 inlined: vec![InlinedFn {
1639 name: "inlined_fn".into(),
1640 code_info: Some(CodeInfo {
1641 dir: Some(Path::new("/some/dir").into()),
1642 file: OsStr::new("another-file").into(),
1643 line: Some(42),
1644 column: Some(43),
1645 _non_exhaustive: (),
1646 }),
1647 _non_exhaustive: (),
1648 }]
1649 .into_boxed_slice(),
1650 _non_exhaustive: (),
1651 })];
1652 let syms = convert_symbolizedresults_to_c(results);
1653 assert!(!syms.is_null());
1654 let () = touch_syms(syms);
1655 let () = unsafe { blaze_syms_free(syms) };
1656
1657 let results = vec![
1659 Symbolized::Unknown(Reason::UnknownAddr),
1660 Symbolized::Sym(Sym {
1661 name: "test".into(),
1662 module: Some(Cow::from(OsStr::new("module"))),
1663 addr: 0x1337,
1664 offset: 0x1338,
1665 size: None,
1666 code_info: None,
1667 inlined: vec![InlinedFn {
1668 name: "inlined_fn".into(),
1669 code_info: None,
1670 _non_exhaustive: (),
1671 }]
1672 .into_boxed_slice(),
1673 _non_exhaustive: (),
1674 }),
1675 Symbolized::Unknown(Reason::InvalidFileOffset),
1676 ];
1677 let syms = convert_symbolizedresults_to_c(results);
1678 assert!(!syms.is_null());
1679 let () = touch_syms(syms);
1680 let () = unsafe { blaze_syms_free(syms) };
1681 }
1682
1683 #[tag(miri)]
1685 #[test]
1686 fn symbolizer_creation() {
1687 let symbolizer = blaze_symbolizer_new();
1688 let () = unsafe { blaze_symbolizer_free(symbolizer) };
1689 }
1690
1691 #[tag(miri)]
1694 #[test]
1695 fn symbolizer_creation_with_opts() {
1696 let opts = blaze_symbolizer_opts {
1697 demangle: true,
1698 ..Default::default()
1699 };
1700
1701 let symbolizer = unsafe { blaze_symbolizer_new_opts(&opts) };
1702 let () = unsafe { blaze_symbolizer_free(symbolizer) };
1703 }
1704
1705 #[test]
1708 fn symbolize_elf_dwarf_gsym() {
1709 fn test<F>(symbolize: F, has_code_info: bool)
1710 where
1711 F: FnOnce(*mut blaze_symbolizer, *const Addr, usize) -> *const blaze_syms,
1712 {
1713 let symbolizer = blaze_symbolizer_new();
1714 let addrs = [0x2000200];
1715 let result = symbolize(symbolizer, addrs.as_ptr(), addrs.len());
1716
1717 assert!(!result.is_null());
1718
1719 let result = unsafe { &*result };
1720 assert_eq!(result.cnt, 1);
1721 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
1722 let sym = &syms[0];
1723 assert_eq!(
1724 unsafe { CStr::from_ptr(sym.name) },
1725 CStr::from_bytes_with_nul(b"factorial\0").unwrap()
1726 );
1727 assert_eq!(sym.reason, blaze_symbolize_reason::SUCCESS);
1728 assert_eq!(sym.addr, 0x2000200);
1729 assert_eq!(sym.offset, 0);
1730 assert!(sym.size > 0);
1731
1732 if has_code_info {
1733 assert!(!sym.code_info.dir.is_null());
1734 assert!(!sym.code_info.file.is_null());
1735 assert_eq!(
1736 unsafe { CStr::from_ptr(sym.code_info.file) },
1737 CStr::from_bytes_with_nul(b"test-stable-addrs.c\0").unwrap()
1738 );
1739 assert_eq!(sym.code_info.line, 10);
1740 } else {
1741 assert!(sym.code_info.dir.is_null());
1742 assert!(sym.code_info.file.is_null());
1743 assert_eq!(sym.code_info.line, 0);
1744 }
1745
1746 let () = unsafe { blaze_syms_free(result) };
1747 let () = unsafe { blaze_symbolizer_free(symbolizer) };
1748 }
1749
1750 let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
1751 .join("..")
1752 .join("data")
1753 .join("test-stable-addrs-no-dwarf.bin");
1754 let path_c = CString::new(path.to_str().unwrap()).unwrap();
1755 let elf_src = blaze_symbolize_src_elf {
1756 path: path_c.as_ptr(),
1757 debug_syms: true,
1758 ..Default::default()
1759 };
1760
1761 let symbolize = |symbolizer, addrs, addr_cnt| unsafe {
1762 blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs, addr_cnt)
1763 };
1764 test(symbolize, false);
1765
1766 let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
1767 .join("..")
1768 .join("data")
1769 .join("test-stable-addrs-stripped-elf-with-dwarf.bin");
1770 let path_c = CString::new(path.to_str().unwrap()).unwrap();
1771 let elf_src = blaze_symbolize_src_elf {
1772 path: path_c.as_ptr(),
1773 debug_syms: true,
1774 ..Default::default()
1775 };
1776
1777 let symbolize = |symbolizer, addrs, addr_cnt| unsafe {
1778 blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs, addr_cnt)
1779 };
1780 test(symbolize, true);
1781
1782 let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
1783 .join("..")
1784 .join("data")
1785 .join("test-stable-addrs.gsym");
1786 let path_c = CString::new(path.to_str().unwrap()).unwrap();
1787 let gsym_src = blaze_symbolize_src_gsym_file {
1788 path: path_c.as_ptr(),
1789 ..Default::default()
1790 };
1791
1792 let symbolize = |symbolizer, addrs, addr_cnt| unsafe {
1793 blaze_symbolize_gsym_file_virt_offsets(symbolizer, &gsym_src, addrs, addr_cnt)
1794 };
1795 test(symbolize, true);
1796
1797 let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
1798 .join("..")
1799 .join("data")
1800 .join("test-stable-addrs.gsym");
1801 let data = read_file(path).unwrap();
1802 let gsym_src = blaze_symbolize_src_gsym_data {
1803 data: data.as_ptr(),
1804 data_len: data.len(),
1805 ..Default::default()
1806 };
1807
1808 let symbolize = |symbolizer, addrs, addr_cnt| unsafe {
1809 blaze_symbolize_gsym_data_virt_offsets(symbolizer, &gsym_src, addrs, addr_cnt)
1810 };
1811 test(symbolize, true);
1812 }
1813
1814
1815 #[test]
1817 #[cfg_attr(
1818 not(target_pointer_width = "64"),
1819 ignore = "loads 64 bit shared object"
1820 )]
1821 fn symbolize_elf_file_offset() {
1822 let test_so = Path::new(&env!("CARGO_MANIFEST_DIR"))
1823 .join("..")
1824 .join("data")
1825 .join("libtest-so.so")
1826 .canonicalize()
1827 .unwrap();
1828 let so_cstr = CString::new(test_so.clone().into_os_string().into_vec()).unwrap();
1829 let handle = unsafe { libc::dlopen(so_cstr.as_ptr(), libc::RTLD_NOW) };
1830 assert!(!handle.is_null());
1831
1832 let the_answer_addr = unsafe { libc::dlsym(handle, "the_answer\0".as_ptr().cast()) };
1833 assert!(!the_answer_addr.is_null());
1834
1835 let normalizer = normalize::Normalizer::new();
1836 let normalized = normalizer
1837 .normalize_user_addrs(Pid::Slf, [the_answer_addr as Addr].as_slice())
1838 .unwrap();
1839 assert_eq!(normalized.outputs.len(), 1);
1840 assert_eq!(normalized.meta.len(), 1);
1841
1842 let rc = unsafe { libc::dlclose(handle) };
1843 assert_eq!(rc, 0, "{}", Error::last_os_error());
1844
1845 let output = normalized.outputs[0];
1846 let meta = &normalized.meta[output.1];
1847 assert_eq!(meta.as_elf().unwrap().path, test_so);
1848
1849 let symbolizer = blaze_symbolizer_new();
1850 let elf_src = blaze_symbolize_src_elf {
1851 path: so_cstr.as_ptr(),
1852 ..Default::default()
1853 };
1854 let offsets = [output.0];
1855 let result = unsafe {
1856 blaze_symbolize_elf_file_offsets(
1857 symbolizer,
1858 &elf_src,
1859 offsets.as_slice().as_ptr(),
1860 offsets.len(),
1861 )
1862 };
1863 assert!(!result.is_null());
1864
1865 let result = unsafe { &*result };
1866 assert_eq!(result.cnt, 1);
1867
1868 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
1869 let sym = &syms[0];
1870 assert!(!sym.name.is_null());
1871 assert!(!sym.module.is_null());
1872 assert!(sym.size > 0);
1873 assert_eq!(
1874 unsafe { CStr::from_ptr(sym.name) },
1875 CStr::from_bytes_with_nul(b"the_answer\0").unwrap()
1876 );
1877
1878 let () = unsafe { blaze_syms_free(result) };
1879 let () = unsafe { blaze_symbolizer_free(symbolizer) };
1880 }
1881
1882 #[tag(other_os)]
1885 #[test]
1886 fn symbolize_elf_cached() {
1887 let dir = tempdir().unwrap();
1888 let path__ = Path::new(&env!("CARGO_MANIFEST_DIR"))
1889 .join("..")
1890 .join("data")
1891 .join("test-stable-addrs.bin");
1892 let path = dir.path().join("test-stable-addrs-temporary.bin");
1893 let _count = copy(&path__, &path).unwrap();
1894
1895 let symbolizer = blaze_symbolizer_new();
1896
1897 let path_c = CString::new(path.to_str().unwrap()).unwrap();
1898 let cache = blaze_cache_src_elf {
1899 path: path_c.as_ptr(),
1900 ..Default::default()
1901 };
1902 let () = unsafe { blaze_symbolize_cache_elf(symbolizer, &cache) };
1903 assert_eq!(blaze_err_last(), blaze_err::OK);
1904
1905 let () = remove_file(&path).unwrap();
1906
1907 let src = blaze_symbolize_src_elf {
1908 path: path_c.as_ptr(),
1909 debug_syms: false,
1910 ..Default::default()
1911 };
1912 let addrs = [0x2000200];
1913 let result = unsafe {
1914 blaze_symbolize_elf_virt_offsets(symbolizer, &src, addrs.as_ptr(), addrs.len())
1915 };
1916 assert!(!result.is_null());
1917
1918 let result = unsafe { &*result };
1919 assert_eq!(result.cnt, 1);
1920 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
1921 let sym = &syms[0];
1922
1923 assert_eq!(
1924 unsafe { CStr::from_ptr(sym.name) },
1925 CStr::from_bytes_with_nul(b"factorial\0").unwrap()
1926 );
1927 assert_eq!(sym.reason, blaze_symbolize_reason::SUCCESS);
1928 assert_eq!(sym.addr, 0x2000200);
1929
1930 let () = unsafe { blaze_syms_free(result) };
1931 let () = unsafe { blaze_symbolizer_free(symbolizer) };
1932 }
1933
1934 #[test]
1937 fn symbolize_dwarf_demangle() {
1938 fn test(path: &Path, addr: Addr) -> Result<(), ()> {
1939 let opts = blaze_symbolizer_opts {
1940 code_info: true,
1941 inlined_fns: true,
1942 ..Default::default()
1943 };
1944
1945 let path_c = CString::new(path.to_str().unwrap()).unwrap();
1946 let elf_src = blaze_symbolize_src_elf {
1947 path: path_c.as_ptr(),
1948 debug_syms: true,
1949 ..Default::default()
1950 };
1951
1952 let symbolizer = unsafe { blaze_symbolizer_new_opts(&opts) };
1953 let addrs = [addr];
1954 let result = unsafe {
1955 blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs.as_ptr(), addrs.len())
1956 };
1957 assert!(!result.is_null());
1958
1959 let result = unsafe { &*result };
1960 assert_eq!(result.cnt, 1);
1961 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
1962 let sym = &syms[0];
1963 let name = unsafe { CStr::from_ptr(sym.name) };
1964 assert!(
1965 name.to_str().unwrap().contains("test13test_function"),
1966 "{:?}",
1967 name
1968 );
1969
1970 if sym.inlined_cnt == 0 {
1971 let () = unsafe { blaze_syms_free(result) };
1972 let () = unsafe { blaze_symbolizer_free(symbolizer) };
1973 return Err(())
1974 }
1975
1976 assert_eq!(sym.inlined_cnt, 1);
1977 let name = unsafe { CStr::from_ptr((*sym.inlined).name) };
1978 assert!(
1979 name.to_str().unwrap().contains("test12inlined_call"),
1980 "{:?}",
1981 name
1982 );
1983
1984 let () = unsafe { blaze_syms_free(result) };
1985 let () = unsafe { blaze_symbolizer_free(symbolizer) };
1986
1987 let opts = blaze_symbolizer_opts {
1989 code_info: true,
1990 inlined_fns: true,
1991 demangle: true,
1992 ..Default::default()
1993 };
1994
1995 let symbolizer = unsafe { blaze_symbolizer_new_opts(&opts) };
1996 let addrs = [addr];
1997 let result = unsafe {
1998 blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs.as_ptr(), addrs.len())
1999 };
2000 assert!(!result.is_null());
2001
2002 let result = unsafe { &*result };
2003 assert_eq!(result.cnt, 1);
2004 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
2005 let sym = &syms[0];
2006 assert_eq!(
2007 unsafe { CStr::from_ptr(sym.name) },
2008 CStr::from_bytes_with_nul(b"test::test_function\0").unwrap()
2009 );
2010 assert_eq!(unsafe { CStr::from_ptr(sym.module) }, &*path_c);
2011
2012 assert_eq!(sym.inlined_cnt, 1);
2013 assert_eq!(
2014 unsafe { CStr::from_ptr((*sym.inlined).name) },
2015 CStr::from_bytes_with_nul(b"test::inlined_call\0").unwrap()
2016 );
2017
2018 let () = unsafe { blaze_syms_free(result) };
2019 let () = unsafe { blaze_symbolizer_free(symbolizer) };
2020 Ok(())
2021 }
2022
2023 let test_dwarf = Path::new(&env!("CARGO_MANIFEST_DIR"))
2024 .join("..")
2025 .join("data")
2026 .join("test-rs.bin");
2027 let elf = inspect::source::Elf::new(&test_dwarf);
2028 let src = inspect::source::Source::Elf(elf);
2029
2030 let inspector = inspect::Inspector::new();
2031 let results = inspector
2032 .lookup(&src, &["_RNvCs69hjMPjVIJK_4test13test_function"])
2033 .unwrap()
2034 .into_iter()
2035 .flatten()
2036 .collect::<Vec<_>>();
2037 assert!(!results.is_empty());
2038
2039 let addr = results[0].addr;
2040 let src = Source::Elf(Elf::new(&test_dwarf));
2041 let symbolizer = Symbolizer::builder().enable_demangling(false).build();
2042 let result = symbolizer
2043 .symbolize_single(&src, Input::VirtOffset(addr))
2044 .unwrap()
2045 .into_sym()
2046 .unwrap();
2047
2048 let addr = result.addr;
2049 let size = result.size.unwrap() as u64;
2050 for inst_addr in addr..addr + size {
2051 if test(&test_dwarf, inst_addr).is_ok() {
2052 return
2053 }
2054 }
2055
2056 panic!("failed to find inlined function call");
2057 }
2058
2059 #[test]
2061 fn symbolize_in_process() {
2062 let process_src = blaze_symbolize_src_process {
2063 pid: 0,
2064 debug_syms: true,
2065 perf_map: true,
2066 ..Default::default()
2067 };
2068
2069 let symbolizer = blaze_symbolizer_new();
2070 let addrs = [blaze_symbolizer_new as Addr];
2071 let result = unsafe {
2072 blaze_symbolize_process_abs_addrs(symbolizer, &process_src, addrs.as_ptr(), addrs.len())
2073 };
2074
2075 assert!(!result.is_null());
2076
2077 let result = unsafe { &*result };
2078 assert_eq!(result.cnt, 1);
2079 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
2080 let sym = &syms[0];
2081 assert_eq!(
2082 unsafe { CStr::from_ptr(sym.name) },
2083 CStr::from_bytes_with_nul(b"blaze_symbolizer_new\0").unwrap()
2084 );
2085
2086 let () = unsafe { blaze_syms_free(result) };
2087 let () = unsafe { blaze_symbolizer_free(symbolizer) };
2088 }
2089
2090 #[test]
2093 fn symbolize_in_process_cached() {
2094 let symbolizer = blaze_symbolizer_new();
2095 let cache = blaze_cache_src_process {
2096 pid: 0,
2097 cache_vmas: true,
2098 ..Default::default()
2099 };
2100 let () = unsafe { blaze_symbolize_cache_process(symbolizer, &cache) };
2101 assert_eq!(blaze_err_last(), blaze_err::OK);
2102
2103 let src = blaze_symbolize_src_process {
2104 pid: 0,
2105 debug_syms: true,
2106 perf_map: true,
2107 ..Default::default()
2108 };
2109 let addrs = [blaze_symbolizer_new as Addr];
2110 let result = unsafe {
2111 blaze_symbolize_process_abs_addrs(symbolizer, &src, addrs.as_ptr(), addrs.len())
2112 };
2113
2114 assert!(!result.is_null());
2115
2116 let result = unsafe { &*result };
2117 assert_eq!(result.cnt, 1);
2118 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
2119 let sym = &syms[0];
2120 assert_eq!(
2121 unsafe { CStr::from_ptr(sym.name) },
2122 CStr::from_bytes_with_nul(b"blaze_symbolizer_new\0").unwrap()
2123 );
2124
2125 let () = unsafe { blaze_syms_free(result) };
2126 let () = unsafe { blaze_symbolizer_free(symbolizer) };
2127 }
2128
2129 #[test]
2132 fn symbolize_in_kernel() {
2133 let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
2134 .join("..")
2135 .join("data")
2136 .join("kallsyms");
2137 let path_c = CString::new(path.to_str().unwrap()).unwrap();
2138 let src = blaze_symbolize_src_kernel {
2139 kallsyms: path_c.as_ptr(),
2140 vmlinux: b"\0" as *const _ as *const c_char,
2141 ..Default::default()
2142 };
2143
2144 let symbolizer = blaze_symbolizer_new();
2145 let addrs = [0xc080a470];
2146 let result = unsafe {
2147 blaze_symbolize_kernel_abs_addrs(symbolizer, &src, addrs.as_ptr(), addrs.len())
2148 };
2149
2150 assert!(!result.is_null());
2151
2152 let result = unsafe { &*result };
2153 assert_eq!(result.cnt, 1);
2154 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
2155 let sym = &syms[0];
2156 assert_eq!(
2157 unsafe { CStr::from_ptr(sym.name) },
2158 CStr::from_bytes_with_nul(b"init_task\0").unwrap()
2159 );
2160 assert_eq!(sym.module, ptr::null_mut());
2161
2162 let () = unsafe { blaze_syms_free(result) };
2163 let () = unsafe { blaze_symbolizer_free(symbolizer) };
2164 }
2165
2166 #[test]
2169 fn symbolize_elf_non_existent() {
2170 let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
2171 .join("..")
2172 .join("data")
2173 .join("does-not-actually-exist.bin");
2174 let path_c = CString::new(path.to_str().unwrap()).unwrap();
2175 let elf_src = blaze_symbolize_src_elf {
2176 path: path_c.as_ptr(),
2177 debug_syms: true,
2178 ..Default::default()
2179 };
2180
2181 let symbolizer = blaze_symbolizer_new();
2182 let addrs = [blaze_symbolizer_new as Addr];
2183 let result = unsafe {
2184 blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs.as_ptr(), addrs.len())
2185 };
2186 assert!(result.is_null());
2187 assert_eq!(blaze_err_last(), blaze_err::NOT_FOUND);
2188
2189 let () = unsafe { blaze_syms_free(result) };
2190 let () = unsafe { blaze_symbolizer_free(symbolizer) };
2191 }
2192
2193 #[test]
2195 fn symbolize_no_debug_dirs() {
2196 let dir = tempdir().unwrap();
2197 let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
2198 .join("..")
2199 .join("data")
2200 .join("test-stable-addrs-stripped-with-link.bin");
2201 let dst = dir.path().join("test-stable-addrs-stripped-with-link.bin");
2202 let _count = copy(path, &dst).unwrap();
2203
2204 let debug_dirs = [];
2205 let opts = blaze_symbolizer_opts {
2206 debug_dirs: debug_dirs.as_ptr(),
2207 debug_dirs_len: debug_dirs.len(),
2208 code_info: true,
2209 inlined_fns: true,
2210 demangle: true,
2211 ..Default::default()
2212 };
2213 let symbolizer = unsafe { blaze_symbolizer_new_opts(&opts) };
2214
2215 let path_c = CString::new(dst.to_str().unwrap()).unwrap();
2216 let elf_src = blaze_symbolize_src_elf {
2217 path: path_c.as_ptr(),
2218 debug_syms: true,
2219 ..Default::default()
2220 };
2221 let addrs = [0x2000200];
2222 let result = unsafe {
2223 blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs.as_ptr(), addrs.len())
2224 };
2225 assert!(!result.is_null());
2226
2227 let result = unsafe { &*result };
2228 assert_eq!(result.cnt, 1);
2229 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
2230 let sym = &syms[0];
2231 assert_eq!(sym.name, ptr::null());
2234 assert_eq!(sym.module, ptr::null());
2235 assert_eq!(sym.reason, blaze_symbolize_reason::MISSING_SYMS);
2236
2237 let () = unsafe { blaze_syms_free(result) };
2238 let () = unsafe { blaze_symbolizer_free(symbolizer) };
2239 }
2240
2241 #[test]
2243 fn symbolize_configurable_debug_dirs() {
2244 let debug_dir1 = tempdir().unwrap();
2245 let debug_dir1_c = CString::new(debug_dir1.path().to_str().unwrap()).unwrap();
2246 let debug_dir2 = tempdir().unwrap();
2247 let debug_dir2_c = CString::new(debug_dir2.path().to_str().unwrap()).unwrap();
2248
2249 let src = Path::new(&env!("CARGO_MANIFEST_DIR"))
2250 .join("..")
2251 .join("data")
2252 .join("test-stable-addrs-dwarf-only.dbg");
2253 let dst = debug_dir2.path().join("test-stable-addrs-dwarf-only.dbg");
2254 let _count = copy(src, dst).unwrap();
2255
2256 let debug_dirs = [debug_dir1_c.as_ptr(), debug_dir2_c.as_ptr()];
2257 let opts = blaze_symbolizer_opts {
2258 debug_dirs: debug_dirs.as_ptr(),
2259 debug_dirs_len: debug_dirs.len(),
2260 code_info: true,
2261 inlined_fns: true,
2262 demangle: true,
2263 ..Default::default()
2264 };
2265 let symbolizer = unsafe { blaze_symbolizer_new_opts(&opts) };
2266
2267 let path = Path::new(&env!("CARGO_MANIFEST_DIR"))
2268 .join("..")
2269 .join("data")
2270 .join("test-stable-addrs-stripped-with-link.bin");
2271 let path_c = CString::new(path.to_str().unwrap()).unwrap();
2272 let elf_src = blaze_symbolize_src_elf {
2273 path: path_c.as_ptr(),
2274 debug_syms: true,
2275 ..Default::default()
2276 };
2277 let addrs = [0x2000200];
2278 let result = unsafe {
2279 blaze_symbolize_elf_virt_offsets(symbolizer, &elf_src, addrs.as_ptr(), addrs.len())
2280 };
2281 assert!(!result.is_null());
2282
2283 let result = unsafe { &*result };
2284 assert_eq!(result.cnt, 1);
2285 let syms = unsafe { slice::from_raw_parts(result.syms.as_ptr(), result.cnt) };
2286 let sym = &syms[0];
2287 let name = unsafe { CStr::from_ptr(sym.name) };
2288 assert_eq!(name.to_str().unwrap(), "factorial");
2289 let module = unsafe { CStr::from_ptr(sym.module) };
2290 assert!(
2291 module
2292 .to_str()
2293 .unwrap()
2294 .ends_with("test-stable-addrs-stripped-with-link.bin"),
2295 "{module:?}"
2296 );
2297
2298 let () = unsafe { blaze_syms_free(result) };
2299 let () = unsafe { blaze_symbolizer_free(symbolizer) };
2300 }
2301}